/**
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2023 AXELL CORPORATION. all rights reserved.
 *************************************************************************************
 * @brief		OSP Sample code
 * @file		osp_sample.c
 * @auther		AXELL
 * @date
 * @description
 * @note
 * @history		2019_12_27  [SDK3.0] サンプルのメッセージ出力をASCIIコードのみに変更 (#2728)
 * @history     2023_03_31  [SDK3.5] 表示のウインドウ属性の予約領域を確実にゼロクリアするように修正 (#4640)
 */
#include "sample_common.h"
#include "com.h"

#include "timr/timrmgr.h"
#include "osp/ospmgr.h"
#include "dsp/dspmgr.h"
#include "sys/spcprm.h"
#include "sys/sscprm.h"
#include "../dsp/dsp_common.h"

/* プロトタイプ */
static int32_t Osp_init_module(void);
static int32_t Osp_term_module(void);
static void Osp_SampleHelp(void);
static void Osp_WriteReg(void);
static void Osp_WriteMem(void);
static void Osp_WriteWindowAttr(void);

static int32_t osp_setup_timer(uint8_t* timch);
static int32_t osp_start_timer(void);
static int32_t osp_stop_timer(void);
static int32_t osp_setup_display(void* frame_buf, void* win_attr);
static int32_t osp_close_display(void);
static void OSP_wait(void);
static void Timr_Tickhdr(void* param);

/* Param */
#define OSP_SMP_HEADER_SIZE		(32)
#define OSP_SMP_CMD_SIZE		(sizeof(uint32_t))
#define OSP_SMP_CH				(0)
#define OSP_SMP_CH_SUB			(1)
#define OSP_SMP_TEST_REG		(0xE8000008)	/* SSCWRITETEST */
#define OSP_SMP_TEST_DATA		(0xDEADBEEF)	/* Test Data */
#define OSP_SMP_MEM_SIZE		(32)
#define OSP_SMP_FLG_PTN			(0x01)
#define OSP_SMP_PX_SIZE			(3)				/* ピクセルサイズ[Byte] */
#define OSP_SMP_COL_NUM			(3)				/* 色数 */
#define OSP_SMP_COL_DEPTH		(256)			/* 色深度 */

/* Macro */
#define OSP_Printf(FMT, ...)	sys_print(0, FMT,##__VA_ARGS__)
#define OSP_ErrPrintf(FMT, ...)	sys_print(1, FMT,##__VA_ARGS__)

/* データ */
static AG903_TIMRMgrHandle	*gTimrHdl;
static AG903_DSPMgrHandle	*gDspHdl;
static void (*func[])() = {
	Osp_SampleHelp,
	Osp_WriteReg,
	Osp_WriteMem,
	Osp_WriteWindowAttr,
};

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

/**
 * @brief	メイン処理
 * @param	param [in] 制御パラメータ
 * @return	none
 * @note	none
*/
void OSP_main(uint8_t param)
{
	uint8_t		mode = 0;
	char		input[2+2];

	((void)param);

	Osp_SampleHelp();	/* Help表示 */

	Osp_init_module();

	while (mode != 0xFF) {
		OSP_Printf(" -- Choose sample number (00h<HELP> to FFh): ");
		COM_GetNum_Wait(input, sizeof(input));
		mode = (ASCtoBIN(input[0]) << 4)
			 | (ASCtoBIN(input[1]) << 0);
		if(0xFF == mode) {
			break;
		}
		if(mode >= (sizeof(func)/sizeof(void*))) {
			func[0](); /* Help */
		}
		else {
			func[mode]();		/* Sample func実行 */
		}
	}

	Osp_term_module();

	return;
}

/**
 * @brief	サブ処理
 * @param	param [in] 制御パラメータ
 * @return	none
 * @note	none
*/
void OSP_sub(uint32_t param)
{
	((void)param);
	/* 処理なし */
	return;
}

/**
 * @brief	モジュール初期化
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t Osp_init_module(void)
{
	static _Bool	OspInit=false;
	int32_t 		rc=AG903_ENONE;

	if(false == OspInit) {
		rc = AG903_OSPMgrInit();
		if(AG903_ENONE == rc) {
			OspInit=true;
		}
		else {
			OSP_ErrPrintf("ERROR: Mgr Init\r\n");
		}
	}
	if(AG903_ENONE == rc) {
		rc = sys_meminit(AG903_SAMPLE_VRAM_ALLOC_SIZE,
					 (void *)AG903_SAMPLE_VRAM_ADDR,
					 SYSMEM_NORMAL_CACHE_OFF);
	}
	if (AG903_ENONE != rc) {
		OSP_ErrPrintf("ERROR：Mem Init\r\n");
	}

	return rc;
}

/**
 * @brief	モジュール終了
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @note	none
*/
static int32_t Osp_term_module(void)
{
	sys_memfinal();
	return AG903_ENONE;
}

/**
 * @brief	ヘルプ表示
 * @param	none
 * @return	none
 * @note	none
*/
static void Osp_SampleHelp(void)
{
	OSP_Printf("\t# 00 ... Help\r\n");
	OSP_Printf("\t# 01 ... Write Reg [use INT and BAS]\r\n");
	OSP_Printf("\t# 02 ... Write Mem [use EVENT and BAB]\r\n");
	OSP_Printf("\t# 03 ... Write Window Attr [use SETF, CLRF and BAS]\r\n");
	OSP_Printf("\t# FF ... Exit Sample\r\n");
	return;
}

/**
 * @brief	レジスタ書込み
 * @param	none
 * @return	none
 * @note	INTとBASを使用したサンプルです。
*/
static void Osp_WriteReg(void)
{
	AG903_OSPMgrHandle	*handle;
	AG903_OSPCmdBuf	param;
	uint32_t*	cmd_buf;
	uint32_t*	test_reg = (uint32_t*)OSP_SMP_TEST_REG;
	uint32_t	cmd_bufsz;
	int32_t 	ret = AG903_ENONE;
	uint8_t		fifoovf, fifosz;
	uint8_t		timch;

	ret = AG903_OSPMgrGetHandle(&handle);
	if(AG903_ENONE != ret) {
		OSP_ErrPrintf("ERROR: Handle get\r\n");
		return;
	}

	cmd_bufsz = (4*OSP_SMP_CMD_SIZE) + OSP_SMP_HEADER_SIZE;	/* 4=登録コマンド数 */
	cmd_buf = sys_memalign(cmd_bufsz, OSP_SMP_CMD_SIZE);
	if(NULL == cmd_buf) {
		OSP_ErrPrintf("ERROR: Memory alloc\r\n");
		return;
	}

	do{
		param.addr = cmd_buf;
		param.size = cmd_bufsz;
		param.mode = AG903_OSP_BUFMODE_RING;
		ret = AG903_OSPMgrSetCommandBuf(handle, &param, true);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command buffer set\r\n");
			break;
		}

		ret = osp_setup_timer(&timch);	/* タイマ設定 */
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer setup\r\n");
			break;
		}

		/* コマンド設定 */
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_INTR_WAIT,  AG903_IRQ0_TIM0+timch); }
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_SINGLE, 1); }
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_ADDR,   OSP_SMP_TEST_REG); }
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_DATA,   OSP_SMP_TEST_DATA); }
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command set\r\n");
			break;
		}

		ret = AG903_OSPMgrSetFIFO(handle, 4);	/* 4=登録コマンド数 */
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: FIFO set\r\n");
			break;
		}

		(*test_reg) = 0;
		OSP_Printf(" OSP Enable\r\n");
		ret = AG903_OSPMgrEnable(handle);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: OSP enable\r\n");
			break;
		}

		ret = osp_start_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer start\r\n");
			break;
		}

		while (1) {	/* FIFOが空になるまでWait */
			ret = AG903_OSPMgrGetFIFOStat(handle, &fifoovf, &fifosz);
			if(AG903_ENONE != ret) {
				OSP_ErrPrintf("ERROR: FIFO status get\r\n");
				break;
			}
			if(AG903_OSP_FIFO_NUM == fifosz) {
				break;
			}
			sys_dlytsk(10);
		}

		OSP_Printf(" Reg  Addr:%08x\r\n", (uint32_t)test_reg);
		OSP_Printf(" Read Data:%08x\r\n", (uint32_t)(*test_reg));
		if(OSP_SMP_TEST_DATA == (*test_reg)) {
			OSP_Printf(" Verify OK\r\n");
		}
		else {
			OSP_ErrPrintf("ERROR: Reg Write\r\n");
		}
		(*test_reg) = 0;

		ret = osp_stop_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer stop\r\n");
			break;
		}
	}while(0);

	AG903_OSPMgrClearCommand(handle);	/* コマンドクリア */
	AG903_OSPMgrReleaseHandle(handle);	/* ハンドル解放 */
	sys_free(cmd_buf);	/* メモリ解放 */

	return;
}

/**
 * @brief	メモリ書込み
 * @param	none
 * @return	none
 * @note	EVENTとBABを使用したサンプルです。
*/
static void Osp_WriteMem(void)
{
	AG903_OSPMgrHandle	*handle;
	AG903_OSPCmdBuf	param;
	uint32_t*	cmd_buf;
	uint32_t*	data_mem;
	uint32_t	cmd_bufsz;
	uint32_t 	loop;
	int32_t 	ret = AG903_ENONE;
	uint8_t		fifoovf, fifosz;
	uint8_t		timch;

	ret = AG903_OSPMgrGetHandle(&handle);
	if(AG903_ENONE != ret) {
		OSP_ErrPrintf("ERROR: Handle get\r\n");
		return;
	}

	cmd_bufsz = ((3+OSP_SMP_MEM_SIZE)*OSP_SMP_CMD_SIZE) + OSP_SMP_HEADER_SIZE;	/* 3=CRLF+BAB+ADDR */
	cmd_buf   = sys_memalign(cmd_bufsz, OSP_SMP_CMD_SIZE);
	data_mem  = sys_memalign((OSP_SMP_MEM_SIZE*sizeof(uint32_t)), sizeof(uint32_t));
	if((NULL == cmd_buf) || (NULL == data_mem)) {
		OSP_ErrPrintf("ERROR: Memory alloc\r\n");
		return;
	}
	sys_memset(data_mem, 0, (OSP_SMP_MEM_SIZE*sizeof(uint32_t)));

	do{
		param.addr = cmd_buf;
		param.size = cmd_bufsz;
		param.mode = AG903_OSP_BUFMODE_RING;
		ret = AG903_OSPMgrSetCommandBuf(handle, &param, true);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command buffer set\r\n");
			break;
		}

		ret = osp_setup_timer(&timch);	/* タイマ設定 */
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer setup\r\n");
			break;
		}

		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_EVNT_WAIT, AG903_EVENT32_TIM0+timch); }
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_BURST_INC, OSP_SMP_MEM_SIZE); }
		if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_ADDR, (uint32_t)data_mem); }
		for(loop=0; loop<OSP_SMP_MEM_SIZE; loop++) {
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(handle, AG903_OSP_CMD_BUS_DATA, loop); }
		}
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command set\r\n");
			break;
		}
		ret = AG903_OSPMgrSetFIFO(handle, (3+OSP_SMP_MEM_SIZE));
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: FIFO set\r\n");
			break;
		}

		OSP_Printf(" OSP Enable\r\n");
		ret = AG903_OSPMgrEnable(handle);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: OSP enable\r\n");
			break;
		}

		ret = osp_start_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer start\r\n");
			break;
		}

		while (1) {	/* FIFOが空になるまでWait */
			ret = AG903_OSPMgrGetFIFOStat(handle, &fifoovf, &fifosz);
			if(AG903_ENONE != ret) {
				OSP_ErrPrintf("ERROR: FIFO status get\r\n");
				break;
			}
			if(AG903_OSP_FIFO_NUM == fifosz) {
				break;
			}
			sys_dlytsk(10);
		}

		OSP_Printf(" Mem  Addr：%08x\r\n", (uint32_t)data_mem);
		for(loop=0; loop<OSP_SMP_MEM_SIZE; loop++) {
			OSP_Printf(" Read Data：%08x\r\n", (uint32_t)(*(data_mem+loop)));
			if(loop != (*(data_mem+loop))) {
				OSP_ErrPrintf("ERROR: Mem Write\r\n");
				break;
			}
		}
		if(OSP_SMP_MEM_SIZE <= loop) {
			OSP_Printf(" Verify OK\r\n");
		}

		ret = osp_stop_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer stop\r\n");
			break;
		}
	}while(0);

	AG903_OSPMgrClearCommand(handle);	/* コマンドクリア */
	AG903_OSPMgrReleaseHandle(handle);	/* ハンドル解放 */
	sys_free(cmd_buf);	/* メモリ解放 */
	sys_free(data_mem);	/* メモリ解放 */

	return;
}

/**
 * @brief	ウインドウアトリビュート更新
 * @param	none
 * @return	none
 * @note	SETFとCLRFを使用したサンプルです。
*/
static void Osp_WriteWindowAttr(void)
{
	AG903_OSPMgrHandle	*hdl00;
	AG903_OSPMgrHandle	*hdl01;
	AG903_OSPCmdBuf	buf00;
	AG903_OSPCmdBuf	buf01;
	uint32_t*	cmd_buf00;
	uint32_t*	cmd_buf01;
	void*		frame_buf;
	void*		win_attr;
	uint32_t	cmd_bufsz00,cmd_bufsz01,frame_bufsz;
	uint32_t	cmd_unit;
	uint32_t	img_posx;
	uint32_t	loop;
	int32_t 	ret = AG903_ENONE;
	uint8_t		fifoovf, fifosz;
	uint8_t		timch;

	ret = AG903_OSPMgrGetHandle(&hdl00);
	if(AG903_ENONE != ret) {
		OSP_ErrPrintf("ERROR: Handle get\r\n");
		return;
	}
	ret = AG903_OSPMgrGetHandle(&hdl01);
	if(AG903_ENONE != ret) {
		OSP_ErrPrintf("ERROR: Handle get\r\n");
		return;
	}

	cmd_bufsz00 = (AG903_OSP_FIFO_NUM*OSP_SMP_CMD_SIZE) + OSP_SMP_HEADER_SIZE;
	cmd_bufsz01 = (AG903_OSP_FIFO_NUM*OSP_SMP_CMD_SIZE) + OSP_SMP_HEADER_SIZE;
	frame_bufsz = OSP_SMP_PX_SIZE*OSP_SMP_COL_NUM*OSP_SMP_COL_DEPTH;
	cmd_buf00 = sys_memalign(cmd_bufsz00, OSP_SMP_CMD_SIZE);
	cmd_buf01 = sys_memalign(cmd_bufsz01, OSP_SMP_CMD_SIZE);
	frame_buf = sys_malloc(frame_bufsz);
	win_attr  = sys_memalign(0x20, 8);	/* ウインドウアトリビュート 32[Byte] */
	if((NULL == cmd_buf00) || (NULL == cmd_buf01) || (NULL == frame_buf) || (NULL == win_attr)){
		OSP_ErrPrintf("ERROR: Memory alloc\r\n");
		return;
	}
	sys_memset(frame_buf, 0, frame_bufsz);
	sys_memset(win_attr, 0, 0x20);

	cmd_unit = AG903_OSP_FIFO_NUM / 4;
	do{
		ret = osp_setup_timer(&timch);	/* タイマ設定 */
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer setup\r\n");
			break;
		}

		/* CRLF側のOSP設定 */
		buf00.addr = cmd_buf00;
		buf00.size = cmd_bufsz00;
		buf00.mode = AG903_OSP_BUFMODE_FIFO;
		ret = AG903_OSPMgrSetCommandBuf(hdl00, &buf00, true);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command buffer set\r\n");
			break;
		}
		img_posx = 0;
		for (loop = 0; loop < cmd_unit; loop++) {
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl00, AG903_OSP_CMD_FLAG_CLR, OSP_SMP_FLG_PTN); }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl00, AG903_OSP_CMD_BUS_SINGLE, 1);  }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl00, AG903_OSP_CMD_BUS_ADDR, (uint32_t)win_attr); }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl00, AG903_OSP_CMD_BUS_DATA, img_posx); }
			img_posx += 1280/32;
		}
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command set\r\n");
			break;
		}
		ret = AG903_OSPMgrSetFIFO(hdl00, AG903_OSP_FIFO_NUM);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: FIFO set\r\n");
			break;
		}

		/* SETF側のOSP設定 */
		buf01.addr = cmd_buf01;
		buf01.size = cmd_bufsz01;
		buf01.mode = AG903_OSP_BUFMODE_FIFO;
		ret = AG903_OSPMgrSetCommandBuf(hdl01, &buf01, true);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command buffer set\r\n");
			break;
		}
		for (loop = 0; loop < cmd_unit; loop++) {
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl01, AG903_OSP_CMD_EVNT_WAIT, AG903_EVENT32_TIM0+timch); }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl01, AG903_OSP_CMD_EVNT_CLR,  AG903_EVENT40_DSP0); }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl01, AG903_OSP_CMD_EVNT_DEC,  AG903_EVENT40_DSP0); }
			if (ret == AG903_ENONE) { ret = AG903_OSPMgrSetCommand(hdl01, AG903_OSP_CMD_FLAG_SET,  OSP_SMP_FLG_PTN); }
		}
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Command set\r\n");
			break;
		}
		ret = AG903_OSPMgrSetFIFO(hdl01, AG903_OSP_FIFO_NUM);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: FIFO set\r\n");
			break;
		}
		ret = AG903_OSPMgrSetEventCntEnable(AG903_EVENT40_DSP0, true, 0);

		/* 表示設定 */
		ret = osp_setup_display(frame_buf, win_attr);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Display setup\r\n");
			break;
		}

		OSP_Printf(" Press enter key to start");
		OSP_wait();	/* CRLF入力待ち */

		/* OSP Enable */
		ret = AG903_OSPMgrEnable(hdl00);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: OSP enable\r\n");
			break;
		}
		ret = AG903_OSPMgrEnable(hdl01);
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: OSP enable\r\n");
			break;
		}

		ret = osp_start_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer start\r\n");
			break;
		}

		while (1) {	/* FIFOが空になるまでWait */
			ret = AG903_OSPMgrGetFIFOStat(hdl00, &fifoovf, &fifosz);
			if(AG903_ENONE != ret) {
				OSP_ErrPrintf("ERROR: FIFO status get\r\n");
				break;
			}
			if(AG903_OSP_FIFO_NUM == fifosz) {
				break;
			}
			sys_dlytsk(10);
		}

		ret = osp_stop_timer();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Timer stop\r\n");
			break;
		}

		ret = osp_close_display();
		if(AG903_ENONE != ret) {
			OSP_ErrPrintf("ERROR: Display close\r\n");
			break;
		}
	}while(0);

	AG903_OSPMgrClearCommand(hdl00);	/* コマンドクリア */
	AG903_OSPMgrClearCommand(hdl01);	/* コマンドクリア */
	AG903_OSPMgrReleaseHandle(hdl00);	/* ハンドル解放 */
	AG903_OSPMgrReleaseHandle(hdl01);	/* ハンドル解放 */
	sys_free(cmd_buf00);	/* メモリ解放 */
	sys_free(cmd_buf01);	/* メモリ解放 */
	sys_free(frame_buf);	/* メモリ解放 */
	sys_free(win_attr);		/* メモリ解放 */

	return;
}

/**
 * @brief	タイマ設定
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t osp_setup_timer(uint8_t* timch)
{
	AG903_TIMRMgrTickCntParam 	tick;
	int32_t 	ret = AG903_ENONE;

	tick.period  = 30000000;	/* 150[ms] (clk 200M[Hz]時) */
	tick.match   = 0;
	tick.resolution = AG903_TIMR_CNT_SYSCLK;
	tick.oneshot = false;
	tick.output  = NULL;
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrGetHandle(&gTimrHdl); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrSetIntHandler(gTimrHdl, (void*)Timr_Tickhdr, (void*)gTimrHdl); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrGetChannel(gTimrHdl, timch); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrSetTickCountMode(gTimrHdl, &tick); }

	return ret;
}

/**
 * @brief	タイマ開始
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t osp_start_timer(void)
{
	int32_t 	ret = AG903_ENONE;

	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrDisableIntMask(gTimrHdl, AG903_TIMR_OVERFLOW_BIT); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrEnableEventTrigger(gTimrHdl, AG903_TIMR_OVERFLOW_BIT); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrStart(gTimrHdl); }

	return ret;
}

/**
 * @brief	タイマ停止
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t osp_stop_timer(void)
{
	int32_t 	ret = AG903_ENONE;
	uint32_t	status;

	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrStop(gTimrHdl); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrEnableIntMask(gTimrHdl, AG903_TIMR_OVERFLOW_BIT); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrGetStatus(gTimrHdl, &status); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrClearStatus(gTimrHdl, status); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrDeleteIntHandler(gTimrHdl, (void*)Timr_Tickhdr); }
	if (ret == AG903_ENONE) { ret = AG903_TIMRMgrReleaseHandle(gTimrHdl); }

	return ret;
}

/**
 * @brief	表示設定
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t osp_setup_display(void* frame_buf, void* win_attr)
{
	AG903_DSPMgrCtrlParam		dsp_ctrl	= {0};
    AG903_DSPMgrCMOSParam		dsp_cmos	= {0};
    AG903_DSPMgrWindowParam		dsp_win		= {0};
	AG903_DSPMgrIntParam		dsp_int		= {0};
	AG903_DSPMgrWinAttribute	*dsp_attr	= NULL;
	AG903_SPCPrmClkSel    clksrc;
	AG903_SSCPrmDspSetup  dsppin;
	uint8_t* fb = (uint8_t*)frame_buf;
	int32_t	 idx, col;
	int32_t  ret = AG903_ENONE;

	/* フレームバッファ作成 */
	for(col=0; col<OSP_SMP_COL_NUM; col++) { /* (MSB)23<-[RGB]->0(LSB) */
		for(idx=0; idx<OSP_SMP_COL_DEPTH; idx++) {
			fb[(col*OSP_SMP_PX_SIZE*OSP_SMP_COL_DEPTH)+(idx*3) + 0] = col%OSP_SMP_COL_NUM == 2 ? idx : 0;
			fb[(col*OSP_SMP_PX_SIZE*OSP_SMP_COL_DEPTH)+(idx*3) + 1] = col%OSP_SMP_COL_NUM == 1 ? idx : 0;
			fb[(col*OSP_SMP_PX_SIZE*OSP_SMP_COL_DEPTH)+(idx*3) + 2] = col%OSP_SMP_COL_NUM == 0 ? idx : 0;
		}
	}

	/* 各表示回路へのクロック供給元選択 */
	AG903_SPCPrmGetClkSrc(&clksrc);
	clksrc.dt0	= AG903_SPC_DT0_CLKSRC_PLL1;
	AG903_SPCPrmSetClkSrc(&clksrc);

	/* CMOS関連端子を全て出力に設定 */
	dsppin.dot0		= 1;
	dsppin.dot1		= 1;
	dsppin.field0	= 1;
	dsppin.field1	= 1;
	dsppin.vsync0	= 1;
	dsppin.vsync1	= 1;
	AG903_SSCPrmSetDspPinDir(&dsppin);

	/* PLL */
	AG903_SPCPrmPllnParam pll = {0};
	AG903_SPCPrmSetPll1Ctrl(&pll);
	sys_delayus(50);
	AG903_SPCPrmSetPll1Ctrl(&DSP_SAMPLE_PLL[DSP_SAMPLE_RES_SXGA]);
	sys_delayus(50);

	if (ret == AG903_ENONE) { ret = AG903_DSPMgrInit(); }
    if (ret == AG903_ENONE) { ret = AG903_DSPMgrGetHandle(0, &gDspHdl); }
	if (ret == AG903_ENONE) { ret = AG903_DSPMgrSetPortSel(gDspHdl, AG903_VOD0_PORTSEL_LVCMOS24); }

    dsp_ctrl.ip_sel = AG903_DSP_VMODE_NONINTERLACE;
    dsp_ctrl.hrz_framesize = 1280;
    dsp_ctrl.vt_framesize  = 1024;
    dsp_ctrl.syncparam = &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_SXGA];

    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.hsync_en        = true;
	dsp_cmos.vsync_en        = true;
	dsp_cmos.rgbde_en        = true;
	dsp_cmos.pixeldata_en    = true;
	if (ret == AG903_ENONE) { ret = AG903_DSPMgrSetCMOSParam(gDspHdl, &dsp_ctrl, &dsp_cmos); }

	dsp_int.bmureq_timing	= AG903_DSP_BMU_WINDATA;
	dsp_int.mask_dspoff		= true;
	dsp_int.mask_error		= true;
	dsp_int.mask_hrz_line	= true;
	dsp_int.mask_vt_blank	= false;
	dsp_int.trigger_out		= AG903_DSP_EVENT_VT;
	dsp_int.trigger_vt		= AG903_DSP_TRG_VT_START_OF_VBLANK;
	if (ret == AG903_ENONE) { ret = AG903_DSPMgrSetIntParam(gDspHdl, &dsp_int); }

    dsp_win.update_timing	= AG903_DSP_ATTR_END_OF_VSYNC;
    dsp_win.background		= 0x00000000;
    dsp_win.num_config 		= 1;
    dsp_win.num_attr   		= 1;
    dsp_win.palette_update 	= 0;
    dsp_win.window_attr_base = (uint32_t)win_attr;
    dsp_win.window_attr_update = 1;
	if (ret == AG903_ENONE) { ret = AG903_DSPMgrSetWindowParam(gDspHdl, &dsp_win); }

	if (ret == AG903_ENONE) { ret = AG903_DSPMgrGetAttribute(gDspHdl, 0, &dsp_attr); }
	if (ret == AG903_ENONE) {
		sys_memset(dsp_attr, 0, 0x20); /* ウインドウ属性の空き領域は必ずゼロクリアが必要 */
		dsp_attr->position_x			= 0;
		dsp_attr->position_y			= 0;
		dsp_attr->destination_width		= dsp_ctrl.hrz_framesize;
		dsp_attr->destination_height	= dsp_ctrl.vt_framesize;
		dsp_attr->framebuffer_base		= (uint32_t)frame_buf;
		dsp_attr->source_width			= 256;
		dsp_attr->source_height			= 3;
		dsp_attr->hrz_size				= dsp_attr->source_width * 3;
		dsp_attr->conf.default_alpha	= 0xFF;
		dsp_attr->conf.framebuffer_format = AG903_DSP_FFMT_R8G8B8;
		dsp_attr->conf.pallet_format	= AG903_DSP_PFMT_R8G8B8;
		dsp_attr->conf.valid			= true;
		AG903_DSPMgrSetAttribute(gDspHdl, 0);
	}

    /* バックライト制御 OFF */
    Board_BacklightSel(0, 0);
    Board_BacklightSel(1, 0);

    /* 表示ON */
    AG903_DSPMgrEnable(gDspHdl);

    /* CMOS出力有効 */
    Board_SelectDviTX();

    sys_dlytsk(1000); /* ディスプレイの安定待ち */

	return ret;
}

/**
 * @brief	表示停止
 * @param	none
 * @return	AG903_ENONE		正常終了
 * @return	AG903_ENONE以外	エラーコード
 * @note	none
*/
static int32_t osp_close_display(void)
{
	int32_t ret = AG903_ENONE;

	if(ret == AG903_ENONE) {ret = AG903_DSPMgrDisable(gDspHdl); }
	while (AG903_DSPMgrCheckStopped(gDspHdl) != AG903_ENONE);
	if(ret == AG903_ENONE) {ret = AG903_DSPMgrReleaseHandle(gDspHdl); }

	return ret;
}

/**
 * @brief	CRLFウェイト
 * @param	none
 * @return	none
 * @note	CRLFが入力されるまでロックする
 */
static void OSP_wait(void)
{
	char	code[2];

	COM_GetNum_Wait(code, sizeof(code));
	return;
}

/**
 * @brief		Tick Count割込み
 * @param[in]	param	タイマハンドル
 * @return		なし
 * @note		なし
 */
static void Timr_Tickhdr(void* param)
{
	AG903_TIMRMgrHandle* handle;

	handle = (AG903_TIMRMgrHandle*)(param);
	AG903_TIMRMgrClearStatus(handle, AG903_TIMR_OVERFLOW_BIT);

	return;
}
