/**
 * @brief           BMU Manager
 * @author          AXELL CORPORATION
 * @description     BMU Manager Layer.
 * @note            none
 * @history         2017_02_22  初版
 * @history         2017_10_26  Ver2.0
 * @history         2019_03_08  [SDK2.2] BMU設定の引数チェックを追加 (#2208)
 * @history         2019_03_08  [SDK2.2] BMUバッファ数0を8とみなす仕様を追加 (#2209)
 */
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include "AG903_common.h"

#include "bmu/bmumgr.h"
#include "bmu/bmuprm.h"

/** BMU1個当たりの最大バッファ数 */
#define AG903_BMU_BUF_NUM_MAX (8)

static AG903_BMUMgrHandle handle_list[AG903_BMU_UNIT_NUM];
static _Bool handle_used_flag[AG903_BMU_UNIT_NUM] = {false};

/**
 * @brief       BMUの初期化
 * @param       unit [in] BMUのチャンネル
 * @return      void
 */
static void AG903_BMUMgrInit(uint8_t unit) {
	AG903_BMUPrmSetCTRL(unit, 0);
	AG903_BMUPrmSetMOD(unit, 0, 0, 0);
	AG903_BMUPrmSetSINKMODULE(unit, 0);
	AG903_BMUPrmSetBASEADR(unit, 0);
	AG903_BMUPrmSetSTRIDE(unit, 0);
	AG903_BMUPrmSetBUFNUM(unit, 0);
}

/**
 * @brief           BMUManagerのハンドルを取得する
 * @param           handles [out] 取得したBMUManagerのハンドルを格納する配列
 * @return          エラーコード
 * @retval          AG903_ENONE   ハンドルの確保に成功
 * @retval          -AG903_EBUSY  使用できるハンドルが不足
 * @description     ハンドラを取得します.
 */
int32_t AG903_BMUMgrGetHandle(AG903_BMUMgrHandle **handle)
{
	int32_t alloc_unit = -1;
	int32_t i;

	/* 空きのunitを検索する */
	for(i = 0 ; i < AG903_BMU_UNIT_NUM ; i++)
	{
		if(handle_used_flag[i] != true)
		{
			alloc_unit = i;
			break;
		}
	}

	if(alloc_unit == -1)
	{
		return -AG903_EBUSY;
	}

	/* 現在の設定を初期化する */
	AG903_BMUMgrInit(alloc_unit);

	(*handle) = &handle_list[alloc_unit];
	handle_used_flag[i] = true;

	(*handle)->unit_id = alloc_unit;
	(*handle)->is_exec = false;

	return AG903_ENONE;
}

/**
 * @brief           BMUManagerのハンドルを解放する
 * @param           handle [in] BMUManagerのハンドル
 * @return          エラーコード
 * @retval          AG903_ENONE  正常
 * @retval          -AG903_EINVAL 無効なハンドルが渡された
 * @retval          -AG903_EBUSY  BMU有効中
 * @description     ハンドルを解放します.
 */
int32_t AG903_BMUMgrReleaseHandle(AG903_BMUMgrHandle *handle)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行時の場合 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}
	handle_used_flag[unit] = false;

	return AG903_ENONE;
}

/**
 * @brief           BMUを有効にする
 * @param           handle[in] BMUManagerのハンドル
 * @return          AG903_ENONE  正常
 * @return          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたチャンネルを有効にします.
 */
int32_t AG903_BMUMgrEnable(AG903_BMUMgrHandle *handle)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	handle->is_exec = true;
	AG903_BMUPrmSetCTRL(unit, 1);

	return AG903_ENONE;
}

/**
 * @brief           BMUを無効にする
 * @param           handle [in] BMUManagerのハンドル
 * @return          エラーコード
 * @retval          AG903_ENONE  正常
 * @retval          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたチャンネルを無効にします.
 */
int32_t AG903_BMUMgrDisable(AG903_BMUMgrHandle *handle)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	handle->is_exec = false;
	AG903_BMUPrmSetCTRL(unit, 0);

	return AG903_ENONE;
}

/**
 * @brief           BMUの動作モードを設定する
 * @param           handle [in]  BMUManagerのハンドル
 * @param           ini [in] シンクモジュールのウェイト動作
 * @param           mgr [in] バッファ管理の方式
 * @return          エラーコード
 * @retval          AG903_ENONE		正常
 * @retval          -AG903_EINVAL	無効なハンドル、パラメータが渡された
 * @retval          -AG903_EBUSY	BMU有効中に設定を変更した場合
 * @description     ハンドラに割り当てられたチャンネルの動作モードを設定します.
 */
int32_t AG903_BMUMgrSetMode(AG903_BMUMgrHandle *handle, uint8_t ini, uint8_t mgr)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行中 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}

	/* パラメータチェック */
	if(ini > AG903_BMU_SINK_WAIT_DISABLE)
	{
		return -AG903_EINVAL;
	}

	if(mgr > AG903_BMU_BUF_MGR_MODE3)
	{
		return -AG903_EINVAL;
	}

	uint8_t cini, cmgr, csrc;
	AG903_BMUPrmGetMOD(unit, &cini, &cmgr, &csrc);
	AG903_BMUPrmSetMOD(unit, ini, mgr, csrc);

	return AG903_ENONE;
}

/**
 * @brief           BMUのソースモジュールを設定する
 * @param           handle [in] BMUManagerのハンドル
 * @param           src [in] BMUのソースモジュールのID
 * @return          エラーコード
 * @retval          AG903_ENONE   正常
 * @retval          -AG903_EINVAL  無効なハンドル、パラメータが渡された
 * @retval          -AG903_EBUSY   BMU有効中に設定を変更した場合
 * @description     ハンドラに割り当てられたチャンネルのソースモジュールを設定します.
 */
int32_t AG903_BMUMgrSetSrcModule(AG903_BMUMgrHandle *handle, uint8_t src)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行中 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}

	/* パラメータチェック */
	if(src > AG903_BMU_SRC_JPG0)
	{
		return -AG903_EINVAL;
	}

	uint8_t ini, mgr, csrc;
	AG903_BMUPrmGetMOD(unit, &ini, &mgr, &csrc);
	AG903_BMUPrmSetMOD(unit, ini, mgr, src);
	handle->src_id = src;

	return AG903_ENONE;
}

/**
 * @brief           BMUのシンクモジュールを追加する
 * @param           handle [in]  BMUManagerのハンドル
 * @param           sink [in]  BMUのシンクモジュールのID
 * @return          エラーコード
 * @retval          AG903_ENONE   正常
 * @retval          -AG903_EINVAL  無効なハンドル、パラメータが渡された
 * @retval          -AG903_EBUSY   BMU有効中に設定を変更した場合
 * @description     ハンドラに割り当てられたチャンネルのシンクモジュールを追加します.<p>
 *                  複数のシンクモジュールを設定することが可能です.<p>
 *                  複数選択した場合は、バッファの切り替わりのタイミングに注意が必要です.
 */
int32_t AG903_BMUMgrAddSinkModule(AG903_BMUMgrHandle *handle, uint8_t sink)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行中 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}

	/* パラメータチェック */
	if(sink > AG903_BMU_SINK_JPG0)
	{
		return -AG903_EINVAL;
	}

	uint32_t reg;
	AG903_BMUPrmGetSINKMODULE(unit, &reg);
	reg |= 1 << sink;
	AG903_BMUPrmSetSINKMODULE(unit, reg);

	return AG903_ENONE;
}

/**
 * @brief           BMUのシンクモジュールを削除する
 * @param           handle [in] BMUManagerのハンドル
 * @param           sink   [in] BMUのシンクモジュールのID
 * @return          エラーコード
 * @retval          AG903_ENONE   正常
 * @retval          -AG903_EINVAL  無効なハンドル、パラメータが渡された
 * @retval          -AG903_EBUSY   BMU有効中に設定を変更した場合
 * @description     ハンドラに割り当てられたチャンネルのシンクモジュールを削除します.
 */
int32_t AG903_BMUMgrRemoveSinkModule(AG903_BMUMgrHandle *handle, uint8_t sink)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行中 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}

	/* パラメータチェック */
	if(sink > AG903_BMU_SINK_JPG0)
	{
		return -AG903_EINVAL;
	}

	uint32_t reg;
	AG903_BMUPrmGetSINKMODULE(unit, &reg);
	reg &= ~(1 << sink);
	AG903_BMUPrmSetSINKMODULE(unit, reg);

	return AG903_ENONE;
}

/**
 * @brief           BMUのシンクモジュールステータスを取得する
 * @param           handle [in] BMUManagerのハンドル
 * @param           sink   [in] BMUのシンクモジュールのID
 * @param           status [out] ステータスの保存先
 * @return          エラーコード
 * @retval          AG903_ENONE   正常
 * @retval          -AG903_EINVAL  無効なハンドル、パラメータが渡された
 * @description     ハンドラに割り当てられたチャンネルのシンクモジュールステータスを取得します.
 */
int32_t AG903_BMUMgrGetSinkStatus(AG903_BMUMgrHandle *handle, uint8_t sink, uint8_t *status)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* パラメータチェック */
	if(sink > AG903_BMU_SINK_JPG0)
	{
		return -AG903_EINVAL;
	}

	uint32_t reg;
	AG903_BMUPrmGetSINKSTAT(unit, &reg);
	*status = (reg >> sink) & 1;

	return AG903_ENONE;
}

/**
 * @brief           BMUのバッファ関連の設定を行う
 * @param           handle  [in] BMUManagerのハンドル
 * @param           buffer  [in] バッファのポインタ
 * @param           stride  [in] 1つのバッファのサイズ（バイト単位）
 * @param           buf_num [in] バッファの数（0〜8 ただし0は8とみなす)
 * @return          エラーコード
 * @retval          AG903_ENONE    正常
 * @retval          -AG903_EINVAL  無効なハンドル、パラメータが渡された
 * @retval          -AG903_EBUSY   BMU有効中に設定を変更した場合
 * @description     ハンドラに割り当てられたチャンネルのバッファ関連の設定を変更します.
 * @note            bufferアドレスとstrideサイズは4096の倍数を推奨します.
 * @note            strideは128バイトの倍数のみ指定可能です.
 * @note            バッファのメモリ属性はキャッシュ不可として下さい.
 */
int32_t AG903_BMUMgrSetBufferConfig(AG903_BMUMgrHandle *handle, void *buffer, uint32_t stride, uint32_t buf_num)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 実行中 */
	if(handle->is_exec)
	{
		return -AG903_EBUSY;
	}

	/* アドレスはエラーチェックを行わない
	   （BMUnBASEADR[31:28]は0で固定されるため） */

	if(stride >= (1<<(24+1)) || (stride & 0x7f))
	{
		return -AG903_EINVAL;
	}

	if(buf_num > AG903_BMU_BUF_NUM_MAX)
	{
		return -AG903_EINVAL;
	}

	if (8 < buf_num)
	{
		return -AG903_EINVAL;
	}
	/* numは8のとき、レジスタ値は0になる */
	if(buf_num == 8)
	{
		buf_num = 0;
	}

	AG903_BMUPrmSetBASEADR(unit, ((uint32_t)buffer) & AG903_BMUn_BASEADR_ADR_MSK);
	AG903_BMUPrmSetSTRIDE (unit, stride & AG903_BMUn_STRIDE_STRIDE_MSK);
	AG903_BMUPrmSetBUFNUM (unit, buf_num);

	return AG903_ENONE;
}

/**
 * @brief           BMUのステータスを取得する
 * @param           handle [in] BMUManagerのハンドル
 * @param           status [out] ステータスの格納先
 * @return          エラーコード
 * @return          AG903_ENONE 正常
 * @return          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたチャンネルのバッファ関連の設定を変更します.
 */
int32_t AG903_BMUMgrGetStatus(AG903_BMUMgrHandle *handle, AG903_BMUMgrStatus *status)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	BMUPrmParamSTAT regstat;
	AG903_BMUPrmGetSTAT(unit, &regstat);
	status->is_valid               = (uint8_t)regstat.val;
	status->is_empty               = (uint8_t)regstat.empty;
	status->is_read_wait_appeared  = (uint8_t)regstat.rwa;
	status->is_read_busy           = (uint8_t)regstat.rbsy;
	status->read_module_num        = (uint8_t)regstat.rnum;
	status->is_full                = (uint8_t)regstat.full;
	status->is_write_wait_appeared = (uint8_t)regstat.wwa;
	status->is_write_busy          = (uint8_t)regstat.wbsy;
	status->write_module_num       = (uint8_t)regstat.wnum;

	return AG903_ENONE;
}

/**
 * @brief           BMU領域（Sink）のアドレスを取得する
 * @param           handle [in] BMUManagerのハンドル
 * @param           addr [out] アドレスの格納先
 * @return          エラーコード
 * @retval          AG903_ENONE 正常
 * @retval          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたチャンネルのシンク側アドレスを取得します.
 * @note            アドレスはA0000000h〜BFFFFFFFhの範囲で取得されます.
 *                  この領域はあらかじめキャッシュ不可として下さい.
 */
int32_t AG903_BMUMgrGetBMUSinkAddress(AG903_BMUMgrHandle *handle, uint32_t *addr)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/*        ユニットID  | BMU領域のオフセット */
	(*addr) = (unit<<24) | ((uint32_t)5<<29);

	return AG903_ENONE;
}

/**
 * @brief           BMU領域（Src）のアドレスを取得する
 * @param           handle [in] BMUManagerのハンドル
 * @param           addr [out] アドレスの格納先
 * @return          エラーコード
 * @retval          AG903_ENONE 正常
 * @retval          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたチャンネルのソース側アドレスを取得します.
 * @note            アドレスはA0000000h〜BFFFFFFFhの範囲で取得されます.
 *                  この領域はあらかじめキャッシュ不可として下さい.
 */
int32_t AG903_BMUMgrGetBMUSrcAddress(AG903_BMUMgrHandle *handle, uint32_t *addr)
{
	uint8_t  src  = handle->src_id;
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	/* 描画の場合、28ビット目を有効にする */
	if(AG903_BMU_SRC_GFX0 <= src && src <= AG903_BMU_SRC_GFX3)
	{
		/*        ユニットID  | BMU領域のオフセット | 描画領域フラグ */
		(*addr) = (unit<<24) | ((uint32_t)5<<29) | ((uint32_t)1<<28);
	}else
	{
		/*        ユニットID  | BMU領域のオフセット */
		(*addr) = (unit<<24) | ((uint32_t)5<<29);
	}

	return AG903_ENONE;
}

/**
 * @brief           BMUのモジュール番号を取得する
 * @param           handle [in] BMUManagerのハンドル
 * @param           id [out]    モジュール番号
 * @return          エラーコード
 * @return          AG903_ENONE 正常
 * @return          -AG903_EINVAL 無効なハンドルが渡された
 * @description     ハンドラに割り当てられたモジュール番号を取得します.
 */
int32_t AG903_BMUMgrGetBMUId(AG903_BMUMgrHandle *handle, uint8_t *id)
{
	uint32_t unit = (handle - handle_list);

	/* 無効なハンドル */
	if(unit >= AG903_BMU_UNIT_NUM || &handle_list[unit] != handle)
	{
		return -AG903_EINVAL;
	}

	(*id) = unit;

	return AG903_ENONE;
}

