/*
 * @history      2019_12_27  [SDK3.0] OpenVGサンプルで呼び出すサービスコールをラッパー経由に変更 (Non-OS版) (#2753)
 * @history      2023_03_31  [SDK3.5] 表示のウインドウ属性の予約領域を確実にゼロクリアするように修正 (#4640)
 */

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

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <kernel.h>
#include <gmalloc.h>
#include "sys/spcprm.h"
#include "dsp/dspmgr.h"
#include "gfx_sample.h"
#include "dsp.h"
#include "bmugfx.h"

#define DSP_CH					0
#define FRM_WIDTH				1280
#define FRM_HEIGHT				1024
#define DSP_INTNO				(32+ 16+DSP_CH) // IRQ16:DSP0, IRQ17:DSP1
#define INVALID_ADDR			0xFFFFFFFF

static AG903_DSPMgrHandle		*dsp_handle;
static uint32_t					next_framebuffer_base;	// FFFFFFFFh:Invalid
static void						*window_attribute_base;
static ER_ID					id_isr;
static ER_ID					id_sem;


static void dsp_isr(void)
{
	AG903_DSPMgrIntStat			stat;
	AG903_DSPMgrWinAttribute	*attr;

	AG903_DSPMgrGetIntStat(dsp_handle, &stat);
	if (stat.int_vt_blank) {
		// 準備ができていればバッファを反転
		if (next_framebuffer_base != INVALID_ADDR) {
			AG903_DSPMgrGetAttribute(dsp_handle, 0, &attr);
			attr->framebuffer_base = next_framebuffer_base;
			AG903_DSPMgrSetAttribute(dsp_handle, 0);
			next_framebuffer_base = INVALID_ADDR;

			// 垂直ブランクを待っているデバイスのためにシグナルを送る
			isig_sem(id_sem);
		}
	}

	// 割り込み要因をクリア
	stat.clr_vt_blank = stat.int_vt_blank;
	stat.clr_hrz_line = stat.int_hrz_line;
	stat.clr_dspoff   = stat.int_dspoff;
	AG903_DSPMgrClearIntStat(dsp_handle, &stat);
}

/* cvt -r 1280 1024 */
/* # 1280x1024 59.79 Hz (CVT 1.31M4-R) hsync: 63.02 kHz; pclk: 90.75 MHz */
/* Modeline "1280x1024R"   90.75  1280 1328 1360 1440  1024 1027 1034 1054 +hsync -vsync */
static void dsp_init_display(AG903_DSPMgrHandle *handle)
{
	AG903_SPCPrmPllnParam		pll = {0};
	AG903_DSPMgrCtrlParam		ctrl = {0};
	AG903_DSPMgrBPwrModeParam	bpwr = {0};
	AG903_DSPMgrExSyncParam		exsync = {0};
	AG903_DSPMgrWindowParam		win = {0};

	AG903_DSPMgrCMOSParam		cmos = {0};
	AG903_DSPMgrSyncParam		sync = {0};

	pll.ms						= 3;
	pll.ns						= 34;
	pll.div						= 2;
	pll.src						= AG903_SPC_PLL1_CLKSRC_XOUT;
	pll.frange					= AG903_SPC_PLL_FR_150_300MHZ; /* CKOUT(272.0MHz) = (FREF(24.0MHz) / MS(3)) * NS(34) */
	pll.en						= true;

	AG903_SPCPrmSetPll1Ctrl(&pll);

	AG903_DSPMgrSetPortSel(handle, AG903_VOD0_PORTSEL_LVCMOS24);

	cmos.rgbde_polarity			= AG903_DSP_POLARITY_POSI;
	cmos.vsync_polarity			= AG903_DSP_POLARITY_NEGA;
	cmos.hsync_polarity			= AG903_DSP_POLARITY_POSI;
	cmos.field_polarity			= AG903_DSP_POLARITY_NEGA;
	cmos.colordetect_polarity	= AG903_DSP_POLARITY_NEGA;
	cmos.rgbde_en				= true;
	cmos.vsync_en				= true;
	cmos.hsync_en				= true;
	cmos.field_en				= true;
	cmos.colordetect_en			= true;
	cmos.pixeldata_en			= true;
	cmos.dotclk_polarity		= AG903_DSP_POLARITY_NEGA;
	cmos.code_en				= FALSE;
	cmos.yuv_mode				= AG903_VOD_MOD_YUV_BT601;

	sync.rgbde_sel				= AG903_DSP_RGBDE_SIGNAL_DATA;
    sync.field_hsync_polarity	= AG903_DSP_POLARITY_POSI;
    sync.vsync_polarity			= AG903_DSP_POLARITY_NEGA;
    sync.hrz_backporch			= 1440 - 1360;
    sync.hrz_pulsewidth			= 1360 - 1328;
    sync.hrz_frontporch			= 1328 - 1280;
    sync.vt_backporch			= 1054 - 1034;
    sync.vt_pulsewidth			= 1034 - 1027;
    sync.vt_frontporch			= 1027 - 1024;
    sync.even_backporch_plus1	= 0;
    sync.even_frontporch_plus1	= 0;
    sync.odd_backporch_plus1	= 0;
    sync.odd_frontporch_plus1	= 0;

	ctrl.ip_sel					= AG903_DSP_VMODE_NONINTERLACE;
	ctrl.syncparam				= &sync;
	ctrl.hrz_framesize			= FRM_WIDTH;
	ctrl.vt_framesize			= FRM_HEIGHT;

	AG903_DSPMgrSetCMOSParam(handle, &ctrl, &cmos);

	bpwr.backlight_en			= true;
	bpwr.cnt_unit				= AG903_VOD_BPWR_UNIT_FRAME;
	bpwr.panel_pwroncnt			= 0;
	bpwr.backlight_pwroncnt		= 0;
	bpwr.backlight_oncnt		= 0;
	bpwr.panel_pwroffcnt		= 5;
	bpwr.backlight_pwroffcnt	= 10;
	bpwr.backlight_offcnt		= 0;
	bpwr.pwm_period				= 0;
	bpwr.pwm_cnt				= 0;

	AG903_DSPMgrSetBPwrModeParam(handle, &bpwr);

	exsync.sync_sel				= 0;

	AG903_DSPMgrSetExSyncParam(handle, &exsync);

	AG903_DSPMgrSetLutParam(handle, false, NULL);

	AG903_DSPMgrSetDithParam(handle, false, NULL);

	AG903_DSPMgrSetCDParam(handle, NULL);

	win.update_timing			= AG903_DSP_ATTR_END_OF_VSYNC;
	win.background				= 0x00000000;
	win.num_config				= 1;
	win.num_attr				= 1;
	win.window_attr_base		= (uint32_t)window_attribute_base;
	win.window_attr_update		= true;
	win.palette_update			= false;

	AG903_DSPMgrSetWindowParam(handle, &win);
}

static void dsp_init_attr(AG903_DSPMgrHandle *handle)
{
	AG903_DSPMgrWinAttribute	*attr;

	AG903_DSPMgrGetAttribute(handle, 0, &attr);

	sys_memset(attr, 0, 0x20); /* ウインドウ属性の空き領域は必ずゼロクリアが必要 */
	attr->position_x			= (FRM_WIDTH - GFX_WIDTH)>>1;
	attr->position_y			= (FRM_HEIGHT - GFX_HEIGHT)>>1;
	attr->destination_width		= GFX_WIDTH;
	attr->destination_height	= GFX_HEIGHT;
	attr->framebuffer_base		= 0x81000000;
	attr->source_width			= GFX_WIDTH;
	attr->source_height			= GFX_HEIGHT;
	attr->hrz_size				= GFX_WIDTH * 2;
	attr->transparent_color_b	= 0xFF;
	attr->transparent_color_g	= 0xFF;
	attr->transparent_color_r	= 0xFF;
	attr->pallet_base			= 0;
	attr->conf.valid				= true;
	attr->conf.biliner				= false;
	attr->conf.hrz_flip				= false;
	attr->conf.vt_flip				= false;
	attr->conf.swap_1bit			= false;
	attr->conf.swap_4bit			= false;
	attr->conf.swap_half			= false;
	attr->conf.swap_word			= false;
	attr->conf.default_alpha		= 0xFF;
	attr->conf.framebuffer_format	= AG903_DSP_FFMT_R5G6B5;
	attr->conf.pallet_format		= AG903_DSP_PFMT_A8R8G8B8;
	attr->conf.transparent_en_b		= false;
	attr->conf.transparent_en_g		= false;
	attr->conf.transparent_en_r		= false;
	attr->conf.transparent_en_a		= false;
	attr->conf.transparent_en		= false;

	AG903_DSPMgrSetAttribute(handle, 0);

	memset((void*)attr->framebuffer_base, 0, attr->hrz_size * attr->destination_height);
}

void dsp_init(void)
{
	T_CSEM	csem;
	T_CISR	cisr;
	AG903_DSPMgrIntParam iprm;

	void *gmptr = gmMalloc(0x20, 8);
	window_attribute_base = (void *)GM2M(gmptr);
	sys_memset(window_attribute_base, 0, 0x20);

	AG903_DSPMgrGetHandle(DSP_CH, &dsp_handle);

	AG903_DSPMgrDisable(dsp_handle);

	dsp_init_display(dsp_handle);

	AG903_DSPMgrEnable(dsp_handle);

	dsp_init_attr(dsp_handle);

	next_framebuffer_base = INVALID_ADDR;

	memset(&csem, 0, sizeof(csem));
	csem.sematr  = TA_HLNG;
	csem.isemcnt = 0;
	csem.maxsem  = 1;
	csem.name    = "";
	id_sem = acre_sem(&csem);

	memset(&cisr, 0, sizeof(cisr));
	cisr.isratr = TA_HLNG;
	cisr.intno  = DSP_INTNO;
	cisr.isr    = &dsp_isr;
	cisr.exinf  = NULL;
	cisr.imask  = 0;
	id_isr = acre_isr(&cisr);

	memset(&iprm, 0, sizeof(iprm));
	iprm.mask_vt_blank = 0;
	iprm.mask_hrz_line = 1;
	iprm.mask_dspoff   = 1;
	iprm.mask_error    = 1;
	AG903_DSPMgrSetIntMask(dsp_handle, &iprm);

	ena_int(DSP_INTNO); //最後に
}

void dsp_term(void)
{
	dis_int(DSP_INTNO); //最初に

	del_isr(id_isr);
	del_sem(id_sem);

	next_framebuffer_base = INVALID_ADDR;

	AG903_DSPMgrDisable(dsp_handle);

	while(AG903_DSPMgrCheckStopped(dsp_handle));

	AG903_DSPMgrReleaseHandle(dsp_handle);

	gmFree((void *)M2GM(window_attribute_base));
}

void dsp_init_bmu(void)
{
	void *gmptr = gmMalloc(0x20, 8);
	window_attribute_base = (void *)GM2M(gmptr);
	sys_memset(window_attribute_base, 0, 0x20);

	AG903_DSPMgrGetHandle(DSP_CH, &dsp_handle);

	AG903_DSPMgrDisable(dsp_handle);

	dsp_init_display(dsp_handle);

	AG903_DSPMgrEnable(dsp_handle);

	dsp_init_attr(dsp_handle);

	AG903_DSPMgrWinAttribute	*attr;

	AG903_DSPMgrGetAttribute(dsp_handle, 0, &attr);
	attr->destination_width		= GB_WIDTH;
	attr->destination_height	= GB_HEIGHT;
#if 1 // 通常
	attr->framebuffer_base		= gfx_gb_snkadr;
#else
	// キャプチャ画像チェック
#ifndef NO_USE_PGP_BMU_GFX
	attr->framebuffer_base		= (uint32_t)gfx_bg_snkadr;
#else
	attr->framebuffer_base		= (uint32_t)gfx_bg_adr;
#endif
#endif
	attr->source_width			= GB_WIDTH;
	attr->source_height			= GB_HEIGHT;
	attr->hrz_size				= GB_WIDTH*GB_BPP/8;
	attr->conf.framebuffer_format	= AG903_DSP_FFMT_R5G6B5;
	AG903_DSPMgrSetAttribute(dsp_handle, 0);
}

void dsp_term_bmu(void)
{
	AG903_DSPMgrDisable(dsp_handle);

	while(AG903_DSPMgrCheckStopped(dsp_handle));

	AG903_DSPMgrReleaseHandle(dsp_handle);

	gmFree((void *)M2GM(window_attribute_base));
}

/**
 * @brief		表示するフレームアドレスを設定します
 * @param		なし
 * @return		なし
 * @note		アドレスの設定は垂直ブランク割り込みに同期して行われ、待ち状態ではスリープします。
 */
void dsp_set_fbase(uint32_t addr)
{
	// 溜まっている垂直ブランク割り込みをクリア
	AG903_DSPMgrIntStat stat = {
		.clr_vt_blank = 1,
		.clr_hrz_line = 0,
		.clr_dspoff   = 0,
	};
	AG903_DSPMgrClearIntStat(dsp_handle, &stat);

	// 垂直ブランク割り込みで表示切替
	next_framebuffer_base = addr; // 表示アドレス兼セマフォシグナル許可
	wai_sem(id_sem); // 表示切替待ち
	
}
