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

int jobTcc;
unsigned int DMAJobCfg[DMA_CFG_COUNT];

TDMATableUnit *CurrentDMAPtr = NULL;	// CurrentDMAPtr을 NULL로 초기화
volatile int DMAIdle = 1;					// DMAIdle 를 1로 초기화

static int jobEmifStart, jobEmifEnd;
static TEmifJob jobEmif[EMIF_JOB_SIZE];

static int jobDMAStart, jobDMAEnd;
static TDMAJobUnit jobDMAQueue[DMA_JOB_SIZE];

unsigned int dummy;

Uint32 DMA_Default_Opt32, DMA_Default_Opt16, DMA_Default_Opt8;

#define MAX_EDMA_HANDLER_COUNT	16

typedef void (* TEDMAEventHandler) (void);

TEDMAEventHandler EDMAEventHandler[MAX_EDMA_HANDLER_COUNT];
CSL_Edma3Handle     hDma;

struct
{
	int HandlerCount;
} EDMAInterruptHandler;

void DMA_TransferComplete(void);

void DefaultEDMAEventHandler(void)
{
	return;
}

void InitDMAScheduler()
{
	int handlerIndex;

//	IRQ_reset(IRQ_EVT_EDMAINT);

	/* clear Parameter RAM of EDMA */
	EDMA_clearPram(0x00000000);

//    jobTcc = EDMA_intAlloc(-1);
/*
	DMA_Default_Opt32 = (Uint32)
       	EDMA_FMK(OPT,PRI,EDMA_OPT_PRI_MEDIUM) |
        EDMA_FMK(OPT,ESIZE,EDMA_OPT_ESIZE_32BIT) |
       	EDMA_FMK(OPT,TCINT,EDMA_OPT_TCINT_YES) |
        EDMA_FMK(OPT,TCC,EDMA_OPT_TCC_OF(jobTcc)) |
   	    EDMA_FMK(OPT,TCCM,EDMA_OPT_TCCM_DEFAULT) |
       	EDMA_FMK(OPT,ATCINT,EDMA_OPT_ATCINT_NO) |
        EDMA_FMK(OPT,ATCC,EDMA_OPT_ATCC_DEFAULT) |
   	    EDMA_FMK(OPT,PDTS,EDMA_OPT_PDTS_DISABLE) |
       	EDMA_FMK(OPT,PDTD,EDMA_OPT_PDTD_DISABLE) |
        EDMA_FMK(OPT,LINK,EDMA_OPT_LINK_NO) |
   	    EDMA_FMK(OPT,FS,EDMA_OPT_FS_YES);

	DMA_Default_Opt16 = (Uint32)
       	EDMA_FMK(OPT,PRI,EDMA_OPT_PRI_MEDIUM) |
        EDMA_FMK(OPT,ESIZE,EDMA_OPT_ESIZE_16BIT) |
       	EDMA_FMK(OPT,TCINT,EDMA_OPT_TCINT_YES) |
        EDMA_FMK(OPT,TCC,EDMA_OPT_TCC_OF(jobTcc)) |
   	    EDMA_FMK(OPT,TCCM,EDMA_OPT_TCCM_DEFAULT) |
       	EDMA_FMK(OPT,ATCINT,EDMA_OPT_ATCINT_NO) |
        EDMA_FMK(OPT,ATCC,EDMA_OPT_ATCC_DEFAULT) |
   	    EDMA_FMK(OPT,PDTS,EDMA_OPT_PDTS_DISABLE) |
       	EDMA_FMK(OPT,PDTD,EDMA_OPT_PDTD_DISABLE) |
        EDMA_FMK(OPT,LINK,EDMA_OPT_LINK_NO) |
   	    EDMA_FMK(OPT,FS,EDMA_OPT_FS_YES);		

	DMA_Default_Opt8 = (Uint32)
       	EDMA_FMK(OPT,PRI,EDMA_OPT_PRI_MEDIUM) |
        EDMA_FMK(OPT,ESIZE,EDMA_OPT_ESIZE_8BIT) |
       	EDMA_FMK(OPT,TCINT,EDMA_OPT_TCINT_YES) |
        EDMA_FMK(OPT,TCC,EDMA_OPT_TCC_OF(jobTcc)) |
   	    EDMA_FMK(OPT,TCCM,EDMA_OPT_TCCM_DEFAULT) |
       	EDMA_FMK(OPT,ATCINT,EDMA_OPT_ATCINT_NO) |
        EDMA_FMK(OPT,ATCC,EDMA_OPT_ATCC_DEFAULT) |
   	    EDMA_FMK(OPT,PDTS,EDMA_OPT_PDTS_DISABLE) |
       	EDMA_FMK(OPT,PDTD,EDMA_OPT_PDTD_DISABLE) |
        EDMA_FMK(OPT,LINK,EDMA_OPT_LINK_NO) |
   	    EDMA_FMK(OPT,FS,EDMA_OPT_FS_YES);		

	DMAJobCfg[EDMA_CFG_NO_NONE_NO_NONE] = (Uint32)
   	    EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_NO) |
       	EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_NONE) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
   	    EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_NONE);

	DMAJobCfg[EDMA_CFG_NO_NONE_NO_INC] = (Uint32)
   	    EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_NO) |
       	EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_NONE) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
   	    EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_INC);

	DMAJobCfg[EDMA_CFG_NO_INC_NO_INC] = (Uint32)
        EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_NO) |
        EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_INC) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
        EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_INC);
    
	DMAJobCfg[EDMA_CFG_YES_INC_NO_INC] = (Uint32)
   	    EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_YES) |
       	EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_INC) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
   	    EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_INC);
   	
	DMAJobCfg[EDMA_CFG_NO_INC_NO_NONE] = (Uint32)
   	    EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_NO) |
       	EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_INC) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
   	    EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_NONE);

	DMAJobCfg[EDMA_CFG_NO_INC_NO_DEC] = (Uint32)
        EDMA_FMK(OPT,2DS,EDMA_OPT_2DS_NO) |
        EDMA_FMK(OPT,SUM,EDMA_OPT_SUM_INC) |
        EDMA_FMK(OPT,2DD,EDMA_OPT_2DD_NO) |
        EDMA_FMK(OPT,DUM,EDMA_OPT_DUM_DEC);
*/
	EDMAInterruptHandler.HandlerCount = 0;
	for (handlerIndex = 0; handlerIndex < MAX_EDMA_HANDLER_COUNT; handlerIndex++)
	{
		EDMAEventHandler[handlerIndex] = DefaultEDMAEventHandler;
	}	

    // Enable EDMA channel interrupt to CPU (CIER)
	jobTcc = AllocEDMAInterruptHandler(DMA_TransferComplete);
    EDMA_intClear(jobTcc);
    EDMA_intEnable(jobTcc);

	CurrentDMAPtr = NULL;	// CurrentDMAPtr을 NULL으로 초기화
	DMAIdle = 1;					// DMAIdle 를 1로 초기화

	jobDMAStart = jobDMAEnd = 0;
	jobEmifStart = jobEmifEnd = 0;	

    // Enable EDMA-CPU interrupt (+clear any previous)
//    IRQ_enable(IRQ_EVT_EDMAINT);
}

int AllocEDMAInterruptHandler(TEDMAEventHandler edmaEventHandler)
{
	int newHandlerIndex;

	newHandlerIndex = EDMAInterruptHandler.HandlerCount;
	EDMAEventHandler[newHandlerIndex] = edmaEventHandler;
	EDMAInterruptHandler.HandlerCount++;

    EDMA_intClear(newHandlerIndex);
    EDMA_intEnable(newHandlerIndex);
	return newHandlerIndex;
}

// DMA job processing (low level)
int DMAJob_IsFull()
{
    return (((jobDMAEnd + 1) & DMA_JOB_MASK) == jobDMAStart);
}

void DMAJobAdd(TDMATableUnit *table, int cellCount)	// table 의 validation 은 middle level 에서 처리한다.
{
	Uint32 gie;

	while (DMAJob_IsFull());	// wait until DMA queue has a space.

	//gie = IRQ_globalDisable();

	// add queue
	jobDMAQueue[jobDMAEnd].DMATable = table;
	jobDMAQueue[jobDMAEnd].CellCount = cellCount;
	jobDMAQueue[jobDMAEnd].CurrentIndex = 0;	
	jobDMAEnd = (jobDMAEnd + 1) & DMA_JOB_MASK;

	if (DMAIdle)	// if DMA Idle, Start first job
	{
		QDMA_SRC = jobDMAQueue[jobDMAStart].DMATable[0].Src;
		QDMA_CNT = jobDMAQueue[jobDMAStart].DMATable[0].Cnt;
		QDMA_DST = jobDMAQueue[jobDMAStart].DMATable[0].Dst;
		QDMA_IDX = jobDMAQueue[jobDMAStart].DMATable[0].Idx;
		S_QDMA_OPT = jobDMAQueue[jobDMAStart].DMATable[0].Opt;
		CurrentDMAPtr = &(jobDMAQueue[jobDMAStart].DMATable[0]);
		jobDMAQueue[jobDMAStart].CurrentIndex++;
		if (jobDMAQueue[jobDMAStart].CurrentIndex == jobDMAQueue[jobDMAStart].CellCount)
		{
			jobDMAStart = (jobDMAStart + 1) & DMA_JOB_MASK;
		}
		DMAIdle = 0;
	}	
	
	//IRQ_globalRestore(gie);
}

// EDMA interrupt dispatcher
#pragma FUNC_EXT_CALLED(edmaIsr)
interrupt void edmaIsr()
{
	register int eventIndex;
	CSL_Edma3Context            context;
	CSL_Status                  status;
	CSL_Edma3Obj                edmaObj;
	
	hDma = CSL_edma3Open(&edmaObj,CSL_EDMA3,NULL,&status);
	
	for (eventIndex = 0; eventIndex < EDMAInterruptHandler.HandlerCount; eventIndex++)
	{	
		if ((CSL_FEXT(hDma->regs->IER,EDMA3CC_IER_REG) & (0x00000001 << eventIndex)) && (CSL_FEXT(hDma->regs->IPR,EDMA3CC_IPR_REG) & (0x00000001 << eventIndex)))
		{
			hDma->regs->ICR ^= (0x00000001 << eventIndex);
			EDMAEventHandler[eventIndex]();
		}
	}
}



int EmifJob_IsFull()
{
    return ((jobEmifEnd + 1) & EMIF_JOB_MASK) == jobEmifStart;
}

void EmifJob_Insert(Uint32 *src1, Uint32 *dst1, Uint32 *fin)
{
	Uint32 gie;

	while (EmifJob_IsFull());	// wait until emif queue has a space

	//gie = IRQ_globalDisable();
	if (DMAIdle) 
	{
		*dst1 = *src1;
		if (fin)
		{
			*fin = 1;
		}
	}
	else
	{
		jobEmif[jobEmifEnd].src1 = src1;
		jobEmif[jobEmifEnd].dst1 = dst1;
		jobEmif[jobEmifEnd].src2 = &dummy;
		jobEmif[jobEmifEnd].dst2 = &dummy;
		jobEmif[jobEmifEnd].finish = fin;
		jobEmifEnd = (jobEmifEnd + 1) & EMIF_JOB_MASK;	
	}	
	//IRQ_globalRestore(gie);
}

void EmifJob_Insert2(Uint32 *src1, Uint32 *dst1, Uint32 *src2, Uint32 *dst2, Uint32 *fin)
{
	Uint32 gie;

	while (EmifJob_IsFull());	// wait until emif queue has a space

	//gie = IRQ_globalDisable();
	if (DMAIdle) 
	{
		*dst1 = *src1;
		*dst2 = *src2;
		if (fin)
		{
			*fin = 1;
		}
	}
	else
	{
		jobEmif[jobEmifEnd].src1 = src1;
		jobEmif[jobEmifEnd].dst1 = dst1;
		jobEmif[jobEmifEnd].src2 = src2;
		jobEmif[jobEmifEnd].dst2 = dst2;
		jobEmif[jobEmifEnd].finish = fin;
		jobEmifEnd = (jobEmifEnd + 1) & EMIF_JOB_MASK;	
	}	
	//IRQ_globalRestore(gie);
}



unsigned int MakeDMAOpt(unsigned int eSize, unsigned int cfg)
{
	if (eSize == 8)
	{
		return (DMA_Default_Opt8 | DMAJobCfg[cfg]);
	}
	else if (eSize == 16)
	{
		return (DMA_Default_Opt16 | DMAJobCfg[cfg]);
	}
	else if (eSize == 32)
	{
		return (DMA_Default_Opt32 | DMAJobCfg[cfg]);
	}
	else
	{
		return (DMA_Default_Opt8 | DMAJobCfg[cfg]);
	}
}


void DMA_TransferComplete(void)
{
	if (CurrentDMAPtr->Fin)
	{
		*(CurrentDMAPtr->Fin) = DMA_FINISH_VALUE;		// notice DMA task was done
	}

	if (jobEmifStart != jobEmifEnd) 
	{
		*(jobEmif[jobEmifStart].dst1) = *(jobEmif[jobEmifStart].src1);
		*(jobEmif[jobEmifStart].dst2) = *(jobEmif[jobEmifStart].src2);
		if (jobEmif[jobEmifStart].finish)
		{
			*(jobEmif[jobEmifStart].finish) = 1;
		}
		jobEmifStart = (jobEmifStart + 1) & EMIF_JOB_MASK;
	}	

	if (jobDMAStart != jobDMAEnd)	// job DMA exist
	{		
		QDMA_SRC = jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex].Src;
		QDMA_CNT = jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex].Cnt;
		QDMA_DST = jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex].Dst;
		QDMA_IDX = jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex].Idx;
		S_QDMA_OPT = jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex].Opt;
		CurrentDMAPtr = &(jobDMAQueue[jobDMAStart].DMATable[jobDMAQueue[jobDMAStart].CurrentIndex]);
		jobDMAQueue[jobDMAStart].CurrentIndex++;
		if (jobDMAQueue[jobDMAStart].CurrentIndex == jobDMAQueue[jobDMAStart].CellCount)
		{
			jobDMAStart = (jobDMAStart + 1) & DMA_JOB_MASK;
		}
	}
	else
	{
		DMAIdle = 1;
	}
}
