audioens.c revision 505c7a699305ccafcfecc1ab0e7d4a25e2bfd1c2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* This driver is used with the original Ensoniq AudioPCI97 card and many
* PCI based Sound Blaster cards by Creative Technologies. For example
*/
/*
* This file is part of Open Sound System
*
* Copyright (C) 4Front Technologies 1996-2008.
*
* This software is released under CDDL 1.0 source license.
* See the COPYING file included in the main directory of this source
* distribution for the license terms and conditions.
*/
#include "audioens.h"
/*
* The original OSS driver used a single duplex engine and a separate
* playback only engine. Instead, we expose three engines, one for input
* and two for output.
*/
/*
* Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
* garbled audio in some cases and setting the latency to higer values fixes it
* Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
*/
int audioens_latency = 0;
/*
* Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
* Values: 1=Enable 0=Disable Default: 0
*/
int audioens_spdif = 0;
/*
* Note: Latest devices can support SPDIF with AC3 pass thru.
* However, in order to do this, one of the two DMA engines must be
* dedicated to this, which would prevent the card from supporting 4
* channel audio. For now we don't bother with the AC3 pass through
* mode, and instead just focus on 4 channel support. In the future,
* this could be selectable via a property.
*/
#define ENSONIQ_VENDOR_ID 0x1274
#define CREATIVE_VENDOR_ID 0x1102
#define ECTIVA_VENDOR_ID 0x1102
#define ENSONIQ_ES1371 0x1371
#define ENSONIQ_ES5880 0x8001
#define ENSONIQ_ES5880A 0x8002
#define ENSONIQ_ES5880B 0x5880
#define ECTIVA_ES1938 0x8938
#define DEFRATE 48000
#define DEFINTS 75
#define DRVNAME "audioens"
typedef struct audioens_port
{
/* Audio parameters */
int speed;
int num;
#define PORT_DAC 0
#define PORT_ADC 1
int nchan;
unsigned fragfr;
unsigned nfrags;
unsigned nframes;
unsigned frameno;
struct audioens_dev *dev;
typedef struct audioens_dev
{
int pintrs;
int rintrs;
static ddi_device_acc_attr_t acc_attr = {
};
static ddi_device_acc_attr_t buf_attr = {
};
/*
* The hardware appears to be able to address up to 16-bits worth of longwords,
* giving a total address space of 256K. Note, however, that we will restrict
* this further when we do fragment and memory allocation. At its very highest
* clock rate (48 kHz) and sample size (16-bit stereo), and lowest interrupt
* rate (32 Hz), we only need 6000 bytes per fragment.
*
* So with an allocated buffer size of 64K, we can support at least 10 frags,
* which is more than enough. (The legacy Sun driver used only 2 fragments.)
*/
#define AUDIOENS_BUF_LEN (65536)
static ddi_dma_attr_t dma_attr = {
DMA_ATTR_VERSION, /* dma_attr_version */
0x0, /* dma_attr_addr_lo */
0xffffffffU, /* dma_attr_addr_hi */
0x3ffff, /* dma_attr_count_max */
0x8, /* dma_attr_align */
0x7f, /* dma_attr_burstsizes */
0x1, /* dma_attr_minxfer */
0x3ffff, /* dma_attr_maxxfer */
0x3ffff, /* dma_attr_seg */
0x1, /* dma_attr_sgllen */
0x1, /* dma_attr_granular */
0 /* dma_attr_flags */
};
static void audioens_init_hw(audioens_dev_t *);
static void audioens_init_port(audioens_port_t *);
static void audioens_start_port(audioens_port_t *);
static void audioens_stop_port(audioens_port_t *);
static void audioens_update_port(audioens_port_t *);
static uint16_t
{
int i, dtemp;
/* wait for WIP to go away saving the current state for later */
for (i = 0; i < 0x100UL; ++i) {
break;
}
/* now wait for the data (RDY) */
for (i = 0; i < 0x100UL; ++i) {
break;
}
return (dtemp & 0xffff);
}
static void
{
int i, dtemp;
/* wait for WIP to go away */
for (i = 0; i < 0x100UL; ++i) {
break;
}
}
static unsigned short
{
int i, dtemp;
/* wait for ready */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
/* assert a read request */
/* now wait for the data */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
return ((unsigned short) dtemp);
}
static void
{
int i, dtemp;
int writeval;
/* wait for ready */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
/* assert the write request */
}
static void
{
unsigned short N, truncM, truncStart;
if (base != SRC_ADC_BASE) {
/* freeze the channel */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
/* calculate new frequency and write it - preserve accum */
(unsigned short) freq >> 1);
/* un-freeze the channel */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
break;
} else {
/* derive oversample ratio */
N = rate / 3000U;
if (N == 15 || N == 13 || N == 11 || N == 9)
--N;
/* truncate the filter and write n/trunc_start */
if (rate >= 24000U) {
if (truncM > 239)
truncM = 239;
} else {
if (truncM > 119)
truncM = 119;
}
/* calculate new frequency and write it - preserve accum */
(unsigned short) freq >> 1);
}
}
static void
{
int i;
/* Clear all SRC RAM then init - keep SRC disabled until done */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
for (i = 0; i < 0x80; ++i)
SRCRegWrite(dev, (unsigned short) i, 0U);
/* default some rates */
/* now enable the whole deal */
for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
break;
}
}
static void
{
/* Select memory page */
}
static uint32_t
{
}
static uint_t
{
int stats;
int tmp;
unsigned char ackbits = 0;
/*
* NB: The old audioens didn't report spurious interrupts. On
* a system with shared interrupts (typical!) there will
* normally be lots of these (each time the "other" device
* interrupts).
*
* Also, because of the way the interrupt chain handling
* works, reporting of spurious interrupts is probably not
* terribly useful.
*
* However, we can count interrupts where the master interrupt
* bit is set but none of the ackbits that we are prepared to
* process is set. That is probably useful.
*/
return (DDI_INTR_UNCLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
/* DAC1 (synth) interrupt */
if (stats & CONC_STATUS_DAC1INT) {
}
}
/* DAC2 interrupt */
if (stats & CONC_STATUS_DAC2INT) {
}
/* ADC interrupt */
if (stats & CONC_STATUS_ADCINT) {
}
}
/* UART interrupt - we shouldn't get this! */
if (stats & CONC_STATUS_UARTINT) {
while (uart_stat & CONC_UART_RXRDY)
}
/* Ack the interrupt */
if (ackbits == 0) {
} else {
}
}
if (do_dac)
if (do_adc)
return (DDI_INTR_CLAIMED);
}
/*
* Audio routines
*/
static int
audioens_format(void *arg)
{
/* hardware can also do AUDIO_FORMAT_U8, but no need for it */
return (AUDIO_FORMAT_S16_LE);
}
static int
audioens_channels(void *arg)
{
}
static int
audioens_rate(void *arg)
{
}
static void
{
unsigned tmp;
return;
case PORT_DAC:
/* Set physical address of the DMA buffer */
/* Set DAC rate */
/* Configure the channel setup - SPDIF only uses front */
/* Set format */
/* Set the frame count */
/* Set # of frames between interrupts */
break;
case PORT_ADC:
/* Set physical address of the DMA buffer */
/* Set ADC rate */
/* Set format - for input we only support 16 bit input */
/* Set the frame count */
/* Set # of frames between interrupts */
break;
}
}
static int
{
int intrs;
} else {
}
/* interrupt at least at 25 Hz, and not more than 250 Hz */
return (0);
}
static void
{
case PORT_DAC:
break;
case PORT_ADC:
break;
}
}
}
static void
{
case PORT_DAC:
break;
case PORT_ADC:
break;
}
}
}
static int
audioens_start(void *arg)
{
}
return (0);
}
static void
audioens_stop(void *arg)
{
}
}
static void
{
int frameno, n;
case PORT_DAC:
break;
case PORT_ADC:
break;
}
/*
* Note that the current frame counter is in the high nybble.
*/
}
static uint64_t
audioens_count(void *arg)
{
}
return (val);
}
static void
audioens_close(void *arg)
{
}
static void
{
} else {
}
}
static size_t
audioens_qlen(void *arg)
{
}
static void
{
*incr = 2;
} else {
*incr = 2;
}
}
AUDIO_ENGINE_VERSION, /* version number */
};
void
{
int tmp;
/* Have a ES5880 so enable the codec manually */
tmp |= 0x20;
for (int i = 0; i < 2000; i++)
drv_usecwait(10);
}
#if 0
#endif
/*
* Turn on CODEC (UART and joystick left disabled)
*/
/* Perform AC97 codec warm reset */
drv_usecwait(200);
drv_usecwait(200);
/* XXX: enable SPDIF - PCM only for now */
if (audioens_spdif) {
/* enable SPDIF */
/* SPDIF out = data from DAC */
} else {
/* disable spdif out */
}
/* we want to run each channel independently */
}
}
static int
{
/*
* On this hardware, we want to disable the internal speaker by
* default, if it exists. (We don't have a speakerphone on any
* of these cards, and no SPARC hardware uses it either!)
*/
0);
/*
* Init mixer
*/
return (DDI_FAILURE);
return (DDI_FAILURE);
}
for (int i = 0; i <= PORT_MAX; i++) {
unsigned caps;
unsigned dmaflags;
unsigned ccnt;
switch (i) {
case PORT_DAC:
break;
case PORT_ADC:
break;
}
/*
* Allocate DMA resources.
*/
"port %d: dma handle allocation failed", i);
return (DDI_FAILURE);
}
"port %d: dma memory allocation failed", i);
return (DDI_FAILURE);
}
/* ensure that the buffer is zeroed out properly */
&c, &ccnt) != DDI_DMA_MAPPED) {
"port %d: dma binding failed", i);
return (DDI_FAILURE);
}
/*
* Allocate and configure audio engine.
*/
"port %d: audio_engine_alloc failed", i);
return (DDI_FAILURE);
}
}
/*
* Set up kstats for interrupt reporting.
*/
}
"unable to register with audio framework");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
{
int actual;
(actual != 1)) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
NULL) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
int i;
}
}
/* free up ports, including DMA resources for ports */
for (i = 0; i <= PORT_MAX; i++) {
}
}
}
}
}
}
int
{
const char *chip_name;
const char *chip_vers;
return (DDI_FAILURE);
}
device != ENSONIQ_ES5880B))
goto err_exit;
chip_name = "AudioPCI97";
chip_vers = "unknown";
switch (device) {
case ENSONIQ_ES1371:
chip_name = "AudioPCI97";
switch (revision) {
case 0x02:
case 0x09:
default:
chip_vers = "ES1371";
break;
case 0x04:
case 0x06:
case 0x08:
chip_vers = "ES1373";
break;
case 0x07:
chip_vers = "ES5880";
break;
}
break;
case ENSONIQ_ES5880:
chip_name = "SB PCI128";
chip_vers = "ES5880";
break;
case ENSONIQ_ES5880A:
chip_name = "SB PCI128";
chip_vers = "ES5880A";
break;
case ENSONIQ_ES5880B:
chip_name = "SB PCI128";
chip_vers = "ES5880B";
break;
case ECTIVA_ES1938:
chip_name = "AudioPCI";
chip_vers = "ES1938";
break;
}
goto err_exit;
}
/* set the PCI latency */
(audioens_latency == 96))
/* activate the device */
/* map registers */
goto err_exit;
}
goto err_exit;
}
/* This allocates and configures the engines */
goto err_exit;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
int
{
int tmp;
/* first unregister us from the DDI framework, might be busy */
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
for (int i = 0; i <= PORT_MAX; i++) {
}
/* reinitialize hardware */
/* restore AC97 state */
/* restart ports */
for (int i = 0; i < PORT_MAX; i++) {
/* possibly start it up if was going when we suspended */
}
}
for (int i = 0; i < PORT_MAX; i++) {
/* signal callbacks on resume */
continue;
} else {
}
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
/*
*/
for (int i = 0; i <= PORT_MAX; i++) {
}
/*
* Framework needs to save off AC'97 state.
*/
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
}
/* This disables all DMA engines and interrupts */
return (DDI_SUCCESS);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
return (audioens_attach(dip));
case DDI_RESUME:
return (DDI_FAILURE);
}
return (audioens_resume(dev));
default:
return (DDI_FAILURE);
}
}
static int
{
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
return (audioens_detach(dev));
case DDI_SUSPEND:
return (audioens_suspend(dev));
default:
return (DDI_FAILURE);
}
}
static struct dev_ops audioens_dev_ops = {
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
audioens_ddi_attach, /* attach */
audioens_ddi_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
audioens_quiesce, /* quiesce */
};
static struct modldrv audioens_modldrv = {
&mod_driverops, /* drv_modops */
"Ensoniq 1371/1373 Audio", /* linkinfo */
&audioens_dev_ops, /* dev_ops */
};
static struct modlinkage modlinkage = {
{ &audioens_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}