first and last commit

This commit is contained in:
Chris Nutter committed 2023-07-08 13:03:17 -07:00
commit 1bf4efd552
2612 files changed
+636500

No files matched your search

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __AE2_DMA_H__
#define __AE2_DMA_H__
typedef void * dma_object_t;
/*
* dma_object_t is an object that can be used to control a dma
*/
typedef enum {
kDirectionNone = 0,
kDirectionIn = 1,
kDirectionOut = 2
} DMADirection;
typedef struct LLI
{
uint32_t source;
uint32_t destination;
struct LLI *next;
uint32_t control;
} DMALinkedListItem;
typedef enum {
kI2S_0 = 0,
kI2S_1,
kI2S_2,
kI2S_3,
kMCA_0,
kMCA_1,
kAudioDevice_Last
} AudioDevice_Index;
// create a dma object that will transfer to/from a buffer from/to a device, depending
// on the direction.
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer);
void destroy_dma_object(dma_object_t dma);
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg);
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg);
typedef enum {
kFrameError = 0,
kRXOverrun,
kRXUnderrun,
kTXOverrun,
kTXUnderrun,
kError_last
} Error_Index;
static const char* const kErrorTypeStr[kError_last] = {
"Frame_Error",
"RX_Overrun",
"RX_Underrun",
"TX_Overrun",
"TX_Underrun",
};
uint32_t getErrorCount(dma_object_t dma, Error_Index which);
void startDMAObject(dma_object_t dma);
void stopDMAObject(dma_object_t dma, bool immediate);
#endif /* __AE2_DMA_H__ */
@@ -0,0 +1,305 @@
/*
* Copyright (C) 2010-2011 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.
*/
/**
* The PIO object pretends to be a DMA for MCA.
*/
#include "pio_ae2.h"
#include "ae2_i2s.h"
#include "ae2_mca.h"
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
//Per AE2 Local DMA Controller/Requests
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
static internal_pio_object_t* sPIOObjects[kNumPIOs] = { NULL };
static uint32_t sPIORunning = 0;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readMCA0Reg(uint32_t offset)
{
return readReg(rMCA0_BASE + offset);
}
void writeMCA0Reg(uint32_t offset, uint32_t value)
{
writeReg(rMCA0_BASE + offset, value);
}
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
{
dprintf(DEBUG_CRITICAL, "Creating a PIO object\n");
// hardcoding to support only MCA right now.
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)) || (device != kMCA_0))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
uint32_t whichPIO = (direction == kDirectionIn) ? kPIOReceive : kPIOTransmit;
// fail if device already exists
if (sPIOObjects[whichPIO])
{
dprintf(DEBUG_CRITICAL, "PIO already exists\n");
return NULL;
}
internal_pio_object_t *This = (internal_pio_object_t*)malloc(sizeof(internal_pio_object_t));
if (This)
{
This->mWhichDevice = whichPIO;
This->mInterruptHandler = NULL;
This->mInterruptHandlerData = NULL;
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
This->mErrorCount[i] = 0;
}
if (direction == kDirectionIn)
{
setupPIO(This, sDMAAddressRX[device], buffer, chain, bytesToTransfer);
}
else
{
setupPIO(This, buffer, sDMAAddressTX[device], chain, bytesToTransfer);
}
sPIOObjects[This->mWhichDevice] = This;
// we can do this here repeatedly
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, NULL);
}
return This;
}
void destroy_dma_object(dma_object_t dma)
{
// preliminary stop
stopDMAObject(dma, true);
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
sPIOObjects[This->mWhichDevice] = NULL;
free(This);
}
}
uint32_t getErrorCount(dma_object_t dma, Error_Index which)
{
// preliminary stop
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
return This->mErrorCount[which];
}
return 0;
}
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
This->mInterruptHandler = handler;
This->mInterruptHandlerData = arg;
}
}
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
{
}
void startDMAObject(dma_object_t dma)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
startPIO(This);
// we wait until two of them are ready before we unmask the int
sPIORunning |= (1 << This->mWhichDevice);
if (sPIORunning == 3)
{
unmask_int(AE2_INT_MCA0);
}
}
}
void stopDMAObject(dma_object_t dma, bool immediate)
{
internal_pio_object_t *This = (internal_pio_object_t*)dma;
if (This)
{
sPIORunning &= ~(1 << This->mWhichDevice);
if (sPIORunning != 3)
{
mask_int(AE2_INT_MCA0);
}
stopPIO(This);
}
}
bool receivePIOData(DMALinkedListItem * item)
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
volatile uint32_t * src = (volatile uint32_t *)item->source;
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
{
data[i] = *src;
}
uint8_t * dst = (uint8_t*)item->destination;
memcpy(dst, data, sizeof(data));
dst += sizeof(data);
item->destination = (uint32_t) dst;
item->control -= sizeof(data);
return !item->control;
}
bool transmitPIOData(DMALinkedListItem * item)
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
uint8_t * src = (uint8_t*)item->source;
memcpy(data, src, sizeof(data));
volatile uint32_t * dst = (volatile uint32_t *)item->destination;
for (size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i)
{
*dst = data[i];
}
src += sizeof(data);
item->source = (uint32_t) src;
item->control -= sizeof(data);
return !item->control;
}
void setupNextLLI(DMALinkedListItem * item)
{
*item = *(item->next);
}
void handleAudioDeviceInterrupt()
{
uint32_t status = readMCA0Reg(rMCASTATUS);
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
// clear any sort of sticky-bit errors.
uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
writeMCA0Reg(rMCASTATUS, errors);
if (errors)
{
if (sPIOObjects[kPIOReceive])
{
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kFrameError];
if (status & rMCASTATUS_RXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXOverrun];
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kRXUnderrun];
if (status & rMCASTATUS_TXOVERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXOverrun];
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++sPIOObjects[kPIOReceive]->mErrorCount[kTXUnderrun];
}
}
if (status & (1 << rMCASTATUS_RXHIGHWATER))
{
if (sPIOObjects[kPIOReceive])
{
if (receivePIOData(&sPIOObjects[kPIOReceive]->mCurrentDMAItem))
{
setupNextLLI(&sPIOObjects[kPIOReceive]->mCurrentDMAItem);
if (sPIOObjects[kPIOReceive]->mInterruptHandler)
{
sPIOObjects[kPIOReceive]->mInterruptHandler(sPIOObjects[kPIOReceive]->mInterruptHandlerData);
}
}
}
}
if (status & (1 << rMCASTATUS_TXLOWWATER))
{
if (sPIOObjects[kPIOTransmit])
{
if (transmitPIOData(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem))
{
setupNextLLI(&sPIOObjects[kPIOTransmit]->mCurrentDMAItem);
if (sPIOObjects[kPIOTransmit]->mInterruptHandler)
{
sPIOObjects[kPIOTransmit]->mInterruptHandler(sPIOObjects[kPIOTransmit]->mInterruptHandlerData);
}
}
}
}
}
void setupPIO(internal_pio_object_t * This, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer)
{
uint32_t transferSize = bytesToTransfer;
uint32_t control = transferSize;
DMALinkedListItem *chainElement = chain;
while(chainElement != NULL)
{
chainElement->control = (uint32_t)control;
chainElement = chainElement->next;
if(chainElement == chain)
{
//We've populated all of the control values in this circular linked list, we are done here
break;
}
}
This->mCurrentDMAItem.source = (uint32_t)src;
This->mCurrentDMAItem.destination = (uint32_t)dst;
This->mCurrentDMAItem.next = chain;
This->mCurrentDMAItem.control = control;
}
void startPIO(internal_pio_object_t * pio_object)
{
if (pio_object)
{
if (pio_object->mWhichDevice == kPIOReceive)
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
}
else
{
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG | (1 << rMCAUNSTXCFG_IRQ_EN));
}
}
}
void stopPIO(internal_pio_object_t * pio_object)
{
if (pio_object)
{
if (pio_object->mWhichDevice == kPIOReceive)
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
}
else
{
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
}
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __PL080DMAC_AE2__
#define __PL080DMAC_AE2__
#include <stdint.h>
#include <platform/int.h>
#include "ae2_dma.h"
typedef enum {
kPIOReceive = 0,
kPIOTransmit = 1,
kNumPIOs = 2,
} ePIOType;
enum { kBurstSize = 4 };
typedef struct
{
ePIOType mWhichDevice;
int_handler mInterruptHandler;
void * mInterruptHandlerData;
DMALinkedListItem mCurrentDMAItem;
uint32_t mErrorCount[kAudioDevice_Last];
} internal_pio_object_t;
void handleAudioDeviceInterrupt();
/*
setup pio. Set the DMA linked list for doing PIO operations.
preps the pio_object for doing it's first pio op
*/
void setupPIO(internal_pio_object_t * pio_object, void *src, void *dst, DMALinkedListItem *chain, size_t bytesToTransfer);
/*
Startup PIO
*/
void startPIO(internal_pio_object_t * pio_object);
/*
Stop PIO
*/
void stopPIO(internal_pio_object_t * pio_object);
#endif /* __PL080DMAC_AE2__ */
@@ -0,0 +1,380 @@
/*
* Copyright (C) 2010-2011 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 "pl080dmac_ae2.h"
#include "ae2_i2s.h"
#include "ae2_mca.h"
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
#ifdef MAP_SRAM_CACHED
#error DMA requires the SRAM to be uncached
#endif
typedef struct
{
uint32_t mDMAChannel;
int_handler mInterruptHandler;
void * mInterruptHandlerData;
int_handler mErrorHandler;
void * mErrorHandlerData;
} internal_dma_object_t;
#define validDMAChannel(dmaChannel) (dmaChannel!=0xffffffff)
//Per AE2 Local DMA Controller/Requests
static const uint32_t sDMARequestInterfaceRx[kAudioDevice_Last] = {0, 2, 4, 6, 10, 6};
static const uint32_t sDMARequestInterfaceTx[kAudioDevice_Last] = {1, 3, 5, 7, 11, 7};
static void* sDMAAddressRX[kAudioDevice_Last] = { (void*)rI2S0_RXDB, (void*)rI2S1_RXDB, (void*)rI2S2_RXDB, (void*)rI2S3_RXDB, (void*)rMCA0_RXDATA, (void*)rMCA1_RXDATA };
static void* sDMAAddressTX[kAudioDevice_Last] = { (void*)rI2S0_TXDB, (void*)rI2S1_TXDB, (void*)rI2S2_TXDB, (void*)rI2S3_TXDB, (void*)rMCA0_TXDATA, (void*)rMCA1_TXDATA };
static internal_dma_object_t* sDMAChannelObjects[kDMAChannelsPerController] = { NULL };
static uint32_t sDMACC0InUse = 0;
static uint32_t sDMACC0Running = 0;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readDMAReg(uint32_t offset)
{
return readReg(kDMACBase + offset);
}
void writeDMAReg(uint32_t offset, uint32_t value)
{
writeReg(kDMACBase + offset, value);
}
uint32_t readDMAChannelReg(uint32_t channel, uint32_t offset)
{
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
return readReg(dmaChannelOffset + offset);
}
void writeDMAChannelReg(uint32_t channel, uint32_t offset, uint32_t value)
{
uint32_t dmaChannelOffset = kDMAC0Base + channel * kDMACRegisterSize;
writeReg(dmaChannelOffset + offset, value);
}
dma_object_t create_dma_object(void *buffer, AudioDevice_Index device, DMALinkedListItem *chain, DMADirection direction, size_t bytesToTransfer)
{
dprintf(DEBUG_CRITICAL, "Creating a DMA object\n");
if ((device >= kAudioDevice_Last) || ((direction != kDirectionIn) && (direction != kDirectionOut)))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
internal_dma_object_t *This = (internal_dma_object_t*)malloc(sizeof(internal_dma_object_t));
if (This)
{
uint32_t dmaChannel = acquireDMAChannel();
dprintf(DEBUG_CRITICAL, "channel is %d\n", dmaChannel);
if (dmaChannel == 0xFFFFFFFF)
{
free(This);
return NULL;
}
This->mDMAChannel = dmaChannel;
This->mInterruptHandler = NULL;
This->mInterruptHandlerData = NULL;
This->mErrorHandler = NULL;
This->mErrorHandlerData = NULL;
configureDMA(This->mDMAChannel);
if (direction == kDirectionIn)
{
setupDMA(This->mDMAChannel, sDMAAddressRX[device], buffer, chain, kDirectionIn, sDMARequestInterfaceRx[device], bytesToTransfer);
}
else
{
setupDMA(This->mDMAChannel, buffer, sDMAAddressTX[device], chain, kDirectionOut, sDMARequestInterfaceTx[device], bytesToTransfer);
}
sDMAChannelObjects[This->mDMAChannel] = This;
// we can do this here repeatedly
install_int_handler(AE2_INT_DMACINTTC, handleAudioDeviceDMAInterrupt, NULL);
install_int_handler(AE2_INT_DMACINTERR, handleAudioDeviceDMAInterruptError, NULL);
}
return This;
}
void destroy_dma_object(dma_object_t dma)
{
// preliminary stop
stopDMAObject(dma, true);
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
sDMAChannelObjects[This->mDMAChannel] = NULL;
freeDMAChannel(This->mDMAChannel);
free(This);
}
}
uint32_t getErrorCount(dma_object_t, Error_Index)
{
return 0;
}
void setupInterruptHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
This->mInterruptHandler = handler;
This->mInterruptHandlerData = arg;
}
}
void setupErrorHandler(dma_object_t dma, int_handler handler, void *arg)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
This->mErrorHandler = handler;
This->mErrorHandlerData = arg;
}
}
void startDMAObject(dma_object_t dma)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
startDMA(This->mDMAChannel);
sDMACC0Running |= (1 << This->mDMAChannel);
if (sDMACC0Running)
{
unmask_int(AE2_INT_DMACINTTC);
unmask_int(AE2_INT_DMACINTERR);
}
}
}
void stopDMAObject(dma_object_t dma, bool immediate)
{
internal_dma_object_t *This = (internal_dma_object_t*)dma;
if (This)
{
if (immediate)
{
disableDMAImmediate(This->mDMAChannel);
}
else
{
disableDMA(This->mDMAChannel);
}
sDMACC0Running &= ~(1 << This->mDMAChannel);
if (!sDMACC0Running)
{
mask_int(AE2_INT_DMACINTTC);
mask_int(AE2_INT_DMACINTERR);
}
}
}
void handleAudioDeviceDMAInterrupt()
{
uint32_t status = readDMAReg(kDMACIntTCStatus);
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
uint32_t mask = 1 << channel;
if (status & mask)
{
writeDMAReg(kDMACIntTCClear, mask); //clear int
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
{
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
}
}
}
}
void handleAudioDeviceDMAInterruptError()
{
uint32_t status = readDMAReg(kDMACIntErrStatus);
for (uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
uint32_t mask = 1 << channel;
if (status & mask)
{
writeDMAReg(kDMACIntErrClear, mask); //clear int
if(sDMAChannelObjects[channel] != NULL && sDMAChannelObjects[channel]->mInterruptHandler)
{
sDMAChannelObjects[channel]->mInterruptHandler(sDMAChannelObjects[channel]->mInterruptHandlerData);
}
}
}
}
uint32_t acquireDMAChannel(void)
{
uint32_t result = 0xFFFFFFFF;
for(uint32_t channel = 0; channel < kDMAChannelsPerController; channel++)
{
if(!(sDMACC0InUse & (1 << channel)))
{
sDMACC0InUse |= (1 << channel);
result = channel;
break;
}
}
if (sDMACC0InUse)
{
writeReg(kDMACClockGating, 0);
writeDMAReg(kDMACConfiguration, DMACConfiguration_M2_Little | DMACConfiguration_M1_Little | DMACConfiguration_E_Enable);
}
return result;
}
void freeDMAChannel(uint32_t dmaChannel)
{
sDMACC0InUse &= ~(1 << dmaChannel);
// if there are no channels in use, shut down DMA
if (!sDMACC0InUse)
{
uint32_t config = readDMAReg(kDMACConfiguration);
writeDMAReg(kDMACConfiguration, config & ~DMACConfiguration_E_Enable);
}
}
void configureDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t control = DMACCxControl_I_Enable |
DMACCxControl_D_Master1 |
DMACCxControl_S_Master1 |
DMACCxControl_SWidth_Halfword |
DMACCxControl_DWidth_Halfword |
DMACCxControl_SBSize_4 |
DMACCxControl_DBSize_4;
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
}
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t transferSize = bytesToTransfer >> 1; //assuming burst size of DMACCxControl_DWidth_Halfword
uint32_t control = readDMAChannelReg(dmaChannel, kDMACCtrlOffset);
control &= ~(DMACCxControl_TransferSizeMask | // Clear transfer size
DMACCxControl_SI_Mask | // Clear source increment
DMACCxControl_DI_Mask); // Clear destination increment
control |= (transferSize & 0x0FFF);
uint32_t flowControl, sourcePeripheral, destinationPeripheral;
if(direction == kDirectionIn) // -> peripheral-to-memory
{
control |= DMACCxControl_SI_No_Increment;
control |= DMACCxControl_DI_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA;
sourcePeripheral = peripheral;
destinationPeripheral = 0; //mem
}
else if(direction == kDirectionOut) //kDirectionOut -> memory-to-peripheral
{
control |= DMACCxControl_SI_Increment;
control |= DMACCxControl_DI_No_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA;
sourcePeripheral = 0; //mem
destinationPeripheral = peripheral;
}
else //kDirectionNone -> mem-to-mem here
{
control |= DMACCxControl_SI_Increment;
control |= DMACCxControl_DI_Increment;
flowControl = DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA;
sourcePeripheral = 0; //mem
destinationPeripheral = 0; //mem
}
DMALinkedListItem *chainElement = chain;
while(chainElement != NULL)
{
chainElement->control = (uint32_t)control;
chainElement = chainElement->next;
if(chainElement == chain)
{
//We've populated all of the control values in this circular linked list, we are done here
break;
}
}
writeDMAChannelReg(dmaChannel, kDMACSourceOffset, (uint32_t)src);
writeDMAChannelReg(dmaChannel, kDMACDestOffset, (uint32_t)dst);
writeDMAChannelReg(dmaChannel, kDMACLLIOffset, (uint32_t)chain);
writeDMAChannelReg(dmaChannel, kDMACCtrlOffset, control);
flowControl |= DMACCxConfiguration_ITC_Enable;
flowControl |= DMACCxConfiguration_IError_Enable;
flowControl |= ((destinationPeripheral & 0xF) << 6);
flowControl |= ((sourcePeripheral & 0xF) << 1);
// do this in the start
//flowControl |= DMACCxConfiguration_E_Enable;
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl);
}
void startDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t flowControl = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, flowControl |= DMACCxConfiguration_E_Enable);
}
void disableDMA(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config | DMACCxConfiguration_Halt);
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
// give a chance for the DMA to timeout. But don't wait forever...
int timeToWait = 0x100;
while(config & DMACCxConfiguration_Active && (timeToWait-- > 0))
{
config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
}
disableDMAImmediate(dmaChannel);
}
void disableDMAImmediate(uint32_t dmaChannel)
{
if(!validDMAChannel(dmaChannel)) return;
uint32_t config = readDMAChannelReg(dmaChannel, kDMACConfigOffset);
writeDMAChannelReg(dmaChannel, kDMACConfigOffset, config & ~DMACCxConfiguration_E_Enable);
writeDMAReg(kDMACIntTCClear, 1 << dmaChannel); // clear any outstanding ints
writeDMAReg(kDMACIntErrClear, 1 << dmaChannel); // clear any outstanding ints
}
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef __PL080DMAC_AE2__
#define __PL080DMAC_AE2__
#include <stdint.h>
#include <platform/int.h>
#include "ae2_dma.h"
#define kDMACClockGating 0x341A0004
#define kDMAChannelsPerController 8
#define kDMACBase 0x341A3000
#define kDMAC0Base 0x341A3100
#define kDMACIntStatus 0x0
#define kDMACIntTCStatus 0x4
#define kDMACIntTCClear 0x8
#define kDMACIntErrStatus 0xC
#define kDMACIntErrClear 0x10
#define kDMACConfiguration 0x30
#define kDMACSourceOffset 0x0
#define kDMACDestOffset 0x4
#define kDMACLLIOffset 0x8
#define kDMACCtrlOffset 0xC
#define kDMACConfigOffset 0x10
#define kDMACRegisterSize 0x20
#define DMACConfiguration_M2_Mask (1 << 2)
#define DMACConfiguration_M2_Little (0 << 2)
#define DMACConfiguration_M2_Big (1 << 2)
#define DMACConfiguration_M1_Mask (1 << 1)
#define DMACConfiguration_M1_Little (0 << 1)
#define DMACConfiguration_M1_Big (1 << 1)
#define DMACConfiguration_E_Mask (1 << 0)
#define DMACConfiguration_E_Disable (0 << 0)
#define DMACConfiguration_E_Enable (1 << 0)
#define DMACCxConfiguration_Halt (1 << 18)
#define DMACCxConfiguration_Active (1 << 17)
#define DMACCxConfiguration_ITC_Enable (1 << 15)
#define DMACCxConfiguration_IError_Enable (1 << 14)
#define DMACCxConfiguration_E_Enable (1 << 0)
#define DMACCxControl_I_Mask (1 << 31)
#define DMACCxControl_I_Disable (0 << 31)
#define DMACCxControl_I_Enable (1 << 31)
#define DMACCxControl_SI_Mask (1 << 26)
#define DMACCxControl_SI_No_Increment (0 << 26)
#define DMACCxControl_SI_Increment (1 << 26)
#define DMACCxControl_DI_Mask (1 << 27)
#define DMACCxControl_DI_No_Increment (0 << 27)
#define DMACCxControl_DI_Increment (1 << 27)
#define DMACCxControl_D_Master1 (0 << 25)
#define DMACCxControl_S_Master1 (0 << 24)
#define DMACCxControl_SWidth_Halfword (1 << 18)
#define DMACCxControl_DWidth_Halfword (1 << 21)
#define DMACCxControl_DBSize_1 (0 << 15)
#define DMACCxControl_DBSize_4 (1 << 15)
#define DMACCxControl_SBSize_1 (0 << 12)
#define DMACCxControl_SBSize_4 (1 << 12)
#define DMACCxControl_TransferSizeMask (0x0FFF)
#define DMACCxConfiguration_FlowCntrl_Memory_to_Memory_DMA (0 << 11)
#define DMACCxConfiguration_FlowCntrl_Memory_to_Peripheral_DMA (1 << 11)
#define DMACCxConfiguration_FlowCntrl_Peripheral_to_Memory_DMA (2 << 11)
void handleAudioDeviceDMAInterrupt();
void handleAudioDeviceDMAInterruptError();
/*
Get a free DMA channel
*/
uint32_t acquireDMAChannel(void);
/*
Free a DMA channel
*/
void freeDMAChannel(uint32_t dmaChannel);
/*
Make sure the pl080 clock is enabled and initialize DMA channels 0 and 1
*/
void configureDMA(uint32_t dmaChannel);
/*
Startup DMA from address src to destination dst
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
*/
void setupDMA(uint32_t dmaChannel, void *src, void *dst, DMALinkedListItem *chain, DMADirection direction, uint32_t peripheral, size_t bytesToTransfer);
/*
Startup DMA from address src to destination dst
If chain is not NULL, will setup that in the LLI to auto-restart DMA from the chain
*/
void startDMA(uint32_t dmaChannel);
/*
Halts the channel, then blocks until the DMA is complete and then fully disables it
*/
void disableDMA(uint32_t dmaChannel);
/*
Blocks until the DMA is complete and then fully disables it
*/
void disableDMAImmediate(uint32_t dmaChannel);
#endif /* __PL080DMAC_AE2__ */
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef __AE2_I2S__
#define __AE2_I2S__
#include <stdint.h>
#include <platform/int.h>
#define rI2S_BASE 0x34190000
#define rI2S0_BASE (rI2S_BASE )
#define rI2S1_BASE (rI2S_BASE + 0x1000)
#define rI2S2_BASE (rI2S_BASE + 0x2000)
#define rI2S3_BASE (rI2S_BASE + 0x3000)
#define rI2SCLKCON 0
#define rI2STXCON 0x4
#define rI2STXCOM 0x8
#define rI2STXDB 0x10
#define rI2SRXCON 0x30
#define rI2SRXCOM 0x34
#define rI2SRXDB 0x38
#define rI2SSTATUS 0x3C
#define rI2SBITCLK 0x40
#define rI2SVERSION 0x44
#define rI2S0_CLKCON (rI2S0_BASE + rI2SCLKCON )
#define rI2S0_TXCON (rI2S0_BASE + rI2STXCON )
#define rI2S0_TXCOM (rI2S0_BASE + rI2STXCOM )
#define rI2S0_TXDB (rI2S0_BASE + rI2STXDB )
#define rI2S0_RXCON (rI2S0_BASE + rI2SRXCON )
#define rI2S0_RXCOM (rI2S0_BASE + rI2SRXCOM )
#define rI2S0_RXDB (rI2S0_BASE + rI2SRXDB )
#define rI2S0_STATUS (rI2S0_BASE + rI2SSTATUS )
#define rI2S0_BITCLK (rI2S0_BASE + rI2SBITCLK )
#define rI2S0_VERSION (rI2S0_BASE + rI2SVERSION)
#define rI2S1_CLKCON (rI2S1_BASE + rI2SCLKCON )
#define rI2S1_TXCON (rI2S1_BASE + rI2STXCON )
#define rI2S1_TXCOM (rI2S1_BASE + rI2STXCOM )
#define rI2S1_TXDB (rI2S1_BASE + rI2STXDB )
#define rI2S1_RXCON (rI2S1_BASE + rI2SRXCON )
#define rI2S1_RXCOM (rI2S1_BASE + rI2SRXCOM )
#define rI2S1_RXDB (rI2S1_BASE + rI2SRXDB )
#define rI2S1_STATUS (rI2S1_BASE + rI2SSTATUS )
#define rI2S1_BITCLK (rI2S1_BASE + rI2SBITCLK )
#define rI2S1_VERSION (rI2S1_BASE + rI2SVERSION)
#define rI2S2_CLKCON (rI2S2_BASE + rI2SCLKCON )
#define rI2S2_TXCON (rI2S2_BASE + rI2STXCON )
#define rI2S2_TXCOM (rI2S2_BASE + rI2STXCOM )
#define rI2S2_TXDB (rI2S2_BASE + rI2STXDB )
#define rI2S2_RXCON (rI2S2_BASE + rI2SRXCON )
#define rI2S2_RXCOM (rI2S2_BASE + rI2SRXCOM )
#define rI2S2_RXDB (rI2S2_BASE + rI2SRXDB )
#define rI2S2_STATUS (rI2S2_BASE + rI2SSTATUS )
#define rI2S2_BITCLK (rI2S2_BASE + rI2SBITCLK )
#define rI2S2_VERSION (rI2S2_BASE + rI2SVERSION)
#define rI2S3_CLKCON (rI2S3_BASE + rI2SCLKCON )
#define rI2S3_TXCON (rI2S3_BASE + rI2STXCON )
#define rI2S3_TXCOM (rI2S3_BASE + rI2STXCOM )
#define rI2S3_TXDB (rI2S3_BASE + rI2STXDB )
#define rI2S3_RXCON (rI2S3_BASE + rI2SRXCON )
#define rI2S3_RXCOM (rI2S3_BASE + rI2SRXCOM )
#define rI2S3_RXDB (rI2S3_BASE + rI2SRXDB )
#define rI2S3_STATUS (rI2S3_BASE + rI2SSTATUS )
#define rI2S3_BITCLK (rI2S3_BASE + rI2SBITCLK )
#define rI2S3_VERSION (rI2S3_BASE + rI2SVERSION)
#define rI2SSTATUS_RXOVERRUN 20
#define rI2SSTATUS_TXOVERRUN 19
#define rI2SSTATUS_RXFIFOLVL 13
#define rI2SSTATUS_TXFIFOLVL 7
#define rI2SSTATUS_RXOVERRUN_MASK ( 1 << 20)
#define rI2SSTATUS_TXOVERRUN_MASK ( 1 << 19)
#define rI2SSTATUS_RXFIFOLVL_MASK (0x3F << 13)
#define rI2SSTATUS_TXFIFOLVL_MASK (0x3F << 7 )
#endif /* __AE2_I2S__ */
@@ -0,0 +1,100 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef __AE2_MCA__
#define __AE2_MCA__
#include <stdint.h>
#include <platform/int.h>
#define rMCA_BASE 0x34196000
#define rMCA0_BASE (rMCA_BASE )
#define rMCA1_BASE (rMCA_BASE + 0x1000)
#define rMCAVERSION 0
#define rMCAFIFOSIZE 0x4
#define rMCAFIFOSHIFT 0x8
#define rMCACFG 0xC
#define rMCAUNSCFG 0x10
#define rMCAIDLE 0x14
#define rMCATXCFG 0x18
#define rMCAUNSTXCFG 0x1C
#define rMCATXFIFOCFG 0x20
#define rMCATXMASK 0x24
#define rMCATXDATA 0x28
#define rMCARXCFG 0x2C
#define rMCAUNSRXCFG 0x30
#define rMCARXFIFOCFG 0x34
#define rMCARXMASK 0x38
#define rMCARXDATA 0x3C
#define rMCASTATUS 0x40
#define rMCACTL 0x44
#define rMCA0_VERSION ( rMCA0_BASE + rMCAVERSION )
#define rMCA0_FIFOSHIFT ( rMCA0_BASE + rMCAFIFOSHIFT)
#define rMCA0_CFG ( rMCA0_BASE + rMCACFG )
#define rMCA0_UNSCFG ( rMCA0_BASE + rMCAUNSCFG )
#define rMCA0_IDLE ( rMCA0_BASE + rMCAIDLE )
#define rMCA0_TXCFG ( rMCA0_BASE + rMCATXCFG )
#define rMCA0_UNSTXCFG ( rMCA0_BASE + rMCAUNSTXCFG )
#define rMCA0_TXFIFOCFG ( rMCA0_BASE + rMCATXFIFOCFG)
#define rMCA0_TXMASK ( rMCA0_BASE + rMCATXMASK )
#define rMCA0_TXDATA ( rMCA0_BASE + rMCATXDATA )
#define rMCA0_RXCFG ( rMCA0_BASE + rMCARXCFG )
#define rMCA0_UNSRXCFG ( rMCA0_BASE + rMCAUNSRXCFG )
#define rMCA0_RXFIFOCFG ( rMCA0_BASE + rMCARXFIFOCFG)
#define rMCA0_RXMASK ( rMCA0_BASE + rMCARXMASK )
#define rMCA0_RXDATA ( rMCA0_BASE + rMCARXDATA )
#define rMCA0_STATUS ( rMCA0_BASE + rMCASTATUS )
#define rMCA0_CTL ( rMCA0_BASE + rMCACTL )
#define rMCA1_VERSION ( rMCA1_BASE + rMCAVERSION )
#define rMCA1_FIFOSHIFT ( rMCA1_BASE + rMCAFIFOSHIFT)
#define rMCA1_CFG ( rMCA1_BASE + rMCACFG )
#define rMCA1_UNSCFG ( rMCA1_BASE + rMCAUNSCFG )
#define rMCA1_IDLE ( rMCA1_BASE + rMCAIDLE )
#define rMCA1_TXCFG ( rMCA1_BASE + rMCATXCFG )
#define rMCA1_UNSTXCFG ( rMCA1_BASE + rMCAUNSTXCFG )
#define rMCA1_TXFIFOCFG ( rMCA1_BASE + rMCATXFIFOCFG)
#define rMCA1_TXMASK ( rMCA1_BASE + rMCATXMASK )
#define rMCA1_TXDATA ( rMCA1_BASE + rMCATXDATA )
#define rMCA1_RXCFG ( rMCA1_BASE + rMCARXCFG )
#define rMCA1_UNSRXCFG ( rMCA1_BASE + rMCAUNSRXCFG )
#define rMCA1_RXFIFOCFG ( rMCA1_BASE + rMCARXFIFOCFG)
#define rMCA1_RXMASK ( rMCA1_BASE + rMCARXMASK )
#define rMCA1_RXDATA ( rMCA1_BASE + rMCARXDATA )
#define rMCA1_STATUS ( rMCA1_BASE + rMCASTATUS )
#define rMCA1_CTL ( rMCA1_BASE + rMCACTL )
#define rMCASTATUS_FRAMEERROR 31
#define rMCASTATUS_RXOVERRUN 29
#define rMCASTATUS_RXUNDERRUN 28
#define rMCASTATUS_RXHIGHWATER 27
#define rMCASTATUS_RXLOWWATER 26
#define rMCASTATUS_TXOVERRUN 13
#define rMCASTATUS_TXUNDERRUN 12
#define rMCASTATUS_TXHIGHWATER 11
#define rMCASTATUS_TXLOWWATER 10
#define rMCASTATUS_RXFIFOLVL 16
#define rMCASTATUS_TXFIFOLVL 0
#define rMCASTATUS_FRAMEEEROR_MASK ( 1 << rMCASTATUS_FRAMEERROR)
#define rMCASTATUS_RXOVERRUN_MASK ( 1 << rMCASTATUS_RXOVERRUN)
#define rMCASTATUS_RXUNDERRUN_MASK ( 1 << rMCASTATUS_RXUNDERRUN)
#define rMCASTATUS_TXOVERRUN_MASK ( 1 << rMCASTATUS_TXOVERRUN)
#define rMCASTATUS_TXUNDERRUN_MASK ( 1 << rMCASTATUS_TXUNDERRUN)
#define rMCASTATUS_RXFIFOLVL_MASK (0x1FF << rMCASTATUS_RXFIFOLVL)
#define rMCASTATUS_TXFIFOLVL_MASK (0x1FF << rMCASTATUS_TXFIFOLVL)
#define rMCAUNSRXCFG_IRQ_EN 1
#define rMCAUNSTXCFG_IRQ_EN 1
#endif /* __AE2_MCA__ */
@@ -0,0 +1,50 @@
/*
* AudioUnit_AE2.h
* CAServices-Aspen
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Serves as the base class for AudioUnits on AE2
*/
#ifndef __AudioUnit_AE2_h__
#define __AudioUnit_AE2_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
/**
* AUBase_AE2 is the wrapper to go from AE2 land to CoreAudio land.
* For instance, we assume input channels are 16-bit, whereas CoreAudio
* AudioUnit may require floats. These modules do the translation.
*
* This is not intended to be parity with AudioUnits, but a crippled
* version.
*/
class AUBase_AE2
{
public:
AUBase_AE2() {}
virtual ~AUBase_AE2() {}
virtual OSStatus Initialize() { return noErr; }
virtual void Process(const void *inSourceP,
void *inDestP,
uint32_t inFramesToProcess) {}
// for parameters
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value) { return kAudioUnitErr_InvalidParameter; }
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID) { return 0; }
// for properties
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable) { return kAudioUnitErr_InvalidProperty; }
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData) { return kAudioUnitErr_InvalidProperty; }
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize) { return kAudioUnitErr_InvalidProperty; }
};
#endif // __AudioUnit_AE2_h__
@@ -0,0 +1,149 @@
/*
* AUComponent_AE2.h
* Stripped down version of AUComponent just for what AE2 needs
*
* Copyright 2010 Apple Computer. All rights reserved.
*
*/
#ifndef __AUCOMPONENT_AE2__
#define __AUCOMPONENT_AE2__
/*!
@enum Audio unit errors
@discussion These are the various errors that can be returned by AudioUnit... API calls
@constant kAudioUnitErr_InvalidProperty
The property is not supported
@constant kAudioUnitErr_InvalidParameter
The parameter is not supported
@constant kAudioUnitErr_InvalidElement
The specified element is not valid
@constant kAudioUnitErr_NoConnection
There is no connection (generally an audio unit is asked to render but it has
not input from which to gather data)
@constant kAudioUnitErr_FailedInitialization
The audio unit is unable to be initialised
@constant kAudioUnitErr_TooManyFramesToProcess
When an audio unit is initialised it has a value which specifies the max
number of frames it will be asked to render at any given time. If an audio
unit is asked to render more than this, this error is returned.
@constant kAudioUnitErr_InvalidFile
If an audio unit uses external files as a data source, this error is returned
if a file is invalid (Apple's DLS synth returns this error)
@constant kAudioUnitErr_FormatNotSupported
Returned if an input or output format is not supported
@constant kAudioUnitErr_Uninitialized
Returned if an operation requires an audio unit to be initialised and it is
not.
@constant kAudioUnitErr_InvalidScope
The specified scope is invalid
@constant kAudioUnitErr_PropertyNotWritable
The property cannot be written
@constant kAudioUnitErr_CannotDoInCurrentContext
Returned when an audio unit is in a state where it can't perform the requested
action now - but it could later. Its usually used to guard a render operation
when a reconfiguration of its internal state is being performed.
@constant kAudioUnitErr_InvalidPropertyValue
The property is valid, but the value of the property being provided is not
@constant kAudioUnitErr_PropertyNotInUse
Returned when a property is valid, but it hasn't been set to a valid value at
this time.
@constant kAudioUnitErr_Initialized
Indicates the operation cannot be performed because the audio unit is
initialized.
@constant kAudioUnitErr_InvalidOfflineRender
Used to indicate that the offline render operation is invalid. For instance,
when the audio unit needs to be pre-flighted,
but it hasn't been.
@constant kAudioUnitErr_Unauthorized
Returned by either Open or Initialise, this error is used to indicate that the
audio unit is not authorised, that it cannot be used. A host can then present
a UI to notify the user the audio unit is not able to be used in its current
state.
*/
enum
{
kAudioUnitErr_InvalidProperty = -10879,
kAudioUnitErr_InvalidParameter = -10878,
kAudioUnitErr_InvalidElement = -10877,
kAudioUnitErr_NoConnection = -10876,
kAudioUnitErr_FailedInitialization = -10875,
kAudioUnitErr_TooManyFramesToProcess = -10874,
kAudioUnitErr_InvalidFile = -10871,
kAudioUnitErr_FormatNotSupported = -10868,
kAudioUnitErr_Uninitialized = -10867,
kAudioUnitErr_InvalidScope = -10866,
kAudioUnitErr_PropertyNotWritable = -10865,
kAudioUnitErr_CannotDoInCurrentContext = -10863,
kAudioUnitErr_InvalidPropertyValue = -10851,
kAudioUnitErr_PropertyNotInUse = -10850,
kAudioUnitErr_Initialized = -10849,
kAudioUnitErr_InvalidOfflineRender = -10848,
kAudioUnitErr_Unauthorized = -10847
};
/*!
@typedef AudioUnitPropertyID
@discussion Type used for audio unit properties.
Properties are used to describe the state of an audio unit (for instance,
the input or output audio format)
*/
typedef UInt32 AudioUnitPropertyID;
/*!
@typedef AudioUnitScope
@discussion Type used for audio unit scopes. Apple reserves the 0 < 1024 range for
audio unit scope identifiers.
Scopes are used to delineate a major attribute of an audio unit
(for instance, global, input, output)
*/
typedef UInt32 AudioUnitScope;
/*!
@typedef AudioUnitElement
@discussion Type used for audio unit elements.
Scopes can have one or more member, and a member of a scope is
addressed / described by its element
For instance, input bus 1 is input scope, element 1
*/
typedef UInt32 AudioUnitElement;
/*!
@typedef AudioUnitParameterID
@discussion Type used for audio unit parameters.
Parameters are typically used to control and set render state
(for instance, filter cut-off frequency)
*/
typedef UInt32 AudioUnitParameterID;
/*!
@typedef AudioUnitParameterValue
@discussion Type used for audio unit parameter values.
The value of a given parameter is specified using this type
(typically a Float32)
*/
typedef Float32 AudioUnitParameterValue;
//-----------------------------------------------------------------------------
// Render flags
//-----------------------------------------------------------------------------
enum
{
/* these are obsolete, were never implemented: */
/* kAudioUnitRenderAction_Accumulate = (1 << 0) */
/* kAudioUnitRenderAction_UseProvidedBuffer = (1 << 1) */
kAudioUnitRenderAction_PreRender = (1 << 2),
kAudioUnitRenderAction_PostRender = (1 << 3),
kAudioUnitRenderAction_OutputIsSilence = (1 << 4),
/* Provides hint on return from Render(). if set, the buffer contains all zeroes */
kAudioOfflineUnitRenderAction_Preflight = (1 << 5),
kAudioOfflineUnitRenderAction_Render = (1 << 6),
kAudioOfflineUnitRenderAction_Complete = (1 << 7),
// this flag is set if on the post-render call an error was returned by the AUs render
// in this case, the error can be retrieved through the lastRenderError property
// and the ioData handed to the post-render notification will be invalid.
kAudioUnitRenderAction_PostRenderError = (1 << 8)
};
typedef UInt32 AudioUnitRenderActionFlags;
#endif
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2011 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 "AUNull.h"
#include "string.h"
#include <debug.h>
#define GENERATE_SAWTOOTH 0
AUNull *
AUNull::Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUNull *This = new AUNull;
if (This)
{
if (This->InitWith(sampleRate, numChannels, sampleSize))
{
This->Initialize();
}
else
{
delete This;
This = NULL;
}
}
return This;
}
AUNull::AUNull()
{
}
bool
AUNull::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
mSampleRate = sampleRate;
mNumChannels = numChannels;
mSampleSize = sampleSize;
return true;
}
AUNull::~AUNull()
{
}
#if GENERATE_SAWTOOTH
static int16_t currentValue = 0;
static const uint32_t kFreq = 600;
#endif
void
AUNull::Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess)
{
#if GENERATE_SAWTOOTH
uint16_t amountToAdd = (0x10000L * kFreq) / mSampleRate;
int16_t *dst = static_cast<int16_t*>(inDestP);
size_t i = 0;
for (i = 0; i < (inFramesToProcess * mNumChannels); ++i)
{
*dst++ = currentValue;
currentValue += amountToAdd;
}
#endif
}
@@ -0,0 +1,46 @@
/*
* AUNull.h
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Does necessary translation between AE2 and CA AU speaker protection
*/
#ifndef __AUNull_h__
#define __AUNull_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
#include "AUBase_AE2.h"
class IIRFilter;
/**
* AUNull just passes input to output
* Assumes audio data is 16-bit stereo
*/
class AUNull : public AUBase_AE2
{
public:
static AUNull* Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
virtual ~AUNull();
virtual void Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess);
private:
AUNull();
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
// some constants
typedef AUBase_AE2 super;
size_t mSampleRate;
size_t mNumChannels;
size_t mSampleSize;
};
#endif // __AUNull_h__
@@ -0,0 +1,458 @@
/*
* Copyright (C) 2011 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.
*/
#if USE_SIDETONE
#include "AUSidetone.h"
#include "IIRFiltersA5.h"
#include "VolumeA5.h"
#include "string.h"
#include <debug.h>
// a basic fixed 16-bit mono 44.1 description
static AudioStreamBasicDescription kDefaultMonoFormat = { 44100.0, 'lpcm', 0xC, 2, 1, 2, 1, 16, 0 };
static void SetDefaultSidetoneEQ(BiquadDescriptor *biquadValues, size_t numBands, uint32_t sampleRate)
{
size_t i = 0;
if (i < numBands)
{
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthLowPass;
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 3800.0f : 7400.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 0.f;
++i;
}
if (i < numBands)
{
biquadValues[i].filterType = kAUNBandEQFilterType_2ndOrderButterworthHighPass;
biquadValues[i].frequencyInHertz = (sampleRate == 8000) ? 200.0f : 100.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 0.f;
++i;
}
for (; i < numBands; ++i)
{
biquadValues[i].filterType = 0;
biquadValues[i].frequencyInHertz = 100.0f;
biquadValues[i].gain = 0.f;
biquadValues[i].bw = 0.05;
biquadValues[i].bypassBand = 1.f;
}
}
AUSidetone *
AUSidetone::Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUSidetone *This = new AUSidetone;
if (This)
{
if (This->InitWith(sampleRate, numChannels, sampleSize))
{
This->Initialize();
}
else
{
delete This;
This = NULL;
}
}
return This;
}
AUSidetone::AUSidetone() :
mSampleRate(44100),
mNumChannels(1),
mSidetoneEQ(NULL),
mVolume(NULL),
mNumberBands(kMaxNumBands),
mBiquadValues(NULL),
mVolumeInDB(kDefaultSidetoneGain),
mSidetoneEQBypass(false)
{
}
bool
AUSidetone::InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
if (sampleSize != 2)
{
return false;
}
mSampleRate = sampleRate;
mNumChannels = numChannels;
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
thisFormat.mSampleRate = mSampleRate;
thisFormat.mBytesPerPacket *= mNumChannels;
thisFormat.mBytesPerFrame *= mNumChannels;
thisFormat.mChannelsPerFrame *= mNumChannels;
thisFormat.mBitsPerChannel *= mNumChannels;
mSidetoneEQ = NewAE2IIRFilter(&thisFormat, &thisFormat, kMaxNumBands);
dprintf(DEBUG_INFO, "sidetone eq %p\n", mSidetoneEQ);
mBiquadValues = new BiquadDescriptor[mNumberBands];
if (mBiquadValues)
{
SetDefaultSidetoneEQ(mBiquadValues, mNumberBands, mSampleRate);
if (mSidetoneEQ)
{
mVolume = NewVolume(thisFormat);
dprintf(DEBUG_INFO, "volume %p\n", mVolume);
if (mVolume)
{
if ((SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues) == noErr) &&
(SetGain(mVolume, mVolumeInDB) == noErr))
{
dprintf(DEBUG_INFO, "it is all good\n");
return true;
}
DeleteVolume(mVolume);
mVolume = NULL;
}
DeleteAE2IIRFilter(mSidetoneEQ);
mSidetoneEQ = NULL;
}
delete [] mBiquadValues;
mBiquadValues = NULL;
}
return false;
}
AUSidetone::~AUSidetone()
{
DeleteAE2IIRFilter(mSidetoneEQ);
mSidetoneEQ = NULL;
DeleteVolume(mVolume);
mVolume = NULL;
delete [] mBiquadValues;
mBiquadValues = NULL;
}
// These conversion routines are for translating from UInt32 of the AP/AE2 bridge
// to the floating point of the internal representation. Scaling and offsets applied here.
Float32 AudioUnitParameterValue_To_BypassBand(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0) ? 0.f : 1.f));
return (value == 0) ? 0.f : 1.f;
}
AudioUnitParameterValue BypassBand_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value == 0.f) ? 0 : 1));
return (value == 0.f) ? 0 : 1;
}
UInt32 AudioUnitParameterValue_To_FilterType(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
AudioUnitParameterValue FilterType_To_AudioUnitParameterValue(UInt32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
Float32 AudioUnitParameterValue_To_Frequency(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
AudioUnitParameterValue Frequency_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value));
return value;
}
Float32 AudioUnitParameterValue_To_Gain(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
return -96.0 + (value);
}
AudioUnitParameterValue Gain_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
return (value) - (-96.0);
}
Float32 AudioUnitParameterValue_To_Bandwidth(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value / 100.0f));
return value / 100.0f;
}
AudioUnitParameterValue Bandwidth_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(value * 100.0f));
return value * 100.0f;
}
Float32 AudioUnitParameterValue_To_VolumeInDB(AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t(-96.0 + (value)));
return -96.0 + (value);
}
AudioUnitParameterValue VolumeInDB_To_AudioUnitParameterValue(Float32 value)
{
dprintf(DEBUG_INFO, "%s %d value %d\n", __FUNCTION__, uint32_t(value), uint32_t((value) - (-96.0)));
return (value) - (-96.0);
}
OSStatus
AUSidetone::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value)
{
dprintf(DEBUG_INFO, "SetParameter %d value %d\n", paramID, uint32_t(value));
if (paramID == kAUVolume_InDB)
{
mVolumeInDB = AudioUnitParameterValue_To_VolumeInDB(value);
return SetGain(mVolume, mVolumeInDB);
}
AudioStreamBasicDescription thisFormat = kDefaultMonoFormat;
thisFormat.mSampleRate = mSampleRate;
thisFormat.mBytesPerPacket *= mNumChannels;
thisFormat.mBytesPerFrame *= mNumChannels;
thisFormat.mChannelsPerFrame *= mNumChannels;
thisFormat.mBitsPerChannel *= mNumChannels;
// get the index
uint32_t which = paramID % 1000;
uint32_t type = (paramID / 1000)*1000;
bool success = false;
if (which < mNumberBands)
{
success = true;
switch (type)
{
case kAUNBandEQParam_BypassBand:
mBiquadValues[which].bypassBand = AudioUnitParameterValue_To_BypassBand(value);
break;
case kAUNBandEQParam_FilterType:
mBiquadValues[which].filterType = AudioUnitParameterValue_To_FilterType(value);
break;
case kAUNBandEQParam_Frequency:
mBiquadValues[which].frequencyInHertz = AudioUnitParameterValue_To_Frequency(value);
break;
case kAUNBandEQParam_Gain:
mBiquadValues[which].gain = AudioUnitParameterValue_To_Gain(value);
break;
case kAUNBandEQParam_Bandwidth:
mBiquadValues[which].bw = AudioUnitParameterValue_To_Bandwidth(value);
break;
default:
success = false;
}
}
OSStatus result = 0;
if (success)
{
result = SetAE2IIRFilter(mSidetoneEQ, &thisFormat, mNumberBands, mBiquadValues);
}
else
{
result = super::SetParameter(paramID, value);
}
dprintf(DEBUG_INFO, "result is %d\n", result);
return result;
}
AudioUnitParameterValue
AUSidetone::GetParameter(AudioUnitParameterID paramID)
{
if (paramID == kAUVolume_InDB)
{
return VolumeInDB_To_AudioUnitParameterValue(mVolumeInDB);
}
// get the index
uint32_t which = paramID % 1000;
uint32_t type = (paramID / 1000)*1000;
if (which < mNumberBands)
{
switch (type)
{
case kAUNBandEQParam_BypassBand:
return BypassBand_To_AudioUnitParameterValue(mBiquadValues[which].bypassBand);
case kAUNBandEQParam_FilterType:
return FilterType_To_AudioUnitParameterValue(mBiquadValues[which].filterType);
case kAUNBandEQParam_Frequency:
return Frequency_To_AudioUnitParameterValue(mBiquadValues[which].frequencyInHertz);
case kAUNBandEQParam_Gain:
return Gain_To_AudioUnitParameterValue(mBiquadValues[which].gain);
case kAUNBandEQParam_Bandwidth:
return Bandwidth_To_AudioUnitParameterValue(mBiquadValues[which].bw);
}
}
return super::GetParameter(paramID);
}
OSStatus
AUSidetone::GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
result = GetPropertyInfo(kAUSongbird_SidetoneEQBlockData, outDataSize, outWritable);
if (noErr == result)
{
// leave room for the mVolume field of the AU_SidetoneState;
outDataSize += offsetof(AU_SidetoneState, mFilterDescription);
}
break;
case kAUSongbird_SidetoneEQBlockData:
outDataSize = sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + mNumberBands * sizeof(AU_BiquadFilter);
outWritable = true;
result = noErr;
break;
default:
result = super::GetPropertyInfo(inID, outDataSize, outWritable);
}
return result;
}
OSStatus
AUSidetone::GetProperty(AudioUnitPropertyID inID, void* outData)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
{
AU_SidetoneState * state = static_cast<AU_SidetoneState*>(outData);
state->mVolume = mVolumeInDB;
state->mBypass = mSidetoneEQBypass;
void * biquad_filter_description = &state->mFilterDescription;
result = GetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description);
}
break;
case kAUSongbird_SidetoneEQBlockData:
{
AU_BiquadFilterDescription * description = static_cast<AU_BiquadFilterDescription*>(outData);
if (description)
{
description->mBypass = mSidetoneEQBypass;
description->mNumberFilters = mNumberBands;
BiquadCoefficientsDescriptor * filter = static_cast<BiquadCoefficientsDescriptor*>(static_cast<void*>(description->mFilters));
if (filter)
{
result = GetAE2IIRFilterCoefficients(mSidetoneEQ, mNumberBands, filter);
}
}
}
break;
default:
result = super::GetProperty(inID, outData);
}
return result;
}
OSStatus
AUSidetone::SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize)
{
OSStatus result = -1;
switch (inID)
{
case kAUSongbird_SidetoneState:
{
// determine if have a valid data structure
if (inDataSize < (offsetof(AU_SidetoneState, mFilterDescription)))
{
break;
}
const AU_SidetoneState * state = static_cast<const AU_SidetoneState*>(inData);
const void * biquad_filter_description = &state->mFilterDescription;
result = SetProperty(kAUSongbird_SidetoneEQBlockData, biquad_filter_description, inDataSize - offsetof(AU_SidetoneState, mFilterDescription));
if (noErr == result)
{
mSidetoneEQBypass = state->mBypass;
mVolumeInDB = state->mVolume;
result = SetGain(mVolume, mVolumeInDB);
}
}
break;
case kAUSongbird_SidetoneEQBlockData:
{
// determine if we are talking to a valid structure
if (inDataSize < sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter))
{
break;
}
const AU_BiquadFilterDescription * description = static_cast<const AU_BiquadFilterDescription*>(inData);
if (inDataSize < ((sizeof(AU_BiquadFilterDescription) - sizeof(AU_BiquadFilter) + (sizeof(AU_BiquadFilter) * description->mNumberFilters))))
{
break;
}
if (description->mNumberFilters > kMaxNumBands)
{
break;
}
const BiquadCoefficientsDescriptor * filter = static_cast<const BiquadCoefficientsDescriptor*>(static_cast<const void*>(description->mFilters));
result = SetAE2IIRFilterCoefficients(mSidetoneEQ, description->mNumberFilters, static_cast<const BiquadCoefficientsDescriptor*>(filter));
if (!result)
{
mSidetoneEQBypass = description->mBypass;
mNumberBands = description->mNumberFilters;
}
}
break;
default:
result = super::SetProperty(inID, inData, inDataSize);
}
return result;
}
void
AUSidetone::Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess)
{
if (!mSidetoneEQBypass)
{
ProcessAE2IIRFilter(mSidetoneEQ, inFramesToProcess, inSourceP, inDestP);
}
ProcessVolumeInplace(mVolume, inFramesToProcess, inDestP);
}
#endif
@@ -0,0 +1,88 @@
/*
* AUSidetone.h
*
* Created by richardp on 11/28/11.
* Copyright 2011 Apple, Inc. All rights reserved.
*
* Does necessary translation between AE2 and CA AU speaker protection
*/
#ifndef __AUSidetone_h__
#define __AUSidetone_h__
#include <stdint.h>
#include "AudioUnitProperties_AE2.h"
#include "AUBase_AE2.h"
struct AE2_IIRFilter;
struct AE2_Volume;
struct BiquadDescriptor;
/**
* AUSidetone implements the sidetone eq and gain.
*
* These values are hard-coded for now.
*/
class AUSidetone : public AUBase_AE2
{
public:
static AUSidetone* Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
virtual ~AUSidetone();
virtual void Process(const void *inSourceP,
void *inDestP,
UInt32 inFramesToProcess);
// for parameters
virtual OSStatus SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue value);
virtual AudioUnitParameterValue GetParameter(AudioUnitParameterID paramID);
// for properties
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID, uint32_t& outDataSize, bool& outWritable);
virtual OSStatus GetProperty(AudioUnitPropertyID inID, void* outData);
virtual OSStatus SetProperty(AudioUnitPropertyID inID, const void* inData, uint32_t inDataSize);
private:
AUSidetone();
bool InitWith(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
// some constants
typedef AUBase_AE2 super;
static const size_t kMaxNumBands = 5;
static const float kDefaultSidetoneGain = -32.0;
// Parameters for the AUNBandEQ unit
// Note that the parameter IDs listed correspond to band 0 (zero) of the unit. The parameter IDs for
// higher bands can be obtained by adding the zero-indexed band number to the corresponding band 0
// parameter ID up to the number of bands minus one, where the number of bands is described by the
// AUNBandEQ property kAUNBandEQProperty_NumberOfBands. For example, the parameter ID corresponding
// to the filter type of band 4 would be kAUNBandEQParam_FilterType + 3.
enum {
kAUVolume_InDB = 500,
// Global, Boolean, 0 or 1, 1
kAUNBandEQParam_BypassBand = 1000,
// Global, Indexed, 0->kNumAUNBandEQFilterTypes-1, 0
kAUNBandEQParam_FilterType = 2000,
// Global, Hz, 20->(SampleRate/2), 1000
kAUNBandEQParam_Frequency = 3000,
// Global, dB, -96->24, 0
kAUNBandEQParam_Gain = 4000,
// Global, octaves, 0.05->5.0, 0.5
kAUNBandEQParam_Bandwidth = 5000
};
uint32_t mSampleRate;
uint32_t mNumChannels;
AE2_IIRFilter *mSidetoneEQ;
AE2_Volume *mVolume;
// to hold the coefficients
uint32_t mNumberBands;
BiquadDescriptor *mBiquadValues;
float mVolumeInDB;
bool mSidetoneEQBypass;
};
#endif // __AUSidetone_h__
@@ -0,0 +1,309 @@
/*
File: AudioUnitProperties_AE2.h
Contains: Property constants for AudioUnits, stripped down for AE2
Copyright: (c) 2010 by Apple, Inc., all rights reserved.
*/
#ifndef __AUDIOUNITPROPERTIES_AE2__
#define __AUDIOUNITPROPERTIES_AE2__
#include "CoreAudioTypes_AE2.h"
#include "AUComponent_AE2.h"
#pragma mark -
#pragma mark Core Implementation
#pragma mark -
/*!
@enum Audio Unit scope types
@abstract The scope IDs for audio units define basic roles and contexts for an audio unit's state.
@discussion Each scope is a discrete context. Apple reserves scope IDs from 0 through 1024.
@constant kAudioUnitScope_Global The context for audio unit characteristics that apply to the audio unit as a
whole
@constant kAudioUnitScope_Input The context for audio data coming into an audio unit
@constant kAudioUnitScope_Output The context for audio data leaving an audio unit
@constant kAudioUnitScope_Group A context specific to the control scope of parameters (for instance,
MIDI Channels is an example of this scope)
@constant kAudioUnitScope_Part A distinct rendering context. For instance a single timbre in a multi-timbral
instrument, a single loop in a multi looping capable looper unit, etc.
@constant kAudioUnitScope_Layer A context which functions as a layer within a part and allows
grouped control of Cell-scope parameters.
An example is the percussive attack layer for an electric organ instrument
@constant kAudioUnitScope_Note A scope that can be used to apply changes to an individual note. The
elementID used with this scope is the unique note ID returned from
a started note (see MusicDeviceStartNote)
@constant kAudioUnitScope_Cell A scope which represents the finest level of granularity within an AU.
The individual sample zones and envelope generators within a synth are an
example of this.
*/
enum {
kAudioUnitScope_Global = 0,
kAudioUnitScope_Input = 1,
kAudioUnitScope_Output = 2
};
//=====================================================================================================================
#pragma mark - Parameter Definitions
// assume kAudioUnitParameterUnit_Generic if not found in this enum
/*!
@enum AudioUnitParameterUnit
@constant kAudioUnitParameterUnit_Generic
untyped value generally between 0.0 and 1.0
@constant kAudioUnitParameterUnit_Indexed
takes an integer value (good for menu selections)
@constant kAudioUnitParameterUnit_Boolean
0.0 means FALSE, non-zero means TRUE
@constant kAudioUnitParameterUnit_Percent
usually from 0 -> 100, sometimes -50 -> +50
@constant kAudioUnitParameterUnit_Seconds
absolute or relative time
@constant kAudioUnitParameterUnit_SampleFrames
one sample frame equals (1.0/sampleRate) seconds
@constant kAudioUnitParameterUnit_Phase
-180 to 180 degrees
@constant kAudioUnitParameterUnit_Rate
rate multiplier, for playback speed, etc. (e.g. 2.0 == twice as fast)
@constant kAudioUnitParameterUnit_Hertz
absolute frequency/pitch in cycles/second
@constant kAudioUnitParameterUnit_Cents
unit of relative pitch
@constant kAudioUnitParameterUnit_RelativeSemiTones
useful for coarse detuning
@constant kAudioUnitParameterUnit_MIDINoteNumber
absolute pitch as defined in the MIDI spec (exact freq may depend on tuning table)
@constant kAudioUnitParameterUnit_MIDIController
a generic MIDI controller value from 0 -> 127
@constant kAudioUnitParameterUnit_Decibels
logarithmic relative gain
@constant kAudioUnitParameterUnit_LinearGain
linear relative gain
@constant kAudioUnitParameterUnit_Degrees
-180 to 180 degrees, similar to phase but more general (good for 3D coord system)
@constant kAudioUnitParameterUnit_EqualPowerCrossfade
0 -> 100, crossfade mix two sources according to sqrt(x) and sqrt(1.0 - x)
@constant kAudioUnitParameterUnit_MixerFaderCurve1
0.0 -> 1.0, pow(x, 3.0) -> linear gain to simulate a reasonable mixer channel fader response
@constant kAudioUnitParameterUnit_Pan
standard left to right mixer pan
@constant kAudioUnitParameterUnit_Meters
distance measured in meters
@constant kAudioUnitParameterUnit_AbsoluteCents
absolute frequency measurement :
if f is freq in hertz then absoluteCents = 1200 * log2(f / 440) + 6900
@constant kAudioUnitParameterUnit_Octaves
octaves in relative pitch where a value of 1 is equal to 1200 cents
@constant kAudioUnitParameterUnit_BPM
beats per minute, ie tempo
@constant kAudioUnitParameterUnit_Beats
time relative to tempo, i.e., 1.0 at 120 BPM would equal 1/2 a second
@constant kAudioUnitParameterUnit_Milliseconds
parameter is expressed in milliseconds
@constant kAudioUnitParameterUnit_Ratio
for compression, expansion ratio, etc.
@constant kAudioUnitParameterUnit_CustomUnit
this is the parameter unit type for parameters that present a custom unit name
*/
enum
{
kAudioUnitParameterUnit_Generic = 0,
kAudioUnitParameterUnit_Indexed = 1,
kAudioUnitParameterUnit_Boolean = 2,
kAudioUnitParameterUnit_Percent = 3,
kAudioUnitParameterUnit_Seconds = 4,
kAudioUnitParameterUnit_SampleFrames = 5,
kAudioUnitParameterUnit_Phase = 6,
kAudioUnitParameterUnit_Rate = 7,
kAudioUnitParameterUnit_Hertz = 8,
kAudioUnitParameterUnit_Cents = 9,
kAudioUnitParameterUnit_RelativeSemiTones = 10,
kAudioUnitParameterUnit_MIDINoteNumber = 11,
kAudioUnitParameterUnit_MIDIController = 12,
kAudioUnitParameterUnit_Decibels = 13,
kAudioUnitParameterUnit_LinearGain = 14,
kAudioUnitParameterUnit_Degrees = 15,
kAudioUnitParameterUnit_EqualPowerCrossfade = 16,
kAudioUnitParameterUnit_MixerFaderCurve1 = 17,
kAudioUnitParameterUnit_Pan = 18,
kAudioUnitParameterUnit_Meters = 19,
kAudioUnitParameterUnit_AbsoluteCents = 20,
kAudioUnitParameterUnit_Octaves = 21,
kAudioUnitParameterUnit_BPM = 22,
kAudioUnitParameterUnit_Beats = 23,
kAudioUnitParameterUnit_Milliseconds = 24,
kAudioUnitParameterUnit_Ratio = 25,
kAudioUnitParameterUnit_CustomUnit = 26
};
/*!
@typedef AudioUnitParameterUnit
*/
typedef UInt32 AudioUnitParameterUnit;
/*!
@struct AudioUnitParameterInfo
@field name
UNUSED - set to zero - UTF8 encoded C string (originally).
@field unitName
only valid if kAudioUnitParameterUnit_CustomUnit is set. If kAudioUnitParameterUnit_CustomUnit
is set, this field must contain a valid CFString.
@field clumpID
only valid if kAudioUnitParameterFlag_HasClump
@field cfNameString
only valid if kAudioUnitParameterFlag_HasCFNameString
@field unit
if the "unit" field contains a value not in the enum above, then assume
kAudioUnitParameterUnit_Generic
@field minValue
@field maxValue
@field defaultValue
@field flags
Due to some vagaries about the ways in which Parameter's CFNames have been described, it was
necessary to add a flag: kAudioUnitParameterFlag_CFNameRelease
In normal usage a parameter name is essentially a static object, but sometimes an audio unit will
generate parameter names dynamically.. As these are expected to be CFStrings, in that case
the host should release those names when it is finished with them, but there was no way
to communicate this distinction in behavior.
Thus, if an audio unit will (or could) generate a name dynamically, it should set this flag in
the parameter's info. The host should check for this flag, and if present, release the parameter
name when it is finished with it.
*/
typedef struct AudioUnitParameterInfo
{
//char name[52];
//CFStringRef unitName;
UInt32 clumpID;
//CFStringRef cfNameString;
AudioUnitParameterUnit unit;
AudioUnitParameterValue minValue;
AudioUnitParameterValue maxValue;
AudioUnitParameterValue defaultValue;
UInt32 flags;
} AudioUnitParameterInfo;
/*!
@enum Audio Unit Parameter Flags
@discussion Bit positions 18, 17, and 16 are set aside for display scales. Bit 19 is reserved.
@constant kAudioUnitParameterFlag_CFNameRelease
@constant kAudioUnitParameterFlag_MeterReadOnly
@constant kAudioUnitParameterFlag_DisplayMask
@constant kAudioUnitParameterFlag_DisplaySquareRoot
@constant kAudioUnitParameterFlag_DisplaySquared
@constant kAudioUnitParameterFlag_DisplayCubed
@constant kAudioUnitParameterFlag_DisplayCubeRoot
@constant kAudioUnitParameterFlag_DisplayExponential
@constant kAudioUnitParameterFlag_HasClump
@constant kAudioUnitParameterFlag_ValuesHaveStrings
@constant kAudioUnitParameterFlag_DisplayLogarithmic
@constant kAudioUnitParameterFlag_IsHighResolution
@constant kAudioUnitParameterFlag_NonRealTime
@constant kAudioUnitParameterFlag_CanRamp
@constant kAudioUnitParameterFlag_ExpertMode
@constant kAudioUnitParameterFlag_HasCFNameString
@constant kAudioUnitParameterFlag_IsGlobalMeta
@constant kAudioUnitParameterFlag_IsElementMeta
@constant kAudioUnitParameterFlag_IsReadable
@constant kAudioUnitParameterFlag_IsWritable
*/
enum
{
kAudioUnitParameterFlag_CFNameRelease = (1L << 4),
kAudioUnitParameterFlag_MeterReadOnly = (1L << 15),
// bit positions 18,17,16 are set aside for display scales. bit 19 is reserved.
kAudioUnitParameterFlag_DisplayMask = (7L << 16) | (1L << 22),
kAudioUnitParameterFlag_DisplaySquareRoot = (1L << 16),
kAudioUnitParameterFlag_DisplaySquared = (2L << 16),
kAudioUnitParameterFlag_DisplayCubed = (3L << 16),
kAudioUnitParameterFlag_DisplayCubeRoot = (4L << 16),
kAudioUnitParameterFlag_DisplayExponential = (5L << 16),
kAudioUnitParameterFlag_HasClump = (1L << 20),
kAudioUnitParameterFlag_ValuesHaveStrings = (1L << 21),
kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22),
kAudioUnitParameterFlag_IsHighResolution = (1L << 23),
kAudioUnitParameterFlag_NonRealTime = (1L << 24),
kAudioUnitParameterFlag_CanRamp = (1L << 25),
kAudioUnitParameterFlag_ExpertMode = (1L << 26),
kAudioUnitParameterFlag_HasCFNameString = (1L << 27),
kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28),
kAudioUnitParameterFlag_IsElementMeta = (1L << 29),
kAudioUnitParameterFlag_IsReadable = (1L << 30),
kAudioUnitParameterFlag_IsWritable = (1L << 31)
};
typedef struct AUChannelInfo {
SInt16 inChannels;
SInt16 outChannels;
} AUChannelInfo;
//==================================================================================================
/*
@constant kAUSongbird_SidetoneEQBlockData
@discussion Scope: Global
Value Type: blob
Access: read/write
A property to get/set the biquad coefficients of the AE2 SidetoneEQ block as an
array of AU_BiquadFilterDescription, sized to be the number of stages.
*/
enum {
kAUSongbird_SidetoneEQBlockData = 10000,
kAUSongbird_SidetoneState = 10001
};
/*!
@struct AU_BiquadFilter
@field mBypass
Boolean to indicate if biquad is bypassed;
@field b0
@field b1
@field b2
@field a1
@field a2
Fields for biquad coefficients values;
*/
typedef struct AU_BiquadFilter {
UInt32 mBypass;
Float64 b0, b1, b2, a1, a2;
} AU_BiquadFilter;
/*!
@struct AU_BiquadFilterDescription
kAUSongbird_SidetoneEQBlockData data structure
@field mBypass
Boolean to indicate if all biquads are bypassed;
@field mNumberFilters
Number of active filters in the sidetone eq;
@field mFilters
variable length sized as mNumberFilters AU_BiquadFilter structures;
*/
typedef struct AU_BiquadFilterDescription {
UInt32 mBypass;
UInt32 mNumberFilters;
AU_BiquadFilter mFilters[1]; // this is a variable length array of mNumberFilters elements
} AU_BiquadFilterDescription;
/*!
@struct AU_SidetoneState
kAUSongbird_SidetoneState data structure
@field mVolume
Gain for Sidetone;
@field mFilterDescription
Filter description for sidetone;
*/
typedef struct AU_SidetoneState {
bool mBypass;
Float32 mVolume;
AU_BiquadFilterDescription mFilterDescription;
} AU_SidetoneState;
#endif
@@ -0,0 +1,248 @@
/*
* CoreAudioTypes_AE2.h
* Stripped down version of CoreAudioTypes just for what AE2 needs
*
* Copyright 2010 Apple Computer. All rights reserved.
*
*/
#ifndef __COREAUDIOTYPES_AE2__
#define __COREAUDIOTYPES_AE2__
#define CA_PREFER_FIXED_POINT 1
#include <stdint.h>
typedef double Float64;
typedef float Float32;
typedef SInt32 OSStatus;
typedef SInt32 ComponentResult;
typedef SInt16 AudioSampleType;
typedef SInt32 AudioUnitSampleType;
//XXX find the place where this is really defined
enum {
noErr = 0
};
/*!
@struct AudioStreamBasicDescription
@abstract This structure encapsulates all the information for describing the basic
format properties of a stream of audio data.
@discussion This structure is sufficient to describe any constant bit rate format that has
channels that are the same size. Extensions are required for variable bit rate
data and for constant bit rate data where the channels have unequal sizes.
However, where applicable, the appropriate fields will be filled out correctly
for these kinds of formats (the extra data is provided via separate properties).
In all fields, a value of 0 indicates that the field is either unknown, not
applicable or otherwise is inapproprate for the format and should be ignored.
Note that 0 is still a valid value for most formats in the mFormatFlags field.
In audio data a frame is one sample across all channels. In non-interleaved
audio, the per frame fields identify one channel. In interleaved audio, the per
frame fields identify the set of n channels. In uncompressed audio, a Packet is
one frame, (mFramesPerPacket == 1). In compressed audio, a Packet is an
indivisible chunk of compressed data, for example an AAC packet will contain
1024 sample frames.
@field mSampleRate
The number of sample frames per second of the data in the stream.
@field mFormatID
A four char code indicating the general kind of data in the stream.
@field mFormatFlags
Flags specific to each format.
@field mBytesPerPacket
The number of bytes in a packet of data.
@field mFramesPerPacket
The number of sample frames in each packet of data.
@field mBytesPerFrame
The number of bytes in a single sample frame of data.
@field mChannelsPerFrame
The number of channels in each frame of data.
@field mBitsPerChannel
The number of bits of sample data for each channel in a frame of data.
@field mReserved
Pads the structure out to force an even 8 byte alignment.
*/
struct AudioStreamBasicDescription
{
Float64 mSampleRate;
UInt32 mFormatID;
UInt32 mFormatFlags;
UInt32 mBytesPerPacket;
UInt32 mFramesPerPacket;
UInt32 mBytesPerFrame;
UInt32 mChannelsPerFrame;
UInt32 mBitsPerChannel;
UInt32 mReserved;
};
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
// SMPTETime is also defined in the CoreVideo headers.
#if !defined(__SMPTETime__)
#define __SMPTETime__
/*!
@struct SMPTETime
@abstract A structure for holding a SMPTE time.
@field mSubframes
The number of subframes in the full message.
@field mSubframeDivisor
The number of subframes per frame (typically 80).
@field mCounter
The total number of messages received.
@field mType
The kind of SMPTE time using the SMPTE time type constants.
@field mFlags
A set of flags that indicate the SMPTE state.
@field mHours
The number of hours in the full message.
@field mMinutes
The number of minutes in the full message.
@field mSeconds
The number of seconds in the full message.
@field mFrames
The number of frames in the full message.
*/
struct SMPTETime
{
SInt16 mSubframes;
SInt16 mSubframeDivisor;
UInt32 mCounter;
UInt32 mType;
UInt32 mFlags;
SInt16 mHours;
SInt16 mMinutes;
SInt16 mSeconds;
SInt16 mFrames;
};
typedef struct SMPTETime SMPTETime;
/*!
@enum SMPTE Time Types
@abstract Constants that describe the type of SMPTE time.
@constant kSMPTETimeType24
24 Frame
@constant kSMPTETimeType25
25 Frame
@constant kSMPTETimeType30Drop
30 Drop Frame
@constant kSMPTETimeType30
30 Frame
@constant kSMPTETimeType2997
29.97 Frame
@constant kSMPTETimeType2997Drop
29.97 Drop Frame
@constant kSMPTETimeType60
60 Frame
@constant kSMPTETimeType5994
59.94 Frame
@constant kSMPTETimeType60Drop
60 Drop Frame
@constant kSMPTETimeType5994Drop
59.94 Drop Frame
@constant kSMPTETimeType50
50 Frame
@constant kSMPTETimeType2398
23.98 Frame
*/
enum
{
kSMPTETimeType24 = 0,
kSMPTETimeType25 = 1,
kSMPTETimeType30Drop = 2,
kSMPTETimeType30 = 3,
kSMPTETimeType2997 = 4,
kSMPTETimeType2997Drop = 5,
kSMPTETimeType60 = 6,
kSMPTETimeType5994 = 7,
kSMPTETimeType60Drop = 8,
kSMPTETimeType5994Drop = 9,
kSMPTETimeType50 = 10,
kSMPTETimeType2398 = 11
};
/*!
@enum SMPTE State Flags
@abstract Flags that describe the SMPTE time state.
@constant kSMPTETimeValid
The full time is valid.
@constant kSMPTETimeRunning
Time is running.
*/
enum
{
kSMPTETimeValid = (1 << 0),
kSMPTETimeRunning = (1 << 1)
};
#endif
/*!
@struct AudioTimeStamp
@abstract A structure that holds different representations of the same point in time.
@field mSampleTime
The absolute sample frame time.
@field mHostTime
The host machine's time base, mach_absolute_time.
@field mRateScalar
The ratio of actual host ticks per sample frame to the nominal host ticks
per sample frame.
@field mWordClockTime
The word clock time.
@field mSMPTETime
The SMPTE time.
@field mFlags
A set of flags indicating which representations of the time are valid.
@field mReserved
Pads the structure out to force an even 8 byte alignment.
*/
struct AudioTimeStamp
{
Float64 mSampleTime;
UInt64 mHostTime;
Float64 mRateScalar;
UInt64 mWordClockTime;
SMPTETime mSMPTETime;
UInt32 mFlags;
UInt32 mReserved;
};
typedef struct AudioTimeStamp AudioTimeStamp;
/*!
@enum AudioTimeStamp Flags
@abstract The flags that indicate which fields in an AudioTimeStamp structure are valid.
@constant kAudioTimeStampSampleTimeValid
The sample frame time is valid.
@constant kAudioTimeStampHostTimeValid
The host time is valid.
@constant kAudioTimeStampRateScalarValid
The rate scalar is valid.
@constant kAudioTimeStampWordClockTimeValid
The word clock time is valid.
@constant kAudioTimeStampSMPTETimeValid
The SMPTE time is valid.
*/
enum
{
kAudioTimeStampSampleTimeValid = (1 << 0),
kAudioTimeStampHostTimeValid = (1 << 1),
kAudioTimeStampRateScalarValid = (1 << 2),
kAudioTimeStampWordClockTimeValid = (1 << 3),
kAudioTimeStampSMPTETimeValid = (1 << 4)
};
/*!
@enum Commonly Used Combinations of AudioTimeStamp Flags
@abstract Some commonly used combinations of AudioTimeStamp flags.
@constant kAudioTimeStampSampleHostTimeValid
The sample frame time and the host time are valid.
*/
enum
{
kAudioTimeStampSampleHostTimeValid = (kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid)
};
#endif
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2010 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 "iop_au_interface.h"
#include "AUSidetone.h"
#include "AUNull.h"
#include <debug.h>
extern "C" audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
#if USE_SIDETONE
AUBase_AE2 *This = AUSidetone::Create_AUSidetone(sampleRate, numChannels, sampleSize);
return (audio_unit_t)This;
#else
return NULL;
#endif
}
extern "C" audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize)
{
AUBase_AE2 *This = AUNull::Create_AUNull(sampleRate, numChannels, sampleSize);
return (audio_unit_t)This;
}
extern "C" void Destroy_AudioUnit(audio_unit_t au)
{
AUBase_AE2 *This = (AUBase_AE2*)au;
delete This;
}
extern "C" int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
*parameterValue = This->GetParameter((AudioUnitParameterID)parameter);
return 0;
}
extern "C" int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->SetParameter((AudioUnitParameterID)parameter, (AudioUnitParameterValue)parameterValue);
}
extern "C" int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable)
{
if (!au)
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->GetPropertyInfo(static_cast<AudioUnitPropertyID>(propID), *outSize, *outWritable);
}
extern "C" int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize)
{
if (!au)
{
return -1;
}
uint32_t propSize;
bool propWritable;
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
if (result)
{
return result;
}
if (propSize > *ioSize)
{
return -1;
}
*ioSize = propSize;
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->GetProperty(static_cast<AudioUnitPropertyID>(propID), outData);
}
extern "C" int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize)
{
if (!au)
{
return -1;
}
uint32_t propSize;
bool propWritable;
int32_t result = AudioUnit_GetPropertyInfo(au, propID, &propSize, &propWritable);
if (result)
{
return result;
}
if ((!propWritable))
{
return -1;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
return This->SetProperty(static_cast<AudioUnitPropertyID>(propID), inData, inSize);
}
extern "C" void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess)
{
if (!au)
{
return ;
}
AUBase_AE2 *This = (AUBase_AE2*)au;
This->Process(inSourceP, inDestP, inFramesToProcess);
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _IOP_AU_INTERFACE_H_
#define _IOP_AU_INTERFACE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef void * audio_unit_t;
audio_unit_t Create_AUSidetone(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
audio_unit_t Create_AUNull(uint32_t sampleRate, uint32_t numChannels, uint32_t sampleSize);
void Destroy_AudioUnit(audio_unit_t au);
// parameters are single values that can be adjusted
int32_t AudioUnit_GetParameter(audio_unit_t au, uint32_t parameter, float *parameterValue);
int32_t AudioUnit_SetParameter(audio_unit_t au, uint32_t parameter, float parameterValue);
// properties are blocks of data that can be adjusted, usually a private setting between user and unit
int32_t AudioUnit_GetPropertyInfo(audio_unit_t au, uint32_t propID, uint32_t *outSize, bool *outWritable);
int32_t AudioUnit_GetProperty(audio_unit_t au, uint32_t propID, void *outData, uint32_t *ioSize);
int32_t AudioUnit_SetProperty(audio_unit_t au, uint32_t propID, const void *inData, uint32_t inSize);
void AudioUnit_Process(audio_unit_t au, const void *inSourceP, void *inDestP, uint32_t inFramesToProcess);
#ifdef __cplusplus
}
#endif
#endif // _IOP_AU_INTERFACE_H_
@@ -0,0 +1,134 @@
/*
* Copyright (C) 2010-2011 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_tap.h"
#include "timestamper.h"
#include <debug.h>
#include <AssertMacros.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
#include "iop_au_interface.h"
typedef struct
{
uint8_t *IOBufferBegin;
uint8_t *IOBufferEnd;
uint8_t *IOBuffer;
} internal_debug_tap_t;
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)malloc(sizeof(debug_tap_t));
if (This)
{
This->IOBufferBegin = inIOBegin;
This->IOBufferEnd = inIOEnd;
This->IOBuffer = inIO;
}
return This;
}
void destroy_debug_tap(debug_tap_t tap)
{
free(tap);
}
static void clean_cache(void *addr, size_t size)
{
// the onus is on the caller to make sure the cache is aligned
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_CLEAN, lineBase, sizeToDo);
}
static void invalidate_cache(void *addr, size_t size)
{
void* lineBase = (void*)((uint32_t)addr & ~(CPU_CACHELINE_SIZE-1));
size_t sizeToDo = ((((uint32_t)addr & (CPU_CACHELINE_SIZE-1)) + size)/CPU_CACHELINE_SIZE + 1) * CPU_CACHELINE_SIZE;
platform_cache_operation(CACHE_INVALIDATE, lineBase, sizeToDo);
}
static size_t min(size_t a, size_t b) { return (a < b) ? a : b; }
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
size_t result = 0;
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
return result;
// make sure we don't overflow the IOBuffer
size_t dataToSend = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
memcpy(This->IOBuffer, src, dataToSend);
clean_cache(This->IOBuffer, dataToSend);
This->IOBuffer += dataToSend;
src += dataToSend;
if (This->IOBuffer >= This->IOBufferEnd)
{
// if we've wrapped around, send the size of the IOBuffer to the caller
This->IOBuffer = This->IOBufferBegin;
result = This->IOBufferEnd - This->IOBufferBegin;
}
// now that we've sent the data, how much is left
dataToSend = sizeInBytes - dataToSend;
// if there is anything left, send it
if (dataToSend)
{
memcpy(This->IOBuffer, src, dataToSend);
clean_cache(This->IOBuffer, sizeInBytes);
This->IOBuffer += dataToSend;
}
return result;
}
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes)
{
internal_debug_tap_t *This = (internal_debug_tap_t*)tap;
size_t result = 0;
// don't transfer anything to user if sizeInBytes is larger than IOBuffer:
if (sizeInBytes > (uint32_t)(This->IOBufferEnd - This->IOBufferBegin))
return result;
// make sure we don't overflow the IOBuffer
size_t dataToGet = min(This->IOBufferEnd - This->IOBuffer, sizeInBytes);
invalidate_cache(This->IOBuffer, dataToGet);
memcpy(dst, This->IOBuffer, dataToGet);
This->IOBuffer += dataToGet;
dst += dataToGet;
if (This->IOBuffer >= This->IOBufferEnd)
{
// if we've wrapped around, send the size of the IOBuffer to the caller
This->IOBuffer = This->IOBufferBegin;
result = This->IOBufferEnd - This->IOBufferBegin;
}
// now that we've got the data, how much is left
dataToGet = sizeInBytes - dataToGet;
// if there is anything left, get it
if (dataToGet)
{
invalidate_cache(This->IOBuffer, dataToGet);
memcpy(dst, This->IOBuffer, dataToGet);
This->IOBuffer += dataToGet;
}
return result;
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _DEBUG_TAP_H_
#define _DEBUG_TAP_H_
#include <platform.h>
/*
* A debug_tap is a way of tapping off or tapping in an audio signal from
* user space. Pass the pointer to the beginning, end, and start to create
* a tap. You send or get data from a tap, which causes the write or read
* head to progress around the buffer circularly. When the process wraps,
* the send or get call returns the total number of bytes sent, otherwise
* it returns 0.
*/
typedef void * debug_tap_t;
debug_tap_t create_debug_tap(uint8_t *inIOBegin, uint8_t *inIOEnd, uint8_t *inIO);
void destroy_debug_tap(debug_tap_t tap);
// return value is nonzero on calls where we wrap IO buffer. Returned
// value indicates number bytes to give to "timestamp" function
size_t send_to_tap(debug_tap_t tap, const uint8_t* src, uint32_t sizeInBytes);
size_t get_from_tap(debug_tap_t tap, uint8_t* dst, uint32_t sizeInBytes);
#endif // _DEBUG_TAP_H_
@@ -0,0 +1,893 @@
/*
* 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
@@ -0,0 +1,219 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _IOP_AUDIODSP_PROTOCOL_H_
#define _IOP_AUDIODSP_PROTOCOL_H_
#include <sys/types.h>
/*
* Command size is (somewhat) tunable.
*
* The principal consideration here is the maximum scatter/gather list size
* this permits.
*/
#define kIOPAUDIODSP_COMMAND_SIZE (512)
/*
* IOPAUDIODSP_opcode_t: identifies the command sent and from the AE2
*/
typedef uint32_t IOPAUDIODSP_opcode_t;
#define kIOPAUDIODSP_OPCODE_UNKNOWN ((IOPAUDIODSP_opcode_t) 0)
/*
* Commands for communicating with DSP library(ies)
*/
#define kIOPAUDIODSP_OPCODE_START_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 3)
#define kIOPAUDIODSP_OPCODE_STOP_LOOPBACK_PROCESSING ((IOPAUDIODSP_opcode_t) 4)
#define kIOPAUDIODSP_OPCODE_INITTIMESTAMP ((IOPAUDIODSP_opcode_t) 5)
#define kIOPAUDIODSP_OPCODE_TIMESTAMP ((IOPAUDIODSP_opcode_t) 6)
#define kIOPAUDIODSP_OPCODE_SET_PARAMETER ((IOPAUDIODSP_opcode_t) 7)
#define kIOPAUDIODSP_OPCODE_GET_PARAMETER ((IOPAUDIODSP_opcode_t) 8)
#define kIOPAUDIODSP_OPCODE_DO_TRANSFER ((IOPAUDIODSP_opcode_t) 9)
#define kIOPAUDIODSP_OPCODE_SET_PROPERTY ((IOPAUDIODSP_opcode_t) 10)
#define kIOPAUDIODSP_OPCODE_GET_PROPERTY ((IOPAUDIODSP_opcode_t) 11)
/*
* IOPAUDIODSP_status_t: result from commands
*/
typedef uint32_t IOPAUDIODSP_status_t;
#define kIOPAUDIODSP_STATUS_UNKNOWN ((IOPAUDIODSP_status_t) 0)
#define kIOPAUDIODSP_STATUS_SUCCESS ((IOPAUDIODSP_status_t) 1)
#define kIOPAUDIODSP_STATUS_FAILURE ((IOPAUDIODSP_status_t) 0x80000000)
#define kIOPAUDIODSP_STATUS_DEVICE_ERROR ((IOPAUDIODSP_status_t) 0x80000001)
#define kIOPAUDIODSP_STATUS_DEVICE_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000002)
#define kIOPAUDIODSP_STATUS_DMA_TIMEOUT ((IOPAUDIODSP_status_t) 0x80000003)
#define kIOPAUDIODSP_STATUS_PARAM_INVALID ((IOPAUDIODSP_status_t) 0x80000004)
#define kIOPAUDIODSP_STATUS_UNIMPLEMENTED ((IOPAUDIODSP_status_t) 0x80000005)
/*
* IOPAUDIODSP_module_t: dsp modules
*/
typedef uint32_t IOPAUDIODSP_module_t;
#define kIOPAUDIODSP_MODULE_LOOPBACK_PROCESSING ((IOPAUDIODSP_module_t) 1)
/*
* IOPAUDIODSP_token_t: The opaque token to use for audio processing
*/
typedef uint32_t IOPAUDIODSP_token_t;
// Token for sending messages to the iop_audiodsp system itself
#define kIOPAUDIODSP_system_token_t ((IOPAUDIODSP_opcode_t) 0xFFFFFFFF)
/*
* IOPAUDIODSP_status_t result from commands
*/
struct _IOPAUDIODSP
{
IOPAUDIODSP_opcode_t mOpcode;
IOPAUDIODSP_status_t mStatus;
};
typedef struct _IOPAUDIODSP IOPAUDIODSP;
/*
* Messages. Message layout is dependent on the opcode. For instance, if
* the opcode is kIOPAUDIODSP_OPCODE_START, then the message will be interpreted
* as IOPAUDIODSP_START.
* It is the senders responsiblity to make sure a message is set correctly.
*/
/*
* IOPAUDIODSP_START message
* Message used to start a new audio process.
* If message succeeds, iopToken will contain the token used for processing.
* Fill the input and output with necessary parameters. Additional parameters
* passed through the additional_paramters pointer.
*/
struct _IOPAUDIODSP_START
{
IOPAUDIODSP mIOPHeader;
uint32_t mAdditionalParametersSizeBytes;
uint8_t mAdditionalParameters[];
};
typedef struct _IOPAUDIODSP_START IOPAUDIODSP_START;
/*
* IOPAUDIODSP_STOP message
* Message used to destroy a token.
* If message succeeds, iopToken is no longer valid.
*/
struct _IOPAUDIODSP_STOP
{
IOPAUDIODSP mIOPHeader;
};
typedef struct _IOPAUDIODSP_STOP IOPAUDIODSP_STOP;
/*
* IOPAUDIODSP_INITTIMESTAMP message
*/
struct _IOPAUDIODSP_INITTIMESTAMP
{
IOPAUDIODSP mIOPHeader;
uint32_t mTimeStamperBufferAddr;
};
typedef struct _IOPAUDIODSP_INITTIMESTAMP IOPAUDIODSP_INITTIMESTAMP;
/*
* IOPAUDIODSP_TIMESTAMP message
*/
struct _IOPAUDIODSP_TIMESTAMP
{
IOPAUDIODSP mIOPHeader;
uint64_t mSampleCount;
uint64_t mTimeStamp;
};
typedef struct _IOPAUDIODSP_TIMESTAMP IOPAUDIODSP_TIMESTAMP;
/*
* IOPAUDIODSP_DO_TRANSFER message
*/
struct _IOPAUDIODSP_DO_TRANSFER
{
IOPAUDIODSP mIOPHeader;
uint32_t mIndex;
uint32_t mDirection;
uint32_t mDoTransfer;
// send the address range of the buffer to work with, as well as where processing should start
uint32_t mBufferBegin;
uint32_t mBufferEnd;
uint32_t mBufferStart;
// this are necessary if we do psuedo transfer by timer
uint32_t mSampleRate;
// this is necessary to report samples transferred
uint32_t mBytesPerFrame;
};
typedef struct _IOPAUDIODSP_DO_TRANSFER IOPAUDIODSP_DO_TRANSFER;
/*
* Format of Parameters is (IOPAUDIODSP_PARAMETER_SUBCOMMAND) ...
* {
* parameter (4-bytes)
* parameter_value (float)
* }
*
*/
struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND
{
uint32_t mParameterID;
float mParameterValue;
};
typedef struct _IOPAUDIODSP_PARAMETER_SUBCOMMAND IOPAUDIODSP_PARAMETER_SUBCOMMAND;
/*
* Format of Properties is (IOPAUDIODSP_PROPERTY_SUBCOMMAND) ...
* {
* Property (4-bytes)
* Property_size (4-byte)
* Property (void*)
* }
*
*/
struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND
{
uint32_t mPropertyID;
uint32_t mPropertySizeBytes;
uint8_t mPropertyData[];
};
typedef struct _IOPAUDIODSP_PROPERTY_SUBCOMMAND IOPAUDIODSP_PROPERTY_SUBCOMMAND;
struct _IOPAUDIODSP_MODULE_COMMAND
{
IOPAUDIODSP mIOPHeader;
uint32_t mModule;
union
{
IOPAUDIODSP_PROPERTY_SUBCOMMAND mProperty;
IOPAUDIODSP_PARAMETER_SUBCOMMAND mParameter;
};
};
typedef struct _IOPAUDIODSP_MODULE_COMMAND IOPAUDIODSP_MODULE_COMMAND;
union _IOPAUDIODSP_Command
{
IOPAUDIODSP iopaudiodsp;
IOPAUDIODSP_START start;
IOPAUDIODSP_STOP stop;
IOPAUDIODSP_INITTIMESTAMP init_timestamp;
IOPAUDIODSP_DO_TRANSFER do_transfer;
IOPAUDIODSP_TIMESTAMP timestamp;
IOPAUDIODSP_MODULE_COMMAND module_command;
UInt8 _pad[kIOPAUDIODSP_COMMAND_SIZE];
};
typedef union _IOPAUDIODSP_Command IOPAUDIODSP_Command;
#endif // _IOP_AUDIODSP_PROTOCOL_H_
@@ -0,0 +1,237 @@
/*
* Copyright (C) 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 "loopback_device.h"
#include "ae2_mca.h"
#include <arch/arm/arm.h>
#include <drivers/audio/audio.h>
#include <debug.h>
#include <stdlib.h>
#include <platform/timer.h>
enum { kBurstSize = 4 };
typedef struct
{
audio_unit_t mAudioUnit;
size_t mBytesPerFrame;
uint32_t mErrorCount[kAudioDevice_Last];
} internal_loopback_device_t;
uint32_t readReg(uint32_t address)
{
return *(volatile uint32_t *)address;
}
void writeReg(uint32_t address, uint32_t value)
{
*(volatile uint32_t *)address = value;
}
uint32_t readMCA0Reg(uint32_t offset)
{
return readReg(rMCA0_BASE + offset);
}
void writeMCA0Reg(uint32_t offset, uint32_t value)
{
writeReg(rMCA0_BASE + offset, value);
}
// if this function takes too long, bail. We cannot hang the system here.
static const utime_t kMaxTime = 1000000;
bool startPIO()
{
bool result = true;
const utime_t time_out = system_time() + kMaxTime;
// disable TX and RX
uint32_t MCATXCFG = readMCA0Reg(rMCATXCFG);
writeMCA0Reg(rMCATXCFG, MCATXCFG & ~(1));
uint32_t MCARXCFG = readMCA0Reg(rMCARXCFG);
writeMCA0Reg(rMCARXCFG, MCARXCFG & ~(1));
// Flush the RX and TX fifos
writeMCA0Reg(rMCACTL, 0x300);
uint32_t MCACTL = readMCA0Reg(rMCACTL);
while (result && (MCACTL & 0x300))
{
MCACTL = readMCA0Reg(rMCACTL);
result = system_time() < time_out;
}
// "Prime" the tx with zeros.
uint32_t tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
while (result && (tx_fifo_level < 0x8))
{
writeMCA0Reg(rMCATXDATA, 0);
tx_fifo_level = (readMCA0Reg(rMCASTATUS)) & 0x3FF;
result = system_time() < time_out;
}
// enable RX IRQ (we don't need TX IRQ, we only respond when we have RX samples)
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG | (1 << rMCAUNSRXCFG_IRQ_EN));
// enable TX and RX
writeMCA0Reg(rMCATXCFG, MCATXCFG);
writeMCA0Reg(rMCARXCFG, MCARXCFG);
return result;
}
bool stopPIO()
{
uint32_t MCAUNSRXCFG = readMCA0Reg(rMCAUNSRXCFG);
writeMCA0Reg(rMCAUNSRXCFG, MCAUNSRXCFG & ~((1 << rMCAUNSRXCFG_IRQ_EN)));
uint32_t MCAUNSTXCFG = readMCA0Reg(rMCAUNSTXCFG);
writeMCA0Reg(rMCAUNSTXCFG, MCAUNSTXCFG & ~((1 << rMCAUNSTXCFG_IRQ_EN)));
return true;
}
static void
handleAudioDeviceErrors(internal_loopback_device_t * This, const uint32_t status)
{
// <rdar://problem/13467070> Panic when setting AppleSongbirdDSP sidetone EQ
// clear any sort of sticky-bit errors.
const uint32_t errors = status & ( rMCASTATUS_FRAMEEEROR_MASK | rMCASTATUS_RXOVERRUN_MASK | rMCASTATUS_RXUNDERRUN_MASK | rMCASTATUS_TXOVERRUN_MASK | rMCASTATUS_TXUNDERRUN_MASK );
writeMCA0Reg(rMCASTATUS, errors);
if (errors && This)
{
if (status & rMCASTATUS_FRAMEEEROR_MASK) ++This->mErrorCount[kFrameError];
if (status & rMCASTATUS_RXOVERRUN_MASK) ++This->mErrorCount[kRXOverrun];
if (status & rMCASTATUS_RXUNDERRUN_MASK) ++This->mErrorCount[kRXUnderrun];
if (status & rMCASTATUS_TXOVERRUN_MASK) ++This->mErrorCount[kTXOverrun];
if (status & rMCASTATUS_TXUNDERRUN_MASK) ++This->mErrorCount[kTXUnderrun];
}
}
static void
handleAudioDeviceInterrupt_wFloat(void *arg)
{
internal_loopback_device_t * This = (internal_loopback_device_t *)arg;
const uint32_t status = readMCA0Reg(rMCASTATUS);
handleAudioDeviceErrors(This, status);
// where the magic occurs
if ((status & (1 << rMCASTATUS_RXHIGHWATER)))
{
// we do burst sizes of 16-bit data
int16_t data[kBurstSize];
for (size_t i = 0; i < kBurstSize; ++i)
{
data[i] = readMCA0Reg(rMCARXDATA);
}
if (This && This->mAudioUnit)
{
AudioUnit_Process(This->mAudioUnit, data, data, sizeof(data)/This->mBytesPerFrame);
}
// <rdar://problem/14296993>, only write if there is room in the TX fifo
const uint32_t tx_fifo_level = status & 0x3FF;
const uint32_t k_tx_fifo_size = readMCA0Reg(rMCAFIFOSIZE) & 0x3FF;
if ((tx_fifo_level + kBurstSize) < k_tx_fifo_size)
{
for (size_t i = 0; i < kBurstSize; ++i)
{
writeMCA0Reg(rMCATXDATA, data[i]);
}
}
}
}
// enable floating point code
void handleAudioDeviceInterrupt(void * object)
{
enter_critical_section();
arm_call_fpsaved(object, &handleAudioDeviceInterrupt_wFloat);
exit_critical_section();
}
// create a loopback_device that will read from device rx, optional process, and send to device tx
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame)
{
dprintf(DEBUG_CRITICAL, "Creating a loopback_device object\n");
// hardcoding to support only MCA right now.
if ((device != kMCA_0))
{
dprintf(DEBUG_CRITICAL, "oops, bad arg\n");
return NULL;
}
internal_loopback_device_t *This = (internal_loopback_device_t*)malloc(sizeof(internal_loopback_device_t));
if (This)
{
This->mAudioUnit = optional_au;
This->mBytesPerFrame = bytes_per_frame;
for (uint32_t i = kFrameError; i < kError_last; ++i)
{
This->mErrorCount[i] = 0;
}
// we can do this here repeatedly
install_int_handler(AE2_INT_MCA0, handleAudioDeviceInterrupt, This);
}
return This;
}
void destroy_loopback_device(loopback_device_t device)
{
// preliminary stop
stop_loopback_device(device);
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This)
{
free(This);
}
}
uint32_t getErrorCount(loopback_device_t dma, Error_Index which)
{
// preliminary stop
internal_loopback_device_t *This = (internal_loopback_device_t*)dma;
if (This)
{
return This->mErrorCount[which];
}
return 0;
}
bool start_loopback_device(loopback_device_t device)
{
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This && startPIO())
{
unmask_int(AE2_INT_MCA0);
return true;
}
return false;
}
bool stop_loopback_device(loopback_device_t device)
{
internal_loopback_device_t *This = (internal_loopback_device_t*)device;
if (This)
{
mask_int(AE2_INT_MCA0);
return stopPIO();
}
return false;
}
@@ -0,0 +1,61 @@
/*
* Copyright (C) 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.
*/
#ifndef __LOOPBACK_DEVICE_H__
#define __LOOPBACK_DEVICE_H__
#include "iop_au_interface.h"
typedef void * loopback_device_t;
/*
* loopback_device_t is an object that handles looping device rx to device tx
*/
typedef enum {
kI2S_0 = 0,
kI2S_1,
kI2S_2,
kI2S_3,
kMCA_0,
kMCA_1,
kAudioDevice_Last
} AudioDevice_Index;
// create a loopback_device that will read from device rx, optional process, and send to device tx
loopback_device_t create_loopback_device(AudioDevice_Index device, audio_unit_t optional_au, size_t bytes_per_frame);
void destroy_loopback_device(loopback_device_t device);
typedef enum {
kFrameError = 0,
kRXOverrun,
kRXUnderrun,
kTXOverrun,
kTXUnderrun,
kError_last
} Error_Index;
static const char* const kErrorTypeStr[kError_last] = {
"Frame_Error",
"RX_Overrun",
"RX_Underrun",
"TX_Overrun",
"TX_Underrun",
};
uint32_t getErrorCount(loopback_device_t device, Error_Index which);
// return true on success
bool start_loopback_device(loopback_device_t device);
bool stop_loopback_device(loopback_device_t device);
#endif /* __LOOPBACK_DEVICE_H__ */
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2010-2011 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 "loopback_process.h"
#include "timestamper.h"
#include "debug_tap.h"
#include <debug.h>
#include <AssertMacros.h>
#include <platform.h>
#include <platform/timer.h>
#include <sys/task.h>
typedef struct
{
uint32_t mNumFrames;
uint32_t mNumChannels;
uint32_t mSampleSize;
uint32_t mChannelBitmask;
// the scratch buffer is used by the AudioUnit. We assume it needs
// 8.24 Mono (channel 0) data
audio_unit_t mAudioUnit;
// for debug: which index to send to user
uint32_t mIndex;
debug_tap_t mInputTap;
debug_tap_t mOutputTap;
} internal_loopback_process_t;
// XXX channel bitmask currently not implemented. Assuming audio data is packed.
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask)
{
internal_loopback_process_t *This = (internal_loopback_process_t*)malloc(sizeof(internal_loopback_process_t));
if (This)
{
This->mNumFrames = frames_to_process;
This->mNumChannels = channels_to_process;
This->mSampleSize = sample_size;
This->mChannelBitmask = channel_bitmask;
This->mAudioUnit = au;
This->mIndex = 0;
This->mInputTap = NULL;
This->mOutputTap = NULL;
}
return This;
}
void destroy_loopback_process(loopback_process_t loopback)
{
internal_loopback_process_t *This = (internal_loopback_process_t*)loopback;
free(This);
}
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
// should we turn ints off for this?
This->mInputTap = inputTap;
This->mOutputTap = outputTap;
}
void set_tap_point(loopback_process_t loopback, uint32_t index)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
// should we turn ints off for this?
This->mIndex = index;
}
void process_data(loopback_process_t loopback, void *processBuffer)
{
internal_loopback_process_t *This = (internal_loopback_process_t *)loopback;
uint32_t sizeOfInput = This->mNumFrames * This->mNumChannels * This->mSampleSize;
uint32_t amountToSend = 0;
// DebugTap1
if (This->mInputTap && (This->mIndex == 1))
{
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
// DebugInput1
if (This->mOutputTap && (This->mIndex == 1))
{
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
AudioUnit_Process(This->mAudioUnit, processBuffer, processBuffer, This->mNumFrames);
// DebugTap3
if (This->mInputTap && (This->mIndex == 3))
{
size_t this_amountToSend = send_to_tap(This->mInputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
// DebugInput1
if (This->mOutputTap && (This->mIndex == 3))
{
size_t this_amountToSend = get_from_tap(This->mOutputTap, (uint8_t*)processBuffer, sizeOfInput);
if (this_amountToSend) amountToSend = this_amountToSend;
}
if (amountToSend)
{
// Should this be done at the beginning?
uint64_t timestamp = timer_get_ticks();
send_timestamp(This->mNumFrames, timestamp);
}
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _SPEAKER_PROT_H_
#define _SPEAKER_PROT_H_
#include "debug_tap.h"
#include "iop_au_interface.h"
#include <platform.h>
typedef void * loopback_process_t;
/*
* loopback process is an object that will process an input buffer in-
* place, applying the passed Audio Unit to it.
*
* debug_taps can be added at specific tap points.
*
* It is assumed that the input number of samples to process matches
* with the call to process.
*
*/
// channel bitmask is to allow cases when audio is not packed.
loopback_process_t create_loopback_process(audio_unit_t au, uint32_t frames_to_process, uint32_t channels_to_process, uint32_t sample_size, uint32_t channel_bitmask);
void destroy_loopback_process(loopback_process_t loopback);
void set_debug_tap(loopback_process_t loopback, debug_tap_t inputTap, debug_tap_t outputTap);
void set_tap_point(loopback_process_t loopback, uint32_t index);
// Assumes that input is as described when loopback created
void process_data(loopback_process_t loopback, void *processBuffer);
#endif // _SPEAKER_PROT_H_
@@ -0,0 +1,48 @@
# Copyright (C) 2010-2011 Apple Inc. All rights reserved.
#
# This document is the property of Apple Computer, 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 Computer, Inc.
#
#
# Audio driver
#
LOCAL_DIR := $(GET_LOCAL_DIR)
OPTIONS += WITH_FUNCTION_AUDIODSP=1 \
USE_SIDETONE=1
IOP_FUNCTIONS += AUDIODSP
ALL_OBJS += $(LOCAL_DIR)/iop_audiodsp.o \
$(LOCAL_DIR)/loopback_process.o \
$(LOCAL_DIR)/loopback_device.o \
$(LOCAL_DIR)/timestamper.o \
$(LOCAL_DIR)/debug_tap.o \
$(LOCAL_DIR)/AudioUnits/iop_au_interface.o \
$(LOCAL_DIR)/AudioUnits/AUSidetone.o \
$(LOCAL_DIR)/AudioUnits/AUNull.o \
INSTALL_HEADERS += $(LOCAL_DIR)/iop_audiodsp_protocol.h
# Assuming 8 k for each task, with 2 task.
# Assuming 16 k heap for au process
# 32 k heap total
export IOP_HEAP_REQUIRED := $(call ADD,$(IOP_HEAP_REQUIRED),32768)
GLOBAL_INCLUDES += $(SDKROOT)/usr/local/standalone/firmware \
$(SDKROOT)/usr/local/standalone/firmware/include \
$(LOCAL_DIR)/AudioUnits \
$(LOCAL_DIR)/AE2_I2S \
$(LOCAL_DIR)/AE2_MCA \
$(LOCAL_DIR)/AE2_DMA \
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/Headers \
$(SDKROOT)/System/Library/Frameworks/AudioUnit.framework/PrivateHeaders
PREBUILT_STATICLIBS += $(SDKROOT)/usr/local/standalone/firmware/libm.a \
$(SDKROOT)/usr/local/standalone/firmware/libvDSP.a
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2010-2011 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 "timestamper.h"
#include <debug.h>
#include <AssertMacros.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>
#define kLogTimestamps 0
static IOPAUDIODSP_Command *timestamper_message_buffer;
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer)
{
timestamper_message_buffer = buffer;
}
static int timestamper_message_channel;
static struct task_event timestamper_message_event;
static struct task_event timestamper_runtask_event;
static int timestamper_task(void *cfg);
static void timestamper_sleep(int mode);
IOP_FUNCTION(timestamper, timestamper_task, 8*1024, AUDIODSP_TIMER_CHANNEL);
IOP_SLEEP_HOOK(timestamper, timestamper_sleep);
/*
* Get a message to send to the Host
*/
static IOPAUDIODSP_Command *
get_timestamper_message(utime_t allowed_delay)
{
utime_t deadline, t;
int idx;
/* nothing we can do if we don't have a buffer */
if (NULL == timestamper_message_buffer)
return(NULL);
/* try to get a slot right away */
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
return(timestamper_message_buffer + idx);
if (IOP_MESSAGE_NO_WAIT == allowed_delay)
return(NULL);
/* spin waiting for a free slot */
deadline = system_time() + allowed_delay;
for (;;) {
/* get the current time and see if we've timed out */
t = system_time();
if (t >= deadline)
return(NULL);
/* sleep waiting for notification or timeout */
event_wait_timeout(&timestamper_message_event, deadline - t);
/* try again to get a message slot */
if (-1 != (idx = qwi_next_send_index(timestamper_message_channel)))
return(timestamper_message_buffer + idx);
}
}
/*
* Reclaim a message the host has accepted.
*/
static void
timestamper_message_wakeup(void *arg __unused)
{
bool replies;
uint32_t message;
/* reap message replies */
replies = false;
while (qwi_receive_item(timestamper_message_channel, &message) != -1)
replies = true;
/* if we got at least one reply, wake anyone trying to send */
if (replies)
{
event_signal(&timestamper_message_event);
}
}
/*
* Try to send a trace message to the host.
*/
void
send_timestamp(uint32_t samplesTransferred, uint64_t timestamp)
{
IOPAUDIODSP_Command *msg;
/* now get a message - wait a little while for it but not forever as we might deadlock the host */
if (NULL != (msg = get_timestamper_message(1000))) {
/* populate the message */
msg->timestamp.mIOPHeader.mOpcode = kIOPAUDIODSP_OPCODE_TIMESTAMP;
msg->timestamp.mIOPHeader.mStatus = kIOPAUDIODSP_STATUS_SUCCESS;
msg->timestamp.mSampleCount = samplesTransferred;
msg->timestamp.mTimeStamp = timestamp;
#if kLogTimestamps
dprintf(DEBUG_CRITICAL, "Timestamp %ull\n", timestamp);
#endif
/* push it to memory */
platform_cache_operation(CACHE_CLEAN, msg, sizeof(*msg));
/* and give it to the host */
qwi_send_item(timestamper_message_channel, QWI_ENCODE_ORDINAL(msg - timestamper_message_buffer));
}
else
{
dprintf(DEBUG_CRITICAL, "couldn't get a timestamp message\n");
}
}
static int
timestamper_task(void *cfg)
{
struct iop_channel_config *channel = (struct iop_channel_config *)cfg;
dprintf(DEBUG_SPEW, "@@ Timestamper task starting\n");
/* establish the host communications channel */
event_init(&timestamper_message_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
event_init(&timestamper_runtask_event, EVENT_FLAG_AUTO_UNSIGNAL, false);
dprintf(DEBUG_SPEW, "** opening audiodsp channel\n");
timestamper_message_channel = qwi_instantiate_channel("audio timestamp",
QWI_ROLE_PRODUCER,
channel->ring_size,
(void *)mem_static_map_cached(channel->ring_base),
timestamper_message_wakeup,
NULL);
#if WITH_VFP && !WITH_VFP_ALWAYS_ON
/* Little doubt we'll need VFP/Neon */
arch_task_fp_enable(true);
#endif
for (;;) {
dprintf(DEBUG_SPEW, "Waiting on run event\n");
event_wait(&timestamper_runtask_event);
}
return(0);
}
static void
timestamper_sleep(int mode)
{
}
@@ -0,0 +1,24 @@
/*
* Copyright (C) 2010-2011 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.
*/
#ifndef _TIMESTAMPER_H_
#define _TIMESTAMPER_H_
#include <platform.h>
#include "iop_audiodsp_protocol.h"
void set_timestamper_message_buffer(IOPAUDIODSP_Command *buffer);
void send_timestamp(uint32_t samplesTransferred, uint64_t timestamp);
#endif // _TIMESTAMPER_H_