audioemu10k.c revision f9ead4a57883f3ef04ef20d83cc47987d98c0687
/*
* 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.
*/
/*
* Copyright (C) 4Front Technologies 1996-2009.
*/
/*
* Purpose: Driver for the Creative Sound Blaster Live! and Audigy/2/4
* sound cards
*/
#include "audioemu10k.h"
/*
* Include the DSP files for emu10k1 (Live!) and emu10k2 (Audigy)
*/
#include "emu10k_gpr.h"
#include "emu10k1_dsp.h"
#include "emu10k2_dsp.h"
static struct ddi_device_acc_attr dev_attr = {
};
static struct ddi_device_acc_attr buf_attr = {
};
/*
* EMU10K routing stuff.
*/
#define MAX_SENDS 4
#define SEND_L 0
#define SEND_R 1
#define SEND_SURRL 2
#define SEND_SURRR 3
#define SEND_CEN 4
#define SEND_LFE 5
#define SEND_SIDEL 6
#define SEND_SIDER 7
#define SPDIF_L 20
#define SPDIF_R 21
/*
* Recording sources... we start from 16 to ensure that the
* record sources don't collide with AC'97 record sources in
* the control value.
*/
#define INPUT_AC97 1
#define INPUT_SPD1 2
#define INPUT_SPD2 3
#define INPUT_DIGCD 4
#define INPUT_AUX2 5
#define INPUT_LINE2 6
#define INPUT_STEREOMIX 7
};
};
};
};
0x3f, 0x3f, 0x3f, 0x3f
};
/*
* SB Live! cannot do DMA above 2G addresses. Audigy/2/4 have special 8k page
* mode that supports high addresses. However, we should not need this except
* on SPARC. For simplicity's sake, we are only delivering this driver for
* x86 platforms. If SPARC support is desired, then the code will have to
* be modified to support full 32-bit addressing. (And again, SB Live!
* can't do it anyway.)
*/
static ddi_dma_attr_t dma_attr_buf = {
DMA_ATTR_V0, /* Version */
0x00000000ULL, /* Address low */
0x7ffffff0ULL, /* Address high */
0xffffffffULL, /* Counter max */
1ULL, /* Default byte align */
0x7f, /* Burst size */
0x1, /* Minimum xfer size */
0xffffffffULL, /* Maximum xfer size */
0xffffffffULL, /* Max segment size */
1, /* S/G list length */
1, /* Granularity */
0 /* Flag */
};
static int emu10k_attach(dev_info_t *);
static int emu10k_resume(dev_info_t *);
static int emu10k_detach(emu10k_devc_t *);
static int emu10k_suspend(emu10k_devc_t *);
static int emu10k_open(void *, int, unsigned *, unsigned *, caddr_t *);
static void emu10k_close(void *);
static int emu10k_start(void *);
static void emu10k_stop(void *);
static int emu10k_format(void *);
static int emu10k_channels(void *);
static int emu10k_rate(void *);
static uint64_t emu10k_count(void *);
static void emu10k_sync(void *, unsigned);
static void emu10k_chinfo(void *, int, unsigned *, unsigned *);
static int emu10k_alloc_port(emu10k_devc_t *, int);
static void emu10k_destroy(emu10k_devc_t *);
static int emu10k_setup_intrs(emu10k_devc_t *);
static int emu10k_hwinit(emu10k_devc_t *);
static void emu10k_init_effects(emu10k_devc_t *);
static audio_engine_ops_t emu10k_engine_ops = {
NULL,
};
static uint16_t
{
int dtemp = 0, i;
for (i = 0; i < 10000; i++)
break;
if (i == 1000) {
return (0); /* Timeout */
}
return (dtemp & 0xffff);
}
static void
{
int i;
for (i = 0; i < 10000; i++)
break;
}
static uint32_t
{
0x0fff0000 : 0x07ff0000;
if (reg & 0xff000000) {
}
return (val);
}
static void
{
0x0fff0000 : 0x07ff0000;
if (reg & 0xff000000) {
}
}
static void
{
int i;
unsigned int srda = 0;
for (i = 0; i < 4; i++)
} else {
int fxrt = 0;
for (i = 0; i < 4; i++)
}
}
static void
{
}
static uint_t
{
return (DDI_INTR_UNCLAIMED);
}
if (status == 0) {
return (DDI_INTR_UNCLAIMED);
}
}
}
}
}
if (cons) {
}
if (prod) {
}
return (DDI_INTR_CLAIMED);
}
/*
* Audio routines
*/
static void
{
unsigned int tmp;
unsigned char send[2];
/*
* Each voice operator of EMU10k has 4 sends (0=left, 1=right,
* 2=surround_left, 3=surround_right). The original OSS driver
* used all of them to spread stereo output to two different
* speaker pairs. This Boomer version uses only the first two
* sends. The other sends are set to 0.
*
* Boomer uses multiple voice pairs to play multichannel
* audio. This function is used to update only one of these
* pairs.
*/
/* Analog voice */
send[1] = 0;
} else {
send[0] = 0;
}
}
static void
{
unsigned int nCRA = 0;
int sz;
int start_pos;
/* Size of one stereo sub buffer */
/* set stereo */
/* SDL, ST, CA */
/* This is really a physical address */
/* No volume envelope delay (OK) */
}
static void
{
/* set stereo */
/* SDL, ST, CA */
/* This is really a physical address */
/* No volume envelope delay (OK) */
}
int
{
return (0);
}
void
emu10k_close(void *arg)
{
}
int
emu10k_start(void *arg)
{
}
return (0);
}
void
emu10k_stop(void *arg)
{
}
}
int
emu10k_format(void *arg)
{
return (AUDIO_FORMAT_S16_LE);
}
int
emu10k_channels(void *arg)
{
}
int
emu10k_rate(void *arg)
{
return (SAMPLE_RATE);
}
void
{
}
emu10k_count(void *arg)
{
return (count);
}
static void
{
*incr = 2;
}
/* private implementation bits */
static void
{
unsigned int tmp;
s = !!s;
if (s)
}
static unsigned int
emu10k_rate_to_pitch(unsigned int rate)
{
static unsigned int logMagTable[128] = {
0x00000, 0x02dfc, 0x05b9e, 0x088e6,
0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
0x1663f, 0x1918a, 0x1bc84, 0x1e72e,
0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
0x2b803, 0x2e0e8, 0x30985, 0x331db,
0x359eb, 0x381b6, 0x3a93d, 0x3d081,
0x3f782, 0x41e42, 0x444c1, 0x46b01,
0x49101, 0x4b6c4, 0x4dc49, 0x50191,
0x5269e, 0x54b6f, 0x57006, 0x59463,
0x5b888, 0x5dc74, 0x60029, 0x623a7,
0x646ee, 0x66a00, 0x68cdd, 0x6af86,
0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5,
0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
0x86082, 0x88089, 0x8a064, 0x8c014,
0x8df98, 0x8fef1, 0x91e20, 0x93d26,
0x95c01, 0x97ab4, 0x9993e, 0x9b79f,
0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537,
0xac241, 0xadf26, 0xafbe7, 0xb1885,
0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
0xc1404, 0xc2f50, 0xc4a7b, 0xc6587,
0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
0xceaec, 0xd053f, 0xd1f73, 0xd398a,
0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
0xdba4a, 0xdd3b4, 0xded03, 0xe0636,
0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
0xe829f, 0xe9b31, 0xeb3a9, 0xecc08,
0xee44c, 0xefc78, 0xf148a, 0xf2c83,
0xf4463, 0xf5c2a, 0xf73da, 0xf8b71,
0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
};
static char logSlopeTable[128] = {
0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
};
int i;
if (rate == 0)
return (0); /* Bail out if no leading "1" */
for (i = 31; i > 0; i--) {
return (((unsigned int) (i - 15) << 20) +
}
rate <<= 1;
}
return (0); /* Should never reach this point */
}
static unsigned int
emu10k_rate_to_linearpitch(unsigned int rate)
{
}
static void
{
/* setup CCR regs */
cra = 64;
sample = 0; /* 16 bit silence */
for (i = 0; i < cs; i++)
/* Set current pitch */
}
static void
{
}
static void
int buf_offset)
{
/* Left channel */
/* Intial filter cutoff and attenuation */
/* Volume envelope decay and sustain */
/* Volume target and Filter cutoff target */
/* Pitch target and sends A and B */
/* The same for right channel */
/* now setup the voices and go! */
}
void
{
emu10k_prepare_voice(devc, 0);
/* arrange to receive full loop interrupts on channel 8 */
/* initialize our position counter... */
/* Trigger playback on all voices */
}
void
{
emu10k_stop_voice(devc, 0);
}
void
{
} else {
}
/*
* This way we can use voices 8 and 9 for timing, we have
* programmed them to be just the size of a single fragment,
* that way when they loop we get a clean interrupt.
*/
}
void
{
/*
* Note: position is given as stereo samples, i.e. frames.
*/
} else {
}
}
}
}
void
{
/* Intr enable */
tmp = 0; /* setup 48Kz */
else
}
void
{
}
void
{
case 4096:
sz = 15;
break;
case 8192:
sz = 19;
break;
case 16384:
sz = 23;
break;
case 32768:
sz = 27;
break;
case 65536:
sz = 31;
break;
}
}
void
{
/* given in bytes, we divide all counts by 4 to get samples */
} else {
}
}
int
{
int dir;
unsigned caps;
int i, n;
switch (num) {
case EMU10K_REC:
dir = DDI_DMA_READ;
/* This is the minimum record buffer size. */
break;
case EMU10K_PLAY:
dir = DDI_DMA_WRITE;
/* XXX: this could probably be tunable */
break;
default:
return (DDI_FAILURE);
}
/*
* Fragments that are not powers of two don't seem to work
* at all with EMU10K. For simplicity's sake, we eliminate
* the question and fix the interrupt rate. This is also the
* logical minimum for record, which requires at least 4K for
* the record size.
*/
}
/* Alloc buffers */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Fill the page table
*/
FILL_PAGE_MAP_ENTRY(n + i,
}
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
int
{
int actual;
int rv;
"Can't alloc interrupt handle (rv %d actual %d)",
return (DDI_FAILURE);
}
(void) ddi_intr_free(ih[0]);
return (DDI_FAILURE);
}
DDI_SUCCESS) {
(void) ddi_intr_free(ih[0]);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
}
}
if (devc->silence_paddr) {
}
if (devc->silence_acch) {
}
if (devc->silence_dmah) {
}
}
}
}
for (int i = 0; i < CTL_MAX; i++) {
}
}
for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
if (!portc)
continue;
}
}
}
}
}
}
}
}
}
}
static void
{
else
}
}
static void
{
}
}
}
val &= ~A_IOCFG_GPOUT0;
val &= ~HCFG_GPOUT0;
}
}
int
{
unsigned int tmp, i;
unsigned int reg;
}
}
for (i = 0; i < 64; i++)
emu10k_init_voice(devc, i);
reg = 0;
}
reg |= 0x40;
/* Enable analog outputs on Audigy2 */
int tmp;
/* Setup SRCMulti_I2S SamplingRate */
tmp &= 0xfffff1ff;
/* emu10k_write_reg (devc, SOC, 0, 0x00000000); */
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
/* Setup SRCMulti Input Audio Enable */
/* Setup I2S ASRC Enable (HC register) */
tmp |= 0x00000070;
/*
* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz.
* So, sequence is important
*/
tmp |= 0x0040;
}
/* Enable analog outputs on Audigy2 */
int tmp;
/* Setup SRCMulti_I2S SamplingRate */
tmp &= 0xfffff1ff;
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
/* Setup SRCMulti Input Audio Enable */
/* SPDIF output enable */
}
unsigned int mode = 0;
/* enable IR port */
drv_usecwait(500);
drv_usecwait(100);
} else {
}
/* enable IR port */
drv_usecwait(500);
drv_usecwait(100);
/*
* Start by configuring for analog mode.
*/
}
}
}
return (DDI_SUCCESS);
}
static const int db2lin_101[101] = {
0x00000000,
0x0024B53A, 0x002750CA, 0x002A1BC6, 0x002D198D, 0x00304DBA, 0x0033BC2A,
0x00376901, 0x003B58AF, 0x003F8FF1, 0x004413DF, 0x0048E9EA, 0x004E17E9,
0x0053A419, 0x0059952C, 0x005FF24E, 0x0066C32A, 0x006E0FFB, 0x0075E18D,
0x007E414F, 0x0087395B, 0x0090D482, 0x009B1E5B, 0x00A6234F, 0x00B1F0A7,
0x00BE94A1, 0x00CC1E7C, 0x00DA9E8D, 0x00EA2650, 0x00FAC881, 0x010C9931,
0x011FADDC, 0x01341D87, 0x014A00D8, 0x01617235, 0x017A8DE6, 0x01957233,
0x01B23F8D, 0x01D118B1, 0x01F222D4, 0x021585D1, 0x023B6C57, 0x0264041D,
0x028F7E19, 0x02BE0EBD, 0x02EFEE33, 0x032558A2, 0x035E8E7A, 0x039BD4BC,
0x03DD7551, 0x0423BF61, 0x046F07B5, 0x04BFA91B, 0x051604D5, 0x0572830D,
0x05D59354, 0x063FAD27, 0x06B15080, 0x072B0673, 0x07AD61CD, 0x0838FFCA,
0x08CE88D3, 0x096EB147, 0x0A1A3A53, 0x0AD1F2E0, 0x0B96B889, 0x0C6978A5,
0x0D4B316A, 0x0E3CF31B, 0x0F3FE155, 0x10553469, 0x117E3AD9, 0x12BC5AEA,
0x14111457, 0x157E0219, 0x1704DC5E, 0x18A77A97, 0x1A67D5B6, 0x1C480A87,
0x1E4A5C45, 0x2071374D, 0x22BF3412, 0x25371A37, 0x27DBE3EF, 0x2AB0C18F,
0x2DB91D6F, 0x30F89FFD, 0x34733433, 0x382D0C46, 0x3C2AA6BD, 0x4070D3D9,
0x4504BB66, 0x49EBE2F1, 0x4F2C346F, 0x54CC0565, 0x5AD21E86, 0x6145C3E7,
0x682EBDBD, 0x6F9561C4, 0x77829D4D,
0x7fffffff
};
static int
{
if (val < 0)
val = 0;
if (val > 100)
val = 100;
return (db2lin_101[val]);
}
static void
{
}
}
static int
{
return (EINVAL);
return (0);
}
static int
{
uint32_t v;
if (val > 100)
return (EINVAL);
return (0);
}
static int
{
return (0);
}
static int
{
}
static int
{
}
static int
{
set_val--;
switch (set_val) {
case 0:
case 1:
break;
default:
return (EINVAL);
}
}
val &= ~A_IOCFG_GPOUT0;
val &= ~HCFG_GPOUT0;
}
}
return (0);
}
static int
{
set_val--;
/*
* We start assuming well set up AC'97 for stereomix recording.
*/
switch (set_val) {
case INPUT_AC97:
case INPUT_SPD1:
case INPUT_SPD2:
case INPUT_DIGCD:
case INPUT_AUX2:
case INPUT_LINE2:
case INPUT_STEREOMIX:
break;
default:
return (EINVAL);
}
return (0);
}
static void
{
desc.acd_minvalue = 0;
}
static void
{
desc.acd_minvalue = 0;
}
/*
* AC'97 source. The AC'97 PCM record channel is routed to our
* mixer. While we could support the direct monitoring capability of
* the AC'97 part itself, this would not work correctly with outputs
* that are not routed via AC'97 (such as the Live Drive headphones
* or digital outputs.) So we just offer the ability to select one
* AC'97 source, and then offer independent ability to either monitor
* or record from the AC'97 mixer's PCM record channel.
*/
static void
{
const audio_ctrl_desc_t *acd;
return;
}
for (int i = 0; i < 64; i++) {
const char *n;
continue;
}
/* we suppress some port options */
if ((strcmp(n, AUDIO_PORT_STEREOMIX) == 0) ||
(strcmp(n, AUDIO_PORT_MONOMIX) == 0) ||
(strcmp(n, AUDIO_PORT_VIDEO) == 0)) {
continue;
}
}
}
/*
* Record source... this one is tricky. While the chip will
* conceivably let us *mix* some of the audio streams for recording,
* the AC'97 inputs don't have this capability. Offering it to users
* is likely to be confusing, so we offer a single record source
* selection option. Its not ideal, but it ought to be good enough
* for the vast majority of users.
*/
static void
{
desc.acd_minvalue = 0;
desc.acd_maxvalue = 0;
/* only low order bits set by AC'97 */
/*
* It would be really cool if we could detect whether these
* options are all sensible on a given configuration. Units
* without live-drive support, and units without a physical
* live-drive, simply can't do all these.
*/
} else {
/* next best guess */
}
}
static void
{
}
static void
{
}
}
/*
* 5.1 devices have versa jack. Note that from what we can
* tell, none of the 7.1 devices have or need this versa jack,
* as they all seem to have a dedicated digital I/O port.
*/
}
/* these ones AC'97 can manage directly */
/* set any AC'97 analog outputs to full volume (no attenuation) */
/* Monitor sources */
"ac97-monitor", MONVOL, 0);
AUDIO_PORT_SPDIFIN, MONVOL, 0);
"digital-cd", MONVOL, 0);
AUDIO_PORT_SPDIFIN, MONVOL, 0);
/*
* These ports are only available via an external
* expansion box. Don't expose them for cards that
* don't have support for it.
*/
"spdif2-in", MONVOL, 0);
"line2-in", MONVOL, 0);
AUDIO_PORT_AUX2IN, MONVOL, 0);
}
}
static void
{
int i;
if (ncode > 1024) {
return;
}
return;
}
/* Upload our DSP code */
for (i = 0; i < ncode; i++) {
}
/* Upload the initialization settings */
for (i = 0; i < ninit; i += 2) {
}
}
#define LIVE_NOP() \
pc++
#define LIVE_ACC3(r, a, x, y) /* z=w+x+y */ \
pc++
#define AUDIGY_ACC3(r, a, x, y) /* z=w+x+y */ \
pc++
static void
{
int i;
unsigned short pc;
pc = 0;
for (i = 0; i < 512; i++) {
AUDIGY_NOP();
}
for (i = 0; i < 256; i++)
sizeof (emu10k2_code) / sizeof (emu10k2_code[0]),
sizeof (emu10k2_init) / sizeof (emu10k2_init[0]));
} else {
pc = 0;
for (i = 0; i < 512; i++) {
LIVE_NOP();
}
for (i = 0; i < 256; i++)
sizeof (emu10k1_code) / sizeof (emu10k1_code[0]),
sizeof (emu10k1_init) / sizeof (emu10k1_init[0]));
}
}
/* mixer */
static struct {
const char *model;
const char *prod;
unsigned feature_mask;
} emu10k_cards[] = {
{ 0x2, 0x100a, "SB0220", "Live! 5.1 Digital",
{ 0x4, 0x1002, "SB0240P", "Audigy 2 Platinum",
{ 0x8, 0x1001, "SB0400", "Audigy 2 Value",
{ 0x8, 0x1021, "SB0610", "Audigy 4",
{ 0x8, 0x2001, "SB0530", "Audigy 2 ZS Notebook",
SB_AUDIGY2VAL | SB_71 },
};
int
{
int i;
const char *name;
const char *model;
char namebuf[64];
int feature_mask;
goto error;
}
goto error;
}
goto error;
}
switch (devid) {
case PCI_DEVICE_ID_SBLIVE:
name = "Live!";
model = "CT????";
break;
name = "Audigy 2 Value";
model = "SB????";
break;
case PCI_DEVICE_ID_AUDIGY:
name = "Audigy 2";
model = "SB????";
} else {
name = "Audigy";
model = "SB????";
}
break;
default:
goto error;
}
for (i = 0; emu10k_cards[i].prod; i++) {
break;
}
}
goto error;
/* allocate static page table memory */
/* Allocate page table */
"failed to allocate page table handle");
return (DDI_FAILURE);
}
DDI_SUCCESS) {
"failed to allocate memory for page table");
return (DDI_FAILURE);
}
"failed binding page table DMA handle");
return (DDI_FAILURE);
}
/* Allocate silent page */
"failed to allocate silent page handle");
return (DDI_FAILURE);
}
"failed to allocate silent page memory");
return (DDI_FAILURE);
}
"failed binding silent page DMA handle");
return (DDI_FAILURE);
}
goto error;
}
/* allocate voice 0 for play */
goto error;
goto error;
/* now initialize the hardware */
goto error;
}
/* set up kernel statistics */
}
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
int
{
for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
}
/*
* In case of failure, we leave the chip suspended,
* but don't panic. Audio service is not normally a a
* critical service.
*/
return (DDI_SUCCESS);
}
for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
}
}
/* resume ac97 */
return (DDI_SUCCESS);
}
int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
int
{
}
for (int i = 0; i < EMU10K_NUM_PORTC; i++) {
}
/* stop all voices */
for (int i = 0; i < 64; i++) {
}
for (int i = 0; i < 64; i++) {
}
/*
* Turn off the hardware
*/
/* stop ADC recording */
return (DDI_SUCCESS);
}
static int emu10k_ddi_quiesce(dev_info_t *);
static struct dev_ops emu10k_dev_ops = {
DEVO_REV, /* rev */
0, /* refcnt */
NULL, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
emu10k_ddi_attach, /* attach */
emu10k_ddi_detach, /* detach */
nodev, /* reset */
NULL, /* cb_ops */
NULL, /* bus_ops */
NULL, /* power */
emu10k_ddi_quiesce, /* quiesce */
};
static struct modldrv emu10k_modldrv = {
&mod_driverops, /* drv_modops */
"Creative EMU10K Audio", /* linkinfo */
&emu10k_dev_ops, /* dev_ops */
};
static struct modlinkage modlinkage = {
{ &emu10k_modldrv, NULL }
};
int
_init(void)
{
int rv;
}
return (rv);
}
int
_fini(void)
{
int rv;
}
return (rv);
}
int
{
}
int
{
switch (cmd) {
case DDI_ATTACH:
return (emu10k_attach(dip));
case DDI_RESUME:
return (emu10k_resume(dip));
default:
return (DDI_FAILURE);
}
}
int
{
switch (cmd) {
case DDI_DETACH:
return (emu10k_detach(devc));
case DDI_SUSPEND:
return (emu10k_suspend(devc));
default:
return (DDI_FAILURE);
}
}
int
{
/* stop all voices */
for (int i = 0; i < 64; i++) {
}
for (int i = 0; i < 64; i++) {
}
/*
* Turn off the hardware
*/
/* stop ADC recording */
return (DDI_SUCCESS);
}