#include "DMAScheduler.h"
#include "hardware.h"
#include <csl_edma2.h>
#include <csl_intc.h>
#include <csl_intcAux.h>

volatile int DMAIdle = 1;					// DMAIdle 를 1로 초기화

#define QDMA_QUEUE_SIZE				128
#define QDMA_QUEUE_MASK				127

unsigned int QDMA_EDMATCCNumber;
unsigned int DMAJobCfg[DMA_CFG_COUNT];
int QDMA_Queue_Start, QDMA_Queue_End;

#pragma DATA_SECTION(QDMARequestQueue, ".sdram");
TQDMARequestUnit QDMARequestQueue[QDMA_QUEUE_SIZE];

Uint32 DMA_Default_Opt[DMA_ESIZE_COUNT];
Uint32 QDMA_Default_Opt;


CSL_IntcObj                 intcEDMAObj;
CSL_IntcHandle              hIntcEDMA;
CSL_IntcEventHandlerRecord  EDMAEventRecord;
CSL_IntcParam              EDMAVectId;
CSL_Edma3ChannelObj         QdmachObj;
CSL_Edma3ChannelHandle      QdmaChannel;
CSL_Edma3ParamHandle 	   hQdmaParam;


CSL_Edma3ChannelAttr        qdmachAttr;

extern CSL_Edma3Handle 				hNFAEdma;
CSL_Edma3CmdIntr            QDMA_RegionIntr;

extern Interrupt_status int_status[15];
void QDMA_TransferComplete(void);
void QDMA_SetTrigger(void);


void InitDMAScheduler()
{
	CSL_Status status;
	CSL_Edma3CmdDrae            qdmaregionAccess;
	CSL_Edma3CmdQrae            qraeSetup;
	QDMA_EDMATCCNumber = (CSL_EDMA3_QCHA_3-64);


    

	if(QDMA_EDMATCCNumber < 32)
	{
		qdmaregionAccess.region = CSL_EDMA3_REGION_2;
		qdmaregionAccess.drae =  (0x1 << QDMA_EDMATCCNumber);
		qdmaregionAccess.draeh =  0x0;
	}
	else
	{
		qdmaregionAccess.region = CSL_EDMA3_REGION_2;
		qdmaregionAccess.drae =  0x0;
		qdmaregionAccess.draeh =  (0x1 << (QDMA_EDMATCCNumber - 32));
	}
    	status = CSL_edma3HwControl(hNFAEdma,CSL_EDMA3_CMD_DMAREGION_ENABLE, \
                                &qdmaregionAccess);   

	qraeSetup.region = CSL_EDMA3_REGION_2 ;
	qraeSetup.qrae = (0x1 << QDMA_EDMATCCNumber);
	CSL_edma3HwControl(hNFAEdma,CSL_EDMA3_CMD_QDMAREGION_ENABLE,&qraeSetup);

	if(QDMA_EDMATCCNumber < 32)
	{
		QDMA_RegionIntr.region = CSL_EDMA3_REGION_2;
		QDMA_RegionIntr.intr = (0x1 << QDMA_EDMATCCNumber);
		QDMA_RegionIntr.intrh = 0x0;
	}
	else
	{
		QDMA_RegionIntr.region = CSL_EDMA3_REGION_2;
		QDMA_RegionIntr.intr = 0x0;
		QDMA_RegionIntr.intrh = (0x1 << (QDMA_EDMATCCNumber - 32));
	}

	CSL_edma3HwControl(hNFAEdma ,CSL_EDMA3_CMD_INTR_ENABLE,&QDMA_RegionIntr); 

	qdmachAttr.regionNum = CSL_EDMA3_REGION_2;
	qdmachAttr.chaNum    = CSL_EDMA3_QCHA_3;
	QdmaChannel = CSL_edma3ChannelOpen(&QdmachObj, CSL_EDMA3, &qdmachAttr, &status); 	
	status = CSL_edma3HwChannelControl(QdmaChannel,CSL_EDMA3_CMD_CHANNEL_ENABLE, NULL);
	hQdmaParam = CSL_edma3GetParamHandle(QdmaChannel, CSL_EDMA3_QCHA_3, &status);


	QDMA_Default_Opt = 	CSL_EDMA3_OPT_MAKE( CSL_EDMA3_ITCCH_DIS, CSL_EDMA3_TCCH_DIS, CSL_EDMA3_ITCINT_DIS, CSL_EDMA3_TCINT_EN, QDMA_EDMATCCNumber, CSL_EDMA3_TCC_NORMAL, CSL_EDMA3_FIFOWIDTH_NONE, CSL_EDMA3_STATIC_DIS, CSL_EDMA3_SYNC_A, CSL_EDMA3_ADDRMODE_INCR, CSL_EDMA3_ADDRMODE_INCR);

	
	QDMA_Queue_Start = QDMA_Queue_End = 0;
	DMAIdle = 1;					// DMAIdle 를 1로 초기화

	// make application selectable qdma opt
	DMA_Default_Opt[DMA_ESIZE_8BIT] = 0x50100001 |		//0101 0000 0001 0000 0000 0000 0000 0001
		((QDMA_EDMATCCNumber & EDMA2_TCC_MASK) << EDMA2_TCC_SHIFT ) |
		((QDMA_EDMATCCNumber & EDMA2_TCCM_MASK) << EDMA2_TCCM_SHIFT );

	DMA_Default_Opt[DMA_ESIZE_16BIT] = 0x48100001 |			//0100 1000 0001 0000 0000 0000 0000 0001
		((QDMA_EDMATCCNumber & EDMA2_TCC_MASK) << EDMA2_TCC_SHIFT ) |
		((QDMA_EDMATCCNumber & EDMA2_TCCM_MASK) << EDMA2_TCCM_SHIFT );

	DMA_Default_Opt[DMA_ESIZE_32BIT] = 0x40100001 |			//0100 0000 0001 0000 0000 0000 0000 0001
		((QDMA_EDMATCCNumber & EDMA2_TCC_MASK) << EDMA2_TCC_SHIFT ) |
		((QDMA_EDMATCCNumber & EDMA2_TCCM_MASK) << EDMA2_TCCM_SHIFT );

	DMAJobCfg[EDMA_CFG_NO_NONE_NO_NONE] = 0x00000000; 		//0000 0000 0000 0000 0000 0000 0000 0000
	DMAJobCfg[EDMA_CFG_NO_NONE_NO_INC] = 0x00200000;		//0000 0000 0010 0000 0000 0000 0000 0000
	DMAJobCfg[EDMA_CFG_NO_INC_NO_INC] = 0x01200000;			//0000 0001 0010 0000 0000 0000 0000 0000  
	DMAJobCfg[EDMA_CFG_YES_INC_NO_INC] = 0x05200000;			//0000 0101 0010 0000 0000 0000 0000 0000
	DMAJobCfg[EDMA_CFG_NO_INC_NO_NONE] = 0x01000000;			//0000 0001 0000 0000 0000 0000 0000 0000
	DMAJobCfg[EDMA_CFG_NO_INC_NO_DEC] = 0x01400000;		//0000 0001 0100 0000 0000 0000 0000 0000
	DMAJobCfg[EDMA_CFG_NO_IDX_NO_INC] = 0x03200000;		//0000 0011 0010 0000 0000 0000 0000 0000

	QDMA_Queue_Start = QDMA_Queue_End = 0;
	DMAIdle = 1;					// DMAIdle 를 1로 초기화
}

int QDMA_QueueFull()
{
    return (((QDMA_Queue_End + 1) & QDMA_QUEUE_MASK) == QDMA_Queue_Start);
}

void QDMA_Request(TQDMARequestUnit *requestUnit)	// table 의 validation 은 middle level 에서 처리한다.
{
	register unsigned int isFirstUnit = FALSE;
	CSL_IntcGlobalEnableState 	state;
	
	if (requestUnit->Cnt == 0)
	{
		if (requestUnit->Fin) *(requestUnit->Fin) = DMA_FINISH_VALUE;
		return;
	}
	
	while (QDMA_QueueFull());	// wait until DMA queue has a space.
	CSL_intcGlobalDisable(&state);	
	if (QDMA_Queue_Start == QDMA_Queue_End)
	{
		isFirstUnit = TRUE;
	}
	else
	{
		isFirstUnit = FALSE;
	}

	// add queue
	QDMARequestQueue[QDMA_Queue_End].Opt = requestUnit->Opt;
	QDMARequestQueue[QDMA_Queue_End].Src = requestUnit->Src;
	QDMARequestQueue[QDMA_Queue_End].Cnt = requestUnit->Cnt;
	QDMARequestQueue[QDMA_Queue_End].Dst = requestUnit->Dst;
	QDMARequestQueue[QDMA_Queue_End].Idx = requestUnit->Idx;
	QDMARequestQueue[QDMA_Queue_End].bytesize = 0;
	QDMARequestQueue[QDMA_Queue_End].reduction = 0;	
	QDMARequestQueue[QDMA_Queue_End].Fin = requestUnit->Fin;
	
	QDMA_Queue_End = (QDMA_Queue_End + 1) & QDMA_QUEUE_MASK;

	if (DMAIdle)	
	{
    		QDMA_SetTrigger();
	}	
	CSL_intcGlobalEnable(&state);

}

unsigned int MakeDMAOpt(unsigned int eSize, unsigned int cfg)
{
	return (DMA_Default_Opt[eSize] | DMAJobCfg[cfg]);
}

void QDMA_TransferComplete(void)		// called from isr
{	 
	CSL_IntcGlobalEnableState 	state;
	CSL_intcGlobalDisable(&state);	
	hNFAEdma->regs->QEECR ^= (0x00000001 << QDMA_EDMATCCNumber);
	hNFAEdma->regs->QSECR ^= (0x00000001 << QDMA_EDMATCCNumber);

	CSL_edma3HwControl ((CSL_Edma3Handle)&edmaObj,CSL_EDMA3_CMD_INTRPEND_CLEAR, &QDMA_RegionIntr);

	if (!DMAIdle)
	{
		if (QDMARequestQueue[QDMA_Queue_Start].Fin)
		{
			*(QDMARequestQueue[QDMA_Queue_Start].Fin) = DMA_FINISH_VALUE;		// notice DMA task was done
		}
		QDMA_Queue_Start = (QDMA_Queue_Start + 1) & QDMA_QUEUE_MASK;


		if(QDMA_Queue_Start != QDMA_Queue_End)	// if next QDMA task exists
		{
			QDMA_SetTrigger();
		}
		else{
			DMAIdle = TRUE; 
		}
	}
	CSL_intcGlobalEnable(&state);
}

void QDMA_SetTrigger(void)
{
	CSL_Edma3ParamSetup        QdmaSetup;
	CSL_edma3HwChannelControl(QdmaChannel,CSL_EDMA3_CMD_CHANNEL_ENABLE,NULL);

	CSL_edma3HwControl (hNFAEdma,CSL_EDMA3_CMD_INTR_DISABLE, &QDMA_RegionIntr);		
	DMAIdle = 0;

	if(QDMARequestQueue[QDMA_Queue_Start].bytesize) 
	{
		QdmaSetup.option = CSL_EDMA3_OPT_MAKE( CSL_EDMA3_ITCCH_DIS, CSL_EDMA3_TCCH_DIS, CSL_EDMA3_ITCINT_DIS, CSL_EDMA3_TCINT_EN, QDMA_EDMATCCNumber, CSL_EDMA3_TCC_NORMAL, CSL_EDMA3_FIFOWIDTH_NONE, CSL_EDMA3_STATIC_DIS, CSL_EDMA3_SYNC_AB, CSL_EDMA3_ADDRMODE_INCR, CSL_EDMA3_ADDRMODE_INCR);
		QdmaSetup.srcAddr =QDMARequestQueue[QDMA_Queue_Start].Src;	
		QdmaSetup.dstAddr =QDMARequestQueue[QDMA_Queue_Start].Dst;
		QdmaSetup.aCntbCnt = CSL_EDMA3_CNT_MAKE(QDMARequestQueue[QDMA_Queue_Start].bytesize, QDMARequestQueue[QDMA_Queue_Start].Cnt/QDMARequestQueue[QDMA_Queue_Start].bytesize/QDMARequestQueue[QDMA_Queue_Start].reduction);  
		QdmaSetup.srcDstBidx  = CSL_EDMA3_BIDX_MAKE(QDMARequestQueue[QDMA_Queue_Start].reduction*QDMARequestQueue[QDMA_Queue_Start].bytesize, QDMARequestQueue[QDMA_Queue_Start].bytesize);
		QdmaSetup.linkBcntrld = CSL_EDMA3_LINKBCNTRLD_MAKE(CSL_EDMA3_LINK_NULL,0);  
		QdmaSetup.srcDstCidx  = CSL_EDMA3_CIDX_MAKE(0,0);
		QdmaSetup.cCnt = 1;		
	}
	else
	{
		QdmaSetup.option = QDMA_Default_Opt;
		QdmaSetup.srcAddr =QDMARequestQueue[QDMA_Queue_Start].Src;	
		QdmaSetup.dstAddr =QDMARequestQueue[QDMA_Queue_Start].Dst;
		QdmaSetup.aCntbCnt = CSL_EDMA3_CNT_MAKE(QDMARequestQueue[QDMA_Queue_Start].Cnt*4,1);  
		QdmaSetup.srcDstBidx  = CSL_EDMA3_BIDX_MAKE(1,1);
		QdmaSetup.linkBcntrld = CSL_EDMA3_LINKBCNTRLD_MAKE(CSL_EDMA3_LINK_NULL,0);  
		QdmaSetup.srcDstCidx  = CSL_EDMA3_CIDX_MAKE(0,0);
		QdmaSetup.cCnt = 1;
	}
	CSL_edma3ParamSetup(hQdmaParam, &QdmaSetup);
	CSL_edma3ParamWriteWord(hQdmaParam,7,QdmaSetup.cCnt);
	CSL_edma3HwControl (hNFAEdma,CSL_EDMA3_CMD_INTR_ENABLE, &QDMA_RegionIntr);	
}

void QDMA_Request_ScaleConversion(TQDMARequestUnit *requestUnit, int reduction, int byteSize)
{
	register unsigned int isFirstUnit = FALSE;
	CSL_IntcGlobalEnableState 	state;
	
	if (requestUnit->Cnt == 0)
	{
		if (requestUnit->Fin) *(requestUnit->Fin) = DMA_FINISH_VALUE;
		return;
	}
	
	while (QDMA_QueueFull());	// wait until DMA queue has a space.
	CSL_intcGlobalDisable(&state);	
	if (QDMA_Queue_Start == QDMA_Queue_End)
	{
		isFirstUnit = TRUE;
	}
	else
	{
		isFirstUnit = FALSE;
	}
	// add queue
	QDMARequestQueue[QDMA_Queue_End].Opt = requestUnit->Opt;
	QDMARequestQueue[QDMA_Queue_End].Src = requestUnit->Src;
	QDMARequestQueue[QDMA_Queue_End].Cnt = requestUnit->Cnt;
	QDMARequestQueue[QDMA_Queue_End].Dst = requestUnit->Dst;
	QDMARequestQueue[QDMA_Queue_End].Idx = requestUnit->Idx;
	QDMARequestQueue[QDMA_Queue_End].bytesize = byteSize;
	QDMARequestQueue[QDMA_Queue_End].reduction = reduction;
	QDMARequestQueue[QDMA_Queue_End].Fin = requestUnit->Fin;
	QDMA_Queue_End = (QDMA_Queue_End + 1) & QDMA_QUEUE_MASK;

	if (DMAIdle)	
	{
    		QDMA_SetTrigger();
	}	
	CSL_intcGlobalEnable(&state);

}

void QDMAByteConversion(unsigned int srcAddr, unsigned int dstAddr, unsigned int srcLength)
{
	CSL_Edma3ParamSetup        QdmaSetup;
	CSL_edma3HwChannelControl(QdmaChannel,CSL_EDMA3_CMD_CHANNEL_ENABLE,NULL);

	CSL_edma3HwControl (hNFAEdma,CSL_EDMA3_CMD_INTR_DISABLE, &QDMA_RegionIntr);		
	DMAIdle = 0;
	QdmaSetup.option = 	CSL_EDMA3_OPT_MAKE( CSL_EDMA3_ITCCH_DIS, CSL_EDMA3_TCCH_DIS, CSL_EDMA3_ITCINT_DIS, CSL_EDMA3_TCINT_EN, QDMA_EDMATCCNumber, CSL_EDMA3_TCC_NORMAL, CSL_EDMA3_FIFOWIDTH_NONE, CSL_EDMA3_STATIC_DIS, CSL_EDMA3_SYNC_AB, CSL_EDMA3_ADDRMODE_INCR, CSL_EDMA3_ADDRMODE_INCR);
	QdmaSetup.srcAddr = srcAddr;	
	QdmaSetup.dstAddr = dstAddr;
	QdmaSetup.aCntbCnt = CSL_EDMA3_CNT_MAKE(1,srcLength/2);  
	QdmaSetup.srcDstBidx  = CSL_EDMA3_BIDX_MAKE(2,1);
	QdmaSetup.linkBcntrld = CSL_EDMA3_LINKBCNTRLD_MAKE(CSL_EDMA3_LINK_NULL,0);  
	QdmaSetup.srcDstCidx  = CSL_EDMA3_CIDX_MAKE(0,0);
	QdmaSetup.cCnt = 1;
	CSL_edma3ParamSetup(hQdmaParam, &QdmaSetup);
	CSL_edma3ParamWriteWord(hQdmaParam,7,QdmaSetup.cCnt);
	CSL_edma3HwControl (hNFAEdma,CSL_EDMA3_CMD_INTR_ENABLE, &QDMA_RegionIntr);	
}