/*
*
* Copyright 2015 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 "hal_h264d_api"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>

#include "mpp_mem.h"
#include "mpp_env.h"
#include "mpp_platform.h"
#include "mpp_common.h"
#include "osal_2str.h"

#include "dxva_syntax.h"
#include "h264d_syntax.h"

#include "hal_h264d_global.h"
#include "hal_h264d_api.h"

#include "hal_h264d_rkv_reg.h"
#include "hal_h264d_vdpu34x.h"
#include "hal_h264d_vdpu382.h"
#include "hal_h264d_vdpu2.h"
#include "hal_h264d_vdpu1.h"

RK_U32 hal_h264d_debug = 0;

static void explain_input_buffer(void *hal, HalDecTask *task)
{
    RK_U32 i = 0;
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;
    DXVA2_DecodeBufferDesc *pdes = (DXVA2_DecodeBufferDesc *)task->syntax.data;
    for (i = 0; i < task->syntax.number; i++) {
        switch (pdes[i].CompressedBufferType) {
        case DXVA2_PictureParametersBufferType:
            p_hal->pp = (DXVA_PicParams_H264_MVC *)pdes[i].pvPVPState;
            break;
        case DXVA2_InverseQuantizationMatrixBufferType:
            p_hal->qm = (DXVA_Qmatrix_H264 *)pdes[i].pvPVPState;
            break;
        case DXVA2_SliceControlBufferType:
            p_hal->slice_num = pdes[i].DataSize / sizeof(DXVA_Slice_H264_Long);
            p_hal->slice_long = (DXVA_Slice_H264_Long *)pdes[i].pvPVPState;
            break;
        case DXVA2_BitStreamDateBufferType:
            p_hal->bitstream = (RK_U8 *)pdes[i].pvPVPState;
            p_hal->strm_len = pdes[i].DataSize;
            break;
        default:
            break;
        }
    }
}

/*!
***********************************************************************
* \brief
*    init
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_init(void *hal, MppHalCfg *cfg)
{
    MppHalApi *p_api = NULL;
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;
    MppClientType client_type = VPU_CLIENT_BUTT;

    INP_CHECK(ret, NULL == p_hal);
    memset(p_hal, 0, sizeof(H264dHalCtx_t));


    //!< choose hard mode
    {
        RK_S32 hw_type = -1;
        RK_U32 mode = MODE_NULL;
        RK_U32 vcodec_type = mpp_get_vcodec_type();

        // check codec_type
        if (!(vcodec_type & (HAVE_RKVDEC | HAVE_VDPU1 | HAVE_VDPU2))) {
            mpp_err_f("can not found H.264 decoder hardware on platform %x\n", vcodec_type);
            return ret;
        }
        mpp_env_get_u32("use_mpp_mode", &mode, MODE_NULL);
        if (MODE_NULL == mode) {
            MppDecBaseCfg *base = &cfg->cfg->base;

            if (mpp_check_soc_cap(base->type, base->coding))
                hw_type = base->hw_type;

            if (hw_type > 0) {
                if (vcodec_type & (1 << hw_type)) {
                    mpp_log("init with %s hw\n", strof_client_type(hw_type));
                    client_type = hw_type;
                } else
                    mpp_err_f("invalid hw_type %d with vcodec_type %08x\n",
                              hw_type, vcodec_type);
            }
        }

        if (client_type == VPU_CLIENT_BUTT) {
            if ((mode <= RKVDEC_MODE) && (vcodec_type & HAVE_RKVDEC)) {
                client_type = VPU_CLIENT_RKVDEC;
            } else if (vcodec_type & HAVE_VDPU1) {
                client_type = VPU_CLIENT_VDPU1;
            } else if (vcodec_type & HAVE_VDPU2) {
                client_type = VPU_CLIENT_VDPU2;
            }
        }
        H264D_DBG(H264D_DBG_HARD_MODE, "client_type %d\n", client_type);
    }

    p_api = &p_hal->hal_api;
    switch (client_type) {
    case VPU_CLIENT_RKVDEC : {
        RK_U32 hw_id = mpp_get_client_hw_id(client_type);

        if (hw_id == HWID_VDPU382_RK3528 || hw_id == HWID_VDPU382_RK3562) {
            p_api->init    = vdpu382_h264d_init;
            p_api->deinit  = vdpu382_h264d_deinit;
            p_api->reg_gen = vdpu382_h264d_gen_regs;
            p_api->start   = vdpu382_h264d_start;
            p_api->wait    = vdpu382_h264d_wait;
            p_api->reset   = vdpu382_h264d_reset;
            p_api->flush   = vdpu382_h264d_flush;
            p_api->control = vdpu382_h264d_control;
        } else if (hw_id == HWID_VDPU34X || hw_id == HWID_VDPU38X) {
            p_api->init    = vdpu34x_h264d_init;
            p_api->deinit  = vdpu34x_h264d_deinit;
            p_api->reg_gen = vdpu34x_h264d_gen_regs;
            p_api->start   = vdpu34x_h264d_start;
            p_api->wait    = vdpu34x_h264d_wait;
            p_api->reset   = vdpu34x_h264d_reset;
            p_api->flush   = vdpu34x_h264d_flush;
            p_api->control = vdpu34x_h264d_control;
        } else {
            p_api->init    = rkv_h264d_init;
            p_api->deinit  = rkv_h264d_deinit;
            p_api->reg_gen = rkv_h264d_gen_regs;
            p_api->start   = rkv_h264d_start;
            p_api->wait    = rkv_h264d_wait;
            p_api->reset   = rkv_h264d_reset;
            p_api->flush   = rkv_h264d_flush;
            p_api->control = rkv_h264d_control;
        }

        cfg->support_fast_mode = 1;
    } break;
    case VPU_CLIENT_VDPU1 : {
        p_api->init    = vdpu1_h264d_init;
        p_api->deinit  = vdpu1_h264d_deinit;
        p_api->reg_gen = vdpu1_h264d_gen_regs;
        p_api->start   = vdpu1_h264d_start;
        p_api->wait    = vdpu1_h264d_wait;
        p_api->reset   = vdpu1_h264d_reset;
        p_api->flush   = vdpu1_h264d_flush;
        p_api->control = vdpu1_h264d_control;
    } break;
    case VPU_CLIENT_VDPU2 : {
        p_api->init    = vdpu2_h264d_init;
        p_api->deinit  = vdpu2_h264d_deinit;
        p_api->reg_gen = vdpu2_h264d_gen_regs;
        p_api->start   = vdpu2_h264d_start;
        p_api->wait    = vdpu2_h264d_wait;
        p_api->reset   = vdpu2_h264d_reset;
        p_api->flush   = vdpu2_h264d_flush;
        p_api->control = vdpu2_h264d_control;
    } break;
    default : {
        mpp_err_f("client_type error, value=%d\n", client_type);
        mpp_assert(0);
    } break;
    }

    mpp_env_get_u32("hal_h264d_debug", &hal_h264d_debug, 0);

    ret = mpp_dev_init(&cfg->dev, client_type);
    if (ret) {
        mpp_err("mpp_dev_init failed ret: %d\n", ret);
        goto __FAILED;
    }

    //!< callback function to parser module
    p_hal->dec_cb       = cfg->dec_cb;
    p_hal->cfg          = cfg->cfg;
    p_hal->dev          = cfg->dev;
    p_hal->frame_slots  = cfg->frame_slots;
    p_hal->packet_slots = cfg->packet_slots;
    p_hal->fast_mode    = cfg->cfg->base.fast_parse;

    //< get buffer group
    if (p_hal->buf_group == NULL) {
        FUN_CHECK(ret = mpp_buffer_group_get_internal
                        (&p_hal->buf_group, MPP_BUFFER_TYPE_ION));
    }

    //!< run init funtion
    FUN_CHECK(ret = p_api->init(hal, cfg));
__RETURN:
    return MPP_OK;
__FAILED:
    return ret;
}

/*!
***********************************************************************
* \brief
*    deinit
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_deinit(void *hal)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    FUN_CHECK(ret = p_hal->hal_api.deinit(hal));

    if (p_hal->dev) {
        mpp_dev_deinit(p_hal->dev);
        p_hal->dev = NULL;
    }

    if (p_hal->buf_group) {
        FUN_CHECK(ret = mpp_buffer_group_put(p_hal->buf_group));
    }

    return MPP_OK;
__FAILED:
    return ret;
}

/*!
***********************************************************************
* \brief
*    generate register
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_gen_regs(void *hal, HalTaskInfo *task)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    explain_input_buffer(hal, &task->dec);
    return p_hal->hal_api.reg_gen(hal, task);
}

/*!
***********************************************************************
* \brief h
*    start hard
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_start(void *hal, HalTaskInfo *task)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    return p_hal->hal_api.start(hal, task);
}

/*!
***********************************************************************
* \brief
*    wait hard
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_wait(void *hal, HalTaskInfo *task)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    return p_hal->hal_api.wait(hal, task);
}

/*!
***********************************************************************
* \brief
*    reset
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_reset(void *hal)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    return p_hal->hal_api.reset(hal);
}

/*!
***********************************************************************
* \brief
*    flush
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_flush(void *hal)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    return p_hal->hal_api.flush(hal);
}

/*!
***********************************************************************
* \brief
*    control
***********************************************************************
*/
//extern "C"
MPP_RET hal_h264d_control(void *hal, MpiCmd cmd_type, void *param)
{
    H264dHalCtx_t *p_hal = (H264dHalCtx_t *)hal;

    return p_hal->hal_api.control(hal, cmd_type, param);
}


const MppHalApi hal_api_h264d = {
    .name = "h264d_rkdec",
    .type = MPP_CTX_DEC,
    .coding = MPP_VIDEO_CodingAVC,
    .ctx_size = sizeof(H264dHalCtx_t),
    .flag = 0,
    .init = hal_h264d_init,
    .deinit = hal_h264d_deinit,
    .reg_gen = hal_h264d_gen_regs,
    .start = hal_h264d_start,
    .wait = hal_h264d_wait,
    .reset = hal_h264d_reset,
    .flush = hal_h264d_flush,
    .control = hal_h264d_control,
};

