/**
 * @brief        DisplayManager sample
 * @file         dsp_sample.c
 * @author       AXELL
 * @history      2017_06_30 構成見直し<p>
 * @history      2019_12_27  [SDK3.0] DSPサンプル LVDS-Dual出力時のドットクロック値修正 (#1897)
 * @history      2019_12_27  [SDK3.0] DSPサンプルにLVDSとバックライトのパラメータ選択を追加 (#1801)
 * @history      2023_03_31  [SDK3.5] 表示のウインドウ属性の予約領域を確実にゼロクリアするように修正 (#4640)
 * @description  none
 * @note         このファイルは tab幅=4、改行コード=CRLF で記述されています.
 */

/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2023 AXELL CORPORATION, all rights reserved.
 */

#include <math.h>
#include "kernel.h"

#include "sample_common.h"
#include "dsp/dspmgr.h"
#include "dsp_sample.h"
#include "dsp_common.h"

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

/* DSP各設定パラメータ */
static DSP_SAMPLE_PARAM			dsp;


static int32_t init_module(void);
static int32_t term_module(void);
static void DSP_help(void);
static void DSP_wait_enter(void);
static uint8_t DSP_GetNum_Wait(const char *prompt, uint8_t min, uint8_t max);
static int32_t DSP_setup_data(uint32_t *framebuffer, uint32_t *size);
static int32_t DSP_setup_data2(uint32_t *framebuffer, uint32_t *size);
static int32_t DSP_setup_sys(uint32_t output);
static int32_t DSP_setup_pll(uint32_t pll_ch, uint32_t resolution);
static int32_t DSP_setup_cmos(void);
static int32_t DSP_setup_lvds(uint32_t lvds_mode, uint32_t resolution);
static int32_t DSP_setup_window(uint32_t dsp_ch);
static int32_t DSP_setup_lut(uint32_t dsp_ch, uint32_t lut_mode);
static int32_t DSP_setup_dith(uint32_t dsp_ch, uint32_t dith_mode);

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

/**
 * @brief		メイン処理
 * @param[in]	param	制御パラメータ
 * @retval		なし
 * @note		なし
 */
void DSP_main(uint8_t param)
{
	((void)param);
	int32_t		rc = AG903_ENONE;
	int32_t		idx;
	uint8_t		mode = 0;
	char		input[2+2];

	rc = init_module();

	if(rc == AG903_ENONE)
		rc = DSP_setup_data(&dsp.fb_addr, &dsp.fb_size);
	if(rc == AG903_ENONE)
		rc = DSP_setup_data2(&dsp.fb_addr2, &dsp.fb_size2);

	if(rc == AG903_ENONE) {
		DSP_help();
		while ((rc == AG903_ENONE) && (mode != 0xFF)) {
			DSP_Printf(" -- Enter the sample number(00h<HELP> to FFh): ");
			COM_GetNum_Wait(input, sizeof(input));

			mode = (ASCtoBIN(input[0]) << 4)
				   | (ASCtoBIN(input[1]) << 0);

			switch (mode) {
				case 0x01:		/* LVDSシングルリンク */
					DSP_Printf(" * Select LCD dependent parameter\r\n");
					dsp.lvds_format = DSP_GetNum_Wait("LVDS output format (0-2): ", 0, 2);
					dsp.bpwr_brightness = 50*DSP_GetNum_Wait("Backlight brightness (1:50% 2:100% if supported): ", 1, 2);
					if(rc == AG903_ENONE) rc = DSP_setup_sys(DSP_SAMPLE_OUT_LVDS);
					if(rc == AG903_ENONE) rc = DSP_setup_pll(DSP_SAMPLE_PLLCH_2, DSP_SAMPLE_RES_XGA);
					if(rc == AG903_ENONE) rc = DSP_setup_lvds(DSP_SAMPLE_LVDS_SINGLE, DSP_SAMPLE_RES_XGA);
					if(rc == AG903_ENONE) rc = DSP_setup_window(1);
					if(rc == AG903_ENONE) rc = AG903_DSPMgrEnable(dsp.handle[1]);
					if(rc == AG903_ENONE) {
						DSP_Printf(" -- Press enter to exit.");
						DSP_wait_enter();
						rc = AG903_DSPMgrDisable(dsp.handle[1]);
						while(AG903_DSPMgrCheckStopped(dsp.handle[1]));
					}
					break;
				case 0x02:		/* LVDSデュアルリンク */
					DSP_Printf(" * Select LCD dependent parameter\r\n");
					dsp.lvds_format = DSP_GetNum_Wait("LVDS output format (0-2): ", 0, 2);
					dsp.bpwr_brightness = 50*DSP_GetNum_Wait("Backlight brightness (1:50% 2:100% if supported): ", 1, 2);
					if(rc == AG903_ENONE) rc = DSP_setup_sys(DSP_SAMPLE_OUT_LVDS);
					if(rc == AG903_ENONE) rc = DSP_setup_pll(DSP_SAMPLE_PLLCH_2, DSP_SAMPLE_RES_SXGA);
					if(rc == AG903_ENONE) rc = DSP_setup_lvds(DSP_SAMPLE_LVDS_DUAL, DSP_SAMPLE_RES_SXGA);
					if(rc == AG903_ENONE) rc = DSP_setup_window(1);
					if(rc == AG903_ENONE) rc = AG903_DSPMgrEnable(dsp.handle[1]);
					if(rc == AG903_ENONE) {
						DSP_Printf(" -- Press enter to exit.");
						DSP_wait_enter();
						rc = AG903_DSPMgrDisable(dsp.handle[1]);
						while(AG903_DSPMgrCheckStopped(dsp.handle[1]));
					}
					break;
				case 0x10:		/* CMOS */
					if(rc == AG903_ENONE) rc = DSP_setup_sys(DSP_SAMPLE_OUT_CMOS);
					if(rc == AG903_ENONE) rc = DSP_setup_pll(DSP_SAMPLE_PLLCH_1, DSP_SAMPLE_RES_SXGA);
					if(rc == AG903_ENONE) rc = DSP_setup_cmos();
					if(rc == AG903_ENONE) rc = DSP_setup_window(0);
					if(rc == AG903_ENONE) rc = AG903_DSPMgrEnable(dsp.handle[0]);
					if(rc == AG903_ENONE) {
						DSP_Printf(" -- Press enter to exit.");
						DSP_wait_enter();
						rc = AG903_DSPMgrDisable(dsp.handle[0]);
						while(AG903_DSPMgrCheckStopped(dsp.handle[0]));
					}
					break;
				case 0x11:		/* CMOS(色補正テーブル) */
					if(rc == AG903_ENONE) rc = DSP_setup_sys(DSP_SAMPLE_OUT_CMOS);
					if(rc == AG903_ENONE) rc = DSP_setup_pll(DSP_SAMPLE_PLLCH_1, DSP_SAMPLE_RES_SXGA);
					if(rc == AG903_ENONE) rc = DSP_setup_cmos();
					if(rc == AG903_ENONE) rc = DSP_setup_lut(0, DSP_SAMPLE_LUT_REVERSE);
					dsp.lut_changed[0] = true;
					if(rc == AG903_ENONE) rc = DSP_setup_window(0);
					if(rc == AG903_ENONE) rc = AG903_DSPMgrEnable(dsp.handle[0]);
					if(rc == AG903_ENONE) {
						DSP_Printf(" -- Press enter to exit.");
						DSP_wait_enter();
						rc = AG903_DSPMgrDisable(dsp.handle[0]);
						while(AG903_DSPMgrCheckStopped(dsp.handle[0]));
					}
					break;
				case 0x12:		/* CMOS(ディザリング) */
					if(rc == AG903_ENONE) rc = DSP_setup_sys(DSP_SAMPLE_OUT_CMOS);
					if(rc == AG903_ENONE) rc = DSP_setup_pll(DSP_SAMPLE_PLLCH_1, DSP_SAMPLE_RES_SXGA);
					if(rc == AG903_ENONE) rc = DSP_setup_cmos();
					if(rc == AG903_ENONE) rc = DSP_setup_dith(0, DSP_SAMPLE_DITH_SXGA);
					dsp.dith_enabled[0] = true;
					if(rc == AG903_ENONE) rc = DSP_setup_window(0);
					if(rc == AG903_ENONE) rc = AG903_DSPMgrEnable(dsp.handle[0]);
					if(rc == AG903_ENONE) {
						DSP_Printf(" -- Press enter to exit.");
						DSP_wait_enter();
						rc = AG903_DSPMgrDisable(dsp.handle[0]);
						while(AG903_DSPMgrCheckStopped(dsp.handle[0]));
					}
					break;
				case 0xFF:
					break;

				default:
					DSP_help();
			}

			for(idx=0; idx<AG903_DSP_CH_NUM; idx++) {
				if(dsp.lut_changed[idx]) {
					DSP_setup_lut(idx, DSP_SAMPLE_LUT_OFF);
					dsp.lut_changed[idx] = false;
				}
				if(dsp.dith_enabled[idx]) {
					DSP_setup_dith(idx, DSP_SAMPLE_DITH_DISABLE);
					dsp.dith_enabled[idx] = false;
				}
			}
		}
	}

	rc = term_module();

	return;
}

/** サブタスク（処理なし） */
void DSP_sub(uint32_t param)
{
	(void)param;
	return;
}

/* 操作説明 */
static void DSP_help(void)
{
	DSP_Printf("\t# 00 ... Help\r\n");

	DSP_Printf("\t# 01 ... LVDS(XGA - SINGLE Link)\r\n");
	DSP_Printf("\t# 02 ... LVDS(SXVGA - DUAL Link)\r\n");
	DSP_Printf("\t# 10 ... CMOS(RGB 24bit)\r\n");
	DSP_Printf("\t# 11 ... CMOS(Color correction)\r\n");
	DSP_Printf("\t# 12 ... CMOS(Dithering)\r\n");

	DSP_Printf("\t# FF ... Exit Sample\r\n");
}

/* 改行待機 */
static void DSP_wait_enter(void)
{
	char		input[2];
	COM_GetNum_Wait(input, sizeof(input));
}

static uint8_t DSP_GetNum_Wait(const char *prompt, uint8_t min, uint8_t max)
{
	char input[1+2];
	uint8_t num;

	if (max > 15 || min > max) {
		DSP_Printf("Error\r\n");
		return 0;
	}

	do {
		DSP_Printf("%s", prompt);
		COM_GetNum_Wait(input, sizeof(input));
		num = ASCtoBIN(input[0]) & 0xf;
	} while (num < min || max < num);
	
	return num;
}

/**
 * @brief		フレームバッファ作成
 * @param[out]	framebuffer		フレームバッファアドレス
 * @param[out]	size			フレームバッファサイズ
 * @retval		AG903_ENONE		正常終了
 * @retval		-AG903_ENOMEM	メモリ不足
 * @note		framebufferにRGB(24bit)で単色グラデーションを書き込む.
 */
static int32_t DSP_setup_data(uint32_t *framebuffer, uint32_t *size)
{
	const uint32_t	PX_SIZE		= 3;	/* ピクセルサイズ[Byte] */
	const uint32_t	COL_NUM		= 3;	/* 色数 */
	const uint32_t	COL_DEPTH	= 256;	/* 色深度 */
	int32_t			idx, col;
	uint8_t			*fb;

	*size = COL_DEPTH * PX_SIZE * COL_NUM;
	fb = sys_malloc(*size);
	if(fb == NULL)
		return -AG903_ENOMEM;
	else
		sys_memset(fb, 0, *size);

	for(col=0; col<(int32_t)COL_NUM; col++) { /* (MSB)23<-[RGB]->0(LSB) */
		for(idx=0; idx<(int32_t)COL_DEPTH; idx++) {
			fb[(col*PX_SIZE*COL_DEPTH)+(idx*3) + 0] = col%COL_NUM == 2 ? idx : 0;
			fb[(col*PX_SIZE*COL_DEPTH)+(idx*3) + 1] = col%COL_NUM == 1 ? idx : 0;
			fb[(col*PX_SIZE*COL_DEPTH)+(idx*3) + 2] = col%COL_NUM == 0 ? idx : 0;
		}
	}
	*framebuffer = (uint32_t)fb;

	return AG903_ENONE;
}

/**
 * @brief		フレームバッファ作成2
 * @param[out]	framebuffer		フレームバッファアドレス
 * @param[out]	size			フレームバッファサイズ
 * @retval		AG903_ENONE		正常終了
 * @retval		-AG903_ENOMEM	メモリ不足
 * @note		framebufferにRGB(32bit)で2次元のグラデーション上に丸模様を書き込む.
 */
static int32_t DSP_setup_data2(uint32_t *framebuffer, uint32_t *size)
{
	const int	w = 256;
	const int	h = 256;
	uint8_t		*fb;

	*size = w*h*4;
	fb = sys_malloc(*size);
	if(fb == NULL)
		return -AG903_ENOMEM;
	else
		sys_memset(fb, 0, *size);

	const int cx = w/2;
	const int cy = h/2;
	int y;
	int x;
	for (y = 0; y < h; y++) {
		for (x = 0; x < w; x++) {
			int ix = (y*w + x)*4;

			// Destination color
			fb[ix+0] = x;
			fb[ix+1] = x/2 + y/2;
			fb[ix+2] = y;
			fb[ix+3] = 0xff;
			
			// Source alpha (2x2 over sampling)
			// Source color is 0xFF constant
			float ao;
			int ii;
			for (ao = 0.0f, ii = 0; ii < 2*2; ii++) {
				const int xo[4] = {0,1,0,1};
				const int yo[4] = {0,0,1,1};
				float fdx = (float)(2*x+xo[ii] - 2*cx);
				float fdy = (float)(2*y+yo[ii] - 2*cy);
				float fd  = sqrtf(fdx*fdx + fdy*fdy);
				const int r = w*2;
				if (fd < (float)(r/2)*0.8f && fd > (float)(r/2)*0.6) {
					ao += 1.0f/(float)(2*2);
				}
			}

			// Blend Src and Dest
			fb[ix+0] = (uint8_t)((float)0xff*ao + (float)(fb[ix+0])*(1.0f-ao));
			fb[ix+1] = (uint8_t)((float)0xff*ao + (float)(fb[ix+1])*(1.0f-ao));
			fb[ix+2] = (uint8_t)((float)0xff*ao + (float)(fb[ix+2])*(1.0f-ao));
			fb[ix+3] = (uint8_t)((float)0xff*ao + (float)(fb[ix+3])*(1.0f-ao));
		}
	}
	*framebuffer = (uint32_t)fb;

	return AG903_ENONE;
}

/**
 * @brief		モジュール初期化
 * @param[in]	なし
 * @retval		AG903_ENONE		正常終了
 * @note		なし
 */
static int32_t init_module(void)
{
	int32_t						rc = AG903_ENONE;
	int8_t						ch;

	/* 設定構造体初期化 */
	sys_memset(&dsp, 0, sizeof(DSP_SAMPLE_PARAM));

	/* 動的メモリ初期化 */
	rc = sys_meminit(AG903_SAMPLE_VRAM_ALLOC_SIZE,
					 (void *)AG903_SAMPLE_VRAM_ADDR,
					 SYSMEM_NORMAL_CACHE_OFF);

	/* DisplayManager初期化、ハンドル取得 */
	if (rc == AG903_ENONE) {
		rc = AG903_DSPMgrInit();

		for (ch=0; ch<AG903_DSP_CH_NUM; ch++) {

			if (rc == AG903_ENONE) {
				rc = AG903_DSPMgrGetHandle(ch, &dsp.handle[ch]);	/* ハンドル取得 */
			}

			if (rc == AG903_ENONE) {
				rc = AG903_DSPMgrDisable(dsp.handle[ch]);			/* 表示回路OFF */
				while(AG903_DSPMgrCheckStopped(dsp.handle[ch]));
			}
		}
	}

	/* ウィンドウアトリビュート用メモリの確保 */
	if(rc == AG903_ENONE) {
		/* アトリビュート1つ 32[Byte] */
		void *adr = sys_memalign(0x20 * AG903_DSP_WND_MAX, 8);
		if(adr == NULL)
			rc = -AG903_ENOMEM;
		else
			dsp.attr_addr = (uint32_t)adr;
	}

	return rc;
}

/*
 * 終了処理
 */
static int32_t term_module(void)
{
	uint8_t	ch;

	/* DisplayManager終了 */
	for (ch=0; ch<AG903_DSP_CH_NUM; ch++) {
		if (dsp.handle[ch]) {
			AG903_DSPMgrDisable(dsp.handle[ch]);		/* "DSP表示回路OFF" */
			while(AG903_DSPMgrCheckStopped(dsp.handle[ch]));
			AG903_DSPMgrReleaseHandle(dsp.handle[ch]);	/* "DSPハンドル解放 */
		}
	}

	/* 動的メモリ終了 */
	sys_memfinal();

	return AG903_ENONE;
}

/*
 * 端子関連設定
 */
static int32_t DSP_setup_sys(uint32_t output)
{
	/* ピンファンクションはボード依存部で設定済み */

	/* 各表示回路へのクロック供給元選択 */
	AG903_SPCPrmClkSel clksrc;
	AG903_SPCPrmGetClkSrc(&clksrc);
	if(output == DSP_SAMPLE_OUT_CMOS) {
		clksrc.dt0	= AG903_SPC_DT0_CLKSRC_PLL1;
	}
	else if(output == DSP_SAMPLE_OUT_LVDS) {
		clksrc.dt1	= AG903_SPC_DT1_CLKSRC_PLL2;
		clksrc.lvds	= AG903_SPC_LVDS_CLKSRC_DSP1;
	}
	AG903_SPCPrmSetClkSrc(&clksrc);


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

		Board_SelectDviTX();
	}
	else {
		Board_BacklightSel(0, 1);
		Board_BacklightSel(1, 1);
	}

	return AG903_ENONE;
}

/*
 * クロック関連設定
 */
static int32_t DSP_setup_pll(uint32_t pll_ch, uint32_t resolution)
{
	int32_t rc = AG903_ENONE;
	AG903_SPCPrmPllnParam pll;

	/* PLLの現在の設定値を取得 */
	if(pll_ch == DSP_SAMPLE_PLLCH_1)		{ AG903_SPCPrmGetPll1Ctrl(&pll); }
	else if(pll_ch == DSP_SAMPLE_PLLCH_2)	{ AG903_SPCPrmGetPll2Ctrl(&pll); }
	else									{ rc = -AG903_EINVAL; }

	if(rc == AG903_ENONE) {
		/* PLLが動作中の場合は無効にする */
		if(pll.en == 1) {
			pll.en = 0;
			if(pll_ch == DSP_SAMPLE_PLLCH_1)	{ AG903_SPCPrmSetPll1Ctrl(&pll); }
			else								{ AG903_SPCPrmSetPll2Ctrl(&pll); }
		}

		/* display_common.hから該当のPLL設定値を取得 */
		if(resolution == DSP_SAMPLE_RES_XGA) {
			pll = DSP_SAMPLE_PLL[DSP_SAMPLE_RES_XGA];
		}
		else if(resolution == DSP_SAMPLE_RES_SXGA) {
			pll = DSP_SAMPLE_PLL[DSP_SAMPLE_RES_SXGA];

			if(pll_ch == DSP_SAMPLE_PLLCH_2) {
				/* LVDS-Dual SXGA 適用値54[MHz] */
				pll.div = ((pll.div + 1) * 2) - 1;
			}
		}
		else {
			rc = -AG903_EINVAL;
		}

		/* PLLに設定を反映 */
		if(pll_ch == DSP_SAMPLE_PLLCH_1) {
			AG903_SPCPrmSetPll1Ctrl(&pll);
		} else {
			AG903_SPCPrmSetPll2Ctrl(&pll);
		}
		sys_delayus(50); /* ロックアップ待ち */
	}

	return rc;
}

/*
 * DisplayManager設定（CMOS出力）
 */
static int32_t DSP_setup_cmos(void)
{
	int32_t rc = AG903_ENONE;

	/* PORT */
	AG903_DSPMgrSetPortSel(dsp.handle[0], AG903_VOD0_PORTSEL_LVCMOS24);

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

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

	dsp.param_int.bmureq_timing				= AG903_DSP_BMU_WINDATA;
	dsp.param_int.mask_dspoff				= true;
	dsp.param_int.mask_error				= true;
	dsp.param_int.mask_hrz_line				= true;
	dsp.param_int.mask_vt_blank				= true;
	dsp.param_int.trigger_out				= AG903_DSP_EVENT_VT;
	dsp.param_int.trigger_vt				= AG903_DSP_TRG_VT_START_OF_VBLANK;

    dsp.param_win.update_timing				= AG903_DSP_ATTR_END_OF_VSYNC;
    dsp.param_win.background				= 0x00000000;
    dsp.param_win.num_config				= 2;
    dsp.param_win.num_attr					= 2;
    dsp.param_win.palette_update			= 0;
    dsp.param_win.window_attr_base			= dsp.attr_addr;
    dsp.param_win.window_attr_update		= 1;


	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetCMOSParam(dsp.handle[0], &dsp.param_ctrl, &dsp.param_cmos);
	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetIntParam(dsp.handle[0], &dsp.param_int);
    if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetWindowParam(dsp.handle[0], &dsp.param_win);

	return rc;
}

/*
 * DisplayManager設定（LVDS出力）
 */
static int32_t DSP_setup_lvds(uint32_t lvds_mode, uint32_t resolution)
{
	int32_t rc = AG903_ENONE;

	/* ポート設定 */
	if(lvds_mode == DSP_SAMPLE_LVDS_SINGLE) {
		rc = AG903_DSPMgrSetPortSel(dsp.handle[1], AG903_VOD1_PORTSEL_LVDS_SINGLE);
	} else if(lvds_mode == DSP_SAMPLE_LVDS_DUAL) {
		rc = AG903_DSPMgrSetPortSel(dsp.handle[1], AG903_VOD1_PORTSEL_LVDS_DUAL);
	} else {
		rc = -AG903_EINVAL;
	}

	/* 解像度 */
	if(resolution == DSP_SAMPLE_RES_XGA) {
		dsp.param_ctrl.syncparam		= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_XGA];
		dsp.param_ctrl.hrz_framesize	= 1024;
		dsp.param_ctrl.vt_framesize		= 768;

		dsp.param_lvds.freq_range		= AG903_VOD_FR_54_100MHZ;
	} else if(resolution == DSP_SAMPLE_RES_SXGA) {
		dsp.param_ctrl.syncparam		= &DSP_SAMPLE_SYNCPARAM[DSP_SAMPLE_RES_SXGA];
		dsp.param_ctrl.hrz_framesize	= 1280;
		dsp.param_ctrl.vt_framesize		= 1024;

		dsp.param_lvds.freq_range		= AG903_VOD_FR_54_100MHZ;
	} else {
		rc = -AG903_EINVAL;
	}

	dsp.param_ctrl.ip_sel				= AG903_DSP_VMODE_NONINTERLACE;

	dsp.param_lvds.field_polarity		= AG903_DSP_POLARITY_POSI;
	dsp.param_lvds.hsync_polarity		= AG903_DSP_POLARITY_NEGA;
	dsp.param_lvds.vsync_polarity		= AG903_DSP_POLARITY_NEGA;
	dsp.param_lvds.rgbde_polarity		= AG903_DSP_POLARITY_POSI;
	dsp.param_lvds.colordetect_polarity = AG903_DSP_POLARITY_POSI;
	dsp.param_lvds.field_en				= true;
	dsp.param_lvds.hsync_en				= true;
	dsp.param_lvds.vsync_en				= true;
	dsp.param_lvds.rgbde_en				= true;
	dsp.param_lvds.colordetect_en		= false;
	dsp.param_lvds.pixeldata_en			= true;
	dsp.param_lvds.macro_power			= AG903_VOD_PDX_NORMAL;
	dsp.param_lvds.auto_powermanage		= AG903_VOD_PM_AUTO;
	dsp.param_lvds.ctrl0				= 0;
	dsp.param_lvds.ctrl1				= 0;
	dsp.param_lvds.format				= dsp.lvds_format;

	dsp.param_bpwr.backlight_en			= 1;
	dsp.param_bpwr.cnt_unit				= 1;
	dsp.param_bpwr.panel_pwroncnt		= 100*MSEC;
	dsp.param_bpwr.backlight_pwroncnt	= 100*MSEC;
	dsp.param_bpwr.backlight_oncnt		= 100*MSEC;
	dsp.param_bpwr.panel_pwroffcnt		= 1*MSEC;
	dsp.param_bpwr.backlight_pwroffcnt	= 1*MSEC;
	dsp.param_bpwr.backlight_offcnt		= 1*MSEC;
	dsp.param_bpwr.pwm_period			= MSEC/1; // 1kHz
	dsp.param_bpwr.pwm_cnt				= (dsp.param_bpwr.pwm_period * dsp.bpwr_brightness)/100;

	dsp.param_int.bmureq_timing			= AG903_DSP_BMU_WINDATA;
	dsp.param_int.mask_dspoff			= true;
	dsp.param_int.mask_error			= true;
	dsp.param_int.mask_hrz_line			= true;
	dsp.param_int.mask_vt_blank			= true;
	dsp.param_int.trigger_out			= AG903_DSP_EVENT_VT;
	dsp.param_int.trigger_vt			= AG903_DSP_TRG_VT_START_OF_VBLANK;

	dsp.param_win.update_timing			= AG903_DSP_ATTR_END_OF_VSYNC;
	dsp.param_win.background			= 0x00000000;
	dsp.param_win.num_config			= 2;
	dsp.param_win.num_attr				= 2;
	dsp.param_win.palette_update		= false;
	dsp.param_win.window_attr_base		= dsp.attr_addr;
	dsp.param_win.window_attr_update	= true;


	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetLVDSParam(dsp.handle[1], &dsp.param_ctrl, &dsp.param_lvds);
	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetBPwrModeParam(dsp.handle[1], &dsp.param_bpwr);
	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetIntParam(dsp.handle[1], &dsp.param_int);
    if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetWindowParam(dsp.handle[1], &dsp.param_win);

	return rc;
}


static int32_t DSP_setup_window(uint32_t dsp_ch)
{
	int32_t 					rc				= AG903_ENONE;
	AG903_DSPMgrWinAttribute	*dsp_attr[2]	= {NULL};

	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrGetAttribute(dsp.handle[dsp_ch], 0, &dsp_attr[0]);
	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrGetAttribute(dsp.handle[dsp_ch], 1, &dsp_attr[1]);

	if(rc == AG903_ENONE) {
		/* ウィンドウ 0 */
		sys_memset(dsp_attr[0], 0, 0x20); /* ウインドウ属性の空き領域は必ずゼロクリアが必要 */
		dsp_attr[0]->position_x					= 20;
		dsp_attr[0]->position_y					= 20;
		dsp_attr[0]->source_width				= 256;
		dsp_attr[0]->source_height				= 256;
		dsp_attr[0]->framebuffer_base			= dsp.fb_addr2;
		dsp_attr[0]->destination_width			= 256;
		dsp_attr[0]->destination_height			= 256;
		dsp_attr[0]->hrz_size					= dsp_attr[0]->source_width * 4;
		dsp_attr[0]->transparent_color_a		= 0;
		dsp_attr[0]->transparent_color_r		= 0;
		dsp_attr[0]->transparent_color_g		= 0;
		dsp_attr[0]->transparent_color_b		= 0;
		dsp_attr[0]->pallet_base				= 0x00000000;

		dsp_attr[0]->conf.valid					= true;
		dsp_attr[0]->conf.biliner				= false;
		dsp_attr[0]->conf.hrz_flip				= false;
		dsp_attr[0]->conf.vt_flip				= false;
		dsp_attr[0]->conf.swap_1bit				= false;
		dsp_attr[0]->conf.swap_4bit				= false;
		dsp_attr[0]->conf.swap_half				= false;
		dsp_attr[0]->conf.swap_word				= false;
		dsp_attr[0]->conf.default_alpha			= 0xFF;
		dsp_attr[0]->conf.framebuffer_format	= AG903_DSP_FFMT_X8R8G8B8;
		dsp_attr[0]->conf.pallet_format			= AG903_DSP_PFMT_X8R8G8B8;
		dsp_attr[0]->conf.transparent_en		= false;
		dsp_attr[0]->conf.transparent_en_a		= false;
		dsp_attr[0]->conf.transparent_en_r		= false;
		dsp_attr[0]->conf.transparent_en_g		= false;
		dsp_attr[0]->conf.transparent_en_b		= false;

		/* ウィンドウ 1 */
		sys_memset(dsp_attr[1], 0, 0x20); /* ウインドウ属性の空き領域は必ずゼロクリアが必要 */
		dsp_attr[1]->position_x					= 0;
		dsp_attr[1]->position_y					= 0;
		dsp_attr[1]->source_width				= 256;
		dsp_attr[1]->source_height				= 3;
		dsp_attr[1]->framebuffer_base			= dsp.fb_addr;
		dsp_attr[1]->destination_width			= dsp.param_ctrl.hrz_framesize;
		dsp_attr[1]->destination_height			= dsp.param_ctrl.vt_framesize;
		dsp_attr[1]->hrz_size					= dsp_attr[1]->source_width * 3;
		dsp_attr[1]->transparent_color_a		= 0;
		dsp_attr[1]->transparent_color_r		= 0;
		dsp_attr[1]->transparent_color_g		= 0;
		dsp_attr[1]->transparent_color_b		= 0;
		dsp_attr[1]->pallet_base				= 0x00000000;

		dsp_attr[1]->conf.valid					= true;
		dsp_attr[1]->conf.biliner				= false;
		dsp_attr[1]->conf.hrz_flip				= false;
		dsp_attr[1]->conf.vt_flip				= false;
		dsp_attr[1]->conf.swap_1bit				= false;
		dsp_attr[1]->conf.swap_4bit				= false;
		dsp_attr[1]->conf.swap_half				= false;
		dsp_attr[1]->conf.swap_word				= false;
		dsp_attr[1]->conf.default_alpha			= 0xFF;
		dsp_attr[1]->conf.framebuffer_format	= AG903_DSP_FFMT_R8G8B8;
		dsp_attr[1]->conf.pallet_format			= AG903_DSP_PFMT_R8G8B8;
		dsp_attr[1]->conf.transparent_en		= false;
		dsp_attr[1]->conf.transparent_en_a		= false;
		dsp_attr[1]->conf.transparent_en_r		= false;
		dsp_attr[1]->conf.transparent_en_g		= false;
		dsp_attr[1]->conf.transparent_en_b		= false;
	}

	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetAttribute(dsp.handle[dsp_ch], 0);
	if(rc == AG903_ENONE)
		rc = AG903_DSPMgrSetAttribute(dsp.handle[dsp_ch], 1);

	return rc;
}


/*
 * DisplayManager設定（色補正テーブル）
 */
static int32_t DSP_setup_lut(uint32_t dsp_ch, uint32_t lut_mode)
{
	int32_t idx;
	int32_t rc = AG903_ENONE;
	AG903_DSPMgrLutParam dsp_lut;
	if(lut_mode == DSP_SAMPLE_LUT_NORMAL) {
		/* 初期値 */
		for(idx=0; idx<AG903_DSP_LUT_NUM; idx++) {
			dsp_lut.LUTR[idx].r0 = idx*4 + 0;
			dsp_lut.LUTR[idx].r1 = idx*4 + 1;
			dsp_lut.LUTR[idx].r2 = idx*4 + 2;
			dsp_lut.LUTR[idx].r3 = idx*4 + 3;
			dsp_lut.LUTG[idx].g0 = idx*4 + 0;
			dsp_lut.LUTG[idx].g1 = idx*4 + 1;
			dsp_lut.LUTG[idx].g2 = idx*4 + 2;
			dsp_lut.LUTG[idx].g3 = idx*4 + 3;
			dsp_lut.LUTB[idx].b0 = idx*4 + 0;
			dsp_lut.LUTB[idx].b1 = idx*4 + 1;
			dsp_lut.LUTB[idx].b2 = idx*4 + 2;
			dsp_lut.LUTB[idx].b3 = idx*4 + 3;
			rc = AG903_DSPMgrSetLutParam(dsp.handle[dsp_ch], true, &dsp_lut);
		}
	} else if(lut_mode == DSP_SAMPLE_LUT_REVERSE) {
		/* 逆転 */
		for(idx=0; idx<AG903_DSP_LUT_NUM; idx++) {
			dsp_lut.LUTR[idx].r0 = (255-idx*4) - 0;
			dsp_lut.LUTR[idx].r1 = (255-idx*4) - 1;
			dsp_lut.LUTR[idx].r2 = (255-idx*4) - 2;
			dsp_lut.LUTR[idx].r3 = (255-idx*4) - 3;
			dsp_lut.LUTG[idx].g0 = (255-idx*4) - 0;
			dsp_lut.LUTG[idx].g1 = (255-idx*4) - 1;
			dsp_lut.LUTG[idx].g2 = (255-idx*4) - 2;
			dsp_lut.LUTG[idx].g3 = (255-idx*4) - 3;
			dsp_lut.LUTB[idx].b0 = (255-idx*4) - 0;
			dsp_lut.LUTB[idx].b1 = (255-idx*4) - 1;
			dsp_lut.LUTB[idx].b2 = (255-idx*4) - 2;
			dsp_lut.LUTB[idx].b3 = (255-idx*4) - 3;
			rc = AG903_DSPMgrSetLutParam(dsp.handle[dsp_ch], true, &dsp_lut);
		}
	} else if(lut_mode == DSP_SAMPLE_LUT_OFF) {
		rc = AG903_DSPMgrSetLutParam(dsp.handle[dsp_ch], false, NULL);
	} else {
		rc = -AG903_EINVAL;
	}

	return rc;
}

/*
 * DisplayManager設定（ディザリング）
 */
static int32_t DSP_setup_dith(uint32_t dsp_ch, uint32_t dith_mode)
{
	int32_t rc = AG903_ENONE;
	AG903_DSPMgrDithParam dsp_dith;

	if(dith_mode == DSP_SAMPLE_DITH_DISABLE) {
		/* 無効 */
		rc = AG903_DSPMgrSetDithParam(dsp.handle[dsp_ch], false, NULL);

	} else if(dith_mode == DSP_SAMPLE_DITH_SXGA) {
		/* 設定 */

		/* 左半分を除外（右半分をディザリング） */
		dsp_dith.DITHAREA[0].x0 = 0;
		dsp_dith.DITHAREA[0].y0 = 0;
		dsp_dith.DITHAREA[0].x1 = 1280>>1;
		dsp_dith.DITHAREA[0].y1 = 1024;
		/* 他は無効（初期値） */
		dsp_dith.DITHAREA[1].x0 = 0x7FF;
		dsp_dith.DITHAREA[1].y0 = 0x7FF;
		dsp_dith.DITHAREA[1].x1 = 0x000;
		dsp_dith.DITHAREA[1].y1 = 0x000;
		dsp_dith.DITHAREA[2] = dsp_dith.DITHAREA[1];
		dsp_dith.DITHAREA[3] = dsp_dith.DITHAREA[1];
		rc = AG903_DSPMgrSetDithParam(dsp.handle[dsp_ch], true, &dsp_dith);

	} else {
		rc = -AG903_EINVAL;
	}
	return rc;
}


