📄 dma.cpp
字号:
// Setup the source width
pDMACChannelRegs->DMACCxControl &= ~(DWORD)DMAC_SWIDTH;
pDMACChannelRegs->DMACCxControl |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth << DMAC_SWIDTH_SHIFT);
// Setup the destination burst size
pDMACChannelRegs->DMACCxControl &= ~(DWORD)DMAC_DBSIZE;
pDMACChannelRegs->DMACCxControl |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucDestBurstSize << DMAC_DBSIZE_SHIFT);
// Setup the source burst size
pDMACChannelRegs->DMACCxControl &= ~(DWORD)DMAC_SBSIZE;
pDMACChannelRegs->DMACCxControl |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucSourceBurstSize << DMAC_SBSIZE_SHIFT);
// Determine whether to increment the source and destination
if(DMAInfo.DMAChannels[ucDMAChannel].fIncrementSource)
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_SI; // Increment the source
if(DMAInfo.DMAChannels[ucDMAChannel].fIncrementDest)
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_DI; // Increment the destination
// Setup the dest device
pDMACChannelRegs->DMACCxConfiguration &= ~(DWORD)DMAC_CHCONFIG_DPER;
pDMACChannelRegs->DMACCxConfiguration |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucDestDevice << DMAC_DPER_SHIFT);
// Setup the source device
pDMACChannelRegs->DMACCxConfiguration &= ~(DWORD)DMAC_CHCONFIG_SPER;
pDMACChannelRegs->DMACCxConfiguration |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucSourceDevice << DMAC_SPER_SHIFT);
// Setup the flow control
pDMACChannelRegs->DMACCxConfiguration &= ~(DWORD)DMAC_CHCONFIG_FLOW;
pDMACChannelRegs->DMACCxConfiguration |=
((DWORD)DMAInfo.DMAChannels[ucDMAChannel].ucFlowControl << DMAC_FLOW_SHIFT);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("-InitializeDMAChannel\r\n")));
return TRUE;
}
//
// FreeDMAChannel - Frees the specified DMA channel
//
extern "C" BOOL FreeDMAChannel(PFREE_DMA_PARAMS pParams, PFREE_DMA_RESULT pResults)
{
UCHAR ucDMAChannel;
volatile PDMAC_CHANNEL_REGS pDMACChannelRegs;
pvstSP810Regs pstSP810 = (pvstSP810Regs)(VA_SC_BASE);
DEBUGMSG(1, (TEXT("+FreeDMAChannel(channel %d)\r\n"), pParams->ucChannelNumber));
ucDMAChannel = pParams->ucChannelNumber;
pDMACChannelRegs = DMAInfo.DMAChannels[ucDMAChannel].pDMACChannelRegs;
// Disable the DMA channel to ensure no more transfers can occur on it
pDMACChannelRegs->DMACCxConfiguration &= ~(DWORD)DMAC_CHCONFIG_E;
DMAInfo.DMAChannels[ucDMAChannel].fInUse = FALSE; // Set this channel to 'free'
DEBUGMSG(1, (TEXT("-FreeDMAChannel\r\n")));
// Disable the DMA's peripheral clock if there are no more channels allocated
for(ucDMAChannel=0; ucDMAChannel<MAX_DMA_CHANNELS; ucDMAChannel++)
{
if(DMAInfo.DMAChannels[ucDMAChannel].fInUse) break;
}
DEBUGMSG(1, (TEXT("Freed a channel, no of channels = %d\r\n"),(DWORD)ucDMAChannel));
if(MAX_DMA_CHANNELS == ucDMAChannel)
{
DEBUGMSG(1, (TEXT("Switching DMA clock off.\r\n")));
pstSP810->SCPERDIS = HCLKDMA;
}
return TRUE;
}
//
// FreeDmaSysIntr - Removes the SYSINTR mapping from the chosen channel
//
extern "C" BOOL FreeDmaSysIntr(PFREE_DMA_SYSINTR_PARAMS pParams, PFREE_DMA_SYSINTR_RESULT pResults )
{
DEBUGMSG(1, (TEXT("+FreeDmaSysIntr(Freeing Channel: %d)\r\n"), pParams->ucChannelNumber));
/* Channel number out of range */
if(pParams->ucChannelNumber >= MAX_DMA_CHANNELS)
return FALSE;
/* Attempting to remove an unmapped SYSINTR */
if(SYSINTR_UNDEFINED == DMAInfo.DMAChannels[pParams->ucChannelNumber].dwDMASysIntr) {
DEBUGMSG(1, (TEXT("[ERR]FreeDmaSysIntr(Chennel not mapped)\r\n")));
return FALSE;
}
/* Remove from OAL mapping */
if(ERROR_SUCCESS == OEMReleaseSysIntr(DMAInfo.DMAChannels[pParams->ucChannelNumber].dwDMASysIntr))
{
DEBUGMSG(1, (TEXT("FreeDmaSysIntr(SYSINTR: %d now free)\r\n"),
DMAInfo.DMAChannels[pParams->ucChannelNumber].dwDMASysIntr));
/* Clear in DMAInfo structure */
DMAInfo.DMAChannels[pParams->ucChannelNumber].dwDMASysIntr=SYSINTR_UNDEFINED;
return TRUE;
} else {
DEBUGMSG(1, (TEXT("[ERR]FreeDmaSysIntr(SYSINTR: %d unable to free)\r\n"),
DMAInfo.DMAChannels[pParams->ucChannelNumber].dwDMASysIntr));
}
/* Failed to remove mapping */
return FALSE;
}
//
// StartDMATransfer - Starts a DMA transfer between two devices
//
extern "C" BOOL StartDMATransfer(PSTART_DMA_PARAMS pParams, PSTART_DMA_RESULT pResults)
{
UCHAR ucDMAChannel;
volatile PDMAC_CHANNEL_REGS pDMACChannelRegs;
DWORD dwPages, dwPage;
PDWORD pdwSourceBuffer;
PDWORD pdwDestBuffer;
DWORD dwTotalSize, dwTransferSize, dwTransfers, dwListOffset;
if (!fDMAInitialised) { /* not present */
DEBUGMSG(1, (TEXT("NO DMA blocking StartDMATransfer(channel %d)\r\n"), pParams->ucChannelNumber));
return FALSE;
}
ucDMAChannel = pParams->ucChannelNumber;
if (ucDMAChannel >= MAX_DMA_CHANNELS) { /* Illegal */
DEBUGMSG(1, (TEXT("ERROR : DMA blocked: Illegal channel %d\r\n"), pParams->ucChannelNumber));
return FALSE;
}
DEBUGMSG(1, (TEXT("+StartDMATransfer(channel %d)\r\n"), pParams->ucChannelNumber));
// Save the parameters in case we need to restart the transfer
dwTotalSize = pParams->dwTransferSize;
DMAInfo.DMAChannels[ucDMAChannel].dwTransferSize = pParams->dwTransferSize;
DMAInfo.DMAChannels[ucDMAChannel].pdwSourceBuffer = pParams->pdwSourceBuffer;
DMAInfo.DMAChannels[ucDMAChannel].pdwDestBuffer = pParams->pdwDestBuffer;
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: source buffer = 0x%X\r\n"), pParams->pdwSourceBuffer));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: dest buffer = 0x%X\r\n"), pParams->pdwDestBuffer));
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfer: total size = 0x%X\r\n"), dwTotalSize));
pDMACChannelRegs = DMAInfo.DMAChannels[ucDMAChannel].pDMACChannelRegs;
// Store the buffer locations
pdwSourceBuffer = pParams->pdwSourceBuffer;
pdwDestBuffer = pParams->pdwDestBuffer;
// Setup the DMA control register
pDMACChannelRegs->DMACCxControl &= ~(DWORD)DMAC_TRANSFER_SIZE; // Clear the size
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_PRIV; // We are always in privileged mode
// Setup the address registers to the first physical address
pDMACChannelRegs->DMACCxSrcAddr = pdwSourceBuffer[0];
pDMACChannelRegs->DMACCxDestAddr = pdwDestBuffer[0];
switch (pDMACChannelRegs->DMACCxControl & (((DWORD)DMAC_CTRL_S) | ((DWORD)DMAC_CTRL_D)))
{
case 0:
case ((DWORD)DMAC_CTRL_S) | ((DWORD)DMAC_CTRL_D): // in this case, these two memory spaces' offset should be zero
// Clip the transfer size to one DMA block and create a linked list for the rest
if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE) {
// Calculate the number of pages in the buffer(s)
dwPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(NULL, dwTotalSize);
DEBUGMSG(1, (TEXT("DMA %d - Number of virtual pages required = %d\r\n"), ucDMAChannel, dwPages));
// Check if the transfer is too big for our list buffer
if(dwPages > (DWORD)MAX_DMA_LIST_ENTRIES) {
DEBUGMSG(1, (TEXT("Error - the DMA %d transfer is too large for the list buffer\r\n"), ucDMAChannel));
return FALSE;
}
// Setup the list entry pointer to the start of the list for this channel
dwListOffset = ucDMAChannel * sizeof(LLI_ENTRY) * MAX_DMA_LIST_ENTRIES;
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry = (PLLI_ENTRY)(DMA_LIST_BUFFER_VIRT + dwListOffset);
// Keep a physical copy
DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys = (PLLI_ENTRY)(DMA_LIST_BUFFER_PHYS + dwListOffset);
dwTransferSize = (DWORD)PAGE_SIZE;
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTransferSize - 1;
// Add a 1 byte list entry to make up 1 virtual page
// This is due to the maximum transfer size of the DMAC (4095 bytes)
dwPage = 0;
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTransferSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTransferSize/sizeof(DWORD);
break;
} /* switch(DMAInfo.DMAChannels[] */
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
dwTotalSize -= (DWORD)PAGE_SIZE; // The first block has been taken care of
// Point the DMA controller at the start of the list buffer
// We OR in AHB2 as the master select for loading LLIs (Versatile)
pDMACChannelRegs->DMACCxLLI = (DWORD)((DMA_LIST_BUFFER_PHYS + dwListOffset) | DMAC_LLI_LM);
// Create the linked list
for(dwPage=1;dwPage<dwPages;dwPage++) {
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - virtual list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntry));
DEBUGMSG(1, (TEXT("DMA %d list entry - physical list entry pointer = 0x%X\r\n"),
ucDMAChannel, DMAInfo.DMAChannels[ucDMAChannel].pNextLLIEntryPhys));
// Calculate the size of the transfer
dwTransferSize = MIN(dwTotalSize, (DWORD)PAGE_SIZE);
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA %d list entry - transfer size = 0x%X\r\n"), ucDMAChannel, dwTransferSize));
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, dwTransferSize);
// If we are transfering a page size of bytes we need an extra 1 byte transfer
if(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth == TRANSFER_WIDTH_BYTE &&
dwTransferSize == (DWORD)PAGE_SIZE) {
// Add a 1 byte list entry to make up 1 virtual page
// Again due to the maximum transfer size of the DMAC (4095 bytes)
AddLLIEntry(ucDMAChannel, dwPage, pdwSourceBuffer, pdwDestBuffer, (DWORD)-1);
}
dwTotalSize -= dwTransferSize; // Reduce the overall transfer size accordingly
DEBUGMSG(1, (TEXT("DMA %d list entry - bytes left = 0x%X\r\n"), ucDMAChannel, dwTotalSize));
}
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->pNextEntry = 0; // This is the end of the list
DMAInfo.DMAChannels[ucDMAChannel].pPrevLLIEntry->dwControl |= DMAC_CTRL_I; // Set to interrupt on this last entry
} else {
// Adjust the size to the number of transfers required
switch(DMAInfo.DMAChannels[ucDMAChannel].ucSourceWidth) {
case TRANSFER_WIDTH_BYTE:
dwTransfers = dwTotalSize;
break;
case TRANSFER_WIDTH_WORD:
dwTransfers = dwTotalSize/sizeof(WORD);
break;
case TRANSFER_WIDTH_DWORD:
dwTransfers = dwTotalSize/sizeof(DWORD);
break;
}
DEBUGMSG(DMA_ZONE_OPT, (TEXT("DMA transfers = 0x%X\r\n"), dwTransfers));
pDMACChannelRegs->DMACCxLLI = 0; // There is no linked list for small transfers
pDMACChannelRegs->DMACCxControl |= DMAC_CTRL_I; // Interrupt at the end of the transfer
pDMACChannelRegs->DMACCxControl |= dwTransfers; // Set the number of transfers
} // if(dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE)
break;
case (DWORD)DMAC_CTRL_S:
////////////////////////////
DEBUGMSG(1, (TEXT("DMA %d - BYTE_OFFSET( pdwSourceBuffer[0] ) = 0x%x\r\n"), ucDMAChannel, BYTE_OFFSET( pdwSourceBuffer[0] )));
DEBUGMSG(1, (TEXT("DMA %d - dwTotalSize = 0x%x\r\n"), ucDMAChannel, dwTotalSize));
////////////////////////////
// Clip the transfer size to one DMA block and create a linked list for the rest
if(BYTE_OFFSET( pdwSourceBuffer[0] ) + dwTotalSize > (DWORD)MAX_DMA_TRANSFER_SIZE) //!!!
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -