audioens.c revision 5b1627536384deb03449347af9c01bd4fc2d271e
/*
* 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 2016 Garrett D'Amore <garrett@damore.org>
*/
/*
*
* 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.
*/
/*
* For VMWare platforms, we have to utilize the (emulated) hardware interrupts
* of the device. This is necessary for audio playback to function, as
* the toggling of the interrupt bits apparently triggers logic inside the
* emulated device. So we need to detect this platform, and conditionally
* wire up the interrupt handler.
*/
#ifdef __x86
#include <sys/x86_archext.h>
#endif
#include "audioens.h"
/*
* 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 DRVNAME "audioens"
typedef struct audioens_port
{
/* Audio parameters */
int speed;
int num;
#define PORT_DAC 0
#define PORT_ADC 1
int nchan;
unsigned nframes;
unsigned iframes;
unsigned frameno;
struct audioens_dev *dev;
typedef struct audioens_dev
{
#ifdef __x86
#endif
static ddi_device_acc_attr_t acc_attr = {
};
static ddi_device_acc_attr_t buf_attr = {
};
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 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
{
}
#ifdef __x86
static unsigned
{
uint32_t n;
return (DDI_INTR_UNCLAIMED);
}
if ((status & CONC_STATUS_PENDING) == 0) {
return (DDI_INTR_UNCLAIMED);
}
/* Three interrupts, DAC1, DAC2, and ADC. The UART we just toss. */
if (status & CONC_STATUS_DAC1INT) {
/* current frame counter is in high nybble */
}
if (status & CONC_STATUS_ADCINT) {
/* current frame counter is in high nybble */
}
if (status & CONC_STATUS_DAC2INT) {
}
if (status & CONC_STATUS_UARTINT) {
/*
* Consume data in the UART RX FIFO. We don't support
* the UART for now, so just eat it.
*/
continue;
}
return (DDI_INTR_CLAIMED);
}
static int
{
int act;
goto fail;
}
goto fail;
}
DDI_SUCCESS) {
goto fail;
}
return (DDI_SUCCESS);
fail:
}
return (DDI_FAILURE);
}
#endif /* __x86 */
/*
* 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 int
{
return (0);
}
static int
audioens_start(void *arg)
{
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 */
#ifdef __x86
}
#endif
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 */
#ifdef __x86
}
#endif
break;
}
return (0);
}
static void
audioens_stop(void *arg)
{
case PORT_DAC:
break;
case PORT_ADC:
break;
}
}
static uint64_t
audioens_count(void *arg)
{
int frameno, n;
case PORT_DAC:
break;
case PORT_ADC:
break;
}
#ifdef __x86
#endif
/*
* Note that the current frame counter is in the high nybble.
*/
#ifdef __x86
}
#endif
return (val);
}
static void
audioens_close(void *arg)
{
}
static void
{
} else {
}
}
static void
{
*incr = 2;
} else {
*incr = 2;
}
}
AUDIO_ENGINE_VERSION, /* version number */
NULL,
NULL,
};
void
{
int tmp;
/* Have a ES5880 so enable the codec manually */
tmp |= 0x20;
for (int i = 0; i < 2000; i++)
drv_usecwait(10);
}
/*
* 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;
/*
* We have 48000Hz. At that rate, 128 frames will give
* us an interrupt rate of 375Hz. 2048 frames buys about
* 42ms of buffer. Note that interrupts are only enabled
* for platforms which need them (i.e. VMWare).
*/
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);
}
}
"unable to register with audio framework");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
int i;
#ifdef __x86
}
#endif
/* free up ports, including DMA resources for ports */
for (i = 0; i <= PORT_MAX; i++) {
}
}
}
}
}
}
int
{
const char *chip_name;
const char *chip_vers;
goto err_exit;
}
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;
}
#ifdef __x86
/*
* Virtual platforms (mostly VMWare!) seem to need us to pulse
* the interrupt enables to make progress. So enable (emulated)
* hardware interrupts.
*/
if (get_hwenv() & HW_VIRTUAL) {
goto err_exit;
}
/* Reinitialize the mutex with interrupt priority. */
}
#endif
/* This allocates and configures the engines */
goto err_exit;
}
#ifdef __x86
}
#endif
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);
}
static int
{
/* reinitialize hardware */
/* restore AC97 state */
return (DDI_SUCCESS);
}
static int
{
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
{
}