audiots.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.
*/
/*
* audiots Audio Driver
*
* This Audio Driver controls the T2 audio core in the ALI M1553
* southbridge chip. This chip supports multiple play streams, but just
* a single record stream. It also supports wave table synthesis and
* hardware MIDI and joystick ports. Unfortunately the MIDI ports are
* not available because their pins have been re-assigned to expose
* interrupts. We also aren't going to do anything with the joystick
* ports. The audio core controls an AC-97 V2.1 Codec.
*
* The DMA engine uses a single buffer which is large enough to hold
* two interrupts worth of data. When it gets to the mid point an
* interrupt is generated and data is either sent (for record) or
* requested and put in that half of the buffer (for play). When the
* second half is played we do the same, but the audio core loops the
* pointer back to the beginning.
*
* The audio core has a bug in silicon that doesn't let it read the AC-97
* Codec's register. T2 has provided an algorithm that attempts to read the
* the Codec several times. This is probably heuristic and thus isn't
* absolutely guaranteed to work. However we do have to place a limit on
* the looping, otherwise when we read a valid 0x00 we would never exit
* the loop. Unfortunately there is also a problem with writing the AC-97
* Codec's registers as well. Thus we read it back to verify the write.
*
* The AC'97 common code provides shadow state for AC'97 registers for us,
* so we only need to read those registers during early startup (primarily
* to determine codec id and capabilities.)
*
* We don't save any of the audio controller registers during normal
* operation. When we need to save register state we only have to save
* the aram and eram. The rest of the controller state is never modified
* from the initial programming. Thus restoring the controller state
* can be done from audiots_chip_init() as well.
*
*
* WARNING: The SME birdsnest platform uses a PCI bridge chip between the
* CPU and the southbridge containing the audio core. There is
* a bug in silicon that causes a bogus parity error. With the mixer
* reimplementation project, Bug 4374774, the audio driver is always
* set to the best precision and number of channels. Thus when turning
* the mixer on and off the only thing that changes is the sample rate.
* This change in programming doesn't trigger the silicon error.
* Thus the supported channels must always be 2 and the precision
* must always be 16-bits. This will keep any future change in the
* mixer from exposing this bug.
*
* Due to a hardware bug, system power management is not supported by this
* driver.
*
* CAUTION: If audio controller state is changed outside of aram
* and eram then that information must be saved and restored
* during power management shutdown and bringup.
*
* NOTE: The AC-97 Codec's reset pin is set to PCI reset, so we
* can't power down the Codec all the way.
*
* modules being loaded first.
*
* NOTE: Don't OR the ap_stop register to stop a play or record. This
* will just stop all active channels because a read of ap_stop
* returns ap_start. Just set the ap_stop register with the
* channels you want to stop. The same goes for ap_start.
*
* NOTE: There is a hardware problem with P2 rev motherboards. After
* prolonged use, reading the AC97 register will always return
* busy. The AC97 register is now useless. Consequently, we are no
* longer able to program the Codec. This work around disables
* audio when this state is detected. It's not great, but its
* better than having audio blasting out at 100% all the time.
*
* NOTE: Power Management testing has also exposed this AC97 timeout
* problem. Management has decided this is too risky for customers
* and hence they want power management support removed from the
* audio subsystem. All PM support is now removed.
*/
#include "audiots.h"
/*
* Module linkage routines for the kernel
*/
static int audiots_quiesce(dev_info_t *);
/*
* Entry point routine prototypes
*/
static int audiots_open(void *, int, unsigned *, unsigned *, caddr_t *);
static void audiots_close(void *);
static int audiots_start(void *);
static void audiots_stop(void *);
static int audiots_format(void *);
static int audiots_channels(void *);
static int audiots_rate(void *);
static void audiots_chinfo(void *, int, unsigned *, unsigned *);
static uint64_t audiots_count(void *);
static void audiots_sync(void *, unsigned);
static size_t audiots_qlen(void *);
static audio_engine_ops_t audiots_engine_ops = {
};
/*
* Local Routine Prototypes
*/
static void audiots_power_up(audiots_state_t *);
static void audiots_chip_init(audiots_state_t *);
static void audiots_update_port(audiots_port_t *);
static void audiots_start_port(audiots_port_t *);
static void audiots_stop_port(audiots_port_t *);
static void audiots_stop_everything(audiots_state_t *);
static void audiots_destroy(audiots_state_t *);
static int audiots_alloc_port(audiots_state_t *, int);
static void audiots_reset_port(audiots_port_t *);
/*
* Global variables, but viewable only by this file.
*/
/* anchor for soft state structures */
static void *audiots_statep;
/* driver name, so we don't have to call ddi_driver_name() or hard code strs */
static char *audiots_name = TS_NAME;
/*
* DDI Structures
*/
/* Device operations structure */
static struct dev_ops audiots_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audiots_attach, /* devo_attach */
audiots_detach, /* devo_detach */
nodev, /* devo_reset */
NULL, /* devo_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
audiots_quiesce, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
static struct modldrv audiots_modldrv = {
&mod_driverops, /* drv_modops */
TS_MOD_NAME, /* drv_linkinfo */
&audiots_dev_ops /* drv_dev_ops */
};
/* Module linkage structure */
static struct modlinkage audiots_modlinkage = {
MODREV_1, /* ml_rev */
(void *)&audiots_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* NOTE: Grover OBP v4.0.166 and rev G of the ALI Southbridge chip force the
* audiots driver to use the upper 2 GB DMA address range. However to maintain
* 4 GB DMA range.
*
* Eventually, this will be set back to using the proper high 2 GB DMA range.
*/
/* Device attribute structure - full 4 gig address range */
static ddi_dma_attr_t audiots_attr = {
DMA_ATTR_VERSION, /* version */
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x0000000000003fffLL, /* DMA counter register - 16 bits */
0x0000000000000008LL, /* DMA address alignment, 64-bit */
0x0000007f, /* 1 through 64 byte burst sizes */
0x00000001, /* min effective DMA size */
0x0000000000003fffLL, /* maximum transfer size, 16k */
0x000000000000ffffLL, /* segment boundary, 64k */
0x00000001, /* s/g list length, no s/g */
0x00000001, /* granularity of device, don't care */
0 /* DMA flags */
};
static ddi_device_acc_attr_t ts_acc_attr = {
};
static ddi_device_acc_attr_t ts_regs_attr = {
};
/*
* _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:
* ddi_soft_state_init() status, see ddi_soft_state_init(9f), or
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
/* initialize the soft state */
sizeof (audiots_state_t), 1)) != 0) {
return (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);
}
/* free the soft state internal structures */
/* clean up ops */
return (0);
}
/*
* _info()
*
* Description:
* Module information, returns infomation about the driver.
*
* Arguments:
* modinfo *modinfop Pointer to the opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
int error;
return (error);
}
/*
* audiots_attach()
*
* Description:
* Attach an instance of the audiots driver. This routine does the
* device dependent attach tasks.
*
* 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
{
int instance;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/* we've already allocated the state structure so get ptr */
NULL) {
/* this will probably panic */
"!%s%d: RESUME get soft state failed",
return (DDI_FAILURE);
}
"check port, gain, balance, and mute settings");
/* and clear the fault state flags */
}
/*
* Initialize/reset ports. Done under the lock, to
* avoid race with interrupt service routine.
*/
for (int i = 0; i < TS_NUM_PORTS; i++) {
/* relocate any streams properly */
/* do a hardware reset on the port */
if (port->tp_started) {
} else {
}
}
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* before we do anything make sure that we haven't had a h/w failure */
return (DDI_FAILURE);
}
/* we don't support high level interrupts in this driver */
if (ddi_intr_hilevel(dip, 0) != 0) {
return (DDI_FAILURE);
}
/* allocate the state structure */
return (DDI_FAILURE);
}
/*
* WARNING: From here on all errors require that we free memory,
* including the state structure.
*/
/* get the state structure - cannot fail */
goto error;
}
/* map in the registers, allocate DMA buffers, etc. */
goto error;
}
/* initialize the audio state structures */
goto error;
}
/* power up */
/* initialize the audio controller */
/* initialize the AC-97 Codec */
goto error;
}
/* put the engine interrupts into a known state -- all off */
/* call the framework attach routine */
goto error;
}
/* set up kernel statistics */
}
/* set up the interrupt handler */
"failed to register interrupt handler");
goto error;
}
/* everything worked out, so report the device */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* audiots_detach()
*
* Description:
* Detach an instance of the audiots driver.
*
* 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
{
int instance;
/* get the state structure */
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_DETACH:
break;
case DDI_SUSPEND:
/* we may already be powered down, so only save state if up */
/* stop playing and recording */
(void) audiots_stop_everything(state);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/* attempt to unregister from the framework first */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audiots_quiesce()
*
* Description:
* Quiesce an instance of the audiots driver. Stops all DMA and
* interrupts.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was quiesced
* DDI_SUCCESS The driver was NOT quiesced
*/
static int
{
int instance;
/* get the state structure */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audiots_power_up()
*
* Description
* Ensure that the device is running in PCI power state D0.
*/
static void
{
/* does not implement PCI capabilities -- no PM */
return;
}
for (;;) {
if (ptr == PCI_CAP_NEXT_PTR_NULL) {
/* PM capability not found */
return;
}
/* found it */
break;
}
}
/* if we got here, then got valid PMCSR pointer */
/* check to see if we are already in state D0 */
/* D3hot (or any other state) -> D0 */
pmcsr |= PCI_PMCSR_D0;
}
/*
* Wait for it to power up - PCI spec says 10 ms is enough.
* We double it. Note that no locks are held when this routine
* is called, so we can sleep (we are in attach context only).
*
* We do this delay even if already powerd up, just to make
* sure we aren't seeing something that *just* transitioned
* into D0 state.
*/
/* clear PME# flag */
}
/*
* audiots_chip_init()
*
* Description:
* Initialize the audio core.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
int str;
/* start with all interrupts & dma channels disabled */
/* set global music and wave volume to 0dB */
/* enable end interrupts for all channels. */
/* for each stream, set gain and vol settings */
/*
* Set volume to all off, 1st left and then right.
* These are never changed, so we don't have to save them.
*/
/*
* The envelope engine *MUST* remain in still mode (off).
* Otherwise bad things like gain randomly disappearing might
* happen. See bug #4332773.
*/
/* program the initial eram and aram rate */
1 << TS_SRC_SHIFT);
}
/* program channel 31 for record */
/* do a warm reset, which powers up the Codec */
drv_usecwait(2);
/* do a warm reset via the Codec, yes, I'm being paranoid! */
/* Make sure the Codec is powered up. */
int i = TS_WAIT_CNT;
PCSR_POWERD_UP) != PCSR_POWERD_UP && i--) {
drv_usecwait(1);
}
}
/*
* audiots_get_ac97()
*
* Description:
* Get the value in the specified AC-97 Codec register. There is a
* bug in silicon which forces us to do multiple reads of the Codec's
* register. This algorithm was provided by T2 and is heuristic in
* nature. Unfortunately we have no guarantees that the real answer
* isn't 0x0000, which is what we get when a read fails. So we loop
* TS_LOOP_CNT times before we give up. We just have to hope this is
* sufficient to give us the correct value.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
*
* Returns:
* unsigned short The value in the specified register
*/
static uint16_t
{
int count;
int delay;
} else {
}
/* make sure the register is good */
break;
}
while (delay--) {
}
break;
}
}
/*
* Arggg, if you let the next read happen too soon then it fails.
* 12 usec fails, 13 usec succeeds. So set it to 20 for safety.
*/
return (next);
}
/*
* audiots_init_state()
*
* Description:
* This routine initializes the audio driver's state structure.
* This includes reading the properties.
*
* CAUTION: This routine cannot allocate resources, unless it frees
* them before returning for an error. Also, error_destroy:
* in audiots_attach() would need to be fixed as well.
*
* NOTE: birdsnest supports CD ROM input. We check for the cdrom
* property. If there we turn it on.
*
* Arguments:
* audiots_state_t *state The device's state structure
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS State structure initialized
* DDI_FAILURE State structure not initialized
*/
static int
{
return (DDI_FAILURE);
}
/* save the device info pointer */
/* get the iblock cookie needed for interrupt context */
"cannot get iblock cookie");
return (DDI_FAILURE);
}
/* initialize the state mutexes and condition variables */
for (int i = 0; i < TS_NUM_PORTS; i++) {
return (DDI_FAILURE);
}
}
/* init power management state */
return (DDI_SUCCESS);
}
/*
* audiots_intr()
*
* Description:
* Interrupt service routine for both play and record. For play we
* get the next buffers worth of audio. For record we send it on to
* the mixer.
*
* NOTE: This device needs to make sure any PIO access required to clear
* its interrupt has made it out on the PCI bus before returning from its
* interrupt handler so that the interrupt has been deasserted. This is
* done by rereading the address engine interrupt register.
*
* Arguments:
* caddr_t T Pointer to the interrupting device's state
* structure
*
* Returns:
* DDI_INTR_CLAIMED Interrupt claimed and processed
* DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored
*/
static uint_t
{
audiots_state_t *state = (void *)T;
if (state->ts_suspended) {
return (DDI_INTR_UNCLAIMED);
}
if (interrupts == 0) {
/* no interrupts to process, so it's not us */
return (DDI_INTR_UNCLAIMED);
}
/*
* Clear the interrupts to acknowledge. Also, reread the
* interrupt reg to ensure that PIO write has completed.
*/
/* update the kernel interrupt statistics */
}
for (int i = 0; i < TS_NUM_PORTS; i++) {
(!port->tp_started))
continue;
if (i == TS_INPUT_PORT) {
} else {
}
}
return (DDI_INTR_CLAIMED);
}
/*
* audiots_map_regs()
*
* Description:
* This routine maps the registers in.
*
* Once the config space registers are mapped in we determine if the
* audio core may be power managed. It should, but if it doesn't,
* then trying to may cause the core to hang.
*
* CAUTION: Make sure all errors call audio_dev_warn().
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
* audiots_state_t *state The device's state structure
* Returns:
* DDI_SUCCESS Registers successfully mapped
* DDI_FAILURE Registers not successfully mapped
*/
static int
{
char rev[16];
char *name;
/* map in the registers, the config and memory mapped registers */
"unable to map PCI configuration space");
return (DDI_FAILURE);
}
/* Read the Audio Controller's vendor, device, and revision IDs */
DDI_SUCCESS) {
"unable to map PCI device registers");
return (DDI_FAILURE);
}
case 0x10b95451:
name = "ALI M5451";
break;
default:
name = "audiots";
break;
}
return (DDI_SUCCESS);
}
/*
* audiots_alloc_port()
*
* Description:
* This routine allocates the DMA handles and the memory for the
* DMA engines to use. It then binds each of the buffers to its
* respective handle, getting a DMA cookie.
*
* NOTE: All of the ddi_dma_... routines sleep if they cannot get
* memory. This means these calls should always succeed.
*
* NOTE: ddi_dma_alloc_handle() attempts to use the full 4 GB DMA address
* range. This is to work around Southbridge rev E/G OBP issues.
* (See Grover OBP note above)
*
* CAUTION: Make sure all errors call audio_dev_warn().
*
* Arguments:
* audiots_port_t *state The port structure for a device stream
* int num The port number
*
* Returns:
* DDI_SUCCESS DMA resources mapped
* DDI_FAILURE DMA resources not successfully mapped
*/
int
{
char *prop;
int dir;
unsigned caps;
unsigned count;
int rc;
char *namestr;
if (num == TS_INPUT_PORT) {
prop = "record-interrupts";
dir = DDI_DMA_READ;
} else {
prop = "play-interrupts";
dir = DDI_DMA_WRITE;
port->tp_dma_stream = 0;
}
/* get the number of interrupts per second */
/* make sure the values are good */
}
/*
* Now allocate space. We configure for the worst case. The
* worst (biggest) case is 48000 kHz, at 4 bytes per frame
* (16-bit stereo), with the lowest interrupt frequency. We
* need two fragments though, and each half has to be rounded
* up to allow for alignment considerations.
*/
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* allocate DMA buffer */
if (rc == DDI_FAILURE) {
return (DDI_FAILURE);
}
/* bind DMA buffer */
if (rc != DDI_DMA_MAPPED) {
"ddi_dma_addr_bind_handle failed: %d", rc);
return (DDI_FAILURE);
}
} else {
}
return (DDI_FAILURE);
}
/*
* SPARCLE platform specific hack. For reasons that I can't
* seem to fathom, the SPARCLE platform only gets the
* endianness correct when transferring whole 32-bit words and
* using little endian mapping. That isn't compatible with
* the audio framework's access mode, so we have to set up
* explicit swapping of the left and right channels.
*
* The real mystery here is why this is required for Tadpole
* SPARCLE, but not for any other standard Sun platforms that
* I've tested.
*
* "swap_channels" property can be used in driver.conf to
*/
"name", &namestr);
if (rc == DDI_PROP_SUCCESS) {
}
}
state->ts_swapped);
return (DDI_SUCCESS);
}
/*
* audiots_read_ac97()
*
* Description:
* This routine actually reads the AC-97 Codec's register. It may
* be called several times to succeed.
*
* NOTE:
* Revision M1535D B1-C of the ALI SouthBridge includes a workaround for
* the broken busy flag. Resetting the busy flag requires a software tweak
* to go with the worked around hardware. When we detect failure, we make
* 10 attempts to reset the chip before we fail. This should reset the new
* SB systems. On all SB systems, this will increse the read delay
* slightly, but shouldn't bother it otherwise.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
*
* Returns:
* unsigned short The value in the specified register
*/
static uint16_t
{
int resets = 0;
int i;
} else {
}
/* wait for ready to send read request */
for (i = 0; i < TS_READ_TRIES; i++) {
break;
}
/* don't beat on the bus */
drv_usecwait(1);
}
if (i >= TS_READ_TRIES) {
if (resets < TS_RESET_TRIES) {
/* Attempt to reset */
resets++;
goto first_read;
} else {
"Unable to communicate with AC97 CODEC");
"The audio AC97 register has timed out.");
"Audio is now disabled.");
"Please reboot to restore audio.");
/* Don't flood the console */
}
}
return (0);
}
/* program the register to read */
(~AP_ACWR_W_SELECT_WRITE)));
/* hardware bug work around */
i = TS_WAIT_CNT;
i--;
}
resets = 0;
/* wait again for read to send read request */
for (i = 0; i < TS_READ_TRIES; i++) {
break;
}
/* don't beat on the bus */
drv_usecwait(1);
}
if (i >= TS_READ_TRIES) {
if (resets < TS_RESET_TRIES) {
/* Attempt to reset */
resets++;
goto second_read;
} else {
"Unable to communicate with AC97 CODEC");
"The audio AC97 register has timed out.");
"Audio is now disabled.");
"Please reboot to restore audio.");
/* Don't flood the console */
}
}
return (0);
}
} /* audiots_read_ac97() */
/*
* audiots_set_ac97()
*
* Description:
* Set the value in the specified AC-97 Codec register. Just like
* reading the AC-97 Codec, it is possible there is a problem writing
* it as well. So we loop.
*
* Arguments:
* audiots_state_t *state The device's state structure
* int reg AC-97 register number
* uint16_t value The value to write
*
* Returns:
* void
*/
static void
{
int count;
int i;
/* Don't touch the reserved bits on the pre 35D+ SouthBridge */
} else {
}
/* wait for ready to write */
for (i = 0; i < TS_WAIT_CNT; i++) {
/* ready to write */
/* Write the data */
break;
}
}
if (i >= TS_WAIT_CNT) {
/* try again */
continue;
}
/* wait for write to complete */
for (i = 0; i < TS_WAIT_CNT; i++) {
/* done writing */
break;
}
}
/* verify the value written */
/* successfully loaded, so we can return */
return;
}
}
} /* audiots_set_ac97() */
/*
* audiots_reset_port()
*
* Description:
* Initializes the hardware for a DMA engine.
* We only support stereo 16-bit linear PCM (signed native endian).
*
* The audio core uses a single DMA buffer which is divided into two
* halves. An interrupt is generated when the middle of the buffer has
* been reached and at the end. The audio core resets the pointer back
* to the beginning automatically. After the interrupt the driver clears
* the buffer and asks the mixer for more audio samples. If there aren't
* enough then silence is played out.
*
* Arguments:
* audiots_port_t *port The DMA engine to reset
*
* Returns:
* void
*/
static void
{
unsigned delta;
if (state->ts_suspended)
return;
for (int i = 0; i < 2; i++) {
if (i == 0) {
/* first do the DMA stream */
}
} else {
/* else do the interrupt stream */
/* interrupt stream is silent */
}
/* program the sample rate */
/* program the precision, number of channels and loop mode */
/* program the volume settings */
/* set ALPHA and FMS to 0 */
/* set CSO to 0 */
/* set LBA */
/* set ESO */
}
/* stop the DMA & interrupt engines */
/* enable interrupts */
}
/*
* audiots_open()
*
* Description:
* Opens a DMA engine for use. Will also ensure the device is powered
* up if not already done so.
*
* Arguments:
* void *arg The DMA engine to set up
* int flag Open flags
* unsigned *fragfrp Receives number of frames per fragment
* unsigned *nfragsp Receives number of fragments
* caddr_t *bufp Receives kernel data buffer
*
* Returns:
* 0 on success
* errno on failure
*/
static int
{
unsigned nfrag;
/*
* Round up - we have to have a sample that is a whole number
* of 64-bit words. Since our frames are 4 bytes wide, we
* just need an even number of frames.
*/
/*
* This should always be true because we used a worst case
* assumption when calculating the port->tp_size.
*/
return (0);
}
/*
* audiots_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
*
* Returns:
* void
*/
static void
audiots_close(void *arg)
{
}
/*
* audiots_stop()
*
* Description:
* This is called by the framework to stop a port that is
* transferring data.
*
* Arguments:
* void *arg The DMA engine to stop
*
* Returns:
* void
*/
static void
audiots_stop(void *arg)
{
if (port->tp_started) {
}
}
/*
* audiots_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
audiots_start(void *arg)
{
if (!port->tp_started) {
}
return (0);
}
/*
* audiots_chinfo()
*
* Description:
* This is called by the framework to query the channel offsets
* and ordering.
*
* Arguments:
* void *arg The DMA engine to query
* int chan Channel number.
* unsigned *offset Starting offset of channel.
* unsigned *incr Increment (in samples) between frames.
*
* Returns:
* 0 indicating rate array is range instead of enumeration
*/
static void
{
/* if channels are swapped (SPARCLE), then deal with it */
} else {
}
*incr = 2;
}
/*
* audiots_format()
*
* Description:
* Called by the framework to query the format for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* AUDIO_FORMAT_S16_LE.
*/
static int
audiots_format(void *arg)
{
return (AUDIO_FORMAT_S16_LE);
}
/*
* audiots_channels()
*
* Description:
* Called by the framework to query the channnels for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* 2 (Stereo).
*/
static int
audiots_channels(void *arg)
{
return (2);
}
/*
* audiots_rate()
*
* Description:
* Called by the framework to query the sample rates for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* Sample rate in HZ (always 48000).
*/
static int
audiots_rate(void *arg)
{
}
/*
* audiots_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
audiots_count(void *arg)
{
return (val);
}
/*
* audiots_sync()
*
* Description:
* This is called by the framework to synchronize DMA caches.
* We also leverage this do some endian swapping, because on SPARC
* the chip accesses the DMA region using 32-bit little-endian
* accesses. Its not enough to just use the framework's sample
* conversion logic, because the channels will also be backwards.
*
* Arguments:
* void *arg The DMA engine to sync
*
* Returns:
* void
*/
static void
{
}
/*
* audiots_qlen()
*
* Description:
* This is called by the framework to determine on-device queue length.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* hardware queue length not reported by count (0 for this device)
*/
static size_t
audiots_qlen(void *arg)
{
return (0);
}
/*
* audiots_start_port()
*
* Description:
* The audio core uses a single DMA buffer which is divided into two
* halves. An interrupt is generated when the middle of the buffer has
* been reached and at the end. The audio core resets the pointer back
* to the beginning automatically. After the interrupt the driver clears
* the buffer and asks the mixer for more audio samples. If there aren't
* enough then silence is played out.
*
* Arguments:
* audiots_port_t *port The DMA engine to start up
*
* Returns:
* void
*/
static void
{
/* if suspended then do nothing else */
if (state->ts_suspended) {
return;
}
/* make sure it starts playing */
}
/*
* audiots_stop_port()
*
* Description:
* This routine stops a DMA engine.
*
* Arguments:
* audiots_port_t *port The port to stop
*
* Returns:
* void
*/
static void
{
if (state->ts_suspended)
return;
}
/*
* audiots_update_port()
*
* Description:
* This routine updates the ports frame counter from hardware, and
* gracefully handles wraps.
*
* Arguments:
* audiots_port_t *port The port to stop
*
* Returns:
* void
*/
static void
{
unsigned n;
if (state->ts_suspended)
return;
}
/*
* audiots_stop_everything()
*
* Description:
* This routine disables the address engine interrupt for all 32 DMA
* engines. Just to be sure, it then explicitly issues a stop command to
* the address engine and envelope engines for all 32 channels.
*
* NOTE:
*
* There is a hardware bug that generates a spurious interrupt
* when the DMA engines are stopped. It's not consistent - it
* happens every 1 out of 6 stops or so. It will show up as a
* record interrupt. The problem is that once the driver is
* detached or if the system goes into low power mode, nobody
* will service that interrupt. The system will eventually become
* unusable.
*
* Arguments:
* audiots_state_t *state The device's state structure
*
* Returns:
* void
*/
static void
{
return;
}
/*
* audiots_free_port()
*
* Description:
* This routine unbinds the DMA cookies, frees the DMA buffers,
* deallocates the DMA handles.
*
* Arguments:
* audiots_port_t *port The port structure for a device stream.
*
* Returns:
* None
*/
void
{
return;
}
}
}
}
}
/*
* audiots_destroy()
*
* Description:
* This routine releases all resources held by the device instance,
* as part of either detach or a failure in attach.
*
* Arguments:
* audiots_state_t *state The device soft state.
*
* Returns:
* None
*/
void
{
for (int i = 0; i < TS_NUM_PORTS; i++)
}
}