/*
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* audio810 Audio Driver
*
* The driver is primarily targeted at providing audio support for the
* Intel ICHx family of AC'97 controllers and compatible parts (such
* as those from nVidia and AMD.)
*
* These audio parts have independent channels for PCM in, PCM out,
* mic in, and sometimes modem in, and modem out. The AC'97
* channel has a DMA engine. Currently, we use only the PCM in and PCM
* out channels. Each DMA engine uses one buffer descriptor list. And
* the buffer descriptor list is an array of up to 32 entries, each of
* which describes a data buffer. Each entry contains a pointer to a
* data buffer, control bits, and the length of the buffer being
* pointed to, where the length is expressed as the number of
* samples. This, combined with the 16-bit sample size, gives the
* actual physical length of the buffer.
*
* A workaround for the AD1980 and AD1985 codec:
* Most vendors connect the surr-out of the codecs to the line-out jack.
* So far we haven't found which vendors don't do that. So we assume that
* all vendors swap the surr-out and the line-out outputs. So we need swap
* the two outputs. But we still internally process the
* "ad198x-swap-output" property. If someday some vendors do not swap the
* outputs, we would set "ad198x-swap-output = 0" in the
* /kernel/drv/audio810.conf file, and unload and reload the audio810
* driver (or reboot).
*
* NOTE:
* modules being loaded first.
*
* The audio framework guarantees that our entry points are exclusive
* with suspend and resume. This includes data flow and control entry
* points alike.
*
* The audio framework guarantees that only one control is being
* accessed on any given audio device at a time.
*
* The audio framework guarantees that entry points are themselves
* serialized for a given engine.
*
* We have no interrupt routine or other internal asynchronous routines.
*
* Our device uses completely separate registers for each engine,
* manner that allows for them to be accessed concurrently safely from
* different threads.
*
* Hence, it turns out that we simply don't need any locking in this
* driver.
*/
#include "audio810.h"
/*
* Module linkage routines for the kernel
*/
static int audio810_ddi_quiesce(dev_info_t *);
/*
* Entry point routine prototypes
*/
static int audio810_open(void *, int, unsigned *, caddr_t *);
static void audio810_close(void *);
static int audio810_start(void *);
static void audio810_stop(void *);
static int audio810_format(void *);
static int audio810_channels(void *);
static int audio810_rate(void *);
static uint64_t audio810_count(void *);
static void audio810_sync(void *, unsigned);
static unsigned audio810_playahead(void *);
NULL,
NULL,
};
/*
* Local Routine Prototypes
*/
static int audio810_attach(dev_info_t *);
static int audio810_resume(dev_info_t *);
static int audio810_detach(dev_info_t *);
static int audio810_suspend(dev_info_t *);
static int audio810_codec_sync(audio810_state_t *);
static void audio810_unmap_regs(audio810_state_t *);
static void audio810_stop_dma(audio810_state_t *);
static int audio810_chip_init(audio810_state_t *);
static void audio810_set_channels(audio810_state_t *);
static void audio810_destroy(audio810_state_t *);
/*
* Global variables, but used only by this file.
*/
/*
* DDI Structures
*/
/* Device operations structure */
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audio810_ddi_attach, /* devo_attach */
audio810_ddi_detach, /* devo_detach */
nodev, /* devo_reset */
NULL, /* devi_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
audio810_ddi_quiesce, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
&mod_driverops, /* drv_modops */
I810_MOD_NAME, /* drv_linkinfo */
&audio810_dev_ops, /* drv_dev_ops */
};
/* Module linkage structure */
MODREV_1, /* ml_rev */
(void *)&audio810_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* device access attributes for register mapping
*/
};
};
/*
* DMA attributes of buffer descriptor list
*/
DMA_ATTR_V0, /* version */
0, /* addr_lo */
0xffffffff, /* addr_hi */
0x0000ffff, /* count_max */
8, /* align, BDL must be aligned on a 8-byte boundary */
0x3c, /* burstsize */
8, /* minxfer, set to the size of a BDlist entry */
0x0000ffff, /* maxxfer */
0x00000fff, /* seg, set to the RAM pagesize of intel platform */
1, /* sgllen, there's no scatter-gather list */
8, /* granular, set to the value of minxfer */
0 /* flags, use virtual address */
};
/*
*/
0, /* addr_lo */
0xffffffff, /* addr_hi */
0x0001ffff, /* count_max */
4, /* align, data buffer is aligned on a 4-byte boundary */
0x3c, /* burstsize */
4, /* minxfer, set to the size of a sample data */
0x0001ffff, /* maxxfer */
0x0001ffff, /* seg */
1, /* sgllen, no scatter-gather */
4, /* granular, set to the value of minxfer */
0, /* flags, use virtual address */
};
/*
* _init()
*
* Description:
* Driver initialization, called when driver is first loaded.
* This is how access is initially given to all the static structures.
*
* Arguments:
* None
*
* Returns:
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
}
return (error);
}
/*
* _fini()
*
* Description:
* Module de-initialization, called when the driver is to be unloaded.
*
* Arguments:
* None
*
* Returns:
* mod_remove() status, see mod_remove(9f)
*/
int
_fini(void)
{
int error;
return (error);
}
/* clean up ops */
return (0);
}
/*
* _info()
*
* Description:
* Module information, returns information about the driver.
*
* Arguments:
* modinfo *modinfop Pointer to the opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
}
/* ******************* Driver Entry Points ********************************* */
/*
* audio810_ddi_attach()
*
* Description:
* Implements the DDI attach(9e) entry point.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_attach_cmd_t cmd Attach command
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
switch (cmd) {
case DDI_ATTACH:
return (audio810_attach(dip));
case DDI_RESUME:
return (audio810_resume(dip));
}
return (DDI_FAILURE);
}
/*
* audio810_ddi_detach()
*
* Description:
* Implements the detach(9e) entry point.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_detach_cmd_t cmd Detach command
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached
*/
static int
{
switch (cmd) {
case DDI_DETACH:
return (audio810_detach(dip));
case DDI_SUSPEND:
return (audio810_suspend(dip));
}
return (DDI_FAILURE);
}
/*
* audio810_ddi_quiesce()
*
* Description:
* Implements the quiesce(9e) entry point.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was quiesced
* DDI_FAILURE The driver couldn't be quiesced
*/
static int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* audio810_open()
*
* Description:
* Opens a DMA engine for use.
*
* Arguments:
* void *arg The DMA engine to set up
* int flag Open flags
* unsigned *nframes Receives total number of frames
* caddr_t *bufp Receives kernel data buffer
*
* Returns:
* 0 on success
* errno on failure
*/
static int
{
return (0);
}
/*
* audio810_close()
*
* Description:
* Closes an audio DMA engine that was previously opened. Since
* nobody is using it, we take this opportunity to possibly power
* down the entire device.
*
* Arguments:
* void *arg The DMA engine to shut down
*/
static void
{
}
/*
* audio810_stop()
*
* Description:
* This is called by the framework to stop a port that is
* transferring data.
*
* Arguments:
* void *arg The DMA engine to stop
*/
static void
{
cr &= ~I810_BM_CR_RUN;
}
/*
* audio810_start()
*
* Description:
* This is called by the framework to start a port transferring data.
*
* Arguments:
* void *arg The DMA engine to start
*
* Returns:
* 0 on success (never fails, errno if it did)
*/
static int
{
/* program multiple channel settings */
/*
* SiS 7012 has special unmute bit.
*/
}
}
/*
* Perform full reset of the engine, but leave it turned off.
*/
/* program the offset of the BD list */
/* we set the last index to the full count -- all buffers are valid */
cr |= I810_BM_CR_RUN;
return (0);
}
/*
* audio810_format()
*
* Description:
* This is called by the framework to query the format of the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* Format of the device (fixed at AUDIO_FORMAT_S16_LE)
*/
static int
{
return (AUDIO_FORMAT_S16_LE);
}
/*
* audio810_channels()
*
* Description:
* This is called by the framework to query the num channels of
* the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* 0 number of channels for device
*/
static int
{
}
/*
* audio810_rate()
*
* Description:
* This is called by the framework to query the rate of the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* Rate of device (fixed at 48000 Hz)
*/
static int
{
return (48000);
}
/*
* audio810_count()
*
* Description:
* This is called by the framework to get the engine's frame counter
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* frame count for current engine
*/
static uint64_t
{
/*
* Read the position counters. We also take this opportunity
* to update the last valid index to the one just previous to
* the one we're working on (so we'll fully loop.)
*/
/* SiS counts in bytes, all others in words. */
offset *= 2;
/* counter is reversed */
} else {
}
return (val);
}
/*
* audio810_sync()
*
* Description:
* This is called by the framework to synchronize DMA caches.
*
* Arguments:
* void *arg The DMA engine to sync
*/
static void
{
}
/*
* audio810_playahead()
*
* Description:
* This is called by the framework to determine how much data it
* should queue up. We desire a deeper playahead than most to
* allow for virtualized devices which have less "regular"
* interrupt scheduling.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* Play ahead in frames.
*/
static unsigned
{
/* Older ICH is likely to be emulated, deeper (40 ms) playahead */
}
/* *********************** Local Routines *************************** */
/*
* audio810_attach()
*
* Description:
* Attach an instance of the audio810 driver. This routine does the
* device dependent attach tasks, and registers with the audio framework.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_attach_cmd_t cmd Attach command
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
const char *name;
const char *vers;
int maxch;
/* allocate the soft state structure */
goto error;
}
/* map in the registers, allocate DMA buffers, etc. */
goto error;
}
/* set PCI command register */
goto error;
}
devid <<= 16;
name = "Unknown AC'97";
vers = "";
switch (devid) {
case 0x80862415:
name = "Intel AC'97";
vers = "ICH";
break;
case 0x80862425:
name = "Intel AC'97";
vers = "ICH0";
break;
case 0x80867195:
name = "Intel AC'97";
vers = "440MX";
break;
case 0x80862445:
name = "Intel AC'97";
vers = "ICH2";
break;
case 0x80862485:
name = "Intel AC'97";
vers = "ICH3";
break;
case 0x808624C5:
name = "Intel AC'97";
vers = "ICH4";
break;
case 0x808624D5:
name = "Intel AC'97";
vers = "ICH5";
break;
case 0x8086266E:
name = "Intel AC'97";
vers = "ICH6";
break;
case 0x808627DE:
name = "Intel AC'97";
vers = "ICH7";
break;
case 0x808625A6:
name = "Intel AC'97";
vers = "6300ESB";
break;
case 0x80862698:
name = "Intel AC'97";
vers = "ESB2";
break;
case 0x10397012:
name = "SiS AC'97";
vers = "7012";
break;
case 0x10de01b1: /* nForce */
name = "NVIDIA AC'97";
vers = "MCP1";
break;
case 0x10de006a: /* nForce 2 */
name = "NVIDIA AC'97";
vers = "MCP2";
break;
case 0x10de00da: /* nForce 3 */
name = "NVIDIA AC'97";
vers = "MCP3";
break;
case 0x10de00ea:
name = "NVIDIA AC'97";
vers = "CK8S";
break;
case 0x10de0059:
name = "NVIDIA AC'97";
vers = "CK804";
break;
case 0x10de008a:
name = "NVIDIA AC'97";
vers = "CK8";
break;
case 0x10de003a: /* nForce 4 */
name = "NVIDIA AC'97";
vers = "MCP4";
break;
case 0x10de026b:
name = "NVIDIA AC'97";
vers = "MCP51";
break;
case 0x1022746d:
name = "AMD AC'97";
vers = "8111";
break;
case 0x10227445:
name = "AMD AC'97";
vers = "AMD768";
break;
}
/* set device information */
/* initialize audio controller and AC97 codec */
goto error;
}
/* allocate ac97 handle */
statep);
goto error;
}
/* initialize the AC'97 part */
goto error;
}
/*
* Override "max-channels" property to prevent configuration
* of 4 or 6 (or possibly even 8!) channel audio. The default
* is to support as many channels as the hardware can do.
*
* (Hmmm... perhaps this should be driven in the common
* framework. The framework could even offer simplistic upmix
* and downmix for various standard configs.)
*/
if (maxch < 2) {
maxch = 2;
}
if (gsr & I810_GSR_CAP6CH) {
nch = 6;
} else if (gsr & I810_GSR_CAP4CH) {
nch = 4;
} else {
nch = 2;
}
/* allocate port structures */
DDI_SUCCESS) ||
goto error;
}
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* audio810_resume()
*
* Description:
* Resume operation of the device after sleeping or hibernating.
* Note that this should never fail, even if hardware goes wonky,
* because the current PM framework will panic if it does.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was resumed.
*/
static int
{
/* this should always be valid */
/* Restore the audio810 chip's state */
/*
* Note that PM gurus say we should return success
* here. Failure of audio shouldn't be considered
* FATAL to the system.
*
* It turns out that the only way that the
* audio810_chip_init fails is that the codec won't
* re-initialize. Audio streams may or may not make
* progress; setting changes may or may not have the
* desired effect. What we'd really to do at this
* point is use FMA to offline the part. In the
* meantime, we just muddle on logging the error.
*
* Note that returning from this routine without
* allowing the audio_dev_resume() to take place can
* have bad effects, as the framework does not know
* what to do in the event of a failure of this
* nature. (It may be unsafe to call ENG_CLOSE(), for
* example.)
*/
}
/* Reset the AC'97 codec. */
/* And let the framework know we're ready for business again. */
return (DDI_SUCCESS);
}
/*
* audio810_detach()
*
* Description:
* Detach an instance of the audio810 driver.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached
*/
static int
{
/* don't detach us if we are still in use */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audio810_suspend()
*
* Description:
* Suspend an instance of the audio810 driver, in preparation for
* sleep or hibernation.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was suspended
*/
static int
{
/* stop DMA engines - should be redundant (paranoia) */
return (DDI_SUCCESS);
}
/*
* audio810_alloc_port()
*
* Description:
* This routine allocates the DMA handles and the memory for the
* DMA engines to use. It also configures the BDL lists properly
* for use.
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
*
* Returns:
* DDI_SUCCESS Registers successfully mapped
* DDI_FAILURE Registers not successfully mapped
*/
static int
{
int dir;
unsigned caps;
int rc;
switch (num) {
case I810_PCM_IN:
dir = DDI_DMA_READ;
break;
case I810_PCM_OUT:
dir = DDI_DMA_WRITE;
break;
default:
return (DDI_FAILURE);
}
/*
* SiS 7012 swaps status and picb registers.
*/
} else {
}
/*
* We use one big sample area. The sample area must be larger
* than about 1.5 framework fragment sizes. (Currently 480 *
* 1.5 = 720 frames.) This is necessary to ensure that we
* don't have to involve an interrupt service routine on our
* own, to keep the last valid index updated reasonably.
*/
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* allocate DMA buffer */
if (rc == DDI_FAILURE) {
return (DDI_FAILURE);
}
/* bind DMA buffer */
"ddi_dma_addr_bind_handle failed: %d", rc);
return (DDI_FAILURE);
}
/*
* now, from here we allocate DMA memory for buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* we allocate all buffer descriptors lists in continuous dma memory.
*/
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Wire up the BD list.
*/
for (int i = 0; i < I810_BD_NUMS; i++) {
/* set base address of buffer */
port->samp_paddr);
/* SiS 7012 counts in bytes, all others in words */
bdentry++;
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audio810_free_port()
*
* Description:
* This routine unbinds the DMA cookies, frees the DMA buffers,
* deallocates the DMA handles.
*
* Arguments:
* audio810_port_t *port The port structure for a DMA engine.
*/
static void
{
return;
}
}
}
}
if (port->samp_paddr) {
}
}
}
}
/*
* audio810_map_regs()
*
* Description:
* The registers are mapped in.
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
*
* Returns:
* DDI_SUCCESS Registers successfully mapped
* DDI_FAILURE Registers not successfully mapped
*/
static int
{
int *regs_list;
int i;
int pciBar1 = 0;
int pciBar2 = 0;
int pciBar3 = 0;
int pciBar4 = 0;
/* check the "reg" property to get the length of memory-mapped I/O */
goto error;
}
/*
* and 0x14 BAR separately for native audio mixer BAR and native bus
* mastering BAR. More advanced hardwares, such as Intel ICH4 and ICH5,
* support PCI memory BAR, via PCI 0x18 and 0x1C BAR, that allows for
* higher performance access to the controller register. All features
* can be accessed via this BAR making the I/O BAR (PCI 0x10 and 0x14
* BAR) capabilities obsolete. However, these controller maintain the
* I/O BAR capability to allow for the reuse of legacy code maintaining
* backward compatibility. The I/O BAR is disabled unless system BIOS
* enables the simultaneous backward compatible capability on the 0x41
* register.
*
* When I/O BAR is enabled, the value of "reg" property should be like
* this,
* phys_hi phys_mid phys_lo size_hi size_lo
* --------------------------------------------------------
* 0000fd00 00000000 00000000 00000000 00000000
* 0100fd10 00000000 00000000 00000000 00000100
* 0100fd14 00000000 00000000 00000000 00000040
* 0200fd18 00000000 00000000 00000000 00000200
* 0200fd1c 00000000 00000000 00000000 00000100
*
* When I/O BAR is disabled, the "reg" property of the device node does
* not consist of the description for the I/O BAR. The following example
* illustrates the vaule of "reg" property,
*
* phys_hi phys_mid phys_lo size_hi size_lo
* --------------------------------------------------------
* 0000fd00 00000000 00000000 00000000 00000000
* 0200fd18 00000000 00000000 00000000 00000200
* 0200fd1c 00000000 00000000 00000000 00000100
*
* If the hardware has memory-mapped I/O access, first try to use
* this facility, otherwise we will try I/O access.
*/
case 0x10:
pciBar1 = i;
break;
case 0x14:
pciBar2 = i;
break;
case 0x18:
pciBar3 = i;
break;
case 0x1c:
pciBar4 = i;
break;
default: /* we don't care others */
break;
}
}
/* map audio mixer registers */
"memory am mapping failed");
goto error;
}
/* map bus master register */
"memory bm mapping failed");
goto error;
}
/* map audio mixer registers */
goto error;
}
/* map bus master register */
goto error;
}
} else {
goto error;
}
return (DDI_SUCCESS);
if (nregs > 0) {
}
return (DDI_FAILURE);
}
/*
* audio810_unmap_regs()
*
* Description:
* This routine unmaps control registers.
*
* Arguments:
* audio810_state_t *state The device's state structure
*/
static void
{
if (statep->bm_regs_handle) {
}
if (statep->am_regs_handle) {
}
}
/*
* audio810_chip_init()
*
* Description:
* This routine initializes the audio controller.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS The hardware was initialized properly
* DDI_FAILURE The hardware couldn't be initialized properly
*/
static int
{
int loop;
/*
* Clear the channels bits for now. We'll set them later in
* reset port.
*/
} else {
}
/*
* Datasheet(ICH5, document number of Intel: 252751-001):
* 3.6.5.5(page 37)
* if reset bit(bit1) is "0", driver must set it
* to "1" to de-assert the AC_RESET# signal in AC
* link, thus completing a cold reset. But if the
* bit is "1", then a warm reset is required.
*/
/* according AC'97 spec, wait for codec reset */
if ((gcr & I810_GCR_WARM_RST) == 0) {
break;
}
}
/* codec reset failed */
if (loop < 0) {
return (DDI_FAILURE);
}
/*
* Wait for codec ready. The hardware can provide the state of
* codec ready bit on SDATA_IN[0], SDATA_IN[1] or SDATA_IN[2]
*/
if ((gsr & codec_ready) != 0) {
break;
}
}
if (loop < 0) {
return (DDI_FAILURE);
}
/*
* put the audio controller into quiet state, everything off
*/
return (DDI_SUCCESS);
}
/*
* audio810_set_channels()
*
* Description:
* This routine initializes the multichannel configuration.
*
* Arguments:
* audio810_state_t *state The device's state structure
*/
static void
{
/*
* Configure multi-channel.
*/
/*
* SiS 7012 needs its own special multichannel config.
*/
case 2:
break;
case 4:
break;
case 6:
break;
}
} else {
/*
* All other devices work the same.
*/
case 2:
break;
case 4:
break;
case 6:
break;
}
}
}
/*
* audio810_stop_dma()
*
* Description:
* This routine is used to put each DMA engine into the quiet state.
*
* Arguments:
* audio810_state_t *state The device's state structure
*/
static void
{
return;
}
/* pause bus master (needed for the following reset register) */
/* and then reset the bus master registers for a three DMA engines */
}
/*
* audio810_codec_sync()
*
* Description:
* Serialize access to the AC97 audio mixer registers.
*
* Arguments:
* audio810_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS Ready for an I/O access to the codec
* DDI_FAILURE An I/O access is currently in progress, can't
* perform another I/O access.
*/
static int
{
int i;
for (i = 0; i < 300; i++) {
if ((casr & 1) == 0) {
return (DDI_SUCCESS);
}
drv_usecwait(10);
}
return (DDI_FAILURE);
}
/*
* audio810_write_ac97()
*
* Description:
* Set the specific AC97 Codec register.
*
* Arguments:
* void *arg The device's state structure
* uint8_t reg AC97 register number
* uint16_t data The data want to be set
*/
static void
{
}
}
/*
* audio810_read_ac97()
*
* Description:
* Get the specific AC97 Codec register.
*
* Arguments:
* void *arg The device's state structure
* uint8_t reg AC97 register number
*
* Returns:
* The register value.
*/
static uint16_t
{
}
return (val);
}
/*
* audio810_destroy()
*
* Description:
* This routine releases all resources held by the device instance,
* as part of either detach or a failure in attach.
*
* Arguments:
* audio810_state_t *state The device soft state.
*/
void
{
/* stop DMA engines */
for (int i = 0; i < I810_NUM_PORTS; i++) {
}
}