/**
 * @brief       VIA Manager
 * @author      AXELL CORPORATION
 * @description VIA Manager Layer.
 * @note        none
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2019_03_08  [SDK2.2] VIAにビデオ入力の信号が有効であることを検出する関数を追加 (#2454)
*/
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2019 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include <stdio.h>
#include "via/viaprm.h"
#include "via/viamgr.h"
#include "pgp/pgpprm.h"
#include "sys/spcprm.h"
#include "sys/sscprm.h"

/* 前処理がアイドル状態であることを指します */
#define PGP_IDLE    (0)

/* レジスタの固定値 */
#define GCAT        (0x1)       /* VIA.SRC_SELECT.GCAT */
#define NSNS        (0x1)       /* VIA.SRC_SELECT.NSNS */
#define NONS        (0x1)       /* VIA.SRC_SELECT.NONS */
#define TRAP        (0x0)       /* VIA.SIG_YCS.TRAP */
#define AGCEN       (0x1)       /* VIA.CHD_SETUP3.AGCEN */
#define ACCOV       (0x30)      /* VIA.CHD_SETUP3.ACCOV */
#define ACCTC       (0x1)       /* VIA.CHD_SETUP3.ACCTC */
#define CBPLLULTH   (0x12)      /* VIA.CHD_SETUP5.CBPLLULTH */
#define CBPLLULSV   (0x0)       /* VIA.CHD_SETUP5.CBPLLULSV */
#define AGCGAINSPD  (0x3)       /* VIA.CHD_SETUP5.AGCGAINSPD */
#define AGCOFSTSPD  (0x3)       /* VIA.CHD_SETUP5.AGCOFSTSPD */
#define AGCPOS      (0x0)       /* VIA.CHD_SETUP5.AGCPOS */
#define AGCGAINEN   (0x1)       /* VIA.CHD_SETUP5.AGCGAINEN */
#define AGCOFSTEN   (0x1)       /* VIA.CHD_SETUP5.AGCOFSTEN */
#define CLAMPOFS    (0x10)      /* VIA.CLAMP_LEVEL.CLAMPOFS */

/* 初期化チェック */
static _Bool init_check = false;

/* アナログビデオ入力のコンテキスト */
static struct {
    _Bool                       used_ports[AG903_VIA_MGR_MAX_PORTS];
    AG903_ViaMgrInputHandle     ports[AG903_VIA_MGR_MAX_PORTS];
} ViaContext = {
    {false, false, false, false},
    {{0}}
};

/* アナログビデオ入力のフォーマット値 */
static struct {
    uint16_t    fmt;            /* VIA.SRC_FORMAT.FMT */
    uint16_t    hcycle;         /* VIA.SRC_HCYCLE.HCYCLE */
    uint16_t    hvalid;         /* VIA.SRC_HVALID.HVALID */
    uint16_t    hdelay;         /* VIA.SRC_HDELAY.HDELAY */
    uint16_t    vcycle;         /* VIA.SRC_VCYCLE.VCYCLE */
    uint16_t    vvalid;         /* VIA.SRC_VVALID.VVALID */
    uint16_t    vdelay;         /* VIA.SRC_VDELAY.VDELAY */
    uint16_t    frm;            /* VIA.SRC_FRMFREQ.FRM */
    uint16_t    accmark;        /* VIA.CHD_SETUP3.ACCMARK */
    uint16_t    agcpal;         /* VIA.CHD_SETUP5.AGCPAL */
} ViaFormat[AG903_VIA_MGR_FORMAT_MAX] = {
    { 0x0000, 0x06B4, 0x05A0, 0x001A, 0x020D, 0x01E0, 0x000A, 0x0000, 0x003E, 0x0000 }, /* NTSC-BT */
    { 0x0008, 0x0618, 0x0500, 0x0014, 0x020D, 0x01E0, 0x000A, 0x0000, 0x0039, 0x0000 }, /* NTSC-SQ */
    { 0x0007, 0x071C, 0x0600, 0x0094, 0x020D, 0x01E0, 0x000A, 0x0000, 0x003E, 0x0000 }, /* NTSC-4F */
    { 0x0001, 0x06C0, 0x05A0, 0x0014, 0x0271, 0x0240, 0x000A, 0x0001, 0x003E, 0x0001 }, /* PAL-BT */
    { 0x0009, 0x0760, 0x0600, 0x0014, 0x0271, 0x0240, 0x000A, 0x0001, 0x0032, 0x0001 }, /* PAL-SQ */
};

/* アナログビデオ入力のカラーモード値 */
static struct {
    uint16_t    nsck;       /* VIA.SRC_SELECT.NSCK */
    uint16_t    mono;       /* VIA.SIG_YCS.MONO */
} ViaMode[AG903_VIA_MGR_MODE_MAX] = {
    { 0x0001, 0x0000 }, /* COLOR */
    { 0x0000, 0x0001 }, /* MONO */
};


/**
 * @brief         VIA　初期化
 * @param         void 
 * @return        初期化結果
 * @retval        AG903_ENONE 正常終了 
 * @description   アナログビデオ入力関連のハードウェアを初期化します。 
 * @note          アナログビデオ入力ハンドルを取得する前に呼び出してください。 
*/
int32_t AG903_ViaMgrInit(void)
{
    /* SSC.VIDEOADC_SETUP:0x0000F0FF */
    {
        AG903_SSCPrmVideoAdcParam param = {0};

        param.acq_clr       = 0x0;
        param.acq_en        = 0x0;
        param.dat_en        = 0xF;
        param.clmp_tmg      = 0x0;
        param.clmp_lv       = 0xF;
        param.pga_ctl       = 0xF;

        AG903_SSCPrmSetVideoAdcSetup(&param);
    }
    /* SPC.VIDEOADC_MISC_CTRL1:0x00000001 */
    /* SPC.VIDEOADC_MISC_CTRL2:0xA5A5A5DE */
    /* SPC.VIDEOADC_MISC_CTRL3:0x070707A5 */
    /* SPC.VIDEOADC_MISC_CTRL4:0x20080007 */
    /* SPC.VIDEOADC_MISC_CTRL5:0x00040004 */
    {
        AG903_SPCPrmVideoAdcParam param = {0};
        AG903_SPCPrmVideoAdcEnable enable = {0};

        param.clmp_en0      = 0x0;
        param.clmp_en1      = 0x0;
        param.clmp_en2      = 0x0;
        param.clmp_en3      = 0x0;
        param.clmp_lv0      = 0xA5;
        param.clmp_lv1      = 0xA5;
        param.clmp_lv2      = 0xA5;
        param.clmp_lv3      = 0xA5;
        param.clmp_imp      = 0x3;
        param.gain0         = 0x7;
        param.gain1         = 0x7;
        param.gain2         = 0x7;
        param.gain3         = 0x7;
        param.aaf_ctrl      = 0x20;
        param.qntzr_bias_up = 0x2;
        param.input_range   = 0x1;
        param.ref_bias_up   = 0x1;
        enable.adc          = 0x1;
        enable.ch0          = 0x1;
        enable.ch1          = 0x1;
        enable.ch2          = 0x1;
        enable.ch3          = 0x1;

        AG903_SPCPrmSetVideoAdcCtrl(&param, &enable);
    }

    init_check = true;

    return AG903_ENONE;
}


/**
 * @brief         VIA ハンドル取得
 * @param         ports [in] 取得する入力ポート番号を格納した配列
 * @param         num_ports [in] portsの配列要素数（=取得するハンドル数）
 * @param         handles [out] 取得したハンドルを格納する配列 
 * @return        ハンドル取得結果 
 * @retval        AG903_ENONE 正常終了
 * @retval        -AG903_EINVAL アナログビデオ入力の初期化が行われていない場合<p>ports に 0~3 の範囲外の値が指定された場合<p>ports、handles に NULL 
 *  							が指定された場合
 * @retval        -AG903_ENOMEM ports で指定されたポートに対応するハンドルが取得済の場合 
 * @description   入力ポート番号を指定してナログビデオア入力ハンドル(AG903_ViaMgrInputHandle)を取得します。<p> 
 *  							要求された番号が使用中の場合、handles には何も格納しません。<p>
 *  							handles にはポート番号に指定された番号順にハンドルを格納します。
*/
int32_t AG903_ViaMgrGetInputHandle(int32_t *ports, int32_t num_ports, AG903_ViaMgrInputHandle **handles)
{
    int32_t i;
    _Bool num_check[AG903_VIA_MGR_MAX_PORTS] = {false};

    if (init_check != true) {
        return -AG903_EINVAL;
    }
    if (num_ports > AG903_VIA_MGR_MAX_PORTS) {
        return -AG903_EINVAL;
    }
    if ((ports == NULL) || (handles == NULL)) {
        return -AG903_EINVAL;
    }
    for (i = 0; i < num_ports; i++) {
        if ((ports[i] < 0) || (ports[i] >= AG903_VIA_MGR_MAX_PORTS)) {
            return -AG903_EINVAL;
        }
        if (ViaContext.used_ports[ports[i]] != false) {
            return -AG903_ENOMEM;
        }
        /* 同じ番号が指定された場合はエラー */
        if (num_check[ports[i]] == true) {
            return -AG903_EINVAL;
        }
        num_check[ports[i]] = true;
    }

    for (i = 0; i < num_ports; i++) {
        /* アナログビデオ入力ハンドルをhandlesに格納して初期化 */
        handles[i] = (AG903_ViaMgrInputHandle *)&ViaContext.ports[ports[i]];
        handles[i]->id = AG903_VIA_MGR_INPUT_HANDLE_ID;
        handles[i]->port_no = ports[i];
        /* 取得済のポートに状態遷移させる */
        ViaContext.used_ports[ports[i]] = true;
    }
    return AG903_ENONE;
}


/**
 * @brief         VIA ハンドル解放
 * @param         handle [in] 解放するアナログビデオ入力ハンドル 
 * @return        ハンドル解放結果
 * @retval        AG903_ENONE 正常終了
 * @retval        -AG903_EINVAL handle に不正なアナログビデオ入力ハンドルが指定された場合 
 * @description   指定されたアナログビデオ入力ハンドルを解放します。<p>アナログビデオ入力ハンドルのメンバは、port_no 以外全て 0 で初期化します。
 * @note          本関数をコールする場合は、前処理モジュールが停止されていることを確認してください。
*/
int32_t AG903_ViaMgrReleaseInputHandle(AG903_ViaMgrInputHandle *handle)
{
    /* ハンドルのIDが不正な場合は無効なハンドルとして処理します */
    if ((handle == NULL) || (handle->id != AG903_VIA_MGR_INPUT_HANDLE_ID)) {
        return -AG903_EINVAL;
    }

    /* 未得済のポートに状態遷移させる */
    ViaContext.used_ports[handle->port_no] = false;

    /* port_no以外のメンバは0に初期化（idを0に初期化することで無効なハンドルに遷移させる） */
    handle->id = 0;

    return AG903_ENONE;
}


/**
 * @brief         フォーマット／カラーモード設定
 * @param         handle [in] VIA ハンドル
 * @param         format [in] フォーマット
 * @param         mode [in] カラーモード 
 * @return        設定結果 
 * @retval        AG903_ENONE 正常終了
 * @retval        -AG903_EINVAL handle に不正なアナログビデオ入力ハンドルが指定された場合<p>format に不正な値が指定された場合<p>mode 
 *  							に不正な値が指定された場合
 * @description   指定されたアナログビデオ入力ハンドルに対してフォーマット、カラーモードを設定します。<p> 
 *                format には「VIAフォーマット」、mode には「VIAカラーモード」を指定してください。 
*/
int32_t AG903_ViaMgrSetInputParameter(AG903_ViaMgrInputHandle *handle, uint32_t format, uint32_t mode)
{
    /* ハンドルのIDが不正な場合は無効なハンドルとして処理します */
    if ((handle == NULL) || (handle->id != AG903_VIA_MGR_INPUT_HANDLE_ID)) {
        return -AG903_EINVAL;
    }

    /* formatに不正な値が指定された場合はエラーとします */
    switch (format) {
        case AG903_VIA_MGR_FORMAT_NTSC_BT:
        case AG903_VIA_MGR_FORMAT_NTSC_SQ:
        case AG903_VIA_MGR_FORMAT_NTSC_4F:
        case AG903_VIA_MGR_FORMAT_PAL_BT:
        case AG903_VIA_MGR_FORMAT_PAL_SQ:
            break;
        default:
            return -AG903_EINVAL;
    }

    /* modeに不正な値が指定された場合エラーとします */
    switch (mode) {
        case AG903_VIA_MGR_MODE_COLOR:
        case AG903_VIA_MGR_MODE_MONOCHROME:
            break;
        default:
            return -AG903_EINVAL;
    }

    VIAPrmParamVIASRCSELECT select;
    select.gcat = GCAT;
    select.nsck = ViaMode[mode].nsck;
    select.nsns = NSNS;
    select.nons = NONS;
    AG903_ViaPrmSetVIASRCSELECT(handle->port_no, &select);

    AG903_ViaPrmSetVIASRCFORMAT(handle->port_no, ViaFormat[format].fmt);
    AG903_ViaPrmSetVIASRCHCYCLE(handle->port_no, ViaFormat[format].hcycle);
    AG903_ViaPrmSetVIASRCHVALID(handle->port_no, ViaFormat[format].hvalid);
    AG903_ViaPrmSetVIASRCHDELAY(handle->port_no, ViaFormat[format].hdelay);
    AG903_ViaPrmSetVIASRCVCYCLE(handle->port_no, ViaFormat[format].vcycle);
    AG903_ViaPrmSetVIASRCVVALID(handle->port_no, ViaFormat[format].vvalid);
    AG903_ViaPrmSetVIASRCVDELAY(handle->port_no, ViaFormat[format].vdelay);
    AG903_ViaPrmSetVIASRCFRMFREQ(handle->port_no, ViaFormat[format].frm);
    AG903_ViaPrmSetVIASIGYCS(handle->port_no, TRAP, ViaMode[mode].mono);

    VIAPrmParamVIACHDSETUP3 setup3;
    setup3.agcen   = AGCEN;
    setup3.accov   = ACCOV;
    setup3.acctc   = ACCTC;
    setup3.accmark = ViaFormat[format].accmark;
    AG903_ViaPrmSetVIACHDSETUP3(handle->port_no, &setup3);

    VIAPrmParamVIACHDSETUP5 setup5;
    setup5.cbpllulth  = CBPLLULTH;
    setup5.cbpllulsv  = CBPLLULSV;
    setup5.agcgainspd = AGCGAINSPD;
    setup5.agcofstspd = AGCOFSTSPD;
    setup5.agcpos     = AGCPOS;
    setup5.agcpal     = ViaFormat[format].agcpal;
    setup5.agcgainen  = AGCGAINEN;
    setup5.agcofsten  = AGCOFSTEN;
    AG903_ViaPrmSetVIACHDSETUP5(handle->port_no, &setup5);

    AG903_ViaPrmSetVIACLAMPLEVEL(handle->port_no, CLAMPOFS);

    return AG903_ENONE;
}


/**
 * @brief         ビデオ入力信号の状態取得
 * @param         handle [in] VIA ハンドル
 * @return        設定結果 
 * @retval        1 入力信号が有効
 * @retval        0 入力信号が無効
 * @description   指定されたアナログビデオ入力ハンドルのビデオ入力の信号が有効であるかどうかを検出します。
 * @note          VIAのパラメータを変更してから1秒以上経過してから実行して下さい。
*/
int32_t AG903_ViaMgrIsInputValid(AG903_ViaMgrInputHandle *handle)
{
	VIAPrmParamVIADETSTATUS vdst = {
		.normal = 0,
		.nosync = 1,
	};
	AG903_ViaPrmGetVIADETSTATUS(handle->port_no, &vdst);
	if (!vdst.nosync && vdst.normal) {
		return 1;
	}
	return 0;
}
