iBoot/apps/EmbeddedIOP/function_audiodsp/iop_audiodsp.c

894 lines
27 KiB
C

/*
* Copyright (C) 2010-2013 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <AssertMacros.h>
#include <platform/memmap.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
#include <iop.h>
#include <qwi.h>
#include <qwi_protocol.h>
#include <EmbeddedIOPProtocol.h>
#include <arch.h>
#include <arch/arm/arm.h>
#include <drivers/audio/audio.h>
#include "clock_stepping.h"
#define USE_DMA 0
#include "iop_audiodsp_protocol.h"
#include "iop_au_interface.h"
#include "loopback_process.h"
#include "timestamper.h"
#include "debug_tap.h"
#include "ae2_mca.h"
#if USE_DMA
#include "ae2_i2s.h"
#include "ae2_dma.h"
#else
#include "loopback_device.h"
#endif
static const uint32_t kDefaultSampleRate = 8000;
static const uint32_t kMaxSampleRate = 48000;
// We configure for 0.004 sec of latency.
static const uint32_t kDefaultLatencyInUS = 4000;
static const uint32_t kMaxLatencyInUS = 40000;
// Describing the MCA data format.
static const uint32_t kDefaultNumChannels = 1;
static const uint32_t kMaxNumChannels = 2;
#if USE_DMA
static const uint32_t kValidChannelBitmask = 1;
#endif
static const uint32_t kDefaultSampleSize = 2;
static const size_t kDefaultDMABufferSize = 0x300;
static const AudioDevice_Index kDataDevice = kMCA_0;
#if USE_DMA
static const uint32_t kDataReceivePort = rMCA0_RXDATA;
static const uint32_t kDataTransmitPort = rMCA0_TXDATA;
#endif
static bool audiodsp_message_process(void);
static int iop_audiodsp_task(void *cfg);
static void iop_audiodsp_sleep(int mode);
IOP_FUNCTION(audiodsp, iop_audiodsp_task, 8*1024, AUDIODSP_CONTROL_CHANNEL);
IOP_SLEEP_HOOK(audiodsp, iop_audiodsp_sleep);
static int audiodsp_channel;
#if USE_DMA
static dma_object_t s_input_dma = NULL;
static dma_object_t s_output_dma = NULL;
static void audiodevice_dma_int_err_handler(void *arg);
static void audiodevice_dma_int_handler(void *arg);
static const size_t kNumDMABuffers = 4;
static int16_t *sBuffer[kNumDMABuffers];
static size_t sCurrentProcessBufferIndex;
static DMALinkedListItem *sLLIDMAInput[kNumDMABuffers], *sLLIDMAOutput[kNumDMABuffers];
#endif
#if !USE_DMA
static loopback_device_t s_loopback_device = NULL;
#endif // USE_DMA
// Loopback processing "globals"
static audio_unit_t s_loopback_au = NULL;
// Audio Unit biquad property.
static void* s_loopback_au_state_property = NULL;
static uint32_t s_loopback_au_state_property_size = 0;
// forward declare here. Make sure to change this if id changes
static const uint32_t k_loopback_au_state_property_id = 10001;
static loopback_process_t s_loopback_process = NULL;
static debug_tap_t sInputTap = NULL;
static debug_tap_t sOutputTap = NULL;
// forward declaration:
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess);
/***
* IMPORTANT!
* We assume that all the SRAM allocations will total less than 0x1000.
* Adjust the SRAM Heap in memmap.h if this assumption changes.
*/
static uint32_t sSRAMMallocOffset = 0;
#ifdef AUDIO_SRAM_RESERVE
static const uint32_t kSRAMMallocMax = AUDIO_SRAM_RESERVE;
#else /* !AUDIO_SRAM_RESERVE */
static const uint32_t kSRAMMallocMax = (uint32_t)-1;
#endif /* AUDIO_SRAM_RESERVE */
void* mallocFromSRAM(uint32_t byteSize)
{
//Make sure its a 4-byte aligned allocation (for DMA xfers)
byteSize = (byteSize + 3) / 4;
byteSize *= 4;
//AUDIO_BASE_ADDR is the start of SRAM in AE2
void *ptr = (void*)(AUDIO_BASE_ADDR + sSRAMMallocOffset);
sSRAMMallocOffset += byteSize;
if (sSRAMMallocOffset > kSRAMMallocMax)
panic("mallocFromSRAM beyond limit");
memset(ptr, 0, byteSize);
return ptr;
}
#if USE_DMA
static bool sDMABuffersInitialized = false;
void
initialize_dma_buffers(void)
{
if (!sDMABuffersInitialized)
{
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
sLLIDMAInput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
sLLIDMAOutput[i] = (DMALinkedListItem *)mallocFromSRAM(sizeof(DMALinkedListItem));
sBuffer[i] = (int16_t *)mallocFromSRAM(kDefaultDMABufferSize);
}
//Setup input chain for infinite quad-buffer looping, control setup handled in startDMA
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
// RX goes into buffer 2 on start, so we start the chain on 3
sLLIDMAInput[i]->destination = (uint32_t)sBuffer[(i+3)%kNumDMABuffers];
sLLIDMAInput[i]->next = sLLIDMAInput[(i+1)%kNumDMABuffers];
sLLIDMAInput[i]->source = kDataReceivePort;
// TX goes from buffer 0 on start, so we start the chain on 1
sLLIDMAOutput[i]->source = (uint32_t)sBuffer[(i+1)%kNumDMABuffers];
sLLIDMAOutput[i]->next = sLLIDMAOutput[(i+1)%kNumDMABuffers];
sLLIDMAOutput[i]->destination = kDataTransmitPort;
}
sDMABuffersInitialized = true;
}
}
void
reset_dma_buffers(void)
{
for(uint32_t i = 0; i < kNumDMABuffers; i++)
{
memset(sBuffer[i], 0, kDefaultDMABufferSize);
}
sCurrentProcessBufferIndex = 1;
}
#endif
static int
iop_audiodsp_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
struct task_event* audiodsp_message_event = (struct task_event*) malloc(sizeof(struct task_event));
dprintf(DEBUG_SPEW, "@@ AudioDSP task starting\n");
check(kIOPAUDIODSP_COMMAND_SIZE == sizeof(IOPAUDIODSP_Command));
/* establish the host communications channel */
event_init(audiodsp_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
audiodsp_channel = qwi_instantiate_channel("audio command",
QWI_ROLE_CONSUMER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
(qwi_channel_hook)event_signal,
audiodsp_message_event);
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
/* Little doubt we'll need VFP/Neon */
arch_task_fp_enable(true);
#endif
// create a audio unit if one does not exist
if (!s_loopback_au)
{
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(kDefaultSampleRate, kDefaultNumChannels, kDefaultSampleSize);
}
for (;;) {
dprintf(DEBUG_SPEW, "@@ waiting for message on audiodsp channel\n");
while (audiodsp_message_process()) {
// eat all available messages
}
event_wait(audiodsp_message_event);
}
return(0);
}
audio_unit_t Create_Loopback_AU(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
audio_unit_t loopback_au = NULL;
dprintf(DEBUG_INFO, "creating loopback\n");
#if USE_SIDETONE
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
#else
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
#endif
if (loopback_au && s_loopback_au_state_property && s_loopback_au_state_property_size)
{
// if we had a previous property
uint32_t propSize = s_loopback_au_state_property_size;
bool propWritable = true;
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
{
if (propWritable && propSize == s_loopback_au_state_property_size)
{
dprintf(DEBUG_INFO, "Restoring previous SidetoneEQ values\n");
if (/*noErr*/0 != AudioUnit_SetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, s_loopback_au_state_property_size))
{
dprintf(DEBUG_INFO, "Warning, could not restore previous SidetoneEQ values\n");
// now what? Destroy the AU and start over again
Destroy_AudioUnit(loopback_au);
loopback_au = NULL;
#if USE_SIDETONE
loopback_au = Create_AUSidetone(sampleRate, channels, kDefaultSampleSize);
#else
loopback_au = Create_AUNull(sampleRate, channels, kDefaultSampleSize);
#endif
}
}
}
}
return loopback_au;
}
void Destroy_Loopback_AU(audio_unit_t loopback_au)
{
if (loopback_au)
{
uint32_t propSize = s_loopback_au_state_property_size;
bool propWritable = true;
if (/*noErr*/0 == AudioUnit_GetPropertyInfo(loopback_au, k_loopback_au_state_property_id, &propSize, &propWritable))
{
// clear the old property block if it won't fit
if (s_loopback_au_state_property && s_loopback_au_state_property_size != propSize)
{
free(s_loopback_au_state_property);
s_loopback_au_state_property = NULL;
s_loopback_au_state_property_size = 0;
}
// if we don't have somewhere to write the properties, create it:
if (!s_loopback_au_state_property && (0 != propSize))
{
s_loopback_au_state_property = malloc(propSize);
s_loopback_au_state_property_size = propSize;
}
// if we do have somewhere to write it, save off what we have
if (s_loopback_au_state_property)
{
dprintf(DEBUG_INFO, "Saving SidetoneEQ values\n");
if (/*noErr*/0 != AudioUnit_GetProperty(loopback_au, k_loopback_au_state_property_id, s_loopback_au_state_property, &s_loopback_au_state_property_size))
{
dprintf(DEBUG_INFO, "Warning, could not save SidetoneEQ values\n");
// for whatever reason, we didn't save the property. free it so we don't try to restore it
free(s_loopback_au_state_property);
s_loopback_au_state_property = NULL;
s_loopback_au_state_property_size = 0;
}
}
}
Destroy_AudioUnit(loopback_au);
loopback_au = NULL;
}
}
#if USE_DMA
// Destroy the loopback processing and AU. Assumes that loopback is not running
bool Destroy_Loopback_Processing()
{
if (s_loopback_process)
{
destroy_loopback_process(s_loopback_process);
s_loopback_process = NULL;
}
if (s_loopback_au)
{
Destroy_Loopback_AU(s_loopback_au);
s_loopback_au = NULL;
}
return true;
}
// Creates the loopback processing and AU. Assumes that loopback is not running
// will always destroy any existing loopbacks
bool Create_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
Destroy_Loopback_Processing();
// initialize the loopback processing. It will be enabled later
// create a audio unit if one does not exist
if (!s_loopback_au)
{
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
}
dprintf(DEBUG_INFO, "loopback is %p\n", s_loopback_au);
// if we have an audio unit, create a loopback proces
if (s_loopback_au && !s_loopback_process)
{
dprintf(DEBUG_INFO, "creating loopback process\n");
s_loopback_process = create_loopback_process(s_loopback_au, framesToProcess, channels, kDefaultSampleSize, kValidChannelBitmask);
}
dprintf(DEBUG_INFO, "loopback process is %p\n", s_loopback_process);
dprintf(DEBUG_INFO, "free mem after %d\n", heap_get_free_mem());
if (s_loopback_process)
{
return true;
}
return false;
}
#endif
static bool s_loopback_enabled = false;
// return true if loop processing stopped
bool Stop_Loopback_Processing()
{
// if loopback processing not running, done
if(!s_loopback_enabled)
{
return !s_loopback_enabled;
}
#if USE_DMA
// log any sort of errors encountered
if (s_input_dma || s_output_dma)
{
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
// from Alexei:
// Ignore the frame errors. They're not causing
// interrupts and they are expected (because L81
// masters BCLK at 12MHz always, which is more than the
// exact number of bits specified in the MCA config;
// unfortunately, there isn't a way to configure MCA to
// say "give me a frame error if there are fewer bits,
// but not if there are more", which is what you'd
// want.)
if (kFrameError == i)
{
continue;
}
uint32_t error_count = ((s_input_dma) ? getErrorCount(s_input_dma, i) : 0) + ((s_output_dma) ? getErrorCount(s_output_dma, i) : 0);
if (error_count)
{
dprintf(DEBUG_CRITICAL, "DMA encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
}
}
}
dprintf(DEBUG_INFO, "Going to stop the DMA\n");
if (s_input_dma)
{
destroy_dma_object(s_input_dma);
s_input_dma = NULL;
}
if (s_output_dma)
{
destroy_dma_object(s_output_dma);
s_output_dma = NULL;
}
dprintf(DEBUG_CRITICAL, "Stopped the DMA\n");
#else
// log any sort of errors encountered
if (s_loopback_device)
{
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
// from Alexei:
// Ignore the frame errors. They're not causing
// interrupts and they are expected (because L81
// masters BCLK at 12MHz always, which is more than the
// exact number of bits specified in the MCA config;
// unfortunately, there isn't a way to configure MCA to
// say "give me a frame error if there are fewer bits,
// but not if there are more", which is what you'd
// want.)
if (kFrameError == i)
{
continue;
}
uint32_t error_count = getErrorCount(s_loopback_device, i);
if (error_count)
{
dprintf(DEBUG_CRITICAL, "loopback encountered %d %s error%s!\n", error_count, kErrorTypeStr[i], error_count > 1 ? "s" : "");
}
}
}
dprintf(DEBUG_INFO, "Going to stop the loopback\n");
if (s_loopback_device)
{
destroy_loopback_device(s_loopback_device);
s_loopback_device = NULL;
}
dprintf(DEBUG_CRITICAL, "Stopped the loopback\n");
#endif
SetClockState(kClockRequestLoopback, kClockValueLow);
s_loopback_enabled = false;
return !s_loopback_enabled;
}
// return true if loopback processing started
bool Start_Loopback_Processing(uint32_t sampleRate, uint32_t channels, uint32_t framesToProcess)
{
dprintf(DEBUG_INFO, "Start_Loopback_Processing(sampleRate %d, channels %d, framesToProcess %d\n", sampleRate, channels, framesToProcess);
if (!Stop_Loopback_Processing())
{
dprintf(DEBUG_CRITICAL, "Could not stop loopback\n");
return false;
}
// if loopback processing already running, done
if (s_loopback_enabled)
{
return s_loopback_enabled;
}
if (sampleRate > 32000)
{
dprintf(DEBUG_INFO, "Increasing clock rate for loopback\n");
SetClockState(kClockRequestLoopback, kClockValueHigh);
}
#if USE_DMA
// create the loopback proessing with this sample rate
if (Create_Loopback_Processing(sampleRate, channels, framesToProcess) && s_loopback_process)
{
dprintf(DEBUG_INFO, "starting up DMA\n");
initialize_dma_buffers();
reset_dma_buffers();
if (s_input_dma)
{
destroy_dma_object(s_input_dma);
s_input_dma = NULL;
}
s_input_dma = create_dma_object(sBuffer[2], kDataDevice, sLLIDMAInput[0], kDirectionIn, framesToProcess*channels*kDefaultSampleSize);
if (s_output_dma)
{
destroy_dma_object(s_output_dma);
s_output_dma = NULL;
}
s_output_dma = create_dma_object(sBuffer[0], kDataDevice, sLLIDMAOutput[0], kDirectionOut, framesToProcess*channels*kDefaultSampleSize);
if (s_input_dma && s_output_dma)
{
setupInterruptHandler(s_input_dma, audiodevice_dma_int_handler, s_loopback_process);
setupErrorHandler(s_input_dma, audiodevice_dma_int_err_handler, NULL);
setupErrorHandler(s_output_dma, audiodevice_dma_int_err_handler, NULL);
startDMAObject(s_input_dma);
startDMAObject(s_output_dma);
s_loopback_enabled = true;
}
else
{
dprintf(DEBUG_CRITICAL, "could not create DMA objects!\n");
}
}
#else
if (s_loopback_au)
{
Destroy_Loopback_AU(s_loopback_au);
s_loopback_au = NULL;
}
dprintf(DEBUG_INFO, "creating loopback\n");
s_loopback_au = Create_Loopback_AU(sampleRate, channels, kDefaultSampleSize);
// create the loopback proessing with this sample rate
if (s_loopback_au)
{
dprintf(DEBUG_INFO, "starting up loopback_device\n");
if (s_loopback_device)
{
destroy_loopback_device(s_loopback_device);
s_loopback_device = NULL;
}
s_loopback_device = create_loopback_device(kDataDevice, s_loopback_au, channels * kDefaultSampleSize);
if (s_loopback_device)
{
s_loopback_enabled = start_loopback_device(s_loopback_device);
}
else
{
dprintf(DEBUG_CRITICAL, "could not create loopback device!\n");
}
}
#endif
return s_loopback_enabled;
}
bool Check_Parameter_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
{
if(command->mModule != kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING)
{
dprintf(DEBUG_CRITICAL, "Invalid kIOPAUDIODSP_OPCODE_SET_PARAMETER received!\n");
return false;
}
if(!s_loopback_au)
{
dprintf(DEBUG_CRITICAL, "No existing loopback processing audio unit!\n");
return false;
}
return true;
}
bool Check_Property_Message_Valid(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
size_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
//make sure its formatted correctly ...
if(command->mProperty.mPropertySizeBytes > max_size_for_property)
{
dprintf(DEBUG_CRITICAL, "Invalid message size received!\n");
return false;
}
return true;
}
bool Handle_OPCODE_GET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_GetParameter(s_loopback_au, command->mParameter.mParameterID, &command->mParameter.mParameterValue))
{
dprintf(DEBUG_CRITICAL, "FAILED getting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
return false;
}
return true;
}
bool Handle_OPCODE_SET_PARAMETER(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Parameter_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_SetParameter(s_loopback_au, command->mParameter.mParameterID, command->mParameter.mParameterValue))
{
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mParameter.mParameterID);
return false;
}
return true;
}
bool Handle_OPCODE_GET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Property_Message_Valid(command))
{
return false;
}
uint32_t max_size_for_property = sizeof(IOPAUDIODSP_Command) - ((uint32_t)&command->mProperty.mPropertyData - (uint32_t)command);
if (AudioUnit_GetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, &max_size_for_property) )
{
dprintf(DEBUG_CRITICAL, "FAILED getting property(%d) on loopback process!\n", command->mProperty.mPropertyID);
return false;
}
command->mProperty.mPropertySizeBytes = max_size_for_property;
return true;
}
bool Handle_OPCODE_SET_PROPERTY(IOPAUDIODSP_MODULE_COMMAND *command)
{
if (!Check_Property_Message_Valid(command))
{
return false;
}
//XXX add support for array of parameters (perhaps with mNumberOfParameterValues)
if (AudioUnit_SetProperty(s_loopback_au, command->mProperty.mPropertyID, command->mProperty.mPropertyData, command->mProperty.mPropertySizeBytes))
{
dprintf(DEBUG_CRITICAL, "FAILED setting parameter(%d) on loopback process!\n", command->mProperty.mPropertyID);
return false;
}
return true;
}
static bool
audiodsp_message_process(void)
{
uint32_t message;
IOPAUDIODSP_Command* command;
dprintf(DEBUG_SPEW, "@@ handling host message\n");
/* look to see if there's an item waiting for us */
if (qwi_receive_item(audiodsp_channel, &message) == -1)
return(false);
dprintf(DEBUG_SPEW, "@@ received audio message\n");
/* find the command structure based on the message */
command = (IOPAUDIODSP_Command*)mem_static_map_cached(message);
/*
* Flush any cached item contents we might have lying around - we are guaranteed
* that the command size is a multiple of our cacheline size.
*/
platform_cache_operation(CACHE_INVALIDATE,
(void *)command,
sizeof(*command));
// assume every command is a failure unless it gets set to success
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_FAILURE;
switch (command->iopaudiodsp.mOpcode) {
case kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING:
{
uint32_t sampleRate = kDefaultSampleRate;
uint32_t channels = kDefaultNumChannels;
uint32_t latency = kDefaultLatencyInUS;
// read values from the paramter block
uint32_t* parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
int32_t parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
if (parameterBlockSize > 0)
{
sampleRate = *parameterBlock;
sampleRate = (sampleRate < kMaxSampleRate) ? sampleRate : kMaxSampleRate;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
latency = *parameterBlock;
latency = (latency < kMaxLatencyInUS) ? latency : kMaxLatencyInUS;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
channels = *parameterBlock;
channels = (channels < kMaxNumChannels) ? channels : kMaxNumChannels;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (!sampleRate || !latency || !channels)
{
dprintf(DEBUG_CRITICAL, "Some parameter not correct (sampleRate = %d, latency = %d, channels = %d)\n", sampleRate, latency, channels);
break;
}
dprintf(DEBUG_CRITICAL, "using values sample rate %d, latency %d, channels %d\n", sampleRate, latency, channels);
uint32_t bytesToProcess = ((sampleRate * latency * channels) / 1000000) * kDefaultSampleSize;
// because we have an input buffer and process buffer, need to cut the value in half
bytesToProcess /= 2;
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
// because the DMA will do burst-4, make sure we have a valid bytes to process size:
uint32_t burstSize = kDefaultSampleSize * 4;
bytesToProcess = ((bytesToProcess + burstSize - 1)/burstSize) * burstSize;
bytesToProcess = (bytesToProcess < kDefaultDMABufferSize) ? bytesToProcess : kDefaultDMABufferSize;
uint32_t framesToProcess = bytesToProcess / (channels * kDefaultSampleSize);
dprintf(DEBUG_CRITICAL, "Start command with sample rate %d, channels %d, frames %d\n", sampleRate, channels, framesToProcess);
// now what would the latency be?
latency = (framesToProcess * 1000000) / sampleRate;
// because we had to half the latency, we need to double it.
latency *= 2;
if (Start_Loopback_Processing(sampleRate, channels, framesToProcess))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
dprintf(DEBUG_CRITICAL, "Started loopback processing!\n");
}
else
{
dprintf(DEBUG_CRITICAL, "Could not start loopback processing!\n");
}
// write values back to the paramter block
parameterBlock = (uint32_t *)command->start.mAdditionalParameters;
parameterBlockSize = command->start.mAdditionalParametersSizeBytes;
if (parameterBlockSize > 0)
{
*parameterBlock = sampleRate;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
*parameterBlock = latency;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
if (parameterBlockSize > 0)
{
*parameterBlock = channels;
++parameterBlock;
parameterBlockSize -= sizeof(parameterBlock[0]);
}
}
break;
case kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING:
{
if (Stop_Loopback_Processing())
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
dprintf(DEBUG_CRITICAL, "Stopped loopback processing!\n");
}
}
break;
case kIOPAUDIODSP_OPCODE_GET_PROPERTY:
{
if (Handle_OPCODE_GET_PROPERTY(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_SET_PROPERTY:
{
if (Handle_OPCODE_SET_PROPERTY(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_GET_PARAMETER:
{
if (Handle_OPCODE_GET_PARAMETER(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_SET_PARAMETER:
{
if (Handle_OPCODE_SET_PARAMETER(&command->module_command))
{
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
}
break;
case kIOPAUDIODSP_OPCODE_INITTIMESTAMP:
{
dprintf(DEBUG_CRITICAL, "Timestamp buffer is 0x%x\n", command->init_timestamp.mTimeStamperBufferAddr);
set_timestamper_message_buffer((IOPAUDIODSP_Command *)mem_static_map_cached(command->init_timestamp.mTimeStamperBufferAddr));
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
break;
case kIOPAUDIODSP_OPCODE_DO_TRANSFER:
{
dprintf(DEBUG_CRITICAL, "Starting Transfer\n");
dprintf(DEBUG_CRITICAL, "Index %d\n", command->do_transfer.mIndex);
dprintf(DEBUG_CRITICAL, "Direction %d\n", command->do_transfer.mDirection);
dprintf(DEBUG_CRITICAL, "Do transfer %d\n", command->do_transfer.mDoTransfer);
dprintf(DEBUG_CRITICAL, "Buffer boundary [0x%x,0x%x), starting at offset 0x%x\n", command->do_transfer.mBufferBegin, command->do_transfer.mBufferEnd, command->do_transfer.mBufferStart);
dprintf(DEBUG_CRITICAL, "Sample Rate %d, bytes per frame %d\n", command->do_transfer.mSampleRate, command->do_transfer.mBytesPerFrame);
bool doTransfer = command->do_transfer.mDoTransfer;
uint32_t index = command->do_transfer.mIndex;
if (s_loopback_process)
{
set_tap_point(s_loopback_process, index);
}
if (doTransfer)
{
uint8_t *IOBufferBegin = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferBegin);
uint8_t *IOBufferEnd = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferEnd);
uint8_t *IOBuffer = (uint8_t*)mem_static_map_cached(command->do_transfer.mBufferStart);
// unload and delete any tap points
set_debug_tap(s_loopback_process, NULL, NULL);
if (sInputTap)
{
destroy_debug_tap(sInputTap);
sInputTap = NULL;
}
if (sOutputTap)
{
destroy_debug_tap(sOutputTap);
sOutputTap = NULL;
}
// create the tap points
if (command->do_transfer.mDirection & 0x1)
{
sInputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
}
if (command->do_transfer.mDirection & 0x2)
{
sOutputTap = create_debug_tap(IOBufferBegin, IOBufferEnd, IOBuffer);
}
// set the tap points
if (s_loopback_process)
{
set_debug_tap(s_loopback_process, sInputTap, sOutputTap);
}
}
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
}
break;
default:
dprintf(DEBUG_CRITICAL, "@@ ERROR: unrecognised audiodsp opcode 0x%x\n",
command->iopaudiodsp.mOpcode);
command->iopaudiodsp.mStatus = kIOPAUDIODSP_STATUS_PARAM_INVALID;
break;
}
dprintf(DEBUG_SPEW, "@@ done processing audiodsp message with status 0x%08x\n", command->iopaudiodsp.mStatus);
platform_cache_operation(CACHE_CLEAN,
(void *)command,
sizeof(IOPAUDIODSP_Command));
qwi_send_item(audiodsp_channel, message);
dprintf(DEBUG_SPEW, "@@ signaled completion of audiodsp message to host\n");
return(true);
}
static void
iop_audiodsp_sleep(int mode)
{
}
#if USE_DMA
static void
audiodevice_dma_int_handler_wFloat(void *arg)
{
sCurrentProcessBufferIndex = (sCurrentProcessBufferIndex+1) % kNumDMABuffers;
process_data((loopback_process_t)arg, sBuffer[sCurrentProcessBufferIndex]);
}
static void
audiodevice_dma_int_handler(void *arg)
{
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
//Enables floating point ops from the ISR
enter_critical_section();
arm_call_fpsaved(arg, &audiodevice_dma_int_handler_wFloat);
exit_critical_section();
#endif
}
static void
audiodevice_dma_int_err_handler(void *arg)
{
dprintf(DEBUG_CRITICAL, "audiodevice_dma_int_err_handler!\n");
}
#endif