/******************************************************************************
 *  Copyright (c) 2018, Xilinx, Inc.
 *  All rights reserved.
 * 
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met:
 *
 *  1.  Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *
 *  2.  Redistributions in binary form must reproduce the above copyright 
 *      notice, this list of conditions and the following disclaimer in the 
 *      documentation and/or other materials provided with the distribution.
 *
 *  3.  Neither the name of the copyright holder nor the names of its 
 *      contributors may be used to endorse or promote products derived from 
 *      this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 *  CONTRIBUTORS 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.
 *
 *****************************************************************************/
/*****************************************************************************/
/**
*
* @file axivdma_channel.c
*
* Re-Implementation of the channel related functions. These functions were
* initially defined by the driver in xaxivdma_i.h.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -------------------------------------------------------
* 1.00  yrq  01/26/2018 Refactor customized code from the xaxivdma driver
*
* </pre>
*
******************************************************************************/

/***************************** Include Files *********************************/
#include "xaxivdma_hw.h"
#include "xaxivdma_i.h"
#include "xstatus.h"
#include "xaxivdma.h"

/************************** Constant Definitions *****************************/
/* Number of polling loops to do to check for reset completion
 *
 * This number is large enough to cover the maximum transfer length
 *
 * However, if the memory operation being throttled by the system, this number
 * is not large enough
 */
#define XAXIVDMA_RESET_POLLING      1000


/************************** Function Prototypes ******************************/

/* BD APIs, used by this file only
 */
static u32 XAxiVdma_BdRead(XAxiVdma_Bd *BdPtr, int Offset);
static void XAxiVdma_BdWrite(XAxiVdma_Bd *BdPtr, int Offset, u32 Value);
static void XAxiVdma_BdSetNextPtr(XAxiVdma_Bd *BdPtr, u32 NextPtr);
static void XAxiVdma_BdSetAddr(XAxiVdma_Bd *BdPtr, u32 Addr);
static int XAxiVdma_BdSetVsize(XAxiVdma_Bd *BdPtr, int Vsize);
static int XAxiVdma_BdSetHsize(XAxiVdma_Bd *BdPtr, int Vsize);
static int XAxiVdma_BdSetStride(XAxiVdma_Bd *BdPtr, int Stride);
static int XAxiVdma_BdSetFrmDly(XAxiVdma_Bd *BdPtr, int FrmDly);

/*****************************************************************************/
/*
 * Translate virtual address to physical address
 *
 * When port this driver to other RTOS, please change this definition to
 * be consistent with your target system.
 *
 * @param VirtAddr is the virtual address to work on
 *
 * @return
 *   The physical address of the virtual address
 *
 * @note
 *   The virtual address and the physical address are the same here.
 *
 *****************************************************************************/
#define XAXIVDMA_VIRT_TO_PHYS(VirtAddr) \
	(VirtAddr)

/*****************************************************************************/
/**
 * Set the channel to enable access to higher Frame Buffer Addresses (SG=0)
 *
 * @param Channel is the pointer to the channel to work on
 *
 *
 *****************************************************************************/
#define XAxiVdma_ChannelHiFrmAddrEnable(Channel) \
{ \
	XAxiVdma_WriteReg(Channel->ChanBase, \
			XAXIVDMA_HI_FRMBUF_OFFSET, XAXIVDMA_REGINDEX_MASK); \
}

/*****************************************************************************/
/**
 * Set the channel to disable access higher Frame Buffer Addresses (SG=0)
 *
 * @param Channel is the pointer to the channel to work on
 *
 *
 *****************************************************************************/
#define XAxiVdma_ChannelHiFrmAddrDisable(Channel) \
{ \
	XAxiVdma_WriteReg(Channel->ChanBase, \
		XAXIVDMA_HI_FRMBUF_OFFSET, (XAXIVDMA_REGINDEX_MASK >> 1)); \
}

/*****************************************************************************/
/**
 * Initialize a channel of a DMA engine
 *
 * This function initializes the BD ring for this channel
 *
 * @param Channel is the pointer to the DMA channel to work on
 *
 * @return
 *   None
 *
 *****************************************************************************/
void XAxiVdma_ChannelInit(XAxiVdma_Channel *Channel)
{
	int i;
	int NumFrames;
	XAxiVdma_Bd *FirstBdPtr = &(Channel->BDs[0]);
	XAxiVdma_Bd *LastBdPtr;

	/* Initialize the BD variables, so proper memory management
	 * can be done
	 */
	NumFrames = Channel->NumFrames;
	Channel->IsValid = 0;
	Channel->HeadBdPhysAddr = 0;
	Channel->HeadBdAddr = 0;
	Channel->TailBdPhysAddr = 0;
	Channel->TailBdAddr = 0;

	LastBdPtr = &(Channel->BDs[NumFrames - 1]);

	/* Setup the BD ring
	 */
	memset((void *)FirstBdPtr, 0, NumFrames * sizeof(XAxiVdma_Bd));

	for (i = 0; i < NumFrames; i++) {
		XAxiVdma_Bd *BdPtr;
		XAxiVdma_Bd *NextBdPtr;

		BdPtr = &(Channel->BDs[i]);

		/* The last BD connects to the first BD
		 */
		if (i == (NumFrames - 1)) {
			NextBdPtr = FirstBdPtr;
		}
		else {
			NextBdPtr = &(Channel->BDs[i + 1]);
		}

		XAxiVdma_BdSetNextPtr(BdPtr,
				XAXIVDMA_VIRT_TO_PHYS((UINTPTR)NextBdPtr));
	}

	Channel->AllCnt = NumFrames;

	/* Setup the BD addresses so that access the head/tail BDs fast
	 *
	 */
	Channel->HeadBdAddr = (UINTPTR)FirstBdPtr;
	Channel->HeadBdPhysAddr = XAXIVDMA_VIRT_TO_PHYS((UINTPTR)FirstBdPtr);

	Channel->TailBdAddr = (UINTPTR)LastBdPtr;
	Channel->TailBdPhysAddr = XAXIVDMA_VIRT_TO_PHYS((UINTPTR)LastBdPtr);


	Channel->IsValid = 1;

	return;
}

/*****************************************************************************/
/**
 * This function checks whether reset operation is done
 *
 * @param Channel is the pointer to the DMA channel to work on
 *
 * @return
 * - 0 if reset is done
 * - 1 if reset is still going
 *
 *****************************************************************************/
int XAxiVdma_ChannelResetNotDone(XAxiVdma_Channel *Channel)
{
	return (XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	        XAXIVDMA_CR_RESET_MASK);
}

/*****************************************************************************/
/**
 * This function resets one DMA channel
 *
 * The registers will be default values after the reset
 *
 * @param Channel is the pointer to the DMA channel to work on
 *
 * @return
 *  None
 *
 *****************************************************************************/
void XAxiVdma_ChannelReset(XAxiVdma_Channel *Channel)
{
	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    XAXIVDMA_CR_RESET_MASK);

	return;
}

/*****************************************************************************/
/*
 * Check whether a DMA channel is running
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 * - non zero if the channel is running
 * - 0 is the channel is idle
 *
 *****************************************************************************/
int XAxiVdma_ChannelIsRunning(XAxiVdma_Channel *Channel)
{
	u32 Bits;

	/* If halted bit set, channel is not running
	 */
	Bits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET) &
	          XAXIVDMA_SR_HALTED_MASK;

	if (Bits) {
		return 0;
	}

	/* If Run/Stop bit low, then channel is not running
	 */
	Bits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	          XAXIVDMA_CR_RUNSTOP_MASK;

	if (!Bits) {
		return 0;
	}

	return 1;
}

/*****************************************************************************/
/**
 * Check whether a DMA channel is busy
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 * - non zero if the channel is busy
 * - 0 is the channel is idle
 *
 *****************************************************************************/
int XAxiVdma_ChannelIsBusy(XAxiVdma_Channel *Channel)
{
	u32 Bits;

	/* If the channel is idle, then it is not busy
	 */
	Bits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET) &
	          XAXIVDMA_SR_IDLE_MASK;

	if (Bits) {
		return 0;
	}

	/* If the channel is halted, then it is not busy
	 */
	Bits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET) &
	          XAXIVDMA_SR_HALTED_MASK;

	if (Bits) {
		return 0;
	}

	/* Otherwise, it is busy
	 */
	return 1;
}

/*****************************************************************************/
/*
 * Check DMA channel errors
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *  	Error bits of the channel, 0 means no errors
 *
 *****************************************************************************/
u32 XAxiVdma_ChannelErrors(XAxiVdma_Channel *Channel)
{
	return (XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET)
			& XAXIVDMA_SR_ERR_ALL_MASK);
}

/*****************************************************************************/
/*
 * Clear DMA channel errors
 *
 * @param Channel is the pointer to the channel to work on
 * @param ErrorMask is the mask of error bits to clear.
 *
 * @return
 *  	None
 *
 *****************************************************************************/
void XAxiVdma_ClearChannelErrors(XAxiVdma_Channel *Channel, u32 ErrorMask)
{
	u32 SrBits;

	/* Write on Clear bits */
        SrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET)
                        | ErrorMask;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET,
	    SrBits);

	return;
}

/*****************************************************************************/
/**
 * Get the current status of a channel
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 * The status of the channel
 *
 *****************************************************************************/
u32 XAxiVdma_ChannelGetStatus(XAxiVdma_Channel *Channel)
{
	return XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET);
}

/*****************************************************************************/
/**
 * Set the channel to run in parking mode
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *   - XST_SUCCESS if everything is fine
 *   - XST_FAILURE if hardware is not running
 *
 *****************************************************************************/
int XAxiVdma_ChannelStartParking(XAxiVdma_Channel *Channel)
{
	u32 CrBits;

	if (!XAxiVdma_ChannelIsRunning(Channel)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel is not running, cannot start park mode\r\n");

		return XST_FAILURE;
	}

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	            ~XAXIVDMA_CR_TAIL_EN_MASK;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Set the channel to run in circular mode, exiting parking mode
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *   None
 *
 *****************************************************************************/
void XAxiVdma_ChannelStopParking(XAxiVdma_Channel *Channel)
{
	u32 CrBits;

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) |
	            XAXIVDMA_CR_TAIL_EN_MASK;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	return;
}

/*****************************************************************************/
/**
 * Set the channel to run in frame count enable mode
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *   None
 *
 *****************************************************************************/
void XAxiVdma_ChannelStartFrmCntEnable(XAxiVdma_Channel *Channel)
{
	u32 CrBits;

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) |
	            XAXIVDMA_CR_FRMCNT_EN_MASK;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	return;
}

/*****************************************************************************/
/**
 * Setup BD addresses to a different memory region
 *
 * In some systems, it is convenient to put BDs into a certain region of the
 * memory. This function enables that.
 *
 * @param Channel is the pointer to the channel to work on
 * @param BdAddrPhys is the physical starting address for BDs
 * @param BdAddrVirt is the Virtual starting address for BDs. For systems that
 *         do not use MMU, then virtual address is the same as physical address
 *
 * @return
 * - XST_SUCCESS for a successful setup
 * - XST_DEVICE_BUSY if the DMA channel is not idle, BDs are still being used
 *
 * @notes
 * We assume that the memory region starting from BdAddrPhys is large enough
 * to hold all the BDs.
 *
 *****************************************************************************/
int XAxiVdma_ChannelSetBdAddrs(XAxiVdma_Channel *Channel, UINTPTR BdAddrPhys,
		UINTPTR BdAddrVirt)
{
	int NumFrames = Channel->AllCnt;
	int i;
	UINTPTR NextPhys = BdAddrPhys;
	UINTPTR CurrVirt = BdAddrVirt;

	if (Channel->HasSG && XAxiVdma_ChannelIsBusy(Channel)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel is busy, cannot setup engine for transfer\r\n");

		return XST_DEVICE_BUSY;
	}

	memset((void *)BdAddrPhys, 0, NumFrames * sizeof(XAxiVdma_Bd));
	memset((void *)BdAddrVirt, 0, NumFrames * sizeof(XAxiVdma_Bd));

	/* Set up the BD link list */
	for (i = 0; i < NumFrames; i++) {
		XAxiVdma_Bd *BdPtr;

		BdPtr = (XAxiVdma_Bd *)CurrVirt;

		/* The last BD connects to the first BD
		 */
		if (i == (NumFrames - 1)) {
			NextPhys = BdAddrPhys;
		}
		else {
			NextPhys += sizeof(XAxiVdma_Bd);
		}

		XAxiVdma_BdSetNextPtr(BdPtr, NextPhys);
		CurrVirt += sizeof(XAxiVdma_Bd);
	}

	/* Setup the BD addresses so that access the head/tail BDs fast
	 *
	 */
	Channel->HeadBdPhysAddr = BdAddrPhys;
	Channel->HeadBdAddr = BdAddrVirt;
	Channel->TailBdPhysAddr = BdAddrPhys +
	                          (NumFrames - 1) * sizeof(XAxiVdma_Bd);
	Channel->TailBdAddr = BdAddrVirt +
	                          (NumFrames - 1) * sizeof(XAxiVdma_Bd);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Start a transfer
 *
 * This function setup the DMA engine and start the engine to do the transfer.
 *
 * @param Channel is the pointer to the channel to work on
 * @param ChannelCfgPtr is the pointer to the setup structure
 *
 * @return
 * - XST_SUCCESS for a successful submission
 * - XST_FAILURE if channel has not being initialized
 * - XST_DEVICE_BUSY if the DMA channel is not idle, BDs are still being used
 * - XST_INVAID_PARAM if parameters in config structure not valid
 *
 *****************************************************************************/
int XAxiVdma_ChannelStartTransfer(XAxiVdma_Channel *Channel,
    XAxiVdma_ChannelSetup *ChannelCfgPtr)
{
	int Status;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		return XST_FAILURE;
	}

	if (Channel->HasSG && XAxiVdma_ChannelIsBusy(Channel)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel is busy, cannot setup engine for transfer\r\n");

		return XST_DEVICE_BUSY;
	}

	Status = XAxiVdma_ChannelConfig(Channel, ChannelCfgPtr);
	if (Status != XST_SUCCESS) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel config failed %d\r\n", Status);

		return Status;
	}

	Status = XAxiVdma_ChannelSetBufferAddr(Channel,
	    ChannelCfgPtr->FrameStoreStartAddr, Channel->AllCnt);
	if (Status != XST_SUCCESS) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel setup buffer addr failed %d\r\n", Status);

		return Status;
	}

	Status = XAxiVdma_ChannelStart(Channel);
	if (Status != XST_SUCCESS) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel start failed %d\r\n", Status);

		return Status;
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Configure one DMA channel using the configuration structure
 *
 * Setup the control register and BDs, however, BD addresses are not set.
 *
 * @param Channel is the pointer to the channel to work on
 * @param ChannelCfgPtr is the pointer to the setup structure
 *
 * @return
 * - XST_SUCCESS if successful
 * - XST_FAILURE if channel has not being initialized
 * - XST_DEVICE_BUSY if the DMA channel is not idle
 * - XST_INVALID_PARAM if fields in ChannelCfgPtr is not valid
 *
 *****************************************************************************/
int XAxiVdma_ChannelConfig(XAxiVdma_Channel *Channel,
        XAxiVdma_ChannelSetup *ChannelCfgPtr)
{
	u32 CrBits;
	int i;
	int NumBds;
	int Status;
	u32 hsize_align;
	u32 stride_align;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		return XST_FAILURE;
	}

	if (Channel->HasSG && XAxiVdma_ChannelIsBusy(Channel)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel is busy, cannot config!\r\n");

		return XST_DEVICE_BUSY;
	}

	Channel->Vsize = ChannelCfgPtr->VertSizeInput;

	/* Check whether Hsize is properly aligned */
	if (Channel->direction == XAXIVDMA_WRITE) {
		if (ChannelCfgPtr->HoriSizeInput < Channel->WordLength) {
			hsize_align = (u32)Channel->WordLength;
		} else {
			hsize_align =
				(u32)(ChannelCfgPtr->HoriSizeInput % Channel->WordLength);
			if (hsize_align > 0)
				hsize_align = (Channel->WordLength - hsize_align);
		}
	} else {
		if (ChannelCfgPtr->HoriSizeInput < Channel->WordLength) {
			hsize_align = (u32)Channel->WordLength;
		} else {
			hsize_align =
				(u32)(ChannelCfgPtr->HoriSizeInput % Channel->StreamWidth);
			if (hsize_align > 0)
				hsize_align = (Channel->StreamWidth - hsize_align);
		}
	}

	/* Check whether Stride is properly aligned */
	if (ChannelCfgPtr->Stride < Channel->WordLength) {
		stride_align = (u32)Channel->WordLength;
	} else {
		stride_align = (u32)(ChannelCfgPtr->Stride % Channel->WordLength);
		if (stride_align > 0)
			stride_align = (Channel->WordLength - stride_align);
	}

	/* If hardware has no DRE, then Hsize and Stride must
	 * be word-aligned
	 */
	if (!Channel->HasDRE) {
		if (hsize_align != 0) {
			/* Adjust hsize to multiples of stream/mm data width*/
			ChannelCfgPtr->HoriSizeInput += hsize_align;
		}
		if (stride_align != 0) {
			/* Adjust stride to multiples of stream/mm data width*/
			ChannelCfgPtr->Stride += stride_align;
		}
	}

	Channel->Hsize = ChannelCfgPtr->HoriSizeInput;

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase,
	     XAXIVDMA_CR_OFFSET);

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	    ~(XAXIVDMA_CR_TAIL_EN_MASK | XAXIVDMA_CR_SYNC_EN_MASK |
	      XAXIVDMA_CR_FRMCNT_EN_MASK | XAXIVDMA_CR_RD_PTR_MASK);

	if (ChannelCfgPtr->EnableCircularBuf) {
		CrBits |= XAXIVDMA_CR_TAIL_EN_MASK;
	}
	else {
		/* Park mode */
		u32 FrmBits;
		u32 RegValue;

		if ((!XAxiVdma_ChannelIsRunning(Channel)) &&
		    Channel->HasSG) {
			xdbg_printf(XDBG_DEBUG_ERROR,
			    "Channel is not running, cannot set park mode\r\n");

			return XST_INVALID_PARAM;
		}

		if (ChannelCfgPtr->FixedFrameStoreAddr > XAXIVDMA_FRM_MAX) {
			xdbg_printf(XDBG_DEBUG_ERROR,
			    "Invalid frame to park on %d\r\n",
			    ChannelCfgPtr->FixedFrameStoreAddr);

			return XST_INVALID_PARAM;
		}

		if (Channel->IsRead) {
			FrmBits = ChannelCfgPtr->FixedFrameStoreAddr &
			              XAXIVDMA_PARKPTR_READREF_MASK;

			RegValue = XAxiVdma_ReadReg(Channel->InstanceBase,
			              XAXIVDMA_PARKPTR_OFFSET);

			RegValue &= ~XAXIVDMA_PARKPTR_READREF_MASK;

			RegValue |= FrmBits;

			XAxiVdma_WriteReg(Channel->InstanceBase,
			    XAXIVDMA_PARKPTR_OFFSET, RegValue);
		}
		else {
			FrmBits = ChannelCfgPtr->FixedFrameStoreAddr <<
			            XAXIVDMA_WRTREF_SHIFT;

			FrmBits &= XAXIVDMA_PARKPTR_WRTREF_MASK;

			RegValue = XAxiVdma_ReadReg(Channel->InstanceBase,
			              XAXIVDMA_PARKPTR_OFFSET);

			RegValue &= ~XAXIVDMA_PARKPTR_WRTREF_MASK;

			RegValue |= FrmBits;

			XAxiVdma_WriteReg(Channel->InstanceBase,
			    XAXIVDMA_PARKPTR_OFFSET, RegValue);
		}
	}

	if (ChannelCfgPtr->EnableSync) {
		if (Channel->GenLock != XAXIVDMA_GENLOCK_MASTER)
			CrBits |= XAXIVDMA_CR_SYNC_EN_MASK;
	}

	if (ChannelCfgPtr->GenLockRepeat) {
		if ((Channel->GenLock == XAXIVDMA_GENLOCK_MASTER) ||
			(Channel->GenLock == XAXIVDMA_DYN_GENLOCK_MASTER))
			CrBits |= XAXIVDMA_CR_GENLCK_RPT_MASK;
	}

	if (ChannelCfgPtr->EnableFrameCounter) {
		CrBits |= XAXIVDMA_CR_FRMCNT_EN_MASK;
	}

	CrBits |= (ChannelCfgPtr->PointNum << XAXIVDMA_CR_RD_PTR_SHIFT) &
	    XAXIVDMA_CR_RD_PTR_MASK;

	/* Write the control register value out
	 */
	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	if (Channel->HasSG) {
		/* Setup the information in BDs
		 *
		 * All information is available except the buffer addrs
		 * Buffer addrs are set through XAxiVdma_ChannelSetBufferAddr()
		 */
		NumBds = Channel->AllCnt;

		for (i = 0; i < NumBds; i++) {
			XAxiVdma_Bd *BdPtr = (XAxiVdma_Bd *)(Channel->HeadBdAddr +
			         i * sizeof(XAxiVdma_Bd));

			Status = XAxiVdma_BdSetVsize(BdPtr,
			             ChannelCfgPtr->VertSizeInput);
			if (Status != XST_SUCCESS) {
				xdbg_printf(XDBG_DEBUG_ERROR,
				    "Set vertical size failed %d\r\n", Status);

				return Status;
			}

			Status = XAxiVdma_BdSetHsize(BdPtr,
			    ChannelCfgPtr->HoriSizeInput);
			if (Status != XST_SUCCESS) {
				xdbg_printf(XDBG_DEBUG_ERROR,
				    "Set horizontal size failed %d\r\n", Status);

				return Status;
			}

			Status = XAxiVdma_BdSetStride(BdPtr,
			    ChannelCfgPtr->Stride);
			if (Status != XST_SUCCESS) {
				xdbg_printf(XDBG_DEBUG_ERROR,
				    "Set stride size failed %d\r\n", Status);

				return Status;
			}

			Status = XAxiVdma_BdSetFrmDly(BdPtr,
			ChannelCfgPtr->FrameDelay);
			if (Status != XST_SUCCESS) {
				xdbg_printf(XDBG_DEBUG_ERROR,
				    "Set frame delay failed %d\r\n", Status);

				return Status;
			}
		}
	}
	else {   /* direct register mode */
		if ((ChannelCfgPtr->VertSizeInput > XAXIVDMA_MAX_VSIZE) ||
		    (ChannelCfgPtr->VertSizeInput <= 0) ||
		    (ChannelCfgPtr->HoriSizeInput > XAXIVDMA_MAX_HSIZE) ||
		    (ChannelCfgPtr->HoriSizeInput <= 0) ||
		    (ChannelCfgPtr->Stride > XAXIVDMA_MAX_STRIDE) ||
		    (ChannelCfgPtr->Stride <= 0) ||
		    (ChannelCfgPtr->FrameDelay < 0) ||
		    (ChannelCfgPtr->FrameDelay > XAXIVDMA_FRMDLY_MAX)) {

			return XST_INVALID_PARAM;
		}

		XAxiVdma_WriteReg(Channel->StartAddrBase,
		    XAXIVDMA_HSIZE_OFFSET, ChannelCfgPtr->HoriSizeInput);

		XAxiVdma_WriteReg(Channel->StartAddrBase,
		    XAXIVDMA_STRD_FRMDLY_OFFSET,
		    (ChannelCfgPtr->FrameDelay << XAXIVDMA_FRMDLY_SHIFT) |
		    ChannelCfgPtr->Stride);
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Configure buffer addresses for one DMA channel
 *
 * The buffer addresses are physical addresses.
 * Access to 32 Frame Buffer Addresses in direct mode is done through
 * XAxiVdma_ChannelHiFrmAddrEnable/Disable Functions.
 * 0 - Access Bank0 Registers (0x5C - 0x98)
 * 1 - Access Bank1 Registers (0x5C - 0x98)
 *
 * @param Channel is the pointer to the channel to work on
 * @param BufferAddrSet is the set of addresses for the transfers
 * @param NumFrames is the number of frames to set the address
 *
 * @return
 * - XST_SUCCESS if successful
 * - XST_FAILURE if channel has not being initialized
 * - XST_DEVICE_BUSY if the DMA channel is not idle, BDs are still being used
 * - XST_INVAID_PARAM if buffer address not valid, for example, unaligned
 * address with no DRE built in the hardware
 *
 *****************************************************************************/
int XAxiVdma_ChannelSetBufferAddr(XAxiVdma_Channel *Channel,
        UINTPTR *BufferAddrSet, int NumFrames)
{
	int i;
	u32 WordLenBits;
	int HiFrmAddr = 0;
	int FrmBound;
	if (Channel->AddrWidth > 32) {
		FrmBound = (XAXIVDMA_MAX_FRAMESTORE_64)/2 - 1;
	} else {
		FrmBound = (XAXIVDMA_MAX_FRAMESTORE)/2 - 1;
	}
	int Loop16 = 0;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		return XST_FAILURE;
	}

	WordLenBits = (u32)(Channel->WordLength - 1);

	/* If hardware has no DRE, then buffer addresses must
	 * be word-aligned
	 */
	for (i = 0; i < NumFrames; i++) {
		if (!Channel->HasDRE) {
			if (BufferAddrSet[i] & WordLenBits) {
				xdbg_printf(XDBG_DEBUG_ERROR,
				    "Unaligned address %d: %x without DRE\r\n",
				    i, BufferAddrSet[i]);

				return XST_INVALID_PARAM;
			}
		}
	}

	for (i = 0; i < NumFrames; i++, Loop16++) {
		XAxiVdma_Bd *BdPtr = (XAxiVdma_Bd *)(Channel->HeadBdAddr +
		         i * sizeof(XAxiVdma_Bd));

		if (Channel->HasSG) {
			XAxiVdma_BdSetAddr(BdPtr, BufferAddrSet[i]);
		}
		else {
			if ((i > FrmBound) && !HiFrmAddr) {
				XAxiVdma_ChannelHiFrmAddrEnable(Channel);
				HiFrmAddr = 1;
				Loop16 = 0;
			}

			if (Channel->AddrWidth > 32) {
				/* For a 40-bit address XAXIVDMA_MAX_FRAMESTORE
				 * value should be set to 16 */
				XAxiVdma_WriteReg(Channel->StartAddrBase,
					XAXIVDMA_START_ADDR_OFFSET +
					Loop16 * XAXIVDMA_START_ADDR_LEN + i*4,
					LOWER_32_BITS(BufferAddrSet[i]));

				XAxiVdma_WriteReg(Channel->StartAddrBase,
					XAXIVDMA_START_ADDR_MSB_OFFSET +
					Loop16 * XAXIVDMA_START_ADDR_LEN + i*4,
					UPPER_32_BITS((u64)BufferAddrSet[i]));
			} else {
				XAxiVdma_WriteReg(Channel->StartAddrBase,
					XAXIVDMA_START_ADDR_OFFSET +
					Loop16 * XAXIVDMA_START_ADDR_LEN,
					BufferAddrSet[i]);
			}


			if ((NumFrames > FrmBound) && (i == (NumFrames - 1)))
				XAxiVdma_ChannelHiFrmAddrDisable(Channel);
		}
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Start one DMA channel
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 * - XST_SUCCESS if successful
 * - XST_FAILURE if channel is not initialized
 * - XST_DMA_ERROR if:
 *   . The DMA channel fails to stop
 *   . The DMA channel fails to start
 * - XST_DEVICE_BUSY is the channel is doing transfers
 *
 *****************************************************************************/
int XAxiVdma_ChannelStart(XAxiVdma_Channel *Channel)
{
	u32 CrBits;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		return XST_FAILURE;
	}

	if (Channel->HasSG && XAxiVdma_ChannelIsBusy(Channel)) {

		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Start DMA channel while channel is busy\r\n");

		return XST_DEVICE_BUSY;
	}

	/* If channel is not running, setup the CDESC register and
	 * set the channel to run
	 */
	if (!XAxiVdma_ChannelIsRunning(Channel)) {

		if (Channel->HasSG) {
			/* Set up the current bd register
			 *
			 * Can only setup current bd register when channel is halted
			 */
			XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CDESC_OFFSET,
			    Channel->HeadBdPhysAddr);
		}

		/* Start DMA hardware
		 */
		CrBits = XAxiVdma_ReadReg(Channel->ChanBase,
		     XAXIVDMA_CR_OFFSET);

		CrBits = XAxiVdma_ReadReg(Channel->ChanBase,
		     XAXIVDMA_CR_OFFSET) | XAXIVDMA_CR_RUNSTOP_MASK;

		XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
		    CrBits);

	}

	if (XAxiVdma_ChannelIsRunning(Channel)) {

		/* Start DMA transfers
		 *
		 */

		if (Channel->HasSG) {
			/* SG mode:
			 * Update the tail pointer so that hardware will start
			 * fetching BDs
			 */
			XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_TDESC_OFFSET,
			   Channel->TailBdPhysAddr);
		}
		else {
			/* Direct register mode:
			 * Update vsize to start the channel
			 */
			XAxiVdma_WriteReg(Channel->StartAddrBase,
			    XAXIVDMA_VSIZE_OFFSET, Channel->Vsize);

		}

		return XST_SUCCESS;
	}
	else {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Failed to start channel %x\r\n",
			    (unsigned int)Channel->ChanBase);

		return XST_DMA_ERROR;
	}
}

/*****************************************************************************/
/**
 * Stop one DMA channel
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *  None
 *
 *****************************************************************************/
void XAxiVdma_ChannelStop(XAxiVdma_Channel *Channel)
{
	u32 CrBits;

	if (!XAxiVdma_ChannelIsRunning(Channel)) {
		return;
	}

	/* Clear the RS bit in CR register
	 */
	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
		(~XAXIVDMA_CR_RUNSTOP_MASK);

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET, CrBits);

	return;
}

/*****************************************************************************/
/**
 * Dump registers from one DMA channel
 * This function has been rewritten, and is the only function that gets
 * changed compared to the original file xaxivdma_channel.c
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *  None
 *
 *****************************************************************************/
void XAxiVdma_ChannelRegisterDump(XAxiVdma_Channel *Channel)
{
	return;
}

/*****************************************************************************/
/**
 * Set the frame counter and delay counter for one channel
 *
 * @param Channel is the pointer to the channel to work on
 * @param FrmCnt is the frame counter value to be set
 * @param DlyCnt is the delay counter value to be set
 *
 * @return
 *   - XST_SUCCESS if setup finishes successfully
 *   - XST_FAILURE if channel is not initialized
 *   - XST_INVALID_PARAM if the configuration structure has invalid values
 *   - XST_NO_FEATURE if Frame Counter or Delay Counter is disabled
 *
 *****************************************************************************/
int XAxiVdma_ChannelSetFrmCnt(XAxiVdma_Channel *Channel, u8 FrmCnt, u8 DlyCnt)
{
	u32 CrBits;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		return XST_FAILURE;
	}

	if (!FrmCnt) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Frame counter value must be non-zero\r\n");

		return XST_INVALID_PARAM;
	}

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
		~(XAXIVDMA_DELAY_MASK | XAXIVDMA_FRMCNT_MASK);

	if (Channel->DbgFeatureFlags & XAXIVDMA_ENABLE_DBG_FRM_CNTR) {
		CrBits |= (FrmCnt << XAXIVDMA_FRMCNT_SHIFT);
	} else {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel Frame counter is disabled\r\n");
		return XST_NO_FEATURE;
	}
	if (Channel->DbgFeatureFlags & XAXIVDMA_ENABLE_DBG_DLY_CNTR) {
		CrBits |= (DlyCnt << XAXIVDMA_DELAY_SHIFT);
	} else {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel Delay counter is disabled\r\n");
		return XST_NO_FEATURE;
	}

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
 * Get the frame counter and delay counter for both channels
 *
 * @param Channel is the pointer to the channel to work on
 * @param FrmCnt is the pointer for the returning frame counter value
 * @param DlyCnt is the pointer for the returning delay counter value
 *
 * @return
 *  None
 *
 * @note
 *  If FrmCnt return as 0, then the channel is not initialized
 *****************************************************************************/
void XAxiVdma_ChannelGetFrmCnt(XAxiVdma_Channel *Channel, u8 *FrmCnt,
        u8 *DlyCnt)
{
	u32 CrBits;

	if (!Channel->IsValid) {
		xdbg_printf(XDBG_DEBUG_ERROR, "Channel not initialized\r\n");

		*FrmCnt = 0;
		return;
	}

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET);

	if (Channel->DbgFeatureFlags & XAXIVDMA_ENABLE_DBG_FRM_CNTR) {
		*FrmCnt = (CrBits & XAXIVDMA_FRMCNT_MASK) >>
				XAXIVDMA_FRMCNT_SHIFT;
	} else {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel Frame counter is disabled\r\n");
	}
	if (Channel->DbgFeatureFlags & XAXIVDMA_ENABLE_DBG_DLY_CNTR) {
		*DlyCnt = (CrBits & XAXIVDMA_DELAY_MASK) >>
				XAXIVDMA_DELAY_SHIFT;
	} else {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Channel Delay counter is disabled\r\n");
	}


	return;
}

/*****************************************************************************/
/**
 * Enable interrupts for a channel. Interrupts that are not specified by the
 * interrupt mask are not affected.
 *
 * @param Channel is the pointer to the channel to work on
 * @param IntrType is the interrupt mask for interrupts to be enabled
 *
 * @return
 *  None.
 *
 *****************************************************************************/
void XAxiVdma_ChannelEnableIntr(XAxiVdma_Channel *Channel, u32 IntrType)
{
	u32 CrBits;

	if ((IntrType & XAXIVDMA_IXR_ALL_MASK) == 0) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Enable intr with null intr mask value %x\r\n",
		    (unsigned int)IntrType);

		return;
	}

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	          ~XAXIVDMA_IXR_ALL_MASK;

	CrBits |= IntrType & XAXIVDMA_IXR_ALL_MASK;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits);

	return;
}

/*****************************************************************************/
/**
 * Disable interrupts for a channel. Interrupts that are not specified by the
 * interrupt mask are not affected.
 *
 * @param Channel is the pointer to the channel to work on
 * @param IntrType is the interrupt mask for interrupts to be disabled
 *
 * @return
 *  None.
 *
 *****************************************************************************/
void XAxiVdma_ChannelDisableIntr(XAxiVdma_Channel *Channel, u32 IntrType)
{
	u32 CrBits;
	u32 IrqBits;

	if ((IntrType & XAXIVDMA_IXR_ALL_MASK) == 0) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Disable intr with null intr mask value %x\r\n",
		    (unsigned int)IntrType);

		return;
	}

	CrBits = XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET);

	IrqBits = (CrBits & XAXIVDMA_IXR_ALL_MASK) &
	           ~(IntrType & XAXIVDMA_IXR_ALL_MASK);

	CrBits &= ~XAXIVDMA_IXR_ALL_MASK;

	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET,
	    CrBits | IrqBits);

	return;
}

/*****************************************************************************/
/**
 * Get pending interrupts of a channel.
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *  The interrupts mask represents pending interrupts.
 *
 *****************************************************************************/
u32 XAxiVdma_ChannelGetPendingIntr(XAxiVdma_Channel *Channel)
{
	return (XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET) &
	          XAXIVDMA_IXR_ALL_MASK);
}

/*****************************************************************************/
/**
 * Clear interrupts of a channel. Interrupts that are not specified by the
 * interrupt mask are not affected.
 *
 * @param Channel is the pointer to the channel to work on
 * @param IntrType is the interrupt mask for interrupts to be cleared
 *
 * @return
 *  None.
 *
 *****************************************************************************/
void XAxiVdma_ChannelIntrClear(XAxiVdma_Channel *Channel, u32 IntrType)
{

	if ((IntrType & XAXIVDMA_IXR_ALL_MASK) == 0) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Clear intr with null intr mask value %x\r\n",
		    (unsigned int)IntrType);

		return;
	}

	/* Only interrupts bits are writable in status register
	 */
	XAxiVdma_WriteReg(Channel->ChanBase, XAXIVDMA_SR_OFFSET,
	    IntrType & XAXIVDMA_IXR_ALL_MASK);

	return;
}

/*****************************************************************************/
/**
 * Get the enabled interrupts of a channel.
 *
 * @param Channel is the pointer to the channel to work on
 *
 * @return
 *  The interrupts mask represents pending interrupts.
 *
 *****************************************************************************/
u32 XAxiVdma_ChannelGetEnabledIntr(XAxiVdma_Channel *Channel)
{
	return (XAxiVdma_ReadReg(Channel->ChanBase, XAXIVDMA_CR_OFFSET) &
	          XAXIVDMA_IXR_ALL_MASK);
}

/*********************** BD Functions ****************************************/
/*****************************************************************************/
/*
 * Read one word from BD
 *
 * @param BdPtr is the BD to work on
 * @param Offset is the byte offset to read from
 *
 * @return
 *  The word value
 *
 *****************************************************************************/
static u32 XAxiVdma_BdRead(XAxiVdma_Bd *BdPtr, int Offset)
{
	return (*(u32 *)((UINTPTR)(void *)BdPtr + Offset));
}

/*****************************************************************************/
/*
 * Set one word in BD
 *
 * @param BdPtr is the BD to work on
 * @param Offset is the byte offset to write to
 * @param Value is the value to write to the BD
 *
 * @return
 *  None
 *
 *****************************************************************************/
static void XAxiVdma_BdWrite(XAxiVdma_Bd *BdPtr, int Offset, u32 Value)
{
	*(u32 *)((UINTPTR)(void *)BdPtr + Offset) = Value;

	return;
}

/*****************************************************************************/
/*
 * Set the next ptr from BD
 *
 * @param BdPtr is the BD to work on
 * @param NextPtr is the next ptr to set in BD
 *
 * @return
 *  None
 *
 *****************************************************************************/
static void XAxiVdma_BdSetNextPtr(XAxiVdma_Bd *BdPtr, u32 NextPtr)
{
	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_NDESC_OFFSET, NextPtr);
	return;
}

/*****************************************************************************/
/*
 * Set the start address from BD
 *
 * The address is physical address.
 *
 * @param BdPtr is the BD to work on
 * @param Addr is the address to set in BD
 *
 * @return
 *  None
 *
 *****************************************************************************/
static void XAxiVdma_BdSetAddr(XAxiVdma_Bd *BdPtr, u32 Addr)
{
	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_START_ADDR_OFFSET, Addr);

	return;
}

/*****************************************************************************/
/*
 * Set the vertical size for a BD
 *
 * @param BdPtr is the BD to work on
 * @param Vsize is the vertical size to set in BD
 *
 * @return
 *  - XST_SUCCESS if successful
 *  - XST_INVALID_PARAM if argument Vsize is invalid
 *
 *****************************************************************************/
static int XAxiVdma_BdSetVsize(XAxiVdma_Bd *BdPtr, int Vsize)
{
	if ((Vsize <= 0) || (Vsize > XAXIVDMA_VSIZE_MASK)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Veritcal size %d is not valid\r\n", Vsize);

		return XST_INVALID_PARAM;
	}

	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_VSIZE_OFFSET, Vsize);
	return XST_SUCCESS;
}

/*****************************************************************************/
/*
 * Set the horizontal size for a BD
 *
 * @param BdPtr is the BD to work on
 * @param Hsize is the horizontal size to set in BD
 *
 * @return
 *  - XST_SUCCESS if successful
 *  - XST_INVALID_PARAM if argument Hsize is invalid
 *
 *****************************************************************************/
static int XAxiVdma_BdSetHsize(XAxiVdma_Bd *BdPtr, int Hsize)
{
	if ((Hsize <= 0) || (Hsize > XAXIVDMA_HSIZE_MASK)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Horizontal size %d is not valid\r\n", Hsize);

		return XST_INVALID_PARAM;
	}

	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_HSIZE_OFFSET, Hsize);
	return XST_SUCCESS;
}

/*****************************************************************************/
/*
 * Set the stride size for a BD
 *
 * @param BdPtr is the BD to work on
 * @param Stride is the stride size to set in BD
 *
 * @return
 *  - XST_SUCCESS if successful
 *  - XST_INVALID_PARAM if argument Stride is invalid
 *
 *****************************************************************************/
static int XAxiVdma_BdSetStride(XAxiVdma_Bd *BdPtr, int Stride)
{
	u32 Bits;

	if ((Stride <= 0) || (Stride > XAXIVDMA_STRIDE_MASK)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "Stride size %d is not valid\r\n", Stride);

		return XST_INVALID_PARAM;
	}

	Bits = XAxiVdma_BdRead(BdPtr, XAXIVDMA_BD_STRIDE_OFFSET) &
	        ~XAXIVDMA_STRIDE_MASK;

	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_STRIDE_OFFSET, Bits | Stride);

	return XST_SUCCESS;
}

/*****************************************************************************/
/*
 * Set the frame delay for a BD
 *
 * @param BdPtr is the BD to work on
 * @param FrmDly is the frame delay value to set in BD
 *
 * @return
 *  - XST_SUCCESS if successful
 *  - XST_INVALID_PARAM if argument FrmDly is invalid
 *
 *****************************************************************************/
static int XAxiVdma_BdSetFrmDly(XAxiVdma_Bd *BdPtr, int FrmDly)
{
	u32 Bits;

	if ((FrmDly < 0) || (FrmDly > XAXIVDMA_FRMDLY_MAX)) {
		xdbg_printf(XDBG_DEBUG_ERROR,
		    "FrmDly size %d is not valid\r\n", FrmDly);

		return XST_INVALID_PARAM;
	}

	Bits = XAxiVdma_BdRead(BdPtr, XAXIVDMA_BD_STRIDE_OFFSET) &
	        ~XAXIVDMA_FRMDLY_MASK;

	XAxiVdma_BdWrite(BdPtr, XAXIVDMA_BD_STRIDE_OFFSET,
	    Bits | (FrmDly << XAXIVDMA_FRMDLY_SHIFT));

	return XST_SUCCESS;
}
