/*
 * @history		2019_12_27  [SDK3.0] BMUバッファのサイズとアラインが4096の倍数になるよう修正 (#2188)
 * @history     2020_07_22  [SDK3.1] GVDが占有するメモリ領域を指定するAPIを追加 (#2965)
 */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2020 AXELL CORPORATION, all rights reserved.
 */

/* uC3 */
#include "kernel.h"
#include "ffsys.h"

/* Standards */
#include <stdint.h>

/* ライブラリ */
#include "bmu/bmumgr.h"
#include "dsp/dspmgr.h"
#include "timr/timrmgr.h"
#include "osp/ospmgr.h"
#include "register/AG903_sscreg.h"

/* MP4 */
#include "mps/mps_api.h"

/* アプリ固有定義 */
#include "sample_common.h"
#include "mps_single.h"

#define MONITOR_SXGA

/* ループ再生回数 */
#define MPS_SINGLE_LOOP_COUNT	0

/* タスク */
/* 非同期処理タスク
 * これ以外のタスクで余った時間にコールされる
 * デコーダの制御も行うため、コールされる頻度が落ちるとデコード性能に影響します */
extern void async_tsk(VP_INT);
static const T_CTSK ctsk_async = {
	TA_HLNG | TA_ACT | TA_FPU, (VP_INT)0, (FP)async_tsk, 9, 0x800, 0, "Async"
};

/* 関数 */
static void SetupDisplay(void);
static void SetupOSP(void);
static void SetupTimer(void);

static void TimerHandler(AG903_TIMRMgrHandle *handle);
static void WindowUpdate();
static int32_t cbMPSDecodeDone(void *param_gvd, void *param_user, gvdError error);
static int32_t cbMPSFatalerror(void *param_gvd, void *param_user, gvdError error);

/* 変数 */
extern gvdFatalErrorInfo	gFatal;					/* 致命的エラー保持用 */
extern int8_t			gFlagMPSDone;			/* 終了フラグ */
extern ER_ID			gSemID_BufSync;			/* フレームバッファ管理用 */
static ER_ID			gAsyncTaskID;			/* 非同期処理タスクID */
static void				*gWindowAttributeAddr;	/* ウィンドウアトリビュートアドレス保持用 */
static void				*gOSPBufferAddr;		/* OSP管理バッファアドレス */
static mpsOutBufInfo	gOutbufinfo[MPS_OUTBUF_COUNT];	/* 出力バッファ情報 */

/* 表示ハンドラ */
static AG903_DSPMgrHandle	*dsp_handle[2];
/* タイマハンドラ */
static AG903_TIMRMgrHandle	*tim_handle;
/* BMUハンドラ */
static AG903_BMUMgrHandle	*bmu_handle[4];
/* OSPハンドラ */
static AG903_OSPMgrHandle	*osp_handle[4];


static int movie_init(void)
{
	int32_t err;
	int32_t cnt;

	mpsSetting mpssetting;
	mpsConfig mpsconfig;
	mpsContext *mpsctx;

	/* MPSの初期化 */
	mpssetting.max_stream			= MPS_MAX_STREAM;
	mpssetting.stream_buffer_size	= MPS_INBUF_SIZE;
	mpssetting.stream_buffer_num	= MPS_INBUF_COUNT;
	mpssetting.func_decode_done_handler =
		(int32_t (*)(void *, void *, gvdError))cbMPSDecodeDone;
	mpssetting.func_fatalerror_handler =
		(int32_t (*)(void *, void *, gvdError))cbMPSFatalerror;
	err = MPS_Initialize(mpssetting);
	if(err)
	{
		MPS_ErrPrintf("ERROR: MPS_Initialize() : %d\n", err);
		return err;
	}

	ERRCHK(GVD_ERR_SUCCESS, GVD_SetFatalErrorAddress((void *)0xFE007000));
	ERRCHK(GVD_ERR_SUCCESS, GVD_SetOccupyMemoryAddress((void *)0x83FF0000));

	/* GVD用ワークメモリの確保 */
	mpsconfig.memory_work_size = MPS_GetRequiredWorkMemorySize();
	MPS_Printf("[Memory] GVD requested %u [Byte]\n", mpsconfig.memory_work_size);

	/* GVD用ワークメモリは32KB以上のアラインが必須 */
	mpsconfig.memory_work_top = MPS_Memalign(mpsconfig.memory_work_size, 32*1024);
	if(mpsconfig.memory_work_top == 0)
	{
		MPS_ErrPrintf("ERROR: [GVD WorkBuffer] allocate faild.\r\n");
		return -AG903_ENOMEM;
	}
	/* 出力バッファ数 */
	mpsconfig.num_out_buffer	= MPS_OUTBUF_COUNT;

	/* GVD用ヒープメモリは2KB以上のアラインが必須 */
	mpsconfig.memory_heap_top = MPS_Memalign(MPS_HEAP_SIZE, 2048);
	if(mpsconfig.memory_heap_top == NULL)
	{
		MPS_ErrPrintf("ERROR: Heap memory alloc failed.\r\n");
		MPS_Free(mpsconfig.memory_work_top);
		return -AG903_ENOMEM;
	}
	mpsconfig.memory_heap_size = MPS_HEAP_SIZE;

	/* MP4コンテナの解析
	 * 以降コンテナ情報が使える */
	err = MPS_Parse(0, STREAM_NAME);
	if (err != AG903_ENONE)
	{
		MPS_ErrPrintf("ERROR: MP4 Parse failed\n");
		return err;
	}

	/* 出力バッファの確保 */
	{
		uint32_t stream_stride;
		mpsctx = MPS_getContext(0);
		if(mpsctx->info.width == 0 && mpsctx->info.height == 0)
		{
			mpsctx->info.width			= STREAM_WIDTH;
			mpsctx->info.height			= STREAM_HEIGHT;
			mpsctx->info.frame_length	= 1;
			mpsctx->info.time_scale		= 30;
		}

		stream_stride = MPS_ALIGNn(64, mpsctx->info.width)
		              * MPS_ALIGNn(32, mpsctx->info.height)
		              * 2;
		/* 一括バッファ確保時に各バッファがアドレスアライン制約を満たすための調整 */
		stream_stride = MPS_ALIGNn(4096, stream_stride);

		/* BMUで使用する分をまとめて確保 */
		gOutbufinfo[0].size_buffer =
			stream_stride * MPS_OUTBUF_COUNT;
		gOutbufinfo[0].buffer =
			MPS_Memalign(gOutbufinfo[0].size_buffer, 4096);
		if(gOutbufinfo[0].buffer == NULL)
		{
			MPS_ErrPrintf("ERROR: [BMU buffer area] allocate faild.\r\n");
			return -AG903_ENOMEM;
		}
		mpsconfig.output_select = GVD_OUTMOD_BMU;

		/* フレームバッファの初期化 */
		uint32_t *p = (uint32_t *)gOutbufinfo[0].buffer;
		for(cnt=0; cnt<gOutbufinfo[0].size_buffer>>2; cnt++)
		{
			*(p + cnt) = 0x00000000;
		}
	}

	/* GVDの初期化 */
	err = MPS_Start(mpsconfig);
	if(err)
	{
		MPS_ErrPrintf("ERROR: MPS_Start() : %d\n", err);
		return err;
	}

	/* 入出力バッファ数を設定 */
	mpsctx = MPS_getContext(0);
	ERRCHK(GVD_ERR_SUCCESS, GVD_SetMaxNumInputBuffers(mpsctx->gvdctx, MPS_INBUF_COUNT));
	ERRCHK(GVD_ERR_SUCCESS, GVD_SetMaxNumOutputBuffers(mpsctx->gvdctx, MPS_OUTBUF_COUNT));

	return 0;
}

static int movie_play(void)
{
	int cnt;
	int32_t err;
	mpsContext *ctx = NULL;

	ctx = MPS_getContext(0);
	gFlagMPSDone = false;

	/* 非同期処理開始 */
	gAsyncTaskID = acre_tsk((T_CTSK*)&ctsk_async);

	/* 動画ストリームのオープン */
	/* ParseしていなければMPS_Open内でParseされる
	 * Parse済みであればコンテナ情報をそのまま使用する */
	err = MPS_Open(0, STREAM_NAME);
	if (err != AG903_ENONE)
	{
		MPS_ErrPrintf("ERROR: Movie file open failed\n");
		return err;
	}

	/* 再生 */
	MPS_Printf("Play #%d\n", 0);
	MPS_Play(0);

	int32_t lpcnt = 0;
	volatile mpsAsyncFlag *f;
	f = &ctx->flag;

	while(1)
	{
		if(f->bDecDone && !f->bSought)
		{
			if(lpcnt < MPS_SINGLE_LOOP_COUNT)
			{
				/* ループ時に先頭フレームへのシークが必要.
				 * 初回のみMPS_Open内で自動的に先頭フレームへシークされる */
				MPS_Seek(0, 0, MPS_SEEK_FRAME);
				MPS_Printf("Play #%d\n", 0);
				MPS_Play(0);
			}
			else
			{
				break;
			}

			lpcnt++;
		}
		sys_dlytsk(100); /* フラグチェック周期 */
	}

	/* 動画ストリームのクローズ */
	MPS_Close(0);

	/* 出力バッファの解放 */
	for(cnt=0; cnt<MPS_OUTBUF_COUNT; cnt++) {
		if(gOutbufinfo[cnt].buffer != NULL) {
			MPS_Free(gOutbufinfo[cnt].buffer);
			gOutbufinfo[cnt].buffer = NULL;
		}
	}

	MPS_Printf("Play done.\r\n");
	gFlagMPSDone = true; /* 再生処理終了を通知 */
	return 0;
}

/* TASK */
void mps_single(void)
{
	/* 変数初期化 */
	{
		gWindowAttributeAddr	= NULL;
		gOSPBufferAddr			= NULL;
		tim_handle				= NULL;
		gAsyncTaskID			= -1;
		sys_memset(gOutbufinfo, 0, sizeof(gOutbufinfo));
		sys_memset(dsp_handle, 0, sizeof(dsp_handle));
		sys_memset(bmu_handle, 0, sizeof(bmu_handle));
		sys_memset(osp_handle, 0, sizeof(osp_handle));
	}

	mpsConfig *mpsconf = MPS_getConfig();

	MPS_Printf("\r\n"
			   "[MOVIE PLAYER SAMPLE : SINGLE STREAM]\r\n"
			   "\r\n");
	MPS_Printf("Setting start >>>\n");

	/* File system */
	{
		ER ercd;

		ercd = mountp('A', 0, (ID)Fsif_GetDevid(FSIF_DEV_CF)); /* CF->'A' */
		if (ercd != E_OK) {
			MPS_ErrPrintf("ERROR: CF mount failed.\r\n");
			return;
		}
	}

	/* GVD */
	{
		int32_t err;
		err = movie_init();
		if(err) {
			goto label_finalize;
		}
	}

	/* BMU */
	ERRCHK(AG903_ENONE, AG903_BMUMgrGetHandle(&bmu_handle[0]));
	ERRCHK(AG903_ENONE, AG903_BMUMgrGetHandle(&bmu_handle[1]));

	/* DSP */
	SetupDisplay();

	/* OSP */
	SetupOSP();

	/* Timer */
	SetupTimer();

	MPS_Printf("<<< Setting done.\r\n");

	movie_play(); /* 再生処理 */

	sys_dlytsk(50); /* 他タスク終了猶予 */

	/* 終了処理 */
label_finalize:
	{
		if(gAsyncTaskID >= 0)
			ERRCHK(E_OK, del_tsk(gAsyncTaskID));

		if(dsp_handle[0] != NULL) {
			ERRCHK(AG903_ENONE, AG903_DSPMgrDisable(dsp_handle[0]));
			while(AG903_DSPMgrCheckStopped(dsp_handle[0]));
			AG903_DSPMgrIntStat intclr = {
				.clr_dspoff		= true,
				.clr_hrz_line	= true,
				.clr_vt_blank	= true,
			};
			AG903_DSPMgrClearIntStat(dsp_handle[0], &intclr);
			ERRCHK(AG903_ENONE, AG903_DSPMgrReleaseHandle(dsp_handle[0]));

			ERRCHK(AG903_ENONE, AG903_OSPMgrClearCommand(osp_handle[0]));
			ERRCHK(AG903_ENONE, AG903_OSPMgrClearFIFO(osp_handle[0]));
			ERRCHK(AG903_ENONE, AG903_OSPMgrReleaseHandle(osp_handle[0]));
		}
		ERRCHK(AG903_ENONE, MPS_Free(gOSPBufferAddr));
		gOSPBufferAddr = NULL;

		ERRCHK(AG903_ENONE, AG903_TIMRMgrStop(tim_handle));
		ERRCHK(AG903_ENONE, AG903_TIMRMgrDeleteIntHandler(tim_handle, (void *)(TimerHandler)));
		ERRCHK(E_OK, del_sem(gSemID_BufSync));

		ERRCHK(AG903_ENONE, MPS_Finalize());
		mpsconf	= MPS_getConfig();
		ERRCHK(AG903_ENONE, MPS_Free(mpsconf->memory_heap_top));
		mpsconf->memory_heap_top = NULL;
		ERRCHK(AG903_ENONE, MPS_Free(mpsconf->memory_work_top));
		mpsconf->memory_work_top = NULL;

		ERRCHK(AG903_ENONE, umountp(0, (ID)Fsif_GetDevid(FSIF_DEV_CF))); /* CFアンマウント */

		ERRCHK(AG903_ENONE, MPS_Free(gWindowAttributeAddr));
		gWindowAttributeAddr = NULL;

		ERRCHK(AG903_ENONE, AG903_BMUMgrDisable(bmu_handle[0]));
		ERRCHK(AG903_ENONE, AG903_BMUMgrReleaseHandle(bmu_handle[0]));
	}

	MPS_Printf("Main task done.\r\n");
	MPS_Printf("\n[SAMPLE END]\n");
}


/***********************************
		   WINDOW CONTROL
***********************************/

static void TimerHandler(AG903_TIMRMgrHandle *handle)
{
	AG903_TIMRMgrClearStatus(handle, 1<<1 | 1<<0);
	WindowUpdate();

	return;
}

/* 表示の更新 */
static void WindowUpdate()
{
	AG903_OSPMgrClearFIFO(osp_handle[0]);
	AG903_OSPMgrSetFIFO(osp_handle[0], 5);
	AG903_OSPMgrEnable(osp_handle[0]);
	return;
}


/* GVDのcommit_output_bufferのコールバック */
static int32_t cbMPSDecodeDone(void *param_gvd, void *param_user, gvdError error)
{
	((void)(error));
	((void)(param_gvd));
	((void)(param_user));
	return 0;
}

/* GVDの致命的エラーのコールバック */
static int32_t cbMPSFatalerror(void *param_gvd, void *param_user, gvdError error)
{
	((void)param_user);
	((void)error);
	gFatal = *(gvdFatalErrorInfo *)param_gvd;
	MPS_ErrPrintf("ERROR: Fatal error detected. cont=%hhu, cause=%hhu\n",
			   gFatal.context_id, gFatal.cause);
	return 0;
}



/* 設定関数 */
static void SetupDisplay(void)
{
	mpsConfig	*mpsconf = MPS_getConfig();
	mpsContext	*mpsctx;

	gWindowAttributeAddr = MPS_Memalign(0x20, 8);
	if(gWindowAttributeAddr == NULL)
	{
		MPS_ErrPrintf("ERROR: Out of memory.\r\n");
		return;
	}

	AG903_DSPMgrCtrlParam		dsp_ctrl		= {0};
	AG903_DSPMgrCMOSParam		dsp_cmos		= {0};
	AG903_DSPMgrWinAttribute	*dsp_attr		= NULL;
	AG903_DSPMgrWindowParam		dsp_win			= {0};
	AG903_DSPMgrIntParam		dsp_int			= {0};

	ERRCHK(AG903_ENONE, AG903_DSPMgrInit());

	MPS_Printf("Initialize display settings...\r\n");

	ERRCHK(AG903_ENONE, AG903_DSPMgrGetHandle(0, &dsp_handle[0]));

	/* PORT */
	AG903_DSPMgrSetPortSel(dsp_handle[0], AG903_VOD0_PORTSEL_LVCMOS24);

	AG903_SPCPrmPllnParam *pllParam;

#if defined(MONITOR_SXGA)

	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];

	pllParam = &DSP_SAMPLE_PLL[DSP_SAMPLE_RES_SXGA];

#elif defined(MONITOR_FHD)
	dsp_ctrl.ip_sel		 		= AG903_DSP_VMODE_NONINTERLACE;
	dsp_ctrl.hrz_framesize		= 1920;
	dsp_ctrl.vt_framesize		= 1080;
	dsp_ctrl.syncparam			= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_FULLHD];

	pllParam = &DSP_SAMPLE_PLL[DSP_SAMPLE_RES_FULLHD];

#elif defined(MONITOR_UXGA)
	AG903_DSPMgrSyncParam dsp_syncparam = {
		.rgbde_sel				= AG903_DSP_RGBDE_SIGNAL_DATA,
		.field_hsync_polarity	= AG903_DSP_POLARITY_POSI,
		.vsync_polarity			= AG903_DSP_POLARITY_POSI,
		.hrz_pulsewidth			= 192,
		.hrz_backporch			= 304,
		.hrz_frontporch			= 64,
		.vt_pulsewidth			= 3,
		.vt_backporch			= 46,
		.vt_frontporch			= 1,
	};

	dsp_ctrl.ip_sel		 		= AG903_DSP_VMODE_NONINTERLACE;
	dsp_ctrl.hrz_framesize		= 1600;
	dsp_ctrl.vt_framesize		= 1200;
	dsp_ctrl.syncparam			= &dsp_syncparam;

	/* PLL */
	pllParam.ms		= 4;
	pllParam.ns		= 27;
	pllParam.div		= 0;
	pllParam.src		= AG903_SPC_PLL1_CLKSRC_XOUT;
	pllParam.frange	= 3; /* dclk = 162000000 */
	pllParam.en		= 1;

#elif defined(MONITOR_WUXGA)
	dsp_ctrl.ip_sel				= AG903_DSP_VMODE_NONINTERLACE;
	dsp_ctrl.hrz_framesize		= 1920;
	dsp_ctrl.vt_framesize		= 1200;
	dsp_ctrl.syncparam			= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_WUXGA];

	pllParam = &DSP_SAMPLE_PLL[DSP_SAMPLE_RES_WUXGA];

#elif defined(MONITOR_VGA)
	dsp_ctrl.ip_sel				= AG903_DSP_VMODE_NONINTERLACE;
	dsp_ctrl.hrz_framesize		= 640;
	dsp_ctrl.vt_framesize		= 480;
	dsp_ctrl.syncparam			= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_VGA];

	pllParam = &DSP_SAMPLE_PLL[DSP_SAMPLE_RES_VGA];

#else
#error "ERROR: MONITOR UNSPECIFIED"
#endif

	AG903_SPCPrmPllnParam pll = {0};
	AG903_SPCPrmSetPll1Ctrl(&pll); /* PLL1初期化 */
	sys_delayus(50);
	AG903_SPCPrmSetPll1Ctrl(pllParam);
	sys_delayus(50); /* PLLロックアップ待ち */

	dsp_cmos.dotclk_polarity	= AG903_VOD_DOTCLK_LATCH_FALL;
	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;

	ERRCHK(AG903_ENONE,
		   AG903_DSPMgrSetCMOSParam(dsp_handle[0], &dsp_ctrl, &dsp_cmos));


	/* VSYNC EVENT OUTPUT ENABLE */
	dsp_int.trigger_out		= AG903_DSP_EVENT_VT;
	dsp_int.trigger_vt		= AG903_DSP_TRG_VT_START_OF_VBLANK;
	dsp_int.int_vt_blank	= AG903_DSP_INT_START_OF_VBLANK;
	dsp_int.mask_vt_blank	= true;
	dsp_int.mask_dspoff		= true;
	dsp_int.mask_error		= true;
	dsp_int.mask_hrz_line	= true;
	AG903_DSPMgrSetIntParam(dsp_handle[0], &dsp_int);


	dsp_win.update_timing		= AG903_DSP_ATTR_END_OF_VSYNC;
	dsp_win.background			= 0xFF000000;
	dsp_win.num_config			= 1;
	dsp_win.num_attr			= 1;
	dsp_win.palette_update		= 0;
	dsp_win.window_attr_base	= (uint32_t)gWindowAttributeAddr;
	dsp_win.window_attr_update	= 1;
	ERRCHK(AG903_ENONE,
		   AG903_DSPMgrSetWindowParam(dsp_handle[0], &dsp_win));

	/* Window 0 */
	mpsctx = MPS_getContext(0);
	ERRCHK(AG903_ENONE, AG903_DSPMgrGetAttribute(dsp_handle[0], 0, &dsp_attr));
	sys_memset(dsp_attr, 0, 0x20);

	if(WINDOW_WIDTH	== 0)
	{
		if(mpsctx->info.width != 0)
			dsp_attr[0].source_width		= mpsctx->info.width;
		else
			dsp_attr[0].source_width		= STREAM_WIDTH;
	}
	else
	{
		dsp_attr[0].source_width			= WINDOW_WIDTH;
	}
	if(WINDOW_HEIGHT == 0)
	{
		if(mpsctx->info.height != 0)
			dsp_attr[0].source_height		= mpsctx->info.height;
		else
			dsp_attr[0].source_height		= STREAM_HEIGHT;
	}
	else
	{
		dsp_attr[0].source_height			= WINDOW_HEIGHT;
	}

	/* ドットバイドット */
	if(dsp_attr[0].source_height == dsp_ctrl.hrz_framesize &&
	   dsp_attr[0].source_width == dsp_ctrl.vt_framesize)
	{
		dsp_attr[0].destination_width	= dsp_ctrl.hrz_framesize;
		dsp_attr[0].destination_height	= dsp_ctrl.vt_framesize;
		dsp_attr[0].position_x			= 0;
		dsp_attr[0].position_y			= 0;
	}
	/* アスペクト比固定, 内接リサイズ */
	else
	{
		uint32_t x = dsp_attr[0].source_width*dsp_ctrl.vt_framesize / dsp_attr[0].source_height;
		uint32_t y = dsp_attr[0].source_height*dsp_ctrl.hrz_framesize / dsp_attr[0].source_width;
		if(x <= dsp_ctrl.hrz_framesize)
		{
			dsp_attr[0].destination_width	= x;
			dsp_attr[0].destination_height	= dsp_ctrl.vt_framesize;
			dsp_attr[0].position_x			= (dsp_ctrl.hrz_framesize-x)/2;
			dsp_attr[0].position_y			= 0;
		}
		else //if(y <= dsp_ctrl.vt_framesize)
		{
			dsp_attr[0].destination_width	= dsp_ctrl.hrz_framesize;
			dsp_attr[0].destination_height	= y;
			dsp_attr[0].position_x			= 0;
			dsp_attr[0].position_y			= (dsp_ctrl.vt_framesize-y)/2;
		}
	}

	dsp_attr[0].framebuffer_base = 0x1 << 29 | /* BMU */
								   0x0 << 28 | /* 0:Sink, 1:Src */
								   0x0 << 24 | /* TextureID */
								   0x0; /* offset */

	dsp_attr[0].hrz_size				= dsp_attr[0].source_width * 2;
	dsp_attr[0].conf.default_alpha		= 0xFF;
	dsp_attr[0].conf.framebuffer_format	= AG903_DSP_FFMT_YUV422_BT601_LIMIT;
	dsp_attr[0].conf.valid				= true;

	ERRCHK(AG903_ENONE, AG903_DSPMgrSetAttribute(dsp_handle[0], 0));

	/* 出力先にBMUを指定 */
	ERRCHK(GVD_OUTMOD_BMU, mpsconf->output_select);

	int32_t bmuid;
	uint32_t addr, paddr, fbase, stride;
	uint8_t bmusrc	= AG903_BMU_SRC_GVD0;
	AG903_DSPMgrWinAttribute *attr;

	ERRCHK(AG903_ENONE, AG903_BMUMgrSetMode(bmu_handle[0],
											AG903_BMU_SINK_WAIT_DISABLE,
											AG903_BMU_BUF_MGR_MODE1));
	mpsctx	= MPS_getContext(0);
	ERRCHK(AG903_ENONE, GVD_GetBMUId(mpsctx->gvdctx, &bmuid));
	switch (bmuid)
	{
	case 0:
		bmusrc	= AG903_BMU_SRC_GVD0;
		break;
	case 1:
		bmusrc	= AG903_BMU_SRC_GVD1;
		break;
	case 2:
		bmusrc	= AG903_BMU_SRC_GVD2;
		break;
	case 3:
		bmusrc	= AG903_BMU_SRC_GVD3;
		break;
	}
	ERRCHK(AG903_ENONE, AG903_BMUMgrSetSrcModule(bmu_handle[0], bmusrc));
	ERRCHK(AG903_ENONE, AG903_BMUMgrAddSinkModule(bmu_handle[0], AG903_BMU_SINK_SYS0));
	ERRCHK(AG903_ENONE, AG903_BMUMgrAddSinkModule(bmu_handle[0], AG903_BMU_SINK_DSP0));
	stride	= gOutbufinfo[0].size_buffer/MPS_OUTBUF_COUNT;
	paddr	= (uint32_t)gOutbufinfo[0].buffer;
	ERRCHK(AG903_ENONE, AG903_BMUMgrSetBufferConfig(bmu_handle[0], (void *)paddr,
													stride, MPS_OUTBUF_COUNT));
	ERRCHK(AG903_ENONE, AG903_BMUMgrGetBMUSinkAddress(bmu_handle[0], &fbase));

	ERRCHK(AG903_ENONE, AG903_DSPMgrGetAttribute(dsp_handle[0], 0, &attr));
	attr[0].framebuffer_base = fbase;
	ERRCHK(AG903_ENONE, AG903_DSPMgrSetAttribute(dsp_handle[0], 0));

	ERRCHK(AG903_ENONE, AG903_BMUMgrGetBMUSrcAddress(bmu_handle[0], &addr));
	ERRCHK(AG903_ENONE, GVD_SetBMUAddr(mpsctx->gvdctx, (void *)addr));
	ERRCHK(AG903_ENONE, AG903_BMUMgrEnable(bmu_handle[0]));

	/* 表示ON */
	ERRCHK(AG903_ENONE, AG903_DSPMgrEnable(dsp_handle[0]));

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

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

static void SetupOSP(void)
{
	ERRCHK(AG903_ENONE, AG903_OSPMgrInit());
	ERRCHK(AG903_ENONE, AG903_OSPMgrGetHandle(&osp_handle[0]));
	gOSPBufferAddr	= MPS_Malloc(5*4 + 32);
	AG903_OSPCmdBuf cmdbuf;
	cmdbuf.addr		= gOSPBufferAddr;
	cmdbuf.size		= (5*4 + 32);
	cmdbuf.mode		= AG903_OSP_BUFMODE_FIFO;
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommandBuf(osp_handle[0], &cmdbuf, true));
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommand(osp_handle[0], AG903_OSP_CMD_EVNT_WAIT, AG903_EVENT40_DSP0));
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommand(osp_handle[0], AG903_OSP_CMD_BUS_BURST_FIX, 2));
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommand(osp_handle[0], AG903_OSP_CMD_BUS_ADDR, (uint32_t)&AG903_SSC->BMU_CONTROL));
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommand(osp_handle[0], AG903_OSP_CMD_BUS_DATA, 1<<AG903_SSC_BMU_CONTROL_CLR_SNK_POS));
	ERRCHK(AG903_ENONE, AG903_OSPMgrSetCommand(osp_handle[0], AG903_OSP_CMD_BUS_DATA, 1<<AG903_SSC_BMU_CONTROL_SET_SNK_POS));
}

static void SetupTimer(void)
{
	ERRCHK(AG903_ENONE, AG903_TIMRMgrInit());

	T_CSEM pk_csem	= {
		.sematr		= TA_TPRI,
		.isemcnt	= 1,
		.maxsem		= 1,
		.name		= "sem_window",
	};
	gSemID_BufSync = acre_sem(&pk_csem);
	if(gSemID_BufSync < 0)
	{
		MPS_ErrPrintf("ERROR: Sem create failed.\r\n");
		return;
	}

	ERRCHK(AG903_ENONE, AG903_TIMRMgrGetHandle(&tim_handle));
	ERRCHK(AG903_ENONE, AG903_TIMRMgrSetIntHandler(tim_handle, (void *)(TimerHandler), tim_handle));
	ERRCHK(AG903_ENONE, AG903_TIMRMgrEnableIntMask(tim_handle, AG903_TIMR_COMPARE_BIT));
	ERRCHK(AG903_ENONE, AG903_TIMRMgrDisableIntMask(tim_handle, AG903_TIMR_OVERFLOW_BIT));
	ERRCHK(AG903_ENONE, AG903_TIMRMgrSetCount(tim_handle, 0));

	mpsContext *mpsctx	= MPS_getContext(0);

	/* システムクロックをカウント */
	AG903_TIMRMgrTickCntParam param	= {
		.match		= 0,
		.resolution	= AG903_TIMR_CNT_SYSCLK,
		.oneshot	= false,
		.output		= NULL,
	};

	/* フレームレートに合わせてカウンタ周期を決定 */
	if(mpsctx->info.time_scale != 0)
	{
		param.period = (uint32_t)(AG903_SAMPLE_SYSCLK *
								  ((float)mpsctx->info.frame_length / mpsctx->info.time_scale));
	}
	else
	{
		param.period = (uint32_t)(AG903_SAMPLE_SYSCLK * ((float)1 / 30));
	}
	ERRCHK(AG903_ENONE, AG903_TIMRMgrSetTickCountMode(tim_handle, &param));
	ERRCHK(AG903_ENONE, AG903_TIMRMgrStart(tim_handle));
}
