/*
* Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2020. All rights reserved.
* Description : hddec
* Author        : sdk
* Create        : 2019/11/21
*/

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pthread.h>

#include "soc_log.h"
#include "mpi_hddec_ext.h"
#include "drv_ioctl_hddec.h"
#include "mpi_stat_ext.h"

static td_s32 g_hddec_dev_fd = -1;

static pthread_mutex_t g_hddec_mutex = PTHREAD_MUTEX_INITIALIZER;

#define ext_hddec_lock()   (td_void)pthread_mutex_lock(&g_hddec_mutex)
#define ext_hddec_unlock() (td_void)pthread_mutex_unlock(&g_hddec_mutex)

#define check_hddec_init(ret)                     \
    do {                                          \
        ext_hddec_lock();                          \
        if (g_hddec_dev_fd < 0) {                 \
            soc_log_err("please init firstly.\n"); \
            (ret) = TD_FAILURE;                   \
        }                                         \
        ext_hddec_unlock();                        \
    } while (0)

/* HDDEC初始化 */
td_s32 ext_mpi_hddec_init(td_void)
{
    struct stat stat_info;

    ext_hddec_lock();

    /* already opened in the process */
    if (g_hddec_dev_fd > 0) {
        ext_hddec_unlock();
        return TD_SUCCESS;
    }

    g_hddec_dev_fd = open("/dev/soc_hddec", O_RDWR | O_NONBLOCK, 0);
    if (g_hddec_dev_fd < 0) {
        soc_log_err("open HDDEC err.\n");
        ext_hddec_unlock();
        return TD_FAILURE;
    }

    if (stat("/dev/soc_hddec", &stat_info) != 0) {
        soc_log_err("HDDEC is not exist.\n");
        close(g_hddec_dev_fd);
        g_hddec_dev_fd = -1;
        ext_hddec_unlock();
        return TD_FAILURE;
    }

    if (!S_ISCHR(stat_info.st_mode)) {
        soc_log_err("HDDEC is not device.\n");
        close(g_hddec_dev_fd);
        g_hddec_dev_fd = -1;
        ext_hddec_unlock();
        return TD_FAILURE;
    }

    ext_hddec_unlock();

    return TD_SUCCESS;
}

/* HDDEC去初始化 */
td_s32 ext_mpi_hddec_de_init(td_void)
{
    ext_hddec_lock();

    if (g_hddec_dev_fd < 0) {
        ext_hddec_unlock();
        return TD_SUCCESS;
    }

    close(g_hddec_dev_fd);
    g_hddec_dev_fd = -1;
    ext_hddec_unlock();

    return TD_SUCCESS;
}

/* 信源连接 */
td_s32 ext_mpi_hddec_connect(const ext_drv_hddec_src_attr *src_attr)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    ext_mpi_stat_event(EXT_STAT_EVENT_CONNECT, 1);
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_CONNECT, src_attr);

    return ret;
}

/* 断开连接 */
td_s32 ext_mpi_hddec_dis_connect(td_void)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_DISCONNECT);

    return ret;
}

/* 获取离线信源的信号是否存在,阻塞接口 */
td_s32 ext_mpi_hddec_get_offline_status(const ext_drv_hddec_src_attr *src_att, td_bool *exist)
{
    td_s32 ret = TD_SUCCESS;
    ext_drv_hddec_offline_param offline_param;

    if (src_att == NULL) {
        soc_log_err("offline is NULL pointer!\n");
        return TD_FAILURE;
    }
    if (exist == NULL) {
        soc_log_err("exist_sig is NULL pointer!\n");
        return TD_FAILURE;
    }

    *exist = TD_FALSE;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    offline_param.src_attr.src_type = src_att->src_type;
    offline_param.src_attr.index = src_att->index;

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_OFFLINE_SIG, &offline_param);
    if (ret != TD_SUCCESS) {
        soc_log_err("get offline param failed\n");
        *exist = TD_FALSE;
        return ret;
    }

    *exist = offline_param.exist;

    return TD_SUCCESS;
}

/* 检测当前的decode状态 */
td_s32 ext_mpi_hddec_get_signal_status(ext_drv_hddec_signal_status *sig_status)
{
    td_s32 ret = TD_SUCCESS;

    if (sig_status == NULL) {
        soc_log_err("sig_status is NULL pointer.\n");
        return TD_FAILURE;
    }

    *sig_status = EXT_DRV_HDDEC_SIG_NO_SIGNAL;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    /* if mpi status change from not support to support, check whether send mpi event */
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_SIGNAL_STATUS, sig_status);

    return ret;
}

/* 检测当前的decode_timing信息 */
td_s32 ext_mpi_hddec_get_timing_info(ext_drv_hddec_timing_info *timing_info)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (timing_info == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_TIMING_INFO, timing_info);

    return ret;
}

/* 检测当前的decode非标信息 */
td_s32 ext_mpi_hddec_get_non_std_info(ext_drv_hddec_nonstd_info *non_std_info)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (non_std_info == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_NONSTD_INFO, non_std_info);

    return ret;
}

/* 启动自动颜色校正 */
td_s32 ext_mpi_hddec_start_calibrate_color(td_void)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_START_CALIB);

    return ret;
}

/* 获取自动颜色校正状态 */
td_s32 ext_mpi_hddec_get_calibration_status(ext_drv_hddec_color_calib_status *calib_status)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (calib_status == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_CALIB_INFO, calib_status);

    return ret;
}

/* 获取手动设置自动颜色校正参数 */
td_s32 ext_mpi_hddec_get_calibration_param(ext_drv_hddec_color_calib_param *calib_param)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (calib_param == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_CALIB_PARAM, calib_param);

    return ret;
}

/* 手动设置自动颜色校正 */
td_s32 ext_mpi_hddec_set_calibration_param(const ext_drv_hddec_color_calib_param *calib_param)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (calib_param == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_S_CALIB_PARAM, calib_param);

    return ret;
}

/* 启动VGA图像自动调整 */
td_s32 ext_mpi_hddec_start_adjust_image_param(td_void)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_START_ADJUST);

    return ret;
}

/* 获取VGA图像自动调整状态 */
td_s32 ext_mpi_hddec_get_adjust_status(ext_drv_hddec_adjust_status *adjust_status)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }
    if (adjust_status == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_ADJUST_INFO, adjust_status);

    return ret;
}

/* 获取手动调整可调范围 */
td_s32 ext_mpi_hddec_get_adjust_range(ext_drv_hddec_adjust_type adjust_type, ext_range *range)
{
    td_s32 ret = TD_SUCCESS;
    ext_drv_hddec_adjust_range adjust_range;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }
    if (range == NULL) {
        soc_log_err("range is NULL pointer!\n");
        return TD_FAILURE;
    }

    adjust_range.type = adjust_type;

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_ADJUST_RANGE, &adjust_range);
    if (ret != TD_SUCCESS) {
        soc_log_err("get adjust range failed.\n");
        return ret;
    }

    range->min = adjust_range.range.min;
    range->max = adjust_range.range.max;

    return TD_SUCCESS;
}

/* 获取调整的参数 */
td_s32 ext_mpi_hddec_get_adjust_param(ext_drv_hddec_adjust_type adjust_type, td_u32 *param)
{
    td_s32 ret = TD_SUCCESS;
    ext_drv_hddec_adjust_param adjust_param;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }
    if (param == NULL) {
        soc_log_err("param is NULL pointer!\n");
        return TD_FAILURE;
    }

    adjust_param.type = adjust_type;

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_ADJUST_PARAM, &adjust_param);
    if (ret != TD_SUCCESS) {
        soc_log_err("get adjust param failed.\n");
        return ret;
    }

    *param = adjust_param.param;

    return TD_SUCCESS;
}

/* 设置手动调整参数 */
td_s32 ext_mpi_hddec_set_adjust_param(ext_drv_hddec_adjust_type adjust_type, td_u32 param)
{
    td_s32 ret = TD_SUCCESS;
    ext_drv_hddec_adjust_param adjust_param;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    if (adjust_type >= EXT_DRV_HDDEC_ADJUST_TYPE_MAX) {
        soc_log_err("adjust is invalid.\n");
        return TD_FAILURE;
    }

    adjust_param.type = adjust_type;
    adjust_param.param = param;

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_S_ADJUST_PARAM, &adjust_param);

    return ret;
}

/* 更新edid */
td_s32 ext_mpi_hddec_update_edid(const ext_drv_hddec_edid *edid)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }

    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_U_EDID, edid);

    return ret;
}

/* 获取能力信息 */
td_s32 ext_mpi_hddec_get_capability(ext_drv_hddec_capability *capability)
{
    td_s32 ret = TD_SUCCESS;

    check_hddec_init(ret);
    if (ret == TD_FAILURE) {
        return TD_FAILURE;
    }
    if (capability == TD_NULL) {
        soc_log_err("NULL ptr.\n");
        return TD_FAILURE;
    }
    ret = ioctl(g_hddec_dev_fd, IOC_HDDEC_G_CAPABILITY, capability);

    return ret;
}