summaryrefslogtreecommitdiff
path: root/drivers/dmad/dmad.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dmad/dmad.c')
-rw-r--r--drivers/dmad/dmad.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/drivers/dmad/dmad.c b/drivers/dmad/dmad.c
new file mode 100644
index 0000000..75187d0
--- /dev/null
+++ b/drivers/dmad/dmad.c
@@ -0,0 +1,300 @@
+/* ----------------------------------------------------------------------------
+ * ATMEL Microcontroller Software Support
+ * ----------------------------------------------------------------------------
+ * Copyright (c) 2008, Atmel Corporation
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the disclaimer below.
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ----------------------------------------------------------------------------
+ */
+
+//------------------------------------------------------------------------------
+// Headers
+//------------------------------------------------------------------------------
+
+#include "dmad.h"
+#include <dma/dma.h>
+#include <irq/irq.h>
+#include <utility/assert.h>
+#include <utility/trace.h>
+
+//------------------------------------------------------------------------------
+// Local types
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+/// DMA transfer descriptor. Tracks the status and parameters of a transfer
+/// on the DMA bus.
+//------------------------------------------------------------------------------
+typedef struct _DmaTransfer {
+ /// Buffer transfer status.
+ volatile unsigned char status;
+ /// Transfer buffer size in byte.
+ unsigned int bufSize;
+ /// Total transfer size to byte.
+ volatile unsigned int transferSize;
+ /// Optional callback function.
+ DmaCallback callback;
+} DmaTransfer;
+
+//------------------------------------------------------------------------------
+/// DMAD driver structure. Monitors the status of transfers on all
+/// DMA channels.
+//------------------------------------------------------------------------------
+typedef struct _Dmad {
+
+ /// List of transfers occuring on each channel.
+ DmaTransfer transfers[DMA_CHANNEL_NUM];
+} Dmad;
+
+//------------------------------------------------------------------------------
+// Local variables
+//------------------------------------------------------------------------------
+
+/// Global DMA transfer instance.
+static Dmad dmad;
+
+//------------------------------------------------------------------------------
+// Local functions
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+/// This handler function must be called by the DMAC interrupt service routine.
+/// Identifies which event was activated and calls the associated function.
+//------------------------------------------------------------------------------
+void DMAD_Handler()
+{
+ unsigned int status;
+ unsigned char channel;
+ DmaTransfer *pTransfer;
+ status = DMA_GetStatus();
+ // Check if the buffer transfer completed is set.
+ if(status & AT91C_BTC)
+ {
+ // Scan each channel status.
+ for(channel = 0; channel < DMA_CHANNEL_NUM; channel++) {
+ if(!(status & (DMA_BTC << channel))){
+ continue;
+ }
+
+ dmad.transfers[channel].transferSize -= dmad.transfers[channel].bufSize;
+ // if next buffer is to be the last buffer in the transfer, then clear the automatic mode bit.
+ if(dmad.transfers[channel].transferSize <= dmad.transfers[channel].bufSize) {
+ DMA_ClearAutoMode(channel);
+ }
+ // Transfer finished
+ if(dmad.transfers[channel].transferSize == 0) {
+ pTransfer = &(dmad.transfers[channel]);
+ pTransfer->callback();
+ DMA_DisableIt(DMA_BTC << channel);
+ DMA_DisableChannel(channel);
+ }
+ else
+ {
+ // Write the KEEPON field to clear the STALL states.
+ DMA_KeeponChannel(channel);
+ }
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+/// Initializes the DMA controller.
+/// \param channel Particular channel number
+/// \param defaultHandler Using the default dmad interrupt handler.
+//------------------------------------------------------------------------------
+void DMAD_Initialize(unsigned char channel, unsigned char defaultHandler)
+{
+ unsigned int status;
+ unsigned int flag;
+
+ // Enable peripheral clock
+#if !defined(at91sam9rl64)
+ AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_HDMA;
+#endif
+
+ // Read the channel handler status to ensure the channel is a free channel.
+ status = DMA_GetChannelStatus();
+ TRACE_INFO ("DMAD_Initialize channel %x \n\r", channel);
+ SANITY_CHECK(!(status & (1 << channel)));
+ // Clear any pending interrupts on the channel.
+ DMA_GetStatus();
+ // Disble the channel.
+ DMA_DisableChannel(channel);
+ // Disable the interrupt
+ flag = 0xffffff;
+ DMA_DisableIt(flag);
+ // Enable DMA.
+ DMA_Enable();
+ if(defaultHandler)
+ {
+ IRQ_ConfigureIT(AT91C_ID_HDMA, 0, DMAD_Handler);
+ IRQ_EnableIT(AT91C_ID_HDMA);
+ }
+ // Initialize transfer instance.
+ dmad.transfers[channel].transferSize = 0;
+}
+
+//------------------------------------------------------------------------------
+/// Configure the DMA transfer buffer by giving transfer mode, it could be single
+/// buffer or multi-buffer(LLI/auto-reload/contiguous buffers) with or without
+/// Picture-In-Picture mode.
+/// \param channel Particular channel number.
+/// \param sourceTransferMode Source buffer transfer mode.
+/// \param destTransferMode Destination buffer transfer mode.
+/// \param lli Pointer to a DmaLinkList structure instance.
+/// \param pip Pointer to a PictureInPicture structure.
+//------------------------------------------------------------------------------
+unsigned char DMAD_Configure_Buffer(unsigned char channel,
+ unsigned char sourceTransferMode,
+ unsigned char destTransferMode,
+ DmaLinkList *lli,
+ PictureInPicture *pip)
+{
+ DmaTransfer *pTransfer = &(dmad.transfers[channel]);
+ // Check that no transfer is pending on the channel
+ if (pTransfer-> transferSize > 0 ) {
+ TRACE_ERROR("DAM transfer is already pending\n\r");
+ return DMAD_ERROR_BUSY;
+ }
+ // Configure source transfer mode.
+ DMA_SetSourceBufferMode(channel, sourceTransferMode, 0);
+
+ // Configure destination transfer mode.
+ DMA_SetDestBufferMode(channel, destTransferMode, 0);
+
+ if(lli){
+ DMA_SetDescriptorAddr(channel, (unsigned int)(&lli[0]));
+ }
+ else {
+ DMA_SetDescriptorAddr(channel, 0);
+ }
+
+ if(pip){
+ #if defined(AT91C_SRC_PIP)
+ // If source picture-in-picture mode is enabled, program the DMAC_SPIP.
+ if(pip->pipSourceBoundarySize){
+ // If destination picture-in-picture mode is enabled, program the DMAC_DPIP.
+ DMA_SPIPconfiguration(channel, pip->pipSourceHoleSize, pip->pipSourceBoundarySize);
+ }
+ #endif
+
+ #if defined(AT91C_DST_PIP)
+ if(pip->pipDestBoundarySize){
+ DMA_DPIPconfiguration(channel, pip->pipDestHoleSize, pip->pipDestBoundarySize);
+ }
+ #endif
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+/// Configure the DMA transfer control infomation.
+/// \param channel Particular channel number.
+/// \param bufSize Buffer transfer size in byte.
+/// \param sourceWidth Source transfer width.
+/// \param destWidth Destination transfer width.
+/// \param sourceAddress Destination transfer width.
+/// \param destAddress Destination transfer width.
+//------------------------------------------------------------------------------
+unsigned char DMAD_Configure_TransferController(unsigned char channel,
+ unsigned int bufSize,
+ unsigned char sourceWidth,
+ unsigned char destWidth,
+ unsigned int sourceAddress,
+ unsigned int destAddress)
+{
+ DmaTransfer *pTransfer = &(dmad.transfers[channel]);
+ // Check that no transfer is pending on the channel
+ if (pTransfer-> transferSize > 0 ) {
+ TRACE_ERROR("DAM transfer is already pending\n\r");
+ return DMAD_ERROR_BUSY;
+ }
+ pTransfer->bufSize = bufSize;
+
+ // Set up the transfer width and transfer size.
+ DMA_SetSourceBufferSize(channel, bufSize, sourceWidth, destWidth, 0);
+
+ if(sourceAddress) {
+ // Write the starting source address.
+ DMA_SetSourceAddr(channel, sourceAddress);
+ }
+ if(destAddress){
+ // Write the starting destination address.
+ DMA_SetDestinationAddr(channel, destAddress);
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+/// Starts buffer transfer on the given channel
+/// \param channel Particular channel number.
+/// \param size Total transfer size in byte.
+/// \param callback Optional callback function.
+/// \param polling Polling channel status enable.
+//------------------------------------------------------------------------------
+unsigned char DMAD_BufferTransfer(unsigned char channel,
+ unsigned int size,
+ DmaCallback callback,
+ unsigned char polling)
+{
+ DmaTransfer *pTransfer = &(dmad.transfers[channel]);
+ // Check that no transfer is pending on the channel
+ if (pTransfer-> transferSize > 0 ) {
+ TRACE_ERROR("DAM transfer is already pending\n\r");
+ return DMAD_ERROR_BUSY;
+ }
+ pTransfer->status = DMAD_ERROR_BUSY;
+ pTransfer->transferSize = size;
+ pTransfer->callback = callback;
+
+ if(!polling){
+ DMA_EnableIt(DMA_BTC << channel);
+ }
+ // Enable the channel.
+ DMA_EnableChannel(channel);
+
+ if(polling){
+ while ((DMA_GetChannelStatus() & (DMA_ENA << channel)) == (DMA_ENA << channel));
+ if (pTransfer->callback) {
+ pTransfer->callback();
+ }
+ pTransfer->transferSize = 0;
+ DMA_DisableChannel(channel);
+ }
+ return 0;
+}
+
+//------------------------------------------------------------------------------
+/// Returns 1 if no transfer is currently pending on the given channel;
+/// otherwise, returns 0.
+/// \param channel Channel number.
+//------------------------------------------------------------------------------
+unsigned char DMAD_IsFinished(unsigned char channel)
+{
+ SANITY_CHECK(channel <= DMA_CHANNEL_NUM);
+ if (dmad.transfers[channel].transferSize > 0) {
+ return 0;
+ }
+ else {
+ DMA_DisableChannel(channel);
+ return 1;
+ }
+}
personal git repositories of Harald Welte. Your mileage may vary