/**************************************************************************//**
 * @file     DrvEDMA.c
 * @version  V3.00
 * @brief    N9H20 series EDMA driver source file
 *
 * SPDX-License-Identifier: Apache-2.0
 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include "wblib.h"
#include "DrvEDMA.h"

static PFN_DRVEDMA_CALLBACK  g_pfnEDMACallback[MAX_CHANNEL_NUM+1][4]  = {
								{0,0,0,0},
								{0,0,0,0},
								{0,0,0,0},
								{0,0,0,0},
								{0,0,0,0}															
};								


#define	MAX_GS_TRANSFER_SIZE	32*1024

void DrvEDMA_EnableScatterGather(
    E_DRVEDMA_CHANNEL_INDEX eChannel
);

  
void DrvEDMA_DisableScatterGather(
    E_DRVEDMA_CHANNEL_INDEX eChannel
);

UINT32  
DrvEDMA_Open(void)
{
	// 1.Check I/O pins. If I/O pins are used by other IPs, return error code.
	// 2.Enable IP��s clock --> Enable Channel clock in DrvEDMA_SetCHOperation( ) function
	// 3.Reset IP
	// 4.Configure IP according to inputted arguments.
	// 5.Enable IP I/O pins
	// 6.Return 0 to present success
	return E_SUCCESS;    
}

void DrvEDMA_Close(void)
{
    UINT32 u32Mask;
    
	// 1.Disable IP I/O pins
	// 2.Disable IP��s clock --> Disable Channel Clock in DrvEDMA_SetCHOperation( ) function
	u32Mask = EDMA0_CKE | EDMA1_CKE | EDMA2_CKE | EDMA3_CKE | EDMA4_CKE;
	outp32(REG_AHBCLK,inp32(REG_AHBCLK) & ~u32Mask);					
	
}

// Get Channel Enable/Disable status
ERRCODE  
DrvEDMA_IsCHBusy(
	E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;    
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    
    if (inp32(u32SFR) &  TRIG_EN)
        return TRUE;
    else
        return FALSE;    
}

// Set Channel Enable/Disable & Enable Channel Clock
void DrvEDMA_EnableCH(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_OPERATION eOP
)
{
	UINT32 u32SFR,u32Mask;	
	
	u32Mask = 0x00000400 << eChannel;
	u32SFR = REG_VDMA_CSR + eChannel * 0x100;

	if (eOP == eDRVEDMA_DISABLE)
	{		    
		outp32(u32SFR, inp32(u32SFR) & ~EDMACEN);     
		outp32(REG_AHBCLK,inp32(REG_AHBCLK) & ~u32Mask);					// Disable Channel Clock
	}    	
	else 
	{
		outp32(REG_AHBCLK,inp32(REG_AHBCLK) | u32Mask);						// Enable Channel Clock    	
		outp32(u32SFR, inp32(u32SFR) | EDMACEN); 	   	
	}    	
		
	
}


// Get Channel Enable/Disable status
ERRCODE  
DrvEDMA_IsEnabledCH(
	E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;    
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    
    return inp32(u32SFR) & EDMACEN;
}

// Set Source/Destination Address and Direction and Transfer Length for Channelx
ERRCODE  
DrvEDMA_SetTransferSetting(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    S_DRVEDMA_CH_ADDR_SETTING* psSrcAddr, 
    S_DRVEDMA_CH_ADDR_SETTING* psDestAddr, 
    UINT32 u32TransferLength
)
{
    UINT32 u32SFR, u32Value; 
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;    
    
    DrvEDMA_DisableScatterGather(eChannel);
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    u32Value = inp32(u32SFR);        
        
    outp32(REG_VDMA_SAR + eChannel * 0x100, psSrcAddr->u32Addr);   
    
    u32Value = (u32Value & ~SAD_SEL) | (psSrcAddr->eAddrDirection << SOURCE_DIRECTION_BIT);
    
    outp32(REG_VDMA_DAR + eChannel * 0x100, psDestAddr->u32Addr);
    u32Value = (u32Value & ~DAD_SEL) | (psDestAddr->eAddrDirection << DESTINATION_DIRECTION_BIT);
    outp32(u32SFR,u32Value);

    if (u32TransferLength > MAX_TRANSFER_BYTE_COUNT)
        return E_DRVEDMA_FALSE_INPUT;
        
    outp32(REG_VDMA_BCR + eChannel * 0x100,u32TransferLength);
    
    return E_SUCCESS;     
}

// Get Source/Destination Address and Direction from Channelx
ERRCODE    
DrvEDMA_GetTransferSetting(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_TARGET eTarget, 
    UINT32* pu32Addr, 
    E_DRVEDMA_DIRECTION_SELECT* peDirection
)
{
    UINT32 u32SFR, u32Value;

    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;     
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    
    if ((eTarget != eDRVEDMA_TARGET_SOURCE) && (eTarget != eDRVEDMA_TARGET_DESTINATION))
        return E_DRVEDMA_FALSE_INPUT;
    
    u32Value = inp32(u32SFR);
    
    if (eTarget == eDRVEDMA_TARGET_SOURCE)
    {
        *pu32Addr = inp32(REG_VDMA_SAR + eChannel * 0x100);
        *peDirection = (E_DRVEDMA_DIRECTION_SELECT)((u32Value & SAD_SEL) >> SOURCE_DIRECTION_BIT);
    }
    else
    {
        *pu32Addr = inp32(REG_VDMA_DAR + eChannel * 0x100);
        *peDirection = (E_DRVEDMA_DIRECTION_SELECT)((u32Value & DAD_SEL) >> DESTINATION_DIRECTION_BIT);        
    }
    
    return E_SUCCESS;     
}

// Get Transfer Length from Channelx
ERRCODE    
DrvEDMA_GetTransferLength(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    UINT32* pu32TransferLength
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT; 
            
    u32SFR = REG_VDMA_BCR + eChannel * 0x100;
    
    *pu32TransferLength = inp32(u32SFR);
    
    return E_SUCCESS;       
}


// Set APB Transfer Width for Channelx
ERRCODE  
DrvEDMA_SetAPBTransferWidth(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_TRANSFER_WIDTH eTransferWidth
)
{
    UINT32 u32SFR;
    
    if ((eChannel > MAX_CHANNEL_NUM) || (eChannel ==0))
        return E_DRVEDMA_FALSE_INPUT; 
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    outp32(u32SFR,(inp32(u32SFR) & ~APB_TWS) | (eTransferWidth << TRANSFER_WIDTH_BIT));
        
    return E_SUCCESS;     
}

// Get Transfer Width from Channelx
ERRCODE    
DrvEDMA_GetAPBTransferWidth(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_TRANSFER_WIDTH* peTransferWidth
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT; 
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    *peTransferWidth = (E_DRVEDMA_TRANSFER_WIDTH)((inp32(u32SFR) & APB_TWS) >> TRANSFER_WIDTH_BIT);    
    
    return E_SUCCESS;    
}

// Select EDMA channel for APB Device
ERRCODE  
DrvEDMA_SetCHForAPBDevice(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_APB_DEVICE eDevice,
    E_DRVEDMA_APB_RW eRWAPB    
)
{
    UINT32 u32Value,u32Mask,i,u32OrMask; 
    UINT32 u32SFR;       
    
    if ((eChannel > MAX_CHANNEL_NUM) || (eChannel ==0))
        return E_DRVEDMA_FALSE_INPUT; 
            
    if ((eRWAPB == eDRVEDMA_WRITE_APB) && (eDevice == eDRVEDMA_ADC))
        return E_DRVEDMA_FALSE_INPUT;
            	
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
                	
    // Let Tx and Rx does not use the same channel         	
    if (eRWAPB == eDRVEDMA_WRITE_APB)
    {
	    outp32(u32SFR,(inp32(u32SFR) & ~MODE_SEL) | (eDRVEDMA_MEMORY_TO_DEVICE << MODE_SELECT_BIT));    
        u32Mask = 0x000F0000 << ((eChannel-1)*4);
        u32OrMask = 0x0007 << ((eChannel-1)*4);
	}        
    else
    {
	    outp32(u32SFR,(inp32(u32SFR) & ~MODE_SEL) | (eDRVEDMA_DEVICE_TO_MEMORY << MODE_SELECT_BIT));      
        u32Mask = 0x0000000F << ((eChannel-1)*4);
        u32OrMask = 0x00070000 << ((eChannel-1)*4);        
	}        

    u32Value = inp32(REG_EDSSR) & ~u32Mask;
    
    if ((eDevice == eDRVEDMA_SPIMS0) || (eDevice == eDRVEDMA_SPIMS1))
	    u32Value &= ~u32OrMask;    
    else
	    u32Value |= u32OrMask;

	// let Tx or Rx does not use two channel concurrently    
    if (eRWAPB == eDRVEDMA_WRITE_APB)
    {
	    u32Mask = 0x070000;
       	for(i=1;i<=4;i++)
       	{
       		if (i != eChannel)
       		{
	       		if (((u32Value & u32Mask)>>((i-1)*4+16)) == eDevice)
	       		{
	       			u32Value |= u32Mask;
	       			
			   		if ((eDevice == eDRVEDMA_SPIMS0) || (eDevice == eDRVEDMA_SPIMS1))
			   			u32Value |=  0x07 << ((i-1)*4);	       			
				}	       			
			}       			
       		u32Mask <<= 4;	
		}        
		

   		u32Value |=  eDevice << ((eChannel-1)*4 + 16);
   		
   		// TX and RX use same channel for SPIM0 & 1 
   		if ((eDevice == eDRVEDMA_SPIMS0) || (eDevice == eDRVEDMA_SPIMS1))
   			u32Value |=  eDevice << ((eChannel-1)*4);
    }
    else
    {
	    u32Mask = 0x07;
       	for(i=1;i<=4;i++)
       	{
       		if (i != eChannel)
       		{
	      		if (((u32Value & u32Mask)>>(i-1)*4) == eDevice)
	      		{
	       			u32Value |= u32Mask;
	       			
			   		if ((eDevice == eDRVEDMA_SPIMS0) || (eDevice == eDRVEDMA_SPIMS1))
			   			u32Value |=  0x070000 << ((i-1)*4);	  	       			
				}			   			
       		}	
       		u32Mask <<= 4;	
		}  
		    
    	u32Value |=  eDevice << ((eChannel-1)*4);    
    	
    	// TX and RX use same channel for SPIM0 & 1 
   		if ((eDevice == eDRVEDMA_SPIMS0) || (eDevice == eDRVEDMA_SPIMS1))
   			u32Value |=  eDevice << ((eChannel-1)*4+16);    	
    }

    outp32(REG_EDSSR,u32Value);
        
    return E_SUCCESS;     
}

// Get EDMA channel for APB Device
E_DRVEDMA_CHANNEL_INDEX  
DrvEDMA_GetCHForAPBDevice(
    E_DRVEDMA_APB_DEVICE eDevice,
    E_DRVEDMA_APB_RW eRWAPB    
)
{
	UINT32 u32Value,i;
	
    if ((eDevice < eDRVEDMA_SPIMS0) || (eDevice > eDRVEDMA_ADC))
    	return (E_DRVEDMA_CHANNEL_INDEX)E_DRVEDMA_FALSE_INPUT;
    	
	if (eRWAPB == eDRVEDMA_WRITE_APB)
	{
		u32Value = inp32(REG_EDSSR)>>16;
	}
	else
	{
		u32Value = inp32(REG_EDSSR);
	}
	
   	for(i=1;i<=4;i++)
   	{
   		if (((u32Value >> (i-1)*4) & 0x07) == eDevice)
   			return (E_DRVEDMA_CHANNEL_INDEX)i;
	} 	
	
   	return (E_DRVEDMA_CHANNEL_INDEX)0;  
}

// Set Wrap Around Transfer Byte count interrupt Select for Channelx
ERRCODE  
DrvEDMA_SetWrapIntType(
    E_DRVEDMA_CHANNEL_INDEX eChannel,
    E_DRVEDMA_WRAPAROUND_SELECT eType
)
{
    UINT32 u32SFR;
    
    if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 0))
        return E_DRVEDMA_FALSE_INPUT;
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    
    if (eType > 0x0F)  
        return E_DRVEDMA_FALSE_INPUT;    
        
    outp32(u32SFR,(inp32(u32SFR) & ~WAR_BCR_SEL) | (eType<<12));    
    
    return E_SUCCESS;  
}

// Get Wrap Around Transfer Byte count interrupt Select from Channelx
E_DRVEDMA_WRAPAROUND_SELECT  
DrvEDMA_GetWrapIntType(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 0))
        return (E_DRVEDMA_WRAPAROUND_SELECT)E_DRVEDMA_FALSE_INPUT;
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;  
    
    return (E_DRVEDMA_WRAPAROUND_SELECT)((inp32(u32SFR) & WAR_BCR_SEL )>> 12);  
}

// Software reset Channelx
ERRCODE  
DrvEDMA_CHSoftwareReset(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;  
    
    outp32(u32SFR, inp32(u32SFR) | SW_RST);  
    
    return E_SUCCESS;     
}

// Enable EDMA data read or write Transfer
ERRCODE  
DrvEDMA_CHEnablelTransfer(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;
            
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;  
    
    outp32(u32SFR, inp32(u32SFR) | TRIG_EN | EDMACEN);  
    
    return E_SUCCESS;      
}

// Get Current Source Address from Channelx
UINT32  
DrvEDMA_GetCurrentSourceAddr(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_CSAR + eChannel * 0x100;   
    
    return inp32(u32SFR);      
}

// Get Current Destination Address from Channelx
UINT32  
DrvEDMA_GetCurrentDestAddr(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_CDAR + eChannel * 0x100;   
    
    return inp32(u32SFR);      
}

// Get Current Transfer Count from Channelx
UINT32  
DrvEDMA_GetCurrentTransferCount(
    E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_CBCR + eChannel * 0x100;   
    
    return inp32(u32SFR);   
}

// Enable Interrupt for Channelx
ERRCODE  
DrvEDMA_EnableInt(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_INT_ENABLE eIntSource
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_IER + eChannel * 0x100;        
    
    if ((eIntSource <1) || (eIntSource >15)) 
        return E_DRVEDMA_FALSE_INPUT;

    outp32(u32SFR, inp32(u32SFR) | eIntSource);  
    
    return E_SUCCESS;      
}

// Disable Interrupt for Channelx
void DrvEDMA_DisableInt(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_INT_ENABLE eIntSource
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_IER + eChannel * 0x100;        
    
    outp32(u32SFR, inp32(u32SFR) & ~eIntSource);  
              
}

// Check if the specified interrupt source is enabled in Channelx
UINT32  
DrvEDMA_IsIntEnabled(
    E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_INT_ENABLE eIntSource    
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_ISR + eChannel * 0x100;    
    
    switch(eIntSource)
    {
    	case eDRVEDMA_TABORT:
		    return inp32(u32SFR) & EDMATABORT_IE;     	
//    		break;
    	case eDRVEDMA_BLKD:
		    return inp32(u32SFR) & BLKD_IE;     	
//    		break;
    	case eDRVEDMA_WAR:
		    return inp32(u32SFR) & WAR_IE;     	
//    		break;
    	case eDRVEDMA_SG:
		    return inp32(u32SFR) & SG_IEN;     	
//    		break;    		
		default :
	    	return E_DRVEDMA_FALSE_INPUT;		    		
	}    
}

// Clear Interrupt Status for Channelx
void DrvEDMA_ClearInt(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_INT_FLAG eIntFlag
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_ISR + eChannel * 0x100;    
    
    outp32(u32SFR, eIntFlag);
}

BOOL
DrvEDMA_PollInt(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_INT_FLAG eIntFlag
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_ISR + eChannel * 0x100;    
    
    return inp32(u32SFR) & eIntFlag;
}

// Set Color Format Transform for Channel0
ERRCODE  
DrvEDMA_SetColorTransformFormat(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_COLOR_FORMAT eSourceFormat,
	E_DRVEDMA_COLOR_FORMAT eDestFormat
)
{ 
	UINT32 u32SFR;

	 if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 1) || (eChannel == 2) || (eChannel == 3) || (eChannel == 4))
        	return E_DRVEDMA_FALSE_INPUT;
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
	
	outp32(u32SFR, (inp32(u32SFR) & ~(SOUR_FORMAT | DEST_FORMAT)) | eSourceFormat<<24 | eDestFormat<<16);
	return E_SUCCESS;  
}

// Get Color Format Transform from Channel0
void DrvEDMA_GetColorTransformFormat(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_COLOR_FORMAT* peSourceFormat,
	E_DRVEDMA_COLOR_FORMAT* peDestFormat
)
{
	UINT32 u32SFR;	 
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
		
	*peSourceFormat = (E_DRVEDMA_COLOR_FORMAT)((inp32(u32SFR) & SOUR_FORMAT) >> 24);
	*peDestFormat = (E_DRVEDMA_COLOR_FORMAT)((inp32(u32SFR) & DEST_FORMAT) >> 16);	
}

ERRCODE  
DrvEDMA_SetColorTransformOperation(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_OPERATION eColorSpaceTran,
	E_DRVEDMA_OPERATION eStrideMode
)
{ 
	UINT32 u32SFR;

	 if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 1) || (eChannel == 2) || (eChannel == 3) || (eChannel == 4))
        	return E_DRVEDMA_FALSE_INPUT;
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
		
	outp32(u32SFR, (inp32(u32SFR) & ~(COL_TRA_EN | STRIDE_EN)) | eColorSpaceTran<<1 | eStrideMode);
	return E_SUCCESS;  
}

void DrvEDMA_GetColorTransformOperation(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_OPERATION* peColorSpaceTran,
	E_DRVEDMA_OPERATION* peStrideMode
)
{ 
	UINT32 u32SFR;	 
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
	
	*peColorSpaceTran = (E_DRVEDMA_OPERATION)((inp32(u32SFR) & COL_TRA_EN) >> 1);
	*peStrideMode = (E_DRVEDMA_OPERATION)(inp32(u32SFR) & STRIDE_EN );	
}



// Set Source Stride Transfer Byte Count & Offset Byte Length
// Only Channel0 support this function
ERRCODE  
DrvEDMA_SetSourceStride(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
    UINT32 u32StrideByteCount,
    UINT32 u32OffsetByteLength
)
{
	UINT32 u32SFR;

	 if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 1) || (eChannel == 2) || (eChannel == 3) || (eChannel == 4))
        	return E_DRVEDMA_FALSE_INPUT;
    
    	u32SFR = REG_VDMA_SASOCR + eChannel * 0x100;
    
    outp32(u32SFR,(u32StrideByteCount<<16) |u32OffsetByteLength);   
    
    return E_SUCCESS;     
}

// Get Source/Destination Stride Transfer Byte Count & Offset Byte Length
void DrvEDMA_GetSourceStride(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
    UINT32* pu32StrideByteCount,
    UINT32* pu32OffsetByteLength
)
{
	UINT32 u32SFR;	
    
    	u32SFR = REG_VDMA_SASOCR + eChannel * 0x100;
     
    *pu32StrideByteCount = (inp32(u32SFR) & STBC) >> 16;   
    *pu32OffsetByteLength = (inp32(u32SFR) & SASTOBL);     
}

// Set Destination Stride Transfer Byte Count & Offset Byte Length
// Only Channel0 support this function
ERRCODE  
DrvEDMA_SetDestinationStrideOffset(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
    UINT32 u32OffsetByteLength
)
{
	UINT32 u32SFR;

	 if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 1) || (eChannel == 2) || (eChannel == 3) || (eChannel == 4))
        	return E_DRVEDMA_FALSE_INPUT;
    
    	u32SFR = REG_VDMA_DASOCR + eChannel * 0x100;
    
    outp32(u32SFR,u32OffsetByteLength & DASTOBL);   
    
    return E_SUCCESS;     
}

// Get Destination Stride Offset Byte Length
void DrvEDMA_GetDestinationStrideOffset(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
    UINT32* pu32OffsetByteLength
)
{
     UINT32 u32SFR;	
    
    	u32SFR = REG_VDMA_DASOCR + eChannel * 0x100;
		
    *pu32OffsetByteLength = (inp32(u32SFR) & DASTOBL);     
}

// Set Channel0 Clamping function 
ERRCODE
DrvEDMA_SetClamping(
	E_DRVEDMA_CHANNEL_INDEX eChannel,
	E_DRVEDMA_OPERATION eOP
)
{
	UINT32 u32SFR;

	 if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 1) || (eChannel == 2) || (eChannel == 3) || (eChannel == 4))
        	return E_DRVEDMA_FALSE_INPUT;
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
    
    if (eOP == eDRVEDMA_DISABLE)
    	outp32(u32SFR, inp32(u32SFR) & ~CLAMPING_EN);     
	else 
    	outp32(u32SFR, inp32(u32SFR) | CLAMPING_EN); 	 

	return E_SUCCESS; 
}

// Get Channel0 Clamping status
E_DRVEDMA_OPERATION  
DrvEDMA_GetClamping(
E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;	 
    
    	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
		
    return (E_DRVEDMA_OPERATION)((inp32(u32SFR) & CLAMPING_EN) >> 7);
}

// Get Channel 1 ~ 4 Internal Buffer Pointer
UINT32  
DrvEDMA_GetInternalBufPointer(
	E_DRVEDMA_CHANNEL_INDEX eChannel
)
{
    UINT32 u32SFR;
    
    //if ((eChannel > MAX_CHANNEL_NUM) || (eChannel == 0))
    //    return E_DRVEDMA_FALSE_INPUT;    
    
    u32SFR = REG_PDMA_POINT1 + (eChannel-1) * 0x100; 
    
    return (inp32(u32SFR) & PDMA_POINT);
}

// Get Shared Buffer Content from Channelx
UINT32  
DrvEDMA_GetSharedBufData(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    UINT32 u32BufIndex
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_EDMA_SBUF0_C0 + eChannel * 0x100 + u32BufIndex * 4;
    
    return inp32(u32SFR);
    
    
}

// EDMA ISR
void DrvEDMA_ISR(void)
{
    UINT32 u32IntStatus;
    UINT32 u32WraparoundStatus;
    
    if (inp32(REG_VDMA_ISR) & INTR)
    {
    	if (inp32(REG_VDMA_ISR) & INTR0)
    	{
	    	u32IntStatus = inp32(REG_VDMA_ISR) & inp32(REG_VDMA_IER);
    		if (u32IntStatus & EDMATABORT_IF)
    		{
    			outp32(REG_VDMA_ISR,EDMATABORT_IF);
    			if (g_pfnEDMACallback[0][0] != 0)
	    			(*g_pfnEDMACallback[0][0])(0);    			
			}    			
    		else 
    		{
    			if (u32IntStatus & EDMABLKD_IF)
    			{
    				outp32(REG_VDMA_ISR,EDMABLKD_IF);
	    			if (g_pfnEDMACallback[0][1] != 0)
		    			(*g_pfnEDMACallback[0][1])(0);	    				    			
				}
				else if (u32IntStatus & EDMASG_IF)
				{
				outp32(REG_VDMA_ISR,EDMASG_IF);
	    			if (g_pfnEDMACallback[0][3] != 0)
		    			(*g_pfnEDMACallback[0][3])(0);	    								
				}
					    			
    		}
    	}
    	else if (inp32(REG_VDMA_ISR) & INTR1)
    	{
    		if (inp32(REG_PDMA_IER1) & WAR_IE)
	    		u32IntStatus = inp32(REG_PDMA_ISR1) & (inp32(REG_PDMA_IER1) | 0x0F00);
	    	else
	    		u32IntStatus = inp32(REG_PDMA_ISR1) & inp32(REG_PDMA_IER1);	    	
	    	
    		if (u32IntStatus & EDMATABORT_IF)
    		{
    			outp32(REG_PDMA_ISR1,EDMATABORT_IF);
    			if (g_pfnEDMACallback[1][0] != 0)    		
	    			(*g_pfnEDMACallback[1][0])(0);    			
			}    			
    		else
    		{
    			if (u32IntStatus & EDMABLKD_IF)
    			{
    				outp32(REG_PDMA_ISR1,EDMABLKD_IF);	
	    			if (g_pfnEDMACallback[1][1] != 0)    			
		    			(*g_pfnEDMACallback[1][1])(0);	    			    			
				}	   
	    		else if (u32IntStatus & EDMASG_IF)
	    		{
	    			outp32(REG_PDMA_ISR1,EDMASG_IF);
	    			if (g_pfnEDMACallback[1][3] != 0)
		    			(*g_pfnEDMACallback[1][3])(0);	    				    		
	    		}				 			
    			else
    			{
	    			u32WraparoundStatus = inp32(REG_PDMA_ISR1) & 0x0F00;
	    			if (u32WraparoundStatus)
	    			{
		    			if (u32WraparoundStatus & 0x0200)
		    				u32WraparoundStatus = 0x0200;
		    			else if (u32WraparoundStatus & 0x0400)
		    				u32WraparoundStatus = 0x0400;
		    			else  if (u32WraparoundStatus & 0x0800)
		    				u32WraparoundStatus = 0x0800;
		    			else		   
		    				u32WraparoundStatus = 0x0100; 	

					outp32(REG_PDMA_ISR1,u32WraparoundStatus);				
		    			if (g_pfnEDMACallback[1][2] != 0)		    						
			    			(*g_pfnEDMACallback[1][2])(u32WraparoundStatus); 
		    					    			   			
	    			}
				}	    			
    		}    	
    	}
    	else if (inp32(REG_VDMA_ISR) & INTR2)
    	{
    		if (inp32(REG_PDMA_IER2) & WAR_IE)
	    		u32IntStatus = inp32(REG_PDMA_ISR2) & (inp32(REG_PDMA_IER2) | 0x0F00);
	    	else
	    		u32IntStatus = inp32(REG_PDMA_ISR2) & inp32(REG_PDMA_IER2);	    	
	    	
    		if (u32IntStatus & EDMATABORT_IF)
    		{
    			outp32(REG_PDMA_ISR2,EDMATABORT_IF);
    			if (g_pfnEDMACallback[2][0] != 0)    		
	    			(*g_pfnEDMACallback[2][0])(0);    			
			}    			
    		else 
    		{
    			if (u32IntStatus & EDMABLKD_IF)
    			{
    				outp32(REG_PDMA_ISR2,EDMABLKD_IF);
	    			if (g_pfnEDMACallback[2][1] != 0)    			
		    			(*g_pfnEDMACallback[2][1])(0);	    				    			
				}	
	    		else if (u32IntStatus & EDMASG_IF)
	    		{
	    			outp32(REG_PDMA_ISR2,EDMASG_IF);
	    			if (g_pfnEDMACallback[2][3] != 0)
		    			(*g_pfnEDMACallback[2][3])(0);	    				    		
	    		}					    			
    			else
    			{
	    			u32WraparoundStatus = inp32(REG_PDMA_ISR2) & 0x0F00;
	    			if (u32WraparoundStatus)
	    			{
		    			if (u32WraparoundStatus & 0x0200)
		    				u32WraparoundStatus = 0x0200;
		    			else if (u32WraparoundStatus & 0x0400)
		    				u32WraparoundStatus = 0x0400;
		    			else  if (u32WraparoundStatus & 0x0800)
		    				u32WraparoundStatus = 0x0800;
		    			else		   
		    				u32WraparoundStatus = 0x0100; 	

					outp32(REG_PDMA_ISR2,u32WraparoundStatus);		
		    			if (g_pfnEDMACallback[2][2] != 0)		    						
			    			(*g_pfnEDMACallback[2][2])(u32WraparoundStatus); 		    					    			   			
	    			}
				}	    			
    		}     	
    	}
    	else if (inp32(REG_VDMA_ISR) & INTR3)
    	{
    		if (inp32(REG_PDMA_IER3) & WAR_IE)
	    		u32IntStatus = inp32(REG_PDMA_ISR3) & (inp32(REG_PDMA_IER3) | 0x0F00);
	    	else
	    		u32IntStatus = inp32(REG_PDMA_ISR3) & inp32(REG_PDMA_IER3);	    	
	    	
    		if (u32IntStatus & EDMATABORT_IF)
    		{
    			outp32(REG_PDMA_ISR3,EDMATABORT_IF);
    			if (g_pfnEDMACallback[3][0] != 0)    		
	    			(*g_pfnEDMACallback[3][0])(0);    			
			}    			
    		else 
    		{
    			if (u32IntStatus & EDMABLKD_IF)
    			{
    				outp32(REG_PDMA_ISR3,EDMABLKD_IF);
	    			if (g_pfnEDMACallback[3][1] != 0)    			
		    			(*g_pfnEDMACallback[3][1])(0);	    				    			
				}	    
	    		else if (u32IntStatus & EDMASG_IF)
	    		{
	    			outp32(REG_PDMA_ISR3,EDMASG_IF);
	    			if (g_pfnEDMACallback[3][3] != 0)
		    			(*g_pfnEDMACallback[3][3])(0);	    				    		
	    		}						
    			else
    			{
	    			u32WraparoundStatus = inp32(REG_PDMA_ISR3) & 0x0F00;
	    			if (u32WraparoundStatus)
	    			{
		    			if (u32WraparoundStatus & 0x0200)
		    				u32WraparoundStatus = 0x0200;
		    			else if (u32WraparoundStatus & 0x0400)
		    				u32WraparoundStatus = 0x0400;
		    			else  if (u32WraparoundStatus & 0x0800)
		    				u32WraparoundStatus = 0x0800;
		    			else		   
		    				u32WraparoundStatus = 0x0100; 	

					outp32(REG_PDMA_ISR3,u32WraparoundStatus);				
		    			if (g_pfnEDMACallback[3][2] != 0)		    						
			    			(*g_pfnEDMACallback[3][2])(u32WraparoundStatus); 		    					    			   			
	    			}
				}	    			
    		}     	
    	}
    	else 
    	{
    		if (inp32(REG_PDMA_IER4) & WAR_IE)
	    		u32IntStatus = inp32(REG_PDMA_ISR4) & (inp32(REG_PDMA_IER4) | 0x0F00);
	    	else
	    		u32IntStatus = inp32(REG_PDMA_ISR4) & inp32(REG_PDMA_IER4);	    	
	    	
    		if (u32IntStatus & EDMATABORT_IF)
    		{
    			outp32(REG_PDMA_ISR4,EDMATABORT_IF);
    			if (g_pfnEDMACallback[4][0] != 0)    		
	    			(*g_pfnEDMACallback[4][0])(0);    			
			}    			
    		else 
    		{
    			if (u32IntStatus & EDMABLKD_IF)
    			{
    				outp32(REG_PDMA_ISR4,EDMABLKD_IF);
	    			if (g_pfnEDMACallback[4][1] != 0)    			
		    			(*g_pfnEDMACallback[4][1])(0);	    				    			
				}	
	    		else if (u32IntStatus & EDMASG_IF)
	    		{
	    			outp32(REG_PDMA_ISR4,EDMASG_IF);
	    			if (g_pfnEDMACallback[4][3] != 0)
		    			(*g_pfnEDMACallback[4][3])(0);	    				    		
	    		}						
    			else
    			{
	    			u32WraparoundStatus = inp32(REG_PDMA_ISR4) & 0x0F00;
	    			if (u32WraparoundStatus)
	    			{
		    			if (u32WraparoundStatus & 0x0200)
		    				u32WraparoundStatus = 0x0200;
		    			else if (u32WraparoundStatus & 0x0400)
		    				u32WraparoundStatus = 0x0400;
		    			else  if (u32WraparoundStatus & 0x0800)
		    				u32WraparoundStatus = 0x0800;
		    			else		   
		    				u32WraparoundStatus = 0x0100; 	

					outp32(REG_PDMA_ISR4,u32WraparoundStatus);				
		    			if (g_pfnEDMACallback[4][2] != 0)		    						
			    			(*g_pfnEDMACallback[4][2])(u32WraparoundStatus); 		    					    			   			
	    			}
				}	    			
    		}     	
    	}		
	}
}

// Install Call Back Function for Channelx & Interrupt source
ERRCODE  
DrvEDMA_InstallCallBack(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
    E_DRVEDMA_INT_ENABLE eIntSource,
	PFN_DRVEDMA_CALLBACK pfncallback,    
	PFN_DRVEDMA_CALLBACK *pfnOldcallback  	
)
{
    UINT32 index;
          
    if (eChannel > MAX_CHANNEL_NUM)
        return E_DRVEDMA_FALSE_INPUT;
            
    if ((eIntSource !=1) && (eIntSource !=2) && (eIntSource !=4) && (eIntSource !=8)) 
        return E_DRVEDMA_FALSE_INPUT;
/*	
	if (*pfnOldcallback != 0)
		*pfnOldcallback = g_pfnEDMACallback[eChannel][eIntSource>>1];
	///g_pfnEDMACallback[eChannel][eIntSource>>1] = pfncallback;
	for(i=0; i<4; i++)
	{
		if((eIntSource>>i) & 0x01)
			g_pfnEDMACallback[eChannel][i] = pfncallback;
	}
*/	
	for (index=0; index<4; index++)
		if ((1<<index) == eIntSource)
			break;
	if (pfnOldcallback != NULL)
		*pfnOldcallback = g_pfnEDMACallback[eChannel][index];
	g_pfnEDMACallback[eChannel][index] = pfncallback;
	sysInstallISR(IRQ_LEVEL_7, IRQ_EDMA, (PVOID)DrvEDMA_ISR);
	sysSetInterruptType(IRQ_EDMA, HIGH_LEVEL_SENSITIVE);
	sysEnableInterrupt(IRQ_EDMA);	
	    
    return E_SUCCESS;      
}

// Get Information about Scatter Gather Descript Table size and Max Transfer Size per Descript Table  
void DrvEDMA_GetScatterGatherInfo(
	UINT32 *pu32TblSize,
	UINT32 *pu32MaxTransferBytePerTbl
)
{
	*pu32TblSize = sizeof(S_DRVEDMA_DESCRIPT_FORMAT);
	*pu32MaxTransferBytePerTbl = MAX_GS_TRANSFER_SIZE;
	
	return;
}

// Set Scatter Gather Descript Format Table Start Address
void DrvEDMA_SetScatterGatherTblStartAddr(
    E_DRVEDMA_CHANNEL_INDEX eChannel, 
	UINT32	u32TblStartAddr
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_SGAR + eChannel * 0x100;	
    outp32(u32SFR, u32TblStartAddr);
}	

// Enable Scatter Gather Function  
void DrvEDMA_EnableScatterGather(
    E_DRVEDMA_CHANNEL_INDEX eChannel 
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;	
    outp32(u32SFR, inp32(u32SFR)| EDMASG_EN);
}

// Disable Scatter Gather Function
void DrvEDMA_DisableScatterGather(
    E_DRVEDMA_CHANNEL_INDEX eChannel 
)
{
    UINT32 u32SFR;
    
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;	
    outp32(u32SFR, inp32(u32SFR) & ~EDMASG_EN);
}

// Set Descript Format for Scatter Gather
// This function only support Order =0 
ERRCODE 
DrvEDMA_SetScatterGatherSetting(
    E_DRVEDMA_CHANNEL_INDEX eChannel,
	UINT32 u32SGTblStartAddr,
	S_DRVEDMA_DESCRIPT_SETTING* psDescript
)
{
    UINT32 u32SFR, u32Value;     
	S_DRVEDMA_DESCRIPT_FORMAT *psSGFmt;
	UINT32 u32TranferByte=0;
	UINT32 u32TotalByteCount;
	UINT32 u32NewSrcAddr, u32NewDestAddr, u32AddSrcAddr, u32AddDestAddr;	
	E_DRVEDMA_COLOR_FORMAT eSrcFormat, eDestFormat;
	UINT32 u32MaxTxfBytePerTbl;	
	
	if ((int)psDescript->u32TransferByteCount <=0)
        return E_DRVEDMA_FALSE_INPUT;
	
	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
        
    eSrcFormat = (E_DRVEDMA_COLOR_FORMAT)((inp32(u32SFR) & SOUR_FORMAT) >> 24);
    eDestFormat = (E_DRVEDMA_COLOR_FORMAT)((inp32(u32SFR) & DEST_FORMAT) >> 16);        	
        
    u32SFR = REG_VDMA_CSR + eChannel * 0x100;
    u32Value = inp32(u32SFR);         
    u32Value = (u32Value & ~SAD_SEL) | (psDescript->eSrcDirection << SOURCE_DIRECTION_BIT);    
    u32Value = (u32Value & ~DAD_SEL) | (psDescript->eDestDirection << DESTINATION_DIRECTION_BIT);
    outp32(u32SFR,u32Value); 
            
	DrvEDMA_EnableScatterGather(eChannel);
	DrvEDMA_SetScatterGatherTblStartAddr(eChannel, u32SGTblStartAddr);
	psSGFmt = (S_DRVEDMA_DESCRIPT_FORMAT *)u32SGTblStartAddr;
	u32TotalByteCount = psDescript->u32TransferByteCount;
	u32NewSrcAddr = psDescript->u32SourceAddr;
	u32NewDestAddr = psDescript->u32DestAddr;	
	u32AddSrcAddr=0;
	u32AddDestAddr=0;	
	
	// Update TransferByteCount for stride mode	
	u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
	
	if ((psDescript->u32Stride) && (inp32(u32SFR) & STRIDE_EN))
	    u32MaxTxfBytePerTbl = MAX_GS_TRANSFER_SIZE / psDescript->u32Stride * psDescript->u32Stride;	
	else    
	    u32MaxTxfBytePerTbl = MAX_GS_TRANSFER_SIZE;
	
    do {
		// Set Phsyical Source Address and Destination Address
		// Set WrapAround and Fixed Direction in Scatter Gather as Fixed Direction    
		if (psDescript->eSrcDirection == eDRVEDMA_DIRECTION_INCREMENTED)
		{
    	    psSGFmt->u32SourceAddr = u32NewSrcAddr + u32AddSrcAddr;
    	    u32NewSrcAddr += u32AddSrcAddr;
		} 
    	else 
    	{
    	    if (psDescript->eSrcDirection == eDRVEDMA_DIRECTION_DECREMENTED)
    	    {
    	        psSGFmt->u32SourceAddr	= u32NewSrcAddr - u32AddSrcAddr;
	    	    u32NewSrcAddr -= u32AddSrcAddr;    	        
    	    }  
    	    else    
	    	    psSGFmt->u32SourceAddr	= u32NewSrcAddr;    	        
    	}
    	
    	if (psDescript->eDestDirection == eDRVEDMA_DIRECTION_INCREMENTED)
    	{
    	    psSGFmt->u32DestAddr = u32NewDestAddr + u32AddDestAddr;
    	    u32NewDestAddr += u32AddDestAddr;
		} 
    	else
    	{
    	    if (psDescript->eDestDirection == eDRVEDMA_DIRECTION_DECREMENTED)
    	    {
    	        psSGFmt->u32DestAddr = u32NewDestAddr - u32AddDestAddr; 
    	         u32NewDestAddr -= u32AddDestAddr;   	    
			}     	    
			else    
				psSGFmt->u32DestAddr = u32NewDestAddr;    	        
    	}
    	
    	if (u32TotalByteCount >= u32MaxTxfBytePerTbl)
    	{
	    	u32TranferByte = u32MaxTxfBytePerTbl;
	    	u32TotalByteCount -= u32MaxTxfBytePerTbl;	    	
		}	    	
	    else
	    {
	    	u32TranferByte = u32TotalByteCount;	  
	    	u32TotalByteCount = 0;  	
		}	  
		
		// Src/Dest format must be set before DrvEDMA_SetScatterGatterSetting() function to calcuate the next address
		u32SFR = REG_VDMA_CTCSR + eChannel * 0x100;
		if ((psDescript->u32Stride) && (inp32(u32SFR) & STRIDE_EN) && (eChannel ==0))
		{   // Calculate the next Src/Destination for Stride mode
		    u32AddSrcAddr = (u32TranferByte / psDescript->u32Stride) * (psDescript->u32SrcOffset + psDescript->u32Stride) + 
		                    u32TranferByte % psDescript->u32Stride;

	    	if (eSrcFormat == eDRVEDMA_RGB888)
	    	{
	    		if (eDestFormat== eDRVEDMA_RGB888)
	    			u32AddDestAddr = (u32TranferByte / psDescript->u32Stride) * (psDescript->u32DestOffset + psDescript->u32Stride) + 
		                    u32TranferByte % psDescript->u32Stride;	
	    		else	
		    		u32AddDestAddr = (u32TranferByte / psDescript->u32Stride) * (psDescript->u32DestOffset + psDescript->u32Stride/2) + 
		                    u32TranferByte % psDescript->u32Stride/2;
	    	}
	    	else
	    	{
	    		if (eDestFormat== eDRVEDMA_RGB888)
	    			u32AddDestAddr = (u32TranferByte / psDescript->u32Stride) * (psDescript->u32DestOffset + psDescript->u32Stride*2) + 
		                    u32TranferByte % psDescript->u32Stride*2;
	    		else	
		    		u32AddDestAddr = (u32TranferByte / psDescript->u32Stride) * (psDescript->u32DestOffset + psDescript->u32Stride) + 
		                    u32TranferByte % psDescript->u32Stride;	    	
	    	}
              
		}
		else
		{   // Calculate the next Src/Destination for non-Stride mode
		    u32AddSrcAddr = u32TranferByte;
		    
		    if (eChannel ==0)
		    {
		    	if (eSrcFormat == eDRVEDMA_RGB888)
		    	{
		    		if (eDestFormat== eDRVEDMA_RGB888)
		    			u32AddDestAddr = u32TranferByte;
		    		else	
			    		u32AddDestAddr = u32TranferByte/2;
		    	}
		    	else
		    	{
		    		if (eDestFormat== eDRVEDMA_RGB888)
		    			u32AddDestAddr = u32TranferByte*2;
		    		else	
			    		u32AddDestAddr =u32TranferByte;    	
		    	}
	    	}
	    	else
	    	{
		    	u32AddDestAddr = u32TranferByte;
	    	}		    
		}		  
		
		// Set Stride Transfer Byte Count & Byte Count	
    	psSGFmt->u32StrideAndByteCount   = ((psDescript->u32Stride & 0x7FFF) << 17) | u32TranferByte;
    	
    	// Set Source Offset Byte Length and Destination Offset Byte Length
  	   	psSGFmt->u32Offset = ((psDescript->u32DestOffset & 0x7FFF) << 16) |
  	   						  (psDescript->u32SrcOffset & 0x7FFF);      	
  	   						  
		// Set EOT for last Descript Format  	   						  
		if (u32TotalByteCount == 0)
			psSGFmt->u32Offset |= 0x80000000;
			
		// Set Next Scatter Gether Table Address	
		psSGFmt->u32NextSGTblAddr = (UINT32)(psSGFmt+1); 	
		
		psSGFmt++;		
  	   	
	} while(u32TotalByteCount > 0);    
	
	return E_SUCCESS;  
}