/* ----------------------------------------------------------------------------
 *         ATMEL Microcontroller Software Support 
 * ----------------------------------------------------------------------------
 * Copyright (c) 2011, 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.
 * ----------------------------------------------------------------------------
 */

/** \addtogroup dmad_module 
 *
 * \section DmaConfig Dma Configuration Usage
 *
 * To configure a DMA channel, the user has to follow these few steps :
 * <ul>
 * <li> Initialize a DMA driver instance by DMAD_Initialize().</li>
 * <li> choose an available (disabled) channel using DMAD_AllocateChannel().</li>
 * <li> After the DMAC selected channel has been programmed, DMAD_PrepareChannel() is to enable 
 * clock and dma peripheral of the DMA, and set Configuration register to set up the transfer type 
 * (memory or non-memory peripheral for source and destination) and flow control device.</li>
 * <li> Configure DMA multi-buffer transfers using DMAD_PrepareMultiTransfer() to set up the chain of Linked List Items,
 * single-buffer transfers using DMAD_PrepareSingleTransfer().</li>
 * <li> Invoke DMAD_StartTransfer() to start DMA transfer, or DMAD_StopTransfer() to force stop DMA transfer.</li>
 * <li> If picture-in-picture mode is enabled, DMAD_ConfigurePIP() helps to configure PIP mode.</li>
 * <li> Once the buffer of data is transferred, DMAD_IsTransferDone() checks if DMA transfer is finished.</li>
 * <li> DMAD_Handler() handles DMA interrupt, and invoking DMAD_SetCallback() if provided.</li>
 * </ul>
 *
 * Related files:\n
 * \ref dmad.h\n
 * \ref dmad.c.\n
 */

/** \file */

/** \addtogroup dmad_functions
  @{*/
 
/*----------------------------------------------------------------------------
 *        Includes
 *----------------------------------------------------------------------------*/

#include "board.h"
#include <assert.h>

/*----------------------------------------------------------------------------
 *        Local functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Try to allocate a DMA channel for on given controller.
 * \param pDmad  Pointer to DMA driver instance.
 * \param bDmac  DMA controller ID (0 ~ 1).
 * \param bSrcID Source peripheral ID, 0xFF for memory.
 * \param bDstID Destination peripheral ID, 0xFF for memory.
 * \return Channel number if allocation sucessful, return 
 * DMAD_ALLOC_FAILED if allocation failed.
 */
static uint32_t DMAD_AllocateDmacChannel( sDmad *pDmad,
                                          uint8_t bDmac,
                                          uint8_t bSrcID, 
                                          uint8_t bDstID)
{
    uint32_t i;

    /* Can't support peripheral to peripheral */
    if ((( bSrcID != DMAD_TRANSFER_MEMORY ) && ( bDstID != DMAD_TRANSFER_MEMORY ))) 
    {
        return DMAD_ALLOC_FAILED;
    }
    /* dma transfer from peripheral to memory */
    if ( bDstID == DMAD_TRANSFER_MEMORY)
    {
        if( (!DMAIF_IsValidatedPeripherOnDma(bDmac, bSrcID)) )
        {
            return DMAD_ALLOC_FAILED;
        }
    }
    /* dma transfer from memory to peripheral */
    if ( bSrcID == DMAD_TRANSFER_MEMORY )
    {
        if( (!DMAIF_IsValidatedPeripherOnDma(bDmac, bDstID)) )
        {
            return DMAD_ALLOC_FAILED;
        }
    }
    for (i = 0; i < pDmad->numChannels; i ++)
    {
        if ( pDmad->dmaChannels[bDmac][i].state == DMAD_FREE ) 
        {
            /* Allocate the channel */
            pDmad->dmaChannels[bDmac][i].state = DMAD_IN_USE;
            /* Get general informations */
            pDmad->dmaChannels[bDmac][i].bSrcPeriphID = bSrcID;
            pDmad->dmaChannels[bDmac][i].bDstPeriphID = bDstID;
            pDmad->dmaChannels[bDmac][i].bSrcTxIfID =
                DMAIF_Get_ChannelNumber(bDmac, bSrcID, 0);
            pDmad->dmaChannels[bDmac][i].bSrcRxIfID =
                DMAIF_Get_ChannelNumber(bDmac, bSrcID, 1);
            pDmad->dmaChannels[bDmac][i].bDstTxIfID =
                DMAIF_Get_ChannelNumber(bDmac, bDstID, 0);
            pDmad->dmaChannels[bDmac][i].bDstTxIfID =
                DMAIF_Get_ChannelNumber(bDmac, bDstID, 1);

            return ((bDmac << 8)) | ((i) & 0xFF);
        }
    }
    return DMAD_ALLOC_FAILED;
}

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Initialize DMA driver instance.
 * \param pDmad Pointer to DMA driver instance.
 * \param bPollingMode Polling DMA transfer:
 *                     1. Via DMAD_IsTransferDone(); or
 *                     2. Via DMAD_Handler().
 */
void DMAD_Initialize( sDmad *pDmad,
                      uint8_t bPollingMode )
{
    uint32_t i, j;
    
    assert( pDmad != NULL ) ;
    
    pDmad->pDmacs[0] = DMAC0;
    pDmad->pDmacs[1] = DMAC1;
    pDmad->pollingMode = bPollingMode;
    pDmad->numControllers = 2;
    pDmad->numChannels    = 8;
    
    for (i = 0; i < pDmad->numControllers; i ++)
    {
        for (j = 0; j < pDmad->numChannels; j ++)
        {
            pDmad->dmaChannels[i][j].fCallback = 0;
            pDmad->dmaChannels[i][j].pArg      = 0;

            pDmad->dmaChannels[i][j].bIrqOwner    = 0;
            pDmad->dmaChannels[i][j].bSrcPeriphID = 0;
            pDmad->dmaChannels[i][j].bDstPeriphID = 0;
            pDmad->dmaChannels[i][j].bSrcTxIfID   = 0;
            pDmad->dmaChannels[i][j].bSrcRxIfID   = 0;
            pDmad->dmaChannels[i][j].bDstTxIfID   = 0;
            pDmad->dmaChannels[i][j].bDstRxIfID   = 0;
            
            pDmad->dmaChannels[i][j].state = DMAD_FREE;
        }
    }
}

/**
 * \brief DMA interrupt handler
 * \param pDmad Pointer to DMA driver instance.
 */
void DMAD_Handler( sDmad *pDmad )
{
    Dmac *pDmac;
    sDmadChannel *pCh;
    uint32_t _iController, iChannel;
    uint32_t dmaSr, chSr;
    uint32_t dmaRc = DMAD_OK;

    assert( pDmad != NULL ) ;
    
    for (_iController = 0; _iController < pDmad->numControllers; _iController ++)
    {
        pDmac = pDmad->pDmacs[_iController];

        /* Check raw status but not masked one for polling mode support */
        dmaSr = DMAC_GetStatus( pDmac );
        if ((dmaSr & 0x00FFFFFF) == 0) continue;

        chSr  = DMAC_GetChannelStatus( pDmac );
        //printf("iDma(%x,%x)\n\r", dmaSr, chSr);

        for (iChannel = 0; iChannel < pDmad->numChannels; iChannel ++)
        {
            uint8_t bExec = 1;

            pCh = &pDmad->dmaChannels[_iController][iChannel];
            /* Error */
            if (dmaSr & (DMAC_EBCIDR_ERR0 << iChannel))
            {
                DMAC_DisableChannel( pDmac, iChannel );
                if (pCh->state > DMAD_IN_USE)   pCh->state = DMAD_STALL;
                dmaRc = DMAD_ERROR;
            }
            /* Chained buffer complete */
            else if (dmaSr & (DMAC_EBCIDR_CBTC0 << iChannel))
            {
                DMAC_DisableChannel( pDmac, iChannel );
                if (pCh->state > DMAD_IN_USE)   pCh->state = DMAD_IN_USE;
                dmaRc = DMAD_OK;
                
            }
            /* Buffer complete */
            else if (dmaSr & (DMAC_EBCIDR_BTC0 << iChannel))
            {
                dmaRc = DMAD_PARTIAL_DONE;
                /* Re-enable */
                if ((chSr & (DMAC_CHSR_ENA0 << iChannel)) == 0)
                {
                    DMAC_EnableChannel( pDmac, iChannel );
                }
            }
            else
            {
                bExec = 0;
            }
            /* Execute callback */
            if (bExec && pCh->fCallback)
            {
                pCh->fCallback(dmaRc, pCh->pArg);
            }
        }
    }
}

/**
 * \brief Allocate a DMA channel for upper layer.
 * \param pDmad  Pointer to DMA driver instance.
 * \param bSrcID Source peripheral ID, 0xFF for memory.
 * \param bDstID Destination peripheral ID, 0xFF for memory.
 * \return Channel number if allocation sucessful, return 
 * DMAD_ALLOC_FAILED if allocation failed.
 */
uint32_t DMAD_AllocateChannel( sDmad *pDmad,
                               uint8_t bSrcID, 
                               uint8_t bDstID)
{
    uint32_t _iController;
    uint32_t _dwChannel = DMAD_ALLOC_FAILED;

    for ( _iController = 0; _iController < pDmad->numControllers; _iController ++)
    {
        _dwChannel = DMAD_AllocateDmacChannel( pDmad, _iController,
                                              bSrcID, bDstID );
        if (_dwChannel != DMAD_ALLOC_FAILED)
            break;
    }
    return _dwChannel;
}

/**
 * \brief Free the specified DMA channel.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
eDmadRC DMAD_FreeChannel( sDmad *pDmad, uint32_t _dwChannel )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    switch ( pDmad->dmaChannels[_iController][iChannel].state )
    {

    case DMAD_IN_XFR:
        return DMAD_BUSY;

    case DMAD_IN_USE:
        pDmad->dmaChannels[_iController][iChannel].state = DMAD_FREE;
        break;
    }   
	return DMAD_OK;
}

/**
 * \brief Set the callback function for DMA channel transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 * \param fCallback Pointer to callback function.
 * \param pArg Pointer to optional argument for callback.
 */
eDmadRC DMAD_SetCallback( sDmad *pDmad, uint32_t _dwChannel,
                          DmadTransferCallback fCallback, void* pArg )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    else if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;
    
    pDmad->dmaChannels[_iController][iChannel].fCallback = fCallback;
    pDmad->dmaChannels[_iController][iChannel].pArg = pArg;
    
    return DMAD_OK;
}

/**
 * \brief Configure Picture-in-Picture mode for DMA transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 * \param srcPIP    Source PIP setting.
 * \param dstPIP    Destination PIP setting.
 */
eDmadRC DMAD_ConfigurePIP( sDmad *pDmad, 
                           uint32_t _dwChannel,
                           uint32_t dwSrcPIP, 
                           uint32_t dwDstPIP )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[_iController];
    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    else if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;

    DMAC_SetPipMode(pDmac, iChannel, dwSrcPIP, dwDstPIP);
    return DMAD_OK;
}

/**
 * \brief Enable clock of the DMA peripheral, Enable the dma peripheral, 
 * configure configuration register for DMA transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 * \param dwCfg     Configuration value.
 */
eDmadRC DMAD_PrepareChannel( sDmad *pDmad, 
                             uint32_t _dwChannel,
                             uint32_t dwCfg )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    uint32_t _dwdmaId;

    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[_iController];

    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    else if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;
    DMAC_SetCFG( pDmac, iChannel, dwCfg );

    _dwdmaId = (_iController == 0) ? ID_DMAC0 : ID_DMAC1;
    /* Enable clock of the DMA peripheral */
    if (!PMC_IsPeriphEnabled( _dwdmaId ))
    {
        PMC_EnablePeripheral( _dwdmaId );
    }
    /* Enables the DMAC peripheral. */
    DMAC_Enable( pDmac );
    /* Disables DMAC interrupt for the given channel. */
    DMAC_DisableIt (pDmac, 
                    (DMAC_EBCIDR_BTC0 << iChannel)
                   |(DMAC_EBCIDR_CBTC0 << iChannel)
                   |(DMAC_EBCIDR_ERR0 << iChannel) );
    /* Disable the given dma channel. */
    DMAC_DisableChannel( pDmac, iChannel );
    /* Clear dummy status */
    DMAC_GetChannelStatus( pDmac );
    DMAC_GetStatus (pDmac);
    return DMAD_OK;
}

/**
 * \brief Check if DMA transfer is finished.
 *        In polling mode DMAD_Handler() is polled.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
eDmadRC DMAD_IsTransferDone( sDmad *pDmad, uint32_t _dwChannel )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;

    assert( pDmad != NULL ) ;
    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    else if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
    {
        if ( pDmad->pollingMode ) DMAD_Handler( pDmad );
        return DMAD_BUSY;
    }
    return DMAD_OK;
}

/**
 * \brief Clear the automatic mode that services the next-to-last 
    buffer transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
void DMAD_ClearAuto( sDmad *pDmad, uint32_t _dwChannel )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    Dmac *pDmac;
    assert( pDmad != NULL ) ;
    
    pDmac = pDmad->pDmacs[_iController];
    DMAC_DisableAutoMode( pDmac, iChannel );   
}

/**
 * \brief Start DMA transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
eDmadRC DMAD_StartTransfer( sDmad *pDmad, uint32_t _dwChannel )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[_iController];

    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    else if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;
    /* Change state to transferring */
    pDmad->dmaChannels[_iController][iChannel].state = DMAD_IN_XFR;
    DMAC_EnableChannel(pDmac, iChannel);
    if ( pDmad->pollingMode == 0 )
    {
        /* Monitor status in interrupt handler */
        DMAC_EnableIt(pDmac, (DMAC_EBCIDR_BTC0 << iChannel)
                            |(DMAC_EBCIDR_CBTC0 << iChannel)
                            |(DMAC_EBCIDR_ERR0 << iChannel) );
    }
    
    return DMAD_OK;
}

/**
 * \brief Start DMA transfers on the same controller.
 * \param pDmad      Pointer to DMA driver instance.
 * \param bDmac      DMA Controller number. 
 * \param bmChannels Channels bitmap.
 */
eDmadRC DMAD_StartTransfers( sDmad *pDmad, uint8_t bDmac, uint32_t bmChannels )
{
    uint32_t iChannel;
    uint32_t bmChs = 0, bmIts = 0;

    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[bDmac];

    for (iChannel = 0; iChannel < pDmad->numChannels; iChannel ++)
    {
        uint32_t bmChBit = 1 << iChannel;

        /* Skipped channels */
        if ( pDmad->dmaChannels[bDmac][iChannel].state == DMAD_FREE )
            continue;
        else if ( pDmad->dmaChannels[bDmac][iChannel].state == DMAD_IN_XFR )
            continue;
        /* Log to start bit map */
        if (bmChannels & bmChBit)
        {
            bmChs |= bmChBit;
            bmIts |= (  (DMAC_EBCIDR_BTC0 << iChannel)
                       |(DMAC_EBCIDR_CBTC0 << iChannel)
                       |(DMAC_EBCIDR_ERR0 << iChannel) );
            /* Change state */
            pDmad->dmaChannels[bDmac][iChannel].state = DMAD_IN_XFR;
        }
    }

    DMAC_EnableChannels(pDmac, bmChs);
    if ( pDmad->pollingMode == 0 )
    {
        /* Monitor status in interrupt handler */
        DMAC_EnableIt( pDmac, bmIts );
    }
    
    return DMAD_OK;
}

/**
 * \brief Stop DMA transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
eDmadRC DMAD_StopTransfer( sDmad *pDmad, uint32_t _dwChannel )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[_iController];
    sDmadChannel *pCh = &pDmad->dmaChannels[_iController][iChannel];

    uint32_t to = 0x1000;

    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;

    if ( pDmad->dmaChannels[_iController][iChannel].state != DMAD_IN_XFR )
        return DMAD_OK;

    /* Suspend */
    DMAC_SuspendChannel(pDmac, iChannel);

    /* Poll empty */
    for (;to; to --)
    {
        if (DMAC_GetChannelStatus(pDmac) & (DMAC_CHSR_EMPT0 << iChannel))
        {
            break;
        }
    }

    /* Disable channel */
    DMAC_DisableChannel(pDmac, iChannel);
    /* Disable interrupts */
    DMAC_DisableIt(pDmac, (DMAC_EBCIDR_BTC0 << iChannel)
                         |(DMAC_EBCIDR_CBTC0 << iChannel)
                         |(DMAC_EBCIDR_ERR0 << iChannel) );
    /* Clear pending status */
    DMAC_GetChannelStatus(pDmac);
    DMAC_GetStatus(pDmac);
    /* Resume */
    DMAC_RestoreChannel(pDmac, iChannel);
    /* Change state */
    pDmad->dmaChannels[_iController][iChannel].state = DMAD_IN_USE;
    /* Invoke callback */
    if (pCh->fCallback) pCh->fCallback(DMAD_CANCELED, pCh->pArg);
    return DMAD_OK;
}

/**
 * \brief Configure DMA for a single transfer.
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 */
eDmadRC DMAD_PrepareSingleTransfer( sDmad *pDmad, 
                                    uint32_t _dwChannel,
                                    sDmaTransferDescriptor *pXfrDesc )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    Dmac *pDmac = pDmad->pDmacs[_iController];

    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;
    
    DMAC_SetSourceAddr(pDmac, iChannel, pXfrDesc->dwSrcAddr);
    DMAC_SetDestinationAddr(pDmac, iChannel, pXfrDesc->dwDstAddr);
    DMAC_SetDescriptorAddr(pDmac, iChannel, 0, 0);
    DMAC_SetControlA(pDmac, iChannel, pXfrDesc->dwCtrlA);
    DMAC_SetControlB(pDmac, iChannel, pXfrDesc->dwCtrlB);

    return DMAD_OK;
}

/**
 * \brief Configure DMA multi-buffer transfers using linked lists
 * \param pDmad     Pointer to DMA driver instance.
 * \param _dwChannel ControllerNumber << 8 | ChannelNumber.
 * \param pXfrDesc  Pointer to DMA Linked List.
 */
eDmadRC DMAD_PrepareMultiTransfer( sDmad *pDmad, 
                                   uint32_t _dwChannel,
                                   sDmaTransferDescriptor *pXfrDesc )
{
    uint8_t _iController = (_dwChannel >> 8);
    uint8_t iChannel    = (_dwChannel) & 0xFF;
    
    assert( pDmad != NULL ) ;
    Dmac *pDmac = pDmad->pDmacs[_iController];

    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_FREE )
        return DMAD_ERROR;
    if ( pDmad->dmaChannels[_iController][iChannel].state == DMAD_IN_XFR )
        return DMAD_BUSY;
    
    DMAC_SetDescriptorAddr( pDmac, iChannel, (uint32_t)pXfrDesc, 0 );
    DMAC_SetControlB( pDmac, iChannel, 0);

    return DMAD_OK;
}

/**@}*/

