/*
 * Copyright 2018 Rockchip Electronics Co. LTD
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define MODULE_TAG "iep"

#include <math.h>
#include <string.h>

#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_debug.h"
#include "mpp_common.h"

#include "iep_api.h"
#include "iep.h"

#define X   (-1)

static RK_S32 enh_alpha_table[][25] = {
    //      0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24
    /*0*/  {X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X },
    /*1*/  {0,  8, 12, 16, 20, 24, 28,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X },
    /*2*/  {0,  4,  8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X,  X },
    /*3*/  {0,  X,  X,  8,  X,  X, 12,  X,  X, 16,  X,  X, 20,  X,  X, 24,  X,  X, 28,  X,  X,  X,  X,  X,  X },
    /*4*/  {0,  2,  4,  6,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28 },
    /*5*/  {0,  X,  X,  X,  X,  8,  X,  X,  X,  X, 12,  X,  X,  X,  X, 16,  X,  X,  X,  X, 20,  X,  X,  X,  X },
    /*6*/  {0,  X,  X,  4,  X,  X,  8,  X,  X, 10,  X,  X, 12,  X,  X, 14,  X,  X, 16,  X,  X, 18,  X,  X, 20 },
    /*7*/  {0,  X,  X,  X,  X,  X,  X,  8,  X,  X,  X,  X,  X,  X, 12,  X,  X,  X,  X,  X,  X, 16,  X,  X,  X },
    /*8*/  {0,  1,  2,  3,  4,  5,  6,  7,  8,  X,  9,  X, 10,  X, 11,  X, 12,  X, 13,  X, 14,  X,  15, X, 16 }
};

static const char *iep_name = "/dev/iep";

typedef struct IepCtxImpl_t {
    IepMsg      msg;
    RK_S32      fd;
    RK_S32      pid;
    IepHwCap    cap;
} IepCtxImpl;

MPP_RET iep_init(IepCtx *ctx)
{
    if (NULL == ctx) {
        mpp_err_f("invalid NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    RK_S32 fd = -1;
    IepCtxImpl *impl = NULL;

    mpp_env_get_u32("iep_debug", &iep_debug, 0);
    *ctx = NULL;

    do {
        impl = mpp_calloc(IepCtxImpl, 1);
        if (NULL == impl) {
            mpp_err_f("failed to alloc context\n");
            break;
        }

        fd = open(iep_name, O_RDWR | O_CLOEXEC);
        if (fd < 0) {
            mpp_err("can NOT find iep device %s\n", iep_name);
            break;
        }

        if (0 > ioctl(fd, IEP_QUERY_CAP, &impl->cap)) {
            iep_dbg_func("Query IEP capability failed, using default cap\n");
            IepHwCap *cap = &impl->cap;

            cap->scaling_supported = 0;
            cap->i4_deinterlace_supported = 1;
            cap->i2_deinterlace_supported = 1;
            cap->compression_noise_reduction_supported = 1;
            cap->sampling_noise_reduction_supported = 1;
            cap->hsb_enhancement_supported = 1;
            cap->cg_enhancement_supported = 1;
            cap->direct_path_supported = 1;
            cap->max_dynamic_width = 1920;
            cap->max_dynamic_height = 1088;
            cap->max_static_width = 8192;
            cap->max_static_height = 8192;
            cap->max_enhance_radius = 3;
        }

        impl->fd = fd;
        impl->pid = getpid();

        *ctx = impl;
        return MPP_OK;
    } while (0);

    if (fd > 0)
        close(fd);

    MPP_FREE(impl);

    return MPP_NOK;
}

MPP_RET iep_deinit(IepCtx ctx)
{
    if (NULL == ctx) {
        mpp_err_f("invalid NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    IepCtxImpl *impl = (IepCtxImpl *)ctx;

    if (impl->fd > 0) {
        close(impl->fd);
        impl->fd = -1;
    }

    return MPP_OK;
}

static void dump_iep_img(IepImg *img)
{
    mpp_log("image    %p\n", img);
    mpp_log("act_w    %d\n", img->act_w);
    mpp_log("act_h    %d\n", img->act_h);
    mpp_log("x_off    %d\n", img->x_off);
    mpp_log("y_off    %d\n", img->y_off);
    mpp_log("vir_w    %d\n", img->vir_w);
    mpp_log("vir_h    %d\n", img->vir_h);
    mpp_log("format   %d\n", img->format);
    mpp_log("mem_addr %08x\n", img->mem_addr);
    mpp_log("uv_addr  %08x\n", img->uv_addr);
    mpp_log("v_addr   %08x\n", img->v_addr);
}

static RK_U32 check_yuv_enhance_param(IepCmdParamYuvEnhance *yuv_enh)
{
    RK_U32 ret = 0;
    if (yuv_enh->saturation < 0 || yuv_enh->saturation > 1.992) {
        mpp_err("Invalidate parameter, yuv_enh_saturation!\n");
        ret |= 0x0001;
    }

    if (yuv_enh->contrast < 0 || yuv_enh->contrast > 1.992) {
        mpp_err("Invalidate parameter, yuv_enh_contrast!\n");
        ret |= 0x0002;
    }

    if (yuv_enh->brightness < -32 || yuv_enh->brightness > 31) {
        mpp_err("Invalidate parameter, yuv_enh_brightness!\n");
        ret |= 0x0004;
    }

    if (yuv_enh->hue_angle < -30 || yuv_enh->hue_angle > 30) {
        mpp_err("Invalidate parameter, yuv_enh_hue_angle!\n");
        ret |= 0x0008;
    }

    if (yuv_enh->video_mode < 0 || yuv_enh->video_mode > 3) {
        mpp_err("Invalidate parameter, video_mode!\n");
        ret |= 0x0010;
    }

    if (yuv_enh->color_bar_y > 127 || yuv_enh->color_bar_u > 127 ||
        yuv_enh->color_bar_v > 127) {
        mpp_err("Invalidate parameter, color_bar_*!\n");
        ret |= 0x0020;
    }
    return ret;
}

static RK_U32 check_rgb_enhance_param(IepCmdParamRgbEnhance *rgb_enh)
{
    RK_U32 ret = 0;
    if (rgb_enh->alpha_base > 8 || rgb_enh->alpha_base < 0 ||
        rgb_enh->alpha_num  < 0 || rgb_enh->alpha_num  > 24) {
        mpp_err("Invalidate parameter, enh_alpha_base or enh_alpha_num!\n");
        ret |= 0x0001;
    }

    if (enh_alpha_table[rgb_enh->alpha_base][rgb_enh->alpha_num] == -1) {
        mpp_err("Invalidate parameter, enh_alpha_base or enh_alpha_num!\n");
        ret |= 0x0002;
    }

    if (rgb_enh->threshold > 255 || rgb_enh->threshold < 0) {
        mpp_err("Invalidate parameter, enh_threshold!\n");
        ret |= 0x0004;
    }

    if (rgb_enh->radius > 4 || rgb_enh->radius < 1) {
        mpp_err("Invalidate parameter, enh_radius!\n");
        ret |= 0x0008;
    }

    if (rgb_enh->order < IEP_RGB_ENHANCE_ORDER_CG_DDE ||
        rgb_enh->order > IEP_RGB_ENHANCE_ORDER_DDE_CG) {
        mpp_err("Invalidate parameter, rgb_contrast_enhance_mode!\n");
        ret |= 0x0010;
    }

    if (rgb_enh->mode < IEP_RGB_ENHANCE_MODE_NO_OPERATION ||
        rgb_enh->mode > IEP_RGB_ENHANCE_MODE_EDGE_ENHANCE) {
        mpp_err("Invalidate parameter, rgb_enhance_mode!\n");
        ret |= 0x0020;
    }

    if (rgb_enh->coe > 3.96875 || rgb_enh->coe < 0) {
        mpp_err("Invalidate parameter, rgb_enh_coe!\n");
        ret |= 0x0040;
    }

    return ret;
}

static RK_U32 check_msg_image(IepMsg *msg)
{
    RK_U32 ret = 0;
    IepMsgImg *src = &msg->src;
    IepMsgImg *dst = &msg->dst;

    if (!((src->format < IEP_FORMAT_RGB_BUTT) ||
          (src->format >= IEP_FORMAT_YUV_BASE && src->format < IEP_FORMAT_YUV_BUTT))) {
        mpp_err("Invalidate parameter, i/o format!\n");
        ret |= 0x0001;
    }

    if (src->act_w > 4096 || src->act_h > 8192) {
        mpp_err("Invalidate parameter, source image size!\n");
        ret |= 0x0002;
    }

    if (dst->act_w > 4096 || dst->act_h > 4096) {
        mpp_err("Invalidate parameter, destination image size!\n");
        ret |= 0x0004;
    }

    RK_S32 scl_fct_h = src->act_w > dst->act_w ? (src->act_w * 1000 / dst->act_w) : (dst->act_w * 1000 / src->act_w);
    RK_S32 scl_fct_v = src->act_h > dst->act_h ? (src->act_h * 1000 / dst->act_h) : (dst->act_h * 1000 / src->act_h);

    if (scl_fct_h > 16000 || scl_fct_v > 16000) {
        mpp_err("Invalidate parameter, scale factor!\n");
        ret |= 0x0008;
    }
    return ret;
}

// rr = 1.7 rg = 1 rb = 0.6
static RK_U32 cg_tab[] = {
    0x01010100, 0x03020202, 0x04030303, 0x05040404,
    0x05050505, 0x06060606, 0x07070606, 0x07070707,
    0x08080807, 0x09080808, 0x09090909, 0x0a090909,
    0x0a0a0a0a, 0x0b0a0a0a, 0x0b0b0b0b, 0x0c0b0b0b,
    0x0c0c0c0c, 0x0c0c0c0c, 0x0d0d0d0d, 0x0d0d0d0d,
    0x0e0e0d0d, 0x0e0e0e0e, 0x0e0e0e0e, 0x0f0f0f0f,
    0x0f0f0f0f, 0x10100f0f, 0x10101010, 0x10101010,
    0x11111110, 0x11111111, 0x11111111, 0x12121212,
    0x12121212, 0x12121212, 0x13131313, 0x13131313,
    0x13131313, 0x14141414, 0x14141414, 0x14141414,
    0x15151515, 0x15151515, 0x15151515, 0x16161615,
    0x16161616, 0x16161616, 0x17161616, 0x17171717,
    0x17171717, 0x17171717, 0x18181818, 0x18181818,
    0x18181818, 0x19191818, 0x19191919, 0x19191919,
    0x19191919, 0x1a1a1a19, 0x1a1a1a1a, 0x1a1a1a1a,
    0x1a1a1a1a, 0x1b1b1b1b, 0x1b1b1b1b, 0x1b1b1b1b,
    0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
    0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
    0x23222120, 0x27262524, 0x2b2a2928, 0x2f2e2d2c,
    0x33323130, 0x37363534, 0x3b3a3938, 0x3f3e3d3c,
    0x43424140, 0x47464544, 0x4b4a4948, 0x4f4e4d4c,
    0x53525150, 0x57565554, 0x5b5a5958, 0x5f5e5d5c,
    0x63626160, 0x67666564, 0x6b6a6968, 0x6f6e6d6c,
    0x73727170, 0x77767574, 0x7b7a7978, 0x7f7e7d7c,
    0x83828180, 0x87868584, 0x8b8a8988, 0x8f8e8d8c,
    0x93929190, 0x97969594, 0x9b9a9998, 0x9f9e9d9c,
    0xa3a2a1a0, 0xa7a6a5a4, 0xabaaa9a8, 0xafaeadac,
    0xb3b2b1b0, 0xb7b6b5b4, 0xbbbab9b8, 0xbfbebdbc,
    0xc3c2c1c0, 0xc7c6c5c4, 0xcbcac9c8, 0xcfcecdcc,
    0xd3d2d1d0, 0xd7d6d5d4, 0xdbdad9d8, 0xdfdedddc,
    0xe3e2e1e0, 0xe7e6e5e4, 0xebeae9e8, 0xefeeedec,
    0xf3f2f1f0, 0xf7f6f5f4, 0xfbfaf9f8, 0xfffefdfc,
    0x06030100, 0x1b150f0a, 0x3a322922, 0x63584e44,
    0x95887b6f, 0xcebfb0a2, 0xfffeedde, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
};

static void setup_cg_tab(double cg_rate, RK_U32 *base)
{
    RK_S32 i;

    for (i = 0; i < 64; i++) {
        RK_U32 rgb_0 = 4 * i;
        RK_U32 rgb_1 = 4 * i + 1;
        RK_U32 rgb_2 = 4 * i + 2;
        RK_U32 rgb_3 = 4 * i + 3;

        /// need modify later
        RK_U32 cg_0 = pow(rgb_0, cg_rate);
        RK_U32 cg_1 = pow(rgb_1, cg_rate);
        RK_U32 cg_2 = pow(rgb_2, cg_rate);
        RK_U32 cg_3 = pow(rgb_3, cg_rate);

        if (cg_0 > 255) cg_0 = 255;
        if (cg_1 > 255) cg_1 = 255;
        if (cg_2 > 255) cg_2 = 255;
        if (cg_3 > 255) cg_3 = 255;

        RK_U32 tab_0 = (RK_U32)cg_0;
        RK_U32 tab_1 = (RK_U32)cg_1;
        RK_U32 tab_2 = (RK_U32)cg_2;
        RK_U32 tab_3 = (RK_U32)cg_3;

        RK_U32 conf_value = (tab_3 << 24) + (tab_2 << 16) + (tab_1 << 8) + tab_0;
        base[i] = conf_value;
    }
}

MPP_RET iep_control(IepCtx ctx, IepCmd cmd, void *param)
{
    if (NULL == ctx) {
        mpp_err_f("invalid NULL input\n");
        return MPP_ERR_NULL_PTR;
    }

    MPP_RET ret = MPP_OK;
    IepCtxImpl *impl = (IepCtxImpl *)ctx;
    IepMsg *msg = &impl->msg;

    switch (cmd) {
    case IEP_CMD_INIT : {
        memset(msg, 0, sizeof(*msg));
    } break;
    case IEP_CMD_SET_SRC : {
        IepMsgImg *src = &msg->src;

        mpp_assert(param);
        // NOTE: only update which is used
        memcpy(&msg->src, param, sizeof(IepImg));

        if ((src->format == IEP_FORMAT_YCbCr_420_P
             || src->format == IEP_FORMAT_YCbCr_420_SP
             || src->format == IEP_FORMAT_YCbCr_422_P
             || src->format == IEP_FORMAT_YCbCr_422_SP
             || src->format == IEP_FORMAT_YCrCb_420_P
             || src->format == IEP_FORMAT_YCrCb_420_SP
             || src->format == IEP_FORMAT_YCrCb_422_P
             || src->format == IEP_FORMAT_YCrCb_422_SP) &&
            msg->dein_mode == IEP_DEI_MODE_DISABLE) {
            msg->dein_mode = IEP_DEI_MODE_BYPASS;
        }
        if (iep_debug & IEP_DBG_IMAGE) {
            mpp_log("setup src\n");
            dump_iep_img((IepImg *)param);
        }
    } break;
    case IEP_CMD_SET_DST : {
        mpp_assert(param);
        // NOTE: only update which is used
        memcpy(&msg->dst, param, sizeof(IepImg));

        if (iep_debug & IEP_DBG_IMAGE) {
            mpp_log("setup dst\n");
            dump_iep_img((IepImg *)param);
        }
    } break;
    case IEP_CMD_SET_DEI_CFG : {
        IepCmdParamDeiCfg *dei_cfg = (IepCmdParamDeiCfg *)param;

        if (NULL == param) {
            msg->dein_high_fre_en   = 0;
            msg->dein_mode          = IEP_DEI_MODE_I2O1;
            msg->field_order        = IEP_DEI_FLD_ORDER_BOT_FIRST;
            msg->dein_ei_mode       = 0;
            msg->dein_ei_sel        = 0;
            msg->dein_ei_radius     = 0;
            msg->dein_ei_smooth     = 0;
            msg->dein_high_fre_fct  = 0;
        } else {
            msg->dein_high_fre_en   = dei_cfg->dei_high_freq_en;
            msg->dein_mode          = dei_cfg->dei_mode;
            msg->field_order        = dei_cfg->dei_field_order;
            msg->dein_ei_mode       = dei_cfg->dei_ei_mode;
            msg->dein_ei_sel        = dei_cfg->dei_ei_sel;
            msg->dein_ei_radius     = dei_cfg->dei_ei_radius;
            msg->dein_ei_smooth     = dei_cfg->dei_ei_smooth;
            msg->dein_high_fre_fct  = dei_cfg->dei_high_freq_fct;
        }
        switch (msg->dein_mode) {
        case IEP_DEI_MODE_DISABLE : {
            msg->dein_mode = IEP_DEI_MODE_BYPASS;
        } break;
        case IEP_DEI_MODE_I2O1 :
        case IEP_DEI_MODE_I4O1 :
        case IEP_DEI_MODE_I4O2 : {
            // for avoid hardware error we need to config src1 and dst1
            if (!msg->src1.mem_addr)
                memcpy(&msg->src1, &msg->src, sizeof(msg->src));
            if (!msg->dst1.mem_addr)
                memcpy(&msg->dst1, &msg->dst, sizeof(msg->dst));
        } break;
        default : {
        } break;
        }
    } break;
    case IEP_CMD_SET_DEI_SRC1 : {
        mpp_assert(param);
        // NOTE: only update which is used
        memcpy(&msg->src1, param, sizeof(IepImg));
        if (iep_debug & IEP_DBG_IMAGE) {
            mpp_log("setup src1\n");
            dump_iep_img((IepImg *)param);
        }
    } break;
    case IEP_CMD_SET_DEI_DST1 : {
        mpp_assert(param);
        // NOTE: only update which is used
        memcpy(&msg->dst1, param, sizeof(IepImg));
        if (iep_debug & IEP_DBG_IMAGE) {
            mpp_log("setup dst1\n");
            dump_iep_img((IepImg *)param);
        }
    } break;
    case IEP_CMD_SET_YUV_ENHANCE : {
        IepCmdParamYuvEnhance *yuv_enh = (IepCmdParamYuvEnhance *)param;

        if (NULL == yuv_enh) {
            msg->yuv_enhance_en = 1;

            msg->sat_con_int = 0x80;
            msg->contrast_int = 0x80;
            // hue_angle = 0
            msg->cos_hue_int = 0x00;
            msg->sin_hue_int = 0x80;

            msg->yuv_enh_brightness = 0x00;

            msg->video_mode = IEP_VIDEO_MODE_NORMAL_VIDEO;

            msg->color_bar_u = 0;
            msg->color_bar_v = 0;
            msg->color_bar_y = 0;
            break;
        }

        if (check_yuv_enhance_param(yuv_enh)) {
            ret = MPP_NOK;
            break;
        }

        if (msg->src.format >= 1 && msg->src.format <= 5 &&
            msg->dst.format >= 1 && msg->dst.format <= 5) {
            mpp_err("Invalidate parameter, contradition between in/out format and yuv enhance\n");
            ret = MPP_NOK;
            break;
        }

        msg->yuv_enhance_en = 1;

        msg->sat_con_int = (RK_S32)(yuv_enh->saturation * yuv_enh->contrast * 128);
        msg->contrast_int = (RK_S32)(yuv_enh->contrast * 128);
        msg->cos_hue_int = (RK_S32)(cos(yuv_enh->hue_angle) * 128.0);
        msg->sin_hue_int = (RK_S32)(sin(yuv_enh->hue_angle) * 128.0);
        msg->yuv_enh_brightness = yuv_enh->brightness >= 0 ?
                                  yuv_enh->brightness :
                                  (yuv_enh->brightness + 64);

        msg->video_mode  = yuv_enh->video_mode;
        msg->color_bar_y = yuv_enh->color_bar_y;
        msg->color_bar_u = yuv_enh->color_bar_u;
        msg->color_bar_v = yuv_enh->color_bar_v;
    } break;
    case IEP_CMD_SET_RGB_ENHANCE : {
        RK_U32 i;
        IepCmdParamRgbEnhance *rgb_enh = (IepCmdParamRgbEnhance *)param;

        if (NULL == rgb_enh) {
            msg->rgb_color_enhance_en       = 1;
            msg->rgb_enh_coe                = 32;
            msg->rgb_contrast_enhance_mode  = IEP_RGB_ENHANCE_MODE_DETAIL_ENHANCE;
            msg->rgb_cg_en                  = 0;
            msg->enh_threshold              = 255;
            msg->enh_alpha                  = 8;
            msg->enh_radius                 = 3;

            for (i = 0; i < MPP_ARRAY_ELEMS(cg_tab); i++)
                msg->cg_tab[i] = cg_tab[i];

            break;
        }

        if (check_rgb_enhance_param(rgb_enh)) {
            ret = MPP_NOK;
            break;
        }

        if ((msg->src.format & IEP_FORMAT_YUV_BASE) &&
            (msg->dst.format & IEP_FORMAT_YUV_BASE)) {
            mpp_err("Invalidate parameter, contradition between in/out format and yuv enhance\n");
            ret = MPP_NOK;
            break;
        }

        msg->rgb_color_enhance_en       = 1;
        msg->rgb_enh_coe                = (RK_U32)(rgb_enh->coe * 32);
        msg->rgb_contrast_enhance_mode  = rgb_enh->order;
        msg->rgb_cg_en                  = rgb_enh->cg_en;
        msg->rgb_enhance_mode           = rgb_enh->mode;

        msg->enh_threshold = rgb_enh->threshold;
        msg->enh_alpha     =
            enh_alpha_table[rgb_enh->alpha_base][rgb_enh->alpha_num];
        msg->enh_radius    = rgb_enh->radius - 1;

        if (rgb_enh->cg_en) {
            setup_cg_tab(rgb_enh->cg_rb, msg->cg_tab);
            setup_cg_tab(rgb_enh->cg_rg, msg->cg_tab + 64);
            setup_cg_tab(rgb_enh->cg_rb, msg->cg_tab + 128);
        }
    } break;
    case IEP_CMD_SET_SCALE : {
        IepCmdParamScale *scale = (IepCmdParamScale *)param;

        msg->scale_up_mode = (scale) ? (scale->scale_alg) : (IEP_SCALE_ALG_CATROM);
    } break;
    case IEP_CMD_SET_COLOR_CONVERT : {
        IepCmdParamColorConvert *color_cvt = (IepCmdParamColorConvert *)param;

        if (NULL == color_cvt) {
            msg->rgb2yuv_mode       = IEP_COLOR_MODE_BT601_L;
            msg->yuv2rgb_mode       = IEP_COLOR_MODE_BT601_L;
            msg->rgb2yuv_clip_en    = 0;
            msg->yuv2rgb_clip_en    = 0;
            msg->global_alpha_value = 0;
            msg->dither_up_en       = 1;
            msg->dither_down_en     = 1;
            break;
        }

        if (color_cvt->dither_up_en && msg->src.format != IEP_FORMAT_RGB_565) {
            mpp_err("Invalidate parameter, contradiction between dither up enable and source image format!\n");
            ret = MPP_NOK;
            break;
        }

        if (color_cvt->dither_down_en && msg->dst.format != IEP_FORMAT_RGB_565) {
            mpp_err("Invalidate parameter, contradiction between dither down enable and destination image format!\n");
            ret = MPP_NOK;
            break;
        }

        msg->rgb2yuv_mode       = color_cvt->rgb2yuv_mode;
        msg->yuv2rgb_mode       = color_cvt->yuv2rgb_mode;
        msg->rgb2yuv_clip_en    = color_cvt->rgb2yuv_input_clip;
        msg->yuv2rgb_clip_en    = color_cvt->yuv2rgb_input_clip;
        msg->global_alpha_value = color_cvt->global_alpha_value;
        msg->dither_up_en       = color_cvt->dither_up_en;
        msg->dither_down_en     = color_cvt->dither_down_en;
    } break;
    case IEP_CMD_RUN_SYNC : {
        check_msg_image(msg);

        int ops_ret = ioctl(impl->fd, IEP_SET_PARAMETER, msg);
        if (ops_ret < 0)
            mpp_err("pid %d ioctl IEP_SET_PARAMETER failure\n", impl->pid);

        // NOTE: force result sync to avoid iep task queue full
        ops_ret = ioctl(impl->fd, IEP_GET_RESULT_SYNC, 0);
        if (ops_ret)
            mpp_err("pid %d get result failure\n", impl->pid);

        ret = (MPP_RET)ops_ret;
    } break;
    case IEP_CMD_QUERY_CAP : {
        if (param)
            *(IepHwCap **)param = &impl->cap;
        else
            mpp_err("Can NOT query to NULL output\n");
    } break;
    default : {
        mpp_err("invalid command %d\n", cmd);
        ret = MPP_NOK;
    } break;
    }

    return ret;
}

static iep_com_ops iep_ops = {
    .init = iep_init,
    .deinit = iep_deinit,
    .control = iep_control,
    .reset = NULL,
    .release = NULL,
};

iep_com_ctx* rockchip_iep_api_alloc_ctx(void)
{
    iep_com_ctx *com_ctx = (iep_com_ctx *)calloc(sizeof(*com_ctx), 1);
    void *iep_ctx = calloc(sizeof(sizeof(void*)), 1);

    mpp_assert(com_ctx && iep_ctx);

    com_ctx->ops = &iep_ops;
    com_ctx->priv = iep_ctx;

    return com_ctx;
}

void rockchip_iep_api_release_ctx(iep_com_ctx *com_ctx)
{
    if (com_ctx->priv) {
        free(com_ctx->priv);
        com_ctx->priv = NULL;
    }

    free(com_ctx);
}

