/*
 * cap_common.c
 * キャプチャサンプル共通処理
 * @history 2019_03_08  [SDK2.2] DOTCLK_LATCHよりもDOTCLK_CHANGEの使用を推奨 (#1950)
 * @history 2019_12_27  [SDK3.0] キャプチャユースケースでアナログキャプチャのときCMOS出力も選択可能に変更 (#1704)
 */

#include "cap_common.h"
#include "sample_common.h"
#include "com.h"
#include "../dsp/dsp_common.h"

#include "sys/spcprm.h"
#include "sys/sscprm.h"

#define MSEC_IN_SYSCLK	((396000000/2) / 1000)/* システムクロック単位での1msec */

int32_t gCAPErrCode;		/* エラーチェック用 */

static AG903_ViaMgrInputHandle		*VIA_Handle[4]	= {NULL};
static AG903_VidMgrInputHandle		*VID_In_Handle	= NULL;
static AG903_VidMgrOutputHandle		*VID_Out_Handle	= NULL;
static AG903_DSPMgrHandle			*DSP_Handle[2]	= {NULL};

static void *WindowAttributeAddr = NULL;

extern AG903_SPCPrmPllnParam	DSP_SAMPLE_PLL[];
extern AG903_DSPMgrSyncParam	DSP_SAMPLE_SYNCPARAM[];

AG903_ViaMgrInputHandle* CAP_GetVIAHandle(uint8_t ch)
{
	if (ch >= sizeof(VIA_Handle)/sizeof(VIA_Handle[0]))
		return NULL;
	else
		return VIA_Handle[ch];
}

AG903_VidMgrOutputHandle* CAP_GetVIDOutHandle(void)
{
	return VID_Out_Handle;
}

AG903_DSPMgrHandle* CAP_GetDSPHandle(uint8_t ch)
{
	if (ch >= sizeof(DSP_Handle)/sizeof(DSP_Handle[0]))
		return NULL;
	else
		return DSP_Handle[ch];
}

/**
 * DSP設定(LVDS-Dual or CMOS, 1080p)
 */
int32_t CAP_SetupDisplay(int32_t mode)
{
	AG903_DSPMgrCtrlParam		dsp_ctrl	= {0};
	AG903_DSPMgrLVDSParam		dsp_lvds	= {0};
	AG903_DSPMgrBPwrModeParam	dsp_bpwr	= {0};
	AG903_DSPMgrCMOSParam		dsp_cmos	= {0};
	AG903_DSPMgrWindowParam		dsp_win		= {0};
	int32_t dsp_ch = (CAP_MODE_VIA_LVDS == mode || CAP_MODE_VID == mode) ? 1 : 0;

	WindowAttributeAddr = sys_memalign(0x20 * 16, 0x10); /* 16ウィンドウ分を確保 */
	if(WindowAttributeAddr == NULL) {
		CAP_ErrPrintf("ERROR: Window attribute out of memory.\r\n");
		return -AG903_ENOMEM;
	} else {
		sys_memset(WindowAttributeAddr, 0, 0x20*16);
	}

	ERRCHK(AG903_ENONE, AG903_DSPMgrInit());
	ERRCHK(AG903_ENONE, AG903_DSPMgrGetHandle(0, &DSP_Handle[0]));
	ERRCHK(AG903_ENONE, AG903_DSPMgrGetHandle(1, &DSP_Handle[1]));

	/* PORT */
	AG903_DSPMgrSetPortSel(DSP_Handle[0], AG903_VOD0_PORTSEL_LVCMOS24);
	AG903_DSPMgrSetPortSel(DSP_Handle[1], AG903_VOD1_PORTSEL_LVDS_DUAL);

	dsp_ctrl.ip_sel			= AG903_DSP_VMODE_NONINTERLACE;
	dsp_ctrl.hrz_framesize	= CAP_DISPLAY_WIDTH;
	dsp_ctrl.vt_framesize	= CAP_DISPLAY_HEIGHT;
	dsp_ctrl.syncparam		= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_FULLHD];


	if (CAP_MODE_VIA_LVDS == mode || CAP_MODE_VID == mode) {
		/* PLL (IN:24.000[MHz] OUT:74.000[MHz] (74*2link=148[MHz] */
		AG903_SPCPrmPllnParam pll;
		AG903_SPCPrmGetPll2Ctrl(&pll);
		if(pll.en == 1) {
			pll.en = 0;
			AG903_SPCPrmSetPll2Ctrl(&pll);
		}
		pll.ms		= 3;
		pll.ns		= 37;
		pll.div		= 3;
		pll.src		= AG903_SPC_PLL2_CLKSRC_XOUT;
		pll.frange	= 3; /* dclk = 296000000 */
		pll.en		= 1;
		AG903_SPCPrmSetPll2Ctrl(&pll);

		Board_BacklightSel(dsp_ch, 1);

		dsp_lvds.hsync_polarity			= AG903_DSP_POLARITY_NEGA;
		dsp_lvds.vsync_polarity			= AG903_DSP_POLARITY_NEGA;
		dsp_lvds.rgbde_polarity			= AG903_DSP_POLARITY_POSI;
		dsp_lvds.field_polarity			= AG903_DSP_POLARITY_POSI;
		dsp_lvds.hsync_en				= true;
		dsp_lvds.vsync_en				= true;
		dsp_lvds.rgbde_en				= true;
		dsp_lvds.field_en				= true;
		dsp_lvds.pixeldata_en			= true;

		dsp_lvds.format					= 0;
		dsp_lvds.freq_range				= AG903_VOD_FR_54_100MHZ;
		dsp_lvds.auto_powermanage		= AG903_VOD_PM_AUTO;
		dsp_lvds.macro_power			= AG903_VOD_PDX_NORMAL;

		dsp_bpwr.backlight_en			= 1;
		dsp_bpwr.cnt_unit				= 1;
		dsp_bpwr.panel_pwroncnt			= 100*MSEC_IN_SYSCLK;
		dsp_bpwr.backlight_pwroncnt		= 100*MSEC_IN_SYSCLK;
		dsp_bpwr.backlight_oncnt		= 100*MSEC_IN_SYSCLK;
		dsp_bpwr.panel_pwroffcnt		= 1*MSEC_IN_SYSCLK;
		dsp_bpwr.backlight_pwroffcnt	= 1*MSEC_IN_SYSCLK;
		dsp_bpwr.backlight_offcnt		= 1*MSEC_IN_SYSCLK;
		dsp_bpwr.pwm_period				= MSEC_IN_SYSCLK/1; // 1kHz
		dsp_bpwr.pwm_cnt				= (dsp_bpwr.pwm_period * 100)/100;

		ERRCHK(AG903_ENONE,
			   AG903_DSPMgrSetLVDSParam(DSP_Handle[dsp_ch], &dsp_ctrl, &dsp_lvds));
		ERRCHK(AG903_ENONE,
			   AG903_DSPMgrSetBPwrModeParam(DSP_Handle[dsp_ch], &dsp_bpwr));
	} else {
		/* PLL (IN:24.000MHz OUT:148.000MHz) */
		AG903_SPCPrmPllnParam pll;
		AG903_SPCPrmGetPll1Ctrl(&pll);
		if(pll.en == 1) {
			pll.en = 0;
			AG903_SPCPrmSetPll1Ctrl(&pll);
		}
		pll.ms		= 3;
		pll.ns		= 37;
		pll.div		= 1;
		pll.src		= AG903_SPC_PLL1_CLKSRC_XOUT;
		pll.frange	= 3;
		pll.en		= 1;
		AG903_SPCPrmSetPll1Ctrl(&pll);

		Board_BacklightSel(dsp_ch, 0);
		Board_SelectDviTX();

		dsp_cmos.dotclk_polarity		= AG903_VOD_DOTCLK_CHANGE_RISE;
		dsp_cmos.field_polarity			= AG903_DSP_POLARITY_POSI;
		dsp_cmos.hsync_polarity			= AG903_DSP_POLARITY_NEGA;
		dsp_cmos.vsync_polarity			= AG903_DSP_POLARITY_NEGA;
		dsp_cmos.rgbde_polarity			= AG903_DSP_POLARITY_POSI;
		dsp_cmos.colordetect_polarity	= AG903_DSP_POLARITY_POSI;
		dsp_cmos.hsync_en				= true;
		dsp_cmos.vsync_en				= true;
		dsp_cmos.rgbde_en				= true;
		dsp_cmos.pixeldata_en			= true;
		dsp_cmos.colordetect_en			= false;
		dsp_cmos.yuv_mode				= AG903_VOD_MOD_YUV_BT709;

		ERRCHK(AG903_ENONE,
			   AG903_DSPMgrSetCMOSParam(DSP_Handle[dsp_ch], &dsp_ctrl, &dsp_cmos));
	}

	/* VSYNC EVENT OUTPUT ENABLE */
	AG903_DSPMgrIntParam dsp_int;
	dsp_int.trigger_out			= AG903_DSP_EVENT_VT;
	dsp_int.trigger_vt			= AG903_DSP_TRG_VT_START_OF_VBLANK;
	AG903_DSPMgrSetIntParam(DSP_Handle[dsp_ch], &dsp_int);

	dsp_win.update_timing		= AG903_DSP_ATTR_END_OF_VSYNC;
	dsp_win.background			= 0xFFFFFFFF;
	dsp_win.num_config			= 1;
	dsp_win.num_attr			= 15;
	dsp_win.palette_update		= 0;
	dsp_win.window_attr_base	= (uint32_t)WindowAttributeAddr;
	dsp_win.window_attr_update	= 1;
	ERRCHK(AG903_ENONE,
		   AG903_DSPMgrSetWindowParam(DSP_Handle[dsp_ch], &dsp_win));

	if (CAP_MODE_VIA_LVDS == mode || CAP_MODE_VID == mode) {
		CAP_Printf("[Display] DSP1 Setup done. (LVDS-Dual, 1080p)\r\n");
	} else {
		CAP_Printf("[Display] DSP0 Setup done. (CMOS, 1080p)\r\n");
	}

	return AG903_ENONE;
}

int32_t CAP_ReleaseDisplay(void)
{
	AG903_DSPMgrDisable(DSP_Handle[0]);
	AG903_DSPMgrDisable(DSP_Handle[1]);
	while(AG903_DSPMgrCheckStopped(DSP_Handle[0]));
	while(AG903_DSPMgrCheckStopped(DSP_Handle[1]));
	AG903_DSPMgrIntStat intclr = {
		.clr_dspoff		= true,
		.clr_hrz_line	= true,
		.clr_vt_blank	= true,
	};
	AG903_DSPMgrClearIntStat(DSP_Handle[0], &intclr);
	AG903_DSPMgrClearIntStat(DSP_Handle[1], &intclr);
	AG903_DSPMgrReleaseHandle(DSP_Handle[0]);
	AG903_DSPMgrReleaseHandle(DSP_Handle[1]);

	if (WindowAttributeAddr != NULL) {
		sys_free(WindowAttributeAddr);
		WindowAttributeAddr = NULL;
	}

	CAP_Printf("[Display] DSP Released.\r\n");

	return AG903_ENONE;
}

int32_t CAP_SetupVIA(void)
{
	int32_t idx;
	int32_t rc = AG903_ENONE;
	int32_t ports[4] = {0,1,2,3};

	rc = Board_SetupCvbs(TEST_CVBS_NTSC_4F);

	if (rc == AG903_ENONE)
		rc = AG903_ViaMgrGetInputHandle(ports, 4, &VIA_Handle[0]);

	for (idx=0; idx<AG903_VIA_MGR_MAX_PORTS; idx++) {
		if (rc == AG903_ENONE) {
			rc = AG903_ViaMgrSetInputParameter(VIA_Handle[idx],
											   AG903_VIA_MGR_FORMAT_NTSC_4F,
											   AG903_VIA_MGR_MODE_COLOR);
			CAP_Printf("[Analog-IN] VIA%u -> NTSC Color mode.\r\n", VIA_Handle[idx]->port_no);
		}
	}

	return rc;
}


int32_t CAP_ReleaseVIA(void)
{
	int32_t idx;
	int32_t rc = AG903_ENONE;

	for (idx=0; idx<AG903_VIA_MGR_MAX_PORTS; idx++) {
		if (rc == AG903_ENONE && VIA_Handle[idx] != NULL) {
			rc = AG903_ViaMgrReleaseInputHandle(VIA_Handle[idx]);
			VIA_Handle[idx] = NULL;
		}
	}

	if (rc == AG903_ENONE)
		CAP_Printf("[Analog-IN] Released all VIA channels.\r\n");
	else
		CAP_ErrPrintf("ERROR: [Analog-IN] VIA release failed.\r\n");

	return rc;
}

int32_t CAP_SetupVID(void)
{
	AG903_SPCPrmClkSel			clk_src;
	int32_t		vid_port = 0;

	/* FPGA, 端子設定 */
	Board_SelectDviRX();

	AG903_SPCPrmGetClkSrc(&clk_src);
	clk_src.dt0 = AG903_SPC_CP0_CLKSRC_DVOCLK;	/* クロックソースはDOT_CLK0 */
	AG903_SPCPrmSetClkSrc(&clk_src);

	/* VID設定 */
	ERRCHK(AG903_ENONE, AG903_VidMgrGetInputHandle(&vid_port, 1, &VID_In_Handle));
	ERRCHK(AG903_ENONE, AG903_VidMgrGetOutputHandle(&vid_port, 1, &VID_Out_Handle));

	ERRCHK(AG903_ENONE, AG903_VidMgrSetInputFormat(AG903_VID_MGR_24BIT_TO_CH0));
	ERRCHK(AG903_ENONE, AG903_VidMgrSetIOSignal(VID_In_Handle, AG903_VID_MGR_IO_SIGNAL_INPUT));
	ERRCHK(AG903_ENONE, AG903_VidMgrSetIOMode(VID_In_Handle, AG903_VID_MGR_IO_VSYNC_POL_MINUS, AG903_VID_MGR_IO_HSYNC_POL_MINUS,
	                             AG903_VID_MGR_IN_DE_POL_PLUS, AG903_VID_MGR_IN_FIELD_POL_MINUS, AG903_VID_MGR_IN_CLK_POL_MINUS));

	ERRCHK(AG903_ENONE, AG903_VidMgrSetCaptureTDMMode(VID_Out_Handle, AG903_VID_MGR_TDM_NONE));
	ERRCHK(AG903_ENONE, AG903_VidMgrEnableTRSDecode(VID_Out_Handle, false));
	ERRCHK(AG903_ENONE, AG903_VidMgrSetDetectionCycle(VID_Out_Handle, 8192, 8192));

	ERRCHK(AG903_ENONE, AG903_VidMgrEnable(true));
	ERRCHK(AG903_ENONE, AG903_VidMgrCommitSetting());

	return AG903_ENONE;
}

int32_t CAP_ReleaseVID(void)
{
	ERRCHK(AG903_ENONE, AG903_VidMgrEnable(false));

	ERRCHK(AG903_ENONE, AG903_VidMgrReleaseInputHandle(VID_In_Handle));
	ERRCHK(AG903_ENONE, AG903_VidMgrReleaseOutputHandle(VID_Out_Handle));

	return AG903_ENONE;
}

uint32_t getColorByHue(uint8_t index)
{
	/* Hue[0-239]: 0(R), 40(Y), 80(G), 120(C), 160(B), 200(M) */

	uint32_t col = 0;
	float unit = 255.0/39.0; /* 値域:0-19 -> 0-255 */
	uint8_t cd = (uint8_t)((index%40) * unit);

	if(index > 240) {
		return col = 0;
	}

	if(index < 40) {
		col = 0xFFFF0000 + (cd<<8);
	} else if(index < 80) {
		col = 0xFFFFFF00 - (cd<<16);
	} else if(index < 120) {
		col = 0xFF00FF00 + (cd<<0);
	} else if(index < 160) {
		col = 0xFF00FFFF - (cd<<8);
	} else if(index < 200) {
		col = 0xFF0000FF + (cd<<16);
	} else if(index < 240) {
		col = 0xFFFF00FF - (cd<<0);
	}

	return col;
}
