/*
* 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
*/
/*
*/
/*
* Purpose: Driver for the Creative Audigy LS sound card
*/
/*
* Copyright (C) 4Front Technologies 1996-2009.
*/
#include "audiols.h"
};
};
DMA_ATTR_V0, /* version number */
0x00000000, /* low DMA address range */
0xffffffff, /* high DMA address range */
0x000fffff, /* DMA counter (16 bits only in Audigy LS) */
4, /* DMA address alignment */
0x3c, /* DMA burstsizes */
4, /* min effective DMA size */
0xffffffff, /* max DMA xfer size */
0xffffffff, /* segment boundary */
1, /* s/g length */
4, /* granularity of device */
0 /* Bus specific DMA flags */
};
static int audigyls_attach(dev_info_t *);
static int audigyls_resume(dev_info_t *);
static int audigyls_detach(audigyls_dev_t *);
static int audigyls_suspend(audigyls_dev_t *);
static int audigyls_open(void *, int, unsigned *, caddr_t *);
static void audigyls_close(void *);
static int audigyls_start(void *);
static void audigyls_stop(void *);
static int audigyls_format(void *);
static int audigyls_channels(void *);
static int audigyls_rate(void *);
static uint64_t audigyls_count(void *);
static void audigyls_sync(void *, unsigned);
static void audigyls_chinfo(void *, int, unsigned *, unsigned *);
static int audigyls_alloc_port(audigyls_dev_t *, int);
static void audigyls_destroy(audigyls_dev_t *);
static void audigyls_hwinit(audigyls_dev_t *);
NULL,
};
/*
* Audigy LS uses AC'97 strictly for the recording side of things.
* While the chip can supposedly route output to AC'97 for playback,
* the PCI devices use a separate I2S DAC instead. As a result we
* need to suppress controls that the AC'97 codec registers.
*
* Furthermore, even then the AC'97 codec offers inputs that we just
* aren't interested in.
*/
const char *audigyls_remove_ac97[] = {
NULL,
};
/*
* AC'97 sources we don't want to expose.
*/
const char *audigyls_badsrcs[] = {
NULL,
};
static unsigned int
{
/* Pointer */
/* Data */
return (val);
}
static void
{
/* Pointer */
/* Data */
}
static unsigned int
{
}
static void
{
}
static uint16_t
{
int i;
for (i = 0; i < 10000; i++) {
break;
}
if (i == 10000) { /* Timeout */
return (0xffff);
}
return (dtemp);
}
static void
{
int i;
for (i = 0; i < 50000; i++) {
break;
}
if (i == 50000) {
return;
}
}
static void
{
/*
*/
if (mode == 0) {
} else {
}
}
/* only for SBLive 7.1 */
void
{
/* first write the command to the data reg */
for (i = 0; i < 20; i++) {
/* see audigyls.pdf for bits */
/* now wait till controller sets valid bit (0x100) to 0 */
timeout = 0;
for (;;) {
if ((tmp & 0x100) == 0)
break;
if (timeout > 100)
break;
timeout++;
}
/* transaction aborted */
if (tmp & 0x200)
break;
}
}
int
{
unsigned int orig;
unsigned int tmp;
int i, valid;
valid = 0;
/* Wait for status bit to return to 0 */
for (i = 0; i < 1000; i++) {
drv_usecwait(100);
if (!(tmp & 0x10000)) {
valid = 1;
break;
}
}
if (!valid) /* Timed out */
return (0);
return (1);
}
/*
* Audio routines
*/
int
{
return (0);
}
void
{
}
int
{
case AUDIGYLS_PLAY_PORT:
break;
case AUDIGYLS_REC_PORT:
break;
}
return (0);
}
void
{
case AUDIGYLS_PLAY_PORT:
break;
case AUDIGYLS_REC_PORT:
break;
}
}
int
{
return (AUDIO_FORMAT_S16_LE);
}
int
{
}
int
{
return (48000);
}
void
{
}
{
} else {
}
/* get the offset, and switch to frames */
} else {
}
return (count);
}
static void
{
*incr = 2;
} else {
*incr = 2;
}
}
/* private implementation bits */
int
{
int dir;
unsigned caps;
switch (num) {
case AUDIGYLS_REC_PORT:
dir = DDI_DMA_READ;
break;
case AUDIGYLS_PLAY_PORT:
dir = DDI_DMA_WRITE;
break;
default:
return (DDI_FAILURE);
}
/* Alloc buffers */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
&count) != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
for (int i = 0; i < CTL_NUM; i++) {
}
}
}
void
{
for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) {
if (!port)
continue;
}
}
}
}
}
}
}
}
}
}
void
{
static unsigned int spi_dac[] = {
0x00ff, 0x02ff, 0x0400, 0x520, 0x0620, 0x08ff, 0x0aff, 0x0cff,
0x0eff, 0x10ff, 0x1200, 0x1400, 0x1800, 0x1aff, 0x1cff,
0x1e00, 0x0530, 0x0602, 0x0622, 0x1400,
};
int i, tries;
/*
* In P17, there's 8 GPIO pins.
* GPIO register: 0x00XXYYZZ
* XX: Configure GPIO to be either GPI (0) or GPO (1).
* YY: GPO values, applicable if the pin is configure to be GPO.
* ZZ: GPI values, applicable if the pin is configure to be GPI.
*
* in SB570, pin 0-4 and 6 is used as GPO and pin 5 and 7 is
* used as GPI.
*
* GPO0:
* 1 ==> Analog output
* 0 ==> Digital output
* GPO1:
* 1 ==> Enable output on card
* 0 ==> Disable output on card
* GPO2:
* 1 ==> Enable Mic Bias and Mic Path
* 0 ==> Disable Mic Bias and Mic Path
* GPO3:
* 1 ==> Disable SPDIF-IO output
* 0 ==> Enable SPDIF-IO output
* GPO4 and GPO6:
* DAC sampling rate selection:
* Not applicable to SB570 since DAC is controlled through SPI
* GPI5:
* 1 ==> Front Panel is not connected
* 0 ==> Front Panel is connected
* GPI7:
* 1 ==> Front Panel Headphone is not connected
* 0 ==> Front Panel Headphone is connected
*/
else {
/* for SBLive 7.1 */
tries = 0;
tries < 100) {
tries++;
goto again;
}
}
}
else
/* All audio stopped! */
for (i = 0; i < 4; i++) {
/*
* Reset DMA pointers and counters. Note that we do
*/
}
/*
* The 5.1 play port made up channels 0, 1, and 3. The record
* port is channel 2.
*/
/* Record */
/* Set sample rates to 48 kHz. */
}
static uint32_t
{
return (val);
}
static void
{
/* output items */
/* front */
r = (r << 16) | r;
/* surround */
r = (r << 16) | r;
r = (r << 16) | r;
/* spread */
/* input items */
/* recgain */
/*
* For AC'97, we use the AC'97 record gain, unless we are
* in loopback.
*/
} else {
/*
* Otherwise we set the P17 gain.
*/
r = r << 16 | r;
}
/* monitor gain */
/* AC'97 monitor gain is done by the AC'97 codec */
} else {
/* For non-AC'97 devices, just a single master monitor gain */
if (r != 0xff) {
} else {
}
}
/* record source */
} else {
case 1:
break;
case 2:
break;
}
}
/* If loopback, record what you hear instead */
r = 0;
v1 = RECSEL_I2SOUT;
} else {
/*
* You'd think this would be the same as the logic
* above, but experience shows that what you need for
* loopback is different. This whole thing looks
* particularly fishy to me. I suspect someone has
* made a mistake somewhere. But I can't seem to
* figure out where it lies.
*/
r = 0xe4;
for (int i = 0; i < 4; i++)
} else {
v1;
}
}
}
static int
{
case CTL_FRONT:
case CTL_SURROUND:
case CTL_RECORDVOL:
((val & ~0xffff) != 0)) {
return (EINVAL);
}
break;
case CTL_CENTER:
case CTL_LFE:
case CTL_MONGAIN:
if (val > 100) {
return (EINVAL);
}
break;
case CTL_RECSRC:
return (EINVAL);
}
break;
case CTL_SPREAD:
case CTL_LOOP:
switch (val) {
case 0:
case 1:
break;
default:
return (EINVAL);
}
}
return (0);
}
static int
{
return (0);
}
static void
{
switch (num) {
case CTL_FRONT:
desc.acd_minvalue = 0;
break;
case CTL_SURROUND:
desc.acd_minvalue = 0;
break;
case CTL_CENTER:
desc.acd_minvalue = 0;
break;
case CTL_LFE:
desc.acd_minvalue = 0;
break;
case CTL_RECORDVOL:
desc.acd_minvalue = 0;
break;
case CTL_RECSRC:
/*
* For AC'97 devices, we want to expose the reasonable
* AC'97 input sources, but suppress the stereomix,
* because we use loopback instead.
*/
if (dev->ac97_recsrc) {
int i, j;
const char *n;
for (i = 0; i < 64; i++) {
(n == NULL)) {
continue;
}
for (j = 0; audigyls_badsrcs[j]; j++) {
if (strcmp(n, audigyls_badsrcs[j])
== 0) {
n = NULL;
break;
}
}
if (n) {
}
}
} else {
}
break;
case CTL_MONGAIN:
desc.acd_minvalue = 0;
break;
case CTL_SPREAD:
desc.acd_minvalue = 0;
break;
case CTL_LOOP:
desc.acd_minvalue = 0;
break;
}
}
static void
{
}
}
int
{
goto error;
}
goto error;
}
subdevice <<= 16;
if (vendor != PCI_VENDOR_ID_CREATIVE ||
goto error;
}
goto error;
}
/* Function of the orange jack: 0=analog, 1=digital */
DDI_PROP_DONTPASS, "digital-enable", 0);
switch (subdevice) {
case 0x11021001: /* SB0310 */
case 0x11021002: /* SB0310 */
case 0x11021005: /* SB0310b */
name = "Creative Audigy LS";
break;
case 0x11021006:
name = "Creative Sound Blaster Live! 24 bit";
version = "SB0410";
break;
case 0x11021007: /* Dell OEM version */
name = "Creative Sound Blaster Live! 24 bit";
version = "SB0413";
break;
case 0x1102100a:
name = "Creative Audigy SE";
version = "SB0570";
break;
case 0x11021011:
name = "Creative Audigy SE OEM";
version = "SB0570a";
break;
case 0x11021012:
name = "Creative X-Fi Extreme Audio";
version = "SB0790";
break;
case 0x14621009:
name = "MSI K8N Diamond MB";
version = "SB0438";
break;
case 0x12973038:
name = "Shuttle XPC SD31P";
version = "SD31P";
break;
case 0x12973041:
name = "Shuttle XPC SD11G5";
version = "SD11G5";
break;
default:
name = "Creative Audigy LS";
break;
}
if (version)
if (ac97) {
/* Original Audigy LS revision (AC97 based) */
"failed to allocate ac97 handle");
goto error;
}
/* remove the AC'97 controls we don't want to expose */
for (int i = 0; audigyls_remove_ac97[i]; i++) {
audigyls_remove_ac97[i]);
}
}
}
}
goto error;
goto error;
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
int
{
/* allow ac97 operations again */
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
int
{
return (DDI_SUCCESS);
}
static int audigyls_ddi_quiesce(dev_info_t *);
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
audigyls_ddi_attach, /* attach */
audigyls_ddi_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
audigyls_ddi_quiesce, /* quiesce */
};
&mod_driverops, /* drv_modops */
"Creative Audigy LS Audio", /* linkinfo */
&audigyls_dev_ops, /* dev_ops */
};
{ &audigyls_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
int
{
switch (cmd) {
case DDI_ATTACH:
return (audigyls_attach(dip));
case DDI_RESUME:
return (audigyls_resume(dip));
default:
return (DDI_FAILURE);
}
}
int
{
switch (cmd) {
case DDI_DETACH:
return (audigyls_detach(dev));
case DDI_SUSPEND:
return (audigyls_suspend(dev));
default:
return (DDI_FAILURE);
}
}
int
{
/*
* Turn off the hardware
*/
return (DDI_SUCCESS);
}