first and last commit
This commit is contained in:
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__ */
|
||||
|
||||
Reference in new issue
Block a user