/*
 * 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_avsd_plus"

#include <string.h>

#include "mpp_common.h"
#include "mpp_mem.h"
#include "mpp_device.h"

#include "avsd_syntax.h"
#include "hal_avsd_api.h"
#include "hal_avsd_plus_reg.h"
#include "hal_avsd_plus.h"
#include "mpp_dec_cb_param.h"
#include "hal_avsd_base.h"

/*!
 ***********************************************************************
 * \brief
 *    init decoder parameters
 ***********************************************************************
 */
//extern "C"
MPP_RET set_defalut_parameters(AvsdHalCtx_t *p_hal)
{
    AvsdPlusRegs_t *p_regs = (AvsdPlusRegs_t *)p_hal->p_regs;

    p_regs->sw02.dec_out_endian = 1;
    p_regs->sw02.dec_in_endian = 0;
    p_regs->sw02.dec_strendian_e = 1;
    p_regs->sw02.dec_max_burst = 16;
    p_regs->sw02.dec_scmd_dis = 0;

    p_regs->sw02.dec_adv_pre_dis = 0;
    p_regs->sw55.apf_threshold = 8;

    p_regs->sw02.dec_latency = 0;
    p_regs->sw02.dec_data_disc_e = 0;
    p_regs->sw02.dec_outswap32_e = 1;
    p_regs->sw02.dec_inswap32_e = 1;
    p_regs->sw02.dec_strswap32_e = 1;

    p_regs->sw02.dec_timeout_e = 0;
    p_regs->sw02.dec_clk_gate_e = 1;
    p_regs->sw01.dec_irq_dis = 0;

    p_regs->sw58.serv_merge_dis = 0;
    p_regs->sw02.dec_axi_rd_id = 0xFF;
    p_regs->sw03.dec_axi_wr_id = 0;

    p_regs->sw49.pred_bc_tap_0_0 = -1;
    p_regs->sw49.pred_bc_tap_0_1 = 5;
    p_regs->sw49.pred_bc_tap_0_2 = 5;
    p_regs->sw34.pred_bc_tap_0_3 = -1;

    p_regs->sw34.pred_bc_tap_1_0 = 1;
    p_regs->sw34.pred_bc_tap_1_1 = 7;
    p_regs->sw35.pred_bc_tap_1_2 = 7;
    p_regs->sw35.pred_bc_tap_1_3 = 1;

    return MPP_OK;
}

static MPP_RET set_regs_parameters(AvsdHalCtx_t *p_hal, HalDecTask *task)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    AvsdSyntax_t *p_syn = &p_hal->syn;
    AvsdPlusRegs_t *p_regs = (AvsdPlusRegs_t *)p_hal->p_regs;

    p_regs->sw02.dec_timeout_e = 1;

    //!< set wrok_out pic info
    if (p_hal->work_out < 0) {
        p_hal->work_out = get_queue_pic(p_hal);
        if (p_hal->work_out < 0) {
            ret = MPP_NOK;
            mpp_err_f("cannot get a pic_info buffer.\n");
            goto __FAILED;
        }
    }
    {
        AvsdHalPic_t *p_work_out = &p_hal->pic[p_hal->work_out];

        p_work_out->slot_idx = task->output;
        p_work_out->pic_code_type = p_syn->pp.picCodingType;
        p_work_out->picture_distance = p_syn->pp.pictureDistance;
    }
    //!< set register
    set_defalut_parameters(p_hal);
    p_regs->sw04.pic_mb_width = (p_syn->pp.horizontalSize + 15) >> 4;
    p_regs->sw03.dec_mode = 11; //!< DEC_MODE_AVS

    if (p_syn->pp.pictureStructure == FRAMEPICTURE) {
        p_regs->sw03.pic_interlace_e = 0;
        p_regs->sw03.pic_fieldmode_e = 0;
        p_regs->sw03.pic_topfiled_e = 0;
    } else {
        p_regs->sw03.pic_interlace_e = 1;
        p_regs->sw03.pic_fieldmode_e = 1;
        if (p_syn->pp.topFieldFirst) {
            p_regs->sw03.pic_topfiled_e = p_hal->first_field;
        } else {
            p_regs->sw03.pic_topfiled_e = !p_hal->first_field;
        }
    }

    p_regs->sw04.pic_mb_height_p = (p_syn->pp.verticalSize + 15) >> 4;
    p_regs->sw07.avs_h_ext = (p_syn->pp.verticalSize + 15) >> 12;

    if (p_syn->pp.picCodingType == BFRAME) {
        p_regs->sw03.pic_b_e = 1;
    } else {
        p_regs->sw03.pic_b_e = 0;
    }
    p_regs->sw03.pic_inter_e = (p_syn->pp.picCodingType != IFRAME) ? 1 : 0;

    p_regs->sw05.strm_start_bit = 8 * (p_hal->data_offset & 0x7);
    p_hal->data_offset = (p_hal->data_offset & ~0x7);
    p_regs->sw12.rlc_vlc_base = get_packet_fd(p_hal, task->input);
    mpp_dev_set_reg_offset(p_hal->dev, 12, p_hal->data_offset);
    p_regs->sw06.stream_len = p_syn->bitstream_size - p_hal->data_offset;

    p_regs->sw03.pic_fixed_quant = p_syn->pp.fixedPictureQp;
    p_regs->sw06.init_qp = p_syn->pp.pictureQp;
    //!< AVS Plus stuff
    if (p_syn->pp.profileId == 0x48) {
        p_regs->sw44.dec_avsp_ena = 1;
    } else {
        p_regs->sw44.dec_avsp_ena = 0;
    }
    if (p_regs->sw44.dec_avsp_ena) {
        p_regs->sw44.weight_qp_e = p_syn->pp.weightingQuantFlag;
        p_regs->sw44.avs_aec_e = p_syn->pp.aecEnable;
        p_regs->sw44.no_fwd_ref_e = p_syn->pp.noForwardReferenceFlag;
        p_regs->sw44.pb_field_enhance_e = p_syn->pp.pbFieldEnhancedFlag;

        if (p_syn->pp.weightingQuantFlag
            && !p_syn->pp.chromaQuantParamDisable) {
            p_regs->sw44.qp_delta_cb = p_syn->pp.chromaQuantParamDeltaCb;
            p_regs->sw44.qp_delta_cr = p_syn->pp.chromaQuantParamDeltaCr;
        } else {
            p_regs->sw44.qp_delta_cb = 0;
            p_regs->sw44.qp_delta_cr = 0;
        }
        if (p_syn->pp.weightingQuantFlag) {
            p_regs->sw44.weight_qp_model = p_syn->pp.weightingQuantModel;
            p_regs->sw44.weight_qp_0 = p_syn->pp.weightingQuantParam[0];
            p_regs->sw42.weight_qp_1 = p_syn->pp.weightingQuantParam[1];
            p_regs->sw43.weight_qp_2 = p_syn->pp.weightingQuantParam[2];
            p_regs->sw43.weight_qp_3 = p_syn->pp.weightingQuantParam[3];
            p_regs->sw43.weight_qp_4 = p_syn->pp.weightingQuantParam[4];
            p_regs->sw43.weight_qp_5 = p_syn->pp.weightingQuantParam[5];
        }
    }
    //!< AVS Plus end
    if (p_syn->pp.pictureStructure == FRAMEPICTURE || p_hal->first_field) {
        p_regs->sw13.dec_out_base = get_frame_fd(p_hal, task->output);
    } else {
        //!< start of bottom field line
        RK_U32 stride = p_syn->pp.horizontalSize;

        p_regs->sw13.dec_out_base = get_frame_fd(p_hal, task->output);
        mpp_dev_set_reg_offset(p_hal->dev, 13, stride);
    }
    {
        RK_S32 tmp_fwd = -1;
        RK_S32 refer0 = -1;
        RK_S32 refer1 = -1;

        tmp_fwd = (p_hal->work1 < 0) ? p_hal->work0 : p_hal->work1;
        tmp_fwd = (tmp_fwd < 0) ? p_hal->work_out : tmp_fwd;

        refer0 = (task->refer[0] < 0) ? task->output : task->refer[0];
        refer1 = (task->refer[1] < 0) ? refer0 : task->refer[1];
        if (!p_hal->first_field
            && p_syn->pp.pictureStructure == FIELDPICTURE
            && p_syn->pp.picCodingType != BFRAME) {
            p_regs->sw14.refer0_base = get_frame_fd(p_hal, task->output);
            p_regs->sw15.refer1_base = get_frame_fd(p_hal, refer0);
            p_regs->sw16.refer2_base = get_frame_fd(p_hal, refer0);
            p_regs->sw17.refer3_base = get_frame_fd(p_hal, refer1);
        } else {
            p_regs->sw14.refer0_base = get_frame_fd(p_hal, refer0);
            p_regs->sw15.refer1_base = get_frame_fd(p_hal, refer0);
            p_regs->sw16.refer2_base = get_frame_fd(p_hal, refer1);
            p_regs->sw17.refer3_base = get_frame_fd(p_hal, refer1);
        }
    }
    //!< block distances
    if (p_syn->pp.pictureStructure == FRAMEPICTURE) {
        if (p_syn->pp.picCodingType == BFRAME) {
            RK_S32 tmp = 0;
            //!< current to future anchor
            if (p_hal->work0 >= 0) {
                tmp = (2 * p_hal->pic[p_hal->work0].picture_distance -
                       2 * p_syn->pp.pictureDistance + 512) & 0x1FF;
            }
            //!< prevent division by zero
            if (!tmp) tmp = 2;
            p_regs->sw31.ref_dist_cur_2 = tmp;
            p_regs->sw31.ref_dist_cur_3 = tmp;
            p_regs->sw29.ref_invd_cur_2 = 512 / tmp;
            p_regs->sw29.ref_invd_cur_3 = 512 / tmp;

            //!< current to past anchor
            if (p_hal->work1 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            p_regs->sw30.ref_dist_cur_0 = tmp;
            p_regs->sw30.ref_dist_cur_1 = tmp;
            p_regs->sw28.ref_invd_cur_0 = 512 / tmp;
            p_regs->sw28.ref_invd_cur_1 = 512 / tmp;
            //!< future anchor to past anchor
            if (p_hal->work0 >= 0 && p_hal->work1 >= 0) {
                tmp = (2 * p_hal->pic[p_hal->work0].picture_distance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            tmp = 16384 / tmp;
            p_regs->sw32.ref_invd_col_0 = tmp;
            p_regs->sw32.ref_invd_col_1 = tmp;
            //!< future anchor to previous past anchor
            tmp = p_hal->future2prev_past_dist;
            tmp = 16384 / tmp;
            p_regs->sw33.ref_invd_col_2 = tmp;
            p_regs->sw33.ref_invd_col_3 = tmp;
        } else {
            RK_S32 tmp = 0;
            //!< current to past anchor
            if (p_hal->work0 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work0].picture_distance - 512) & 0x1FF;
            }
            if (!tmp) tmp = 2;
            p_regs->sw30.ref_dist_cur_0 = tmp;
            p_regs->sw30.ref_dist_cur_1 = tmp;
            p_regs->sw28.ref_invd_cur_0 = 512 / tmp;
            p_regs->sw28.ref_invd_cur_1 = 512 / tmp;
            //!< current to previous past anchor
            if (p_hal->work1 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            //!< this will become "future to previous past" for next B
            p_hal->future2prev_past_dist = tmp;

            p_regs->sw31.ref_dist_cur_2 = tmp;
            p_regs->sw31.ref_dist_cur_3 = tmp;
            p_regs->sw29.ref_invd_cur_2 = 512 / tmp;
            p_regs->sw29.ref_invd_cur_3 = 512 / tmp;

            p_regs->sw32.ref_invd_col_0 = 0;
            p_regs->sw32.ref_invd_col_1 = 0;
            p_regs->sw33.ref_invd_col_2 = 0;
            p_regs->sw33.ref_invd_col_3 = 0;
        }
    } else {
        //!< field interlaced
        if (p_syn->pp.picCodingType == BFRAME) {
            RK_S32 tmp = 0;
            //!< block distances
            if (p_hal->work0 >= 0) {
                tmp = (2 * p_hal->pic[p_hal->work0].picture_distance -
                       2 * p_syn->pp.pictureDistance + 512) & 0x1FF;
            }
            //!< prevent division by zero
            if (!tmp) tmp = 2;

            if (p_hal->first_field) {
                p_regs->sw31.ref_dist_cur_2 = tmp;
                p_regs->sw31.ref_dist_cur_3 = tmp + 1;
                p_regs->sw29.ref_invd_cur_2 = 512 / tmp;
                p_regs->sw29.ref_invd_cur_3 = 512 / (tmp + 1);
            } else {
                p_regs->sw31.ref_dist_cur_2 = tmp - 1;
                p_regs->sw31.ref_dist_cur_3 = tmp;
                p_regs->sw29.ref_invd_cur_2 = 512 / (tmp - 1);
                p_regs->sw29.ref_invd_cur_3 = 512 / tmp;
            }

            if (p_hal->work1 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            if (p_hal->first_field) {
                p_regs->sw30.ref_dist_cur_0 = (tmp - 1);
                p_regs->sw30.ref_dist_cur_1 = tmp;
                p_regs->sw28.ref_invd_cur_0 = 512 / (tmp - 1);
                p_regs->sw28.ref_invd_cur_1 = 512 / tmp;
            } else {
                p_regs->sw30.ref_dist_cur_0 = tmp;
                p_regs->sw30.ref_dist_cur_1 = tmp + 1;
                p_regs->sw28.ref_invd_cur_0 = 512 / tmp;
                p_regs->sw28.ref_invd_cur_1 = 512 / (tmp + 1);
            }

            if (p_hal->work0 >= 0 && p_hal->work1 >= 0) {
                tmp = (2 * p_hal->pic[p_hal->work0].picture_distance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            //!< AVS Plus stuff
            if (p_syn->pp.pbFieldEnhancedFlag && !p_hal->first_field) {
                //!< in this case, BlockDistanceRef is different with before, the mvRef points to top field
                p_regs->sw32.ref_invd_col_0 = 16384 / (tmp - 1);
                p_regs->sw32.ref_invd_col_1 = 16384 / tmp;

                //!< future anchor to previous past anchor
                tmp = p_hal->future2prev_past_dist;

                p_regs->sw33.ref_invd_col_2 = 16384 / (tmp - 1);
                p_regs->sw33.ref_invd_col_3 = 16384 / tmp;
            } else {
                if (p_hal->first_field) {
                    p_regs->sw32.ref_invd_col_0 = 16384 / (tmp - 1);
                    p_regs->sw32.ref_invd_col_1 = 16384 / tmp;
                } else {
                    p_regs->sw32.ref_invd_col_0 = 16384;
                    p_regs->sw32.ref_invd_col_1 = 16384 / tmp;
                    p_regs->sw33.ref_invd_col_2 = 16384 / (tmp + 1);
                }

                //!< future anchor to previous past anchor
                tmp = p_hal->future2prev_past_dist;

                if (p_hal->first_field) {
                    p_regs->sw33.ref_invd_col_2 = 16384 / (tmp - 1);
                    p_regs->sw33.ref_invd_col_3 = 16384 / tmp;
                } else {
                    p_regs->sw33.ref_invd_col_3 = 16384 / tmp;
                }
            }
        } else {
            RK_S32 tmp = 0;
            if (p_hal->work0 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work0].picture_distance - 512) & 0x1FF;
            }
            if (!tmp) tmp = 2;

            if (!p_hal->first_field) {
                p_regs->sw30.ref_dist_cur_0 = 1;
                p_regs->sw31.ref_dist_cur_2 = tmp + 1;

                p_regs->sw28.ref_invd_cur_0 = 512;
                p_regs->sw29.ref_invd_cur_2 = 512 / (tmp + 1);
            } else {
                p_regs->sw30.ref_dist_cur_0 = tmp - 1;
                p_regs->sw28.ref_invd_cur_0 = 512 / (tmp - 1);
            }
            p_regs->sw30.ref_dist_cur_1 = tmp;
            p_regs->sw28.ref_invd_cur_1 = 512 / tmp;

            if (p_hal->work1 >= 0) {
                tmp = (2 * p_syn->pp.pictureDistance -
                       2 * p_hal->pic[p_hal->work1].picture_distance - 512) & 0x1FF;
                if (!tmp) tmp = 2;
            }
            //!< this will become "future to previous past" for next B
            p_hal->future2prev_past_dist = tmp;
            if (p_hal->first_field) {
                p_regs->sw31.ref_dist_cur_2 = tmp - 1;
                p_regs->sw31.ref_dist_cur_3 = tmp;

                p_regs->sw29.ref_invd_cur_2 = 512 / (tmp - 1);
                p_regs->sw29.ref_invd_cur_3 = 512 / tmp;
            } else {
                p_regs->sw31.ref_dist_cur_3 = tmp;
                p_regs->sw29.ref_invd_cur_3 = 512 / tmp;
            }

            p_regs->sw32.ref_invd_col_0 = 0;
            p_regs->sw32.ref_invd_col_1 = 0;
            p_regs->sw33.ref_invd_col_2 = 0;
            p_regs->sw33.ref_invd_col_3 = 0;
        }
    }
    //!< AVS Plus stuff
    if (p_regs->sw44.dec_avsp_ena) {
        p_regs->sw42.ref_delta_col_0 = 0;
        p_regs->sw42.ref_delta_col_1 = 0;
        p_regs->sw42.ref_delta_col_2 = 0;
        p_regs->sw42.ref_delta_col_3 = 0;
        p_regs->sw42.ref_delta_cur_0 = 0;
        p_regs->sw42.ref_delta_cur_1 = 0;
        p_regs->sw42.ref_delta_cur_2 = 0;
        p_regs->sw42.ref_delta_cur_3 = 0;
        if (p_syn->pp.pictureStructure == FIELDPICTURE
            && p_syn->pp.picCodingType == BFRAME) {
            //!< 1 means delta=2, 3 means delta=-2, 0 means delta=0
            //!< delta1
            p_regs->sw42.ref_delta_col_0 = 2;
            p_regs->sw42.ref_delta_col_1 = 0;
            p_regs->sw42.ref_delta_col_2 = 2;
            p_regs->sw42.ref_delta_col_3 = 0;
            if (p_hal->first_field) {
                //!< deltaFw
                p_regs->sw42.ref_delta_cur_0 = 2;
                p_regs->sw42.ref_delta_cur_1 = 0;
                //!< deltaBw
                p_regs->sw42.ref_delta_cur_2 = 0;
                p_regs->sw42.ref_delta_cur_3 = 0;
            } else {
                //!< deltaFw
                p_regs->sw42.ref_delta_cur_0 = 0;
                p_regs->sw42.ref_delta_cur_1 = 0;
                //!< deltaBw
                p_regs->sw42.ref_delta_cur_2 = 6; //!< (RK_U32)-2
                p_regs->sw42.ref_delta_cur_3 = 6; //!< (RK_U32)-2
            }
        }
    }
    //!< AVS Plus end

    p_regs->sw48.startmb_x = 0;
    p_regs->sw48.startmb_y = 0;

    p_regs->sw03.filtering_dis = p_syn->pp.loopFilterDisable;
    p_regs->sw05.alpha_offset = p_syn->pp.alphaOffset;
    p_regs->sw05.beta_offset = p_syn->pp.betaOffset;
    p_regs->sw03.skip_mode = p_syn->pp.skipModeFlag;
    p_regs->sw04.pic_refer_flag = p_syn->pp.pictureReferenceFlag;

    //!< AVS Plus change
    p_regs->sw03.write_mvs_e = 0;
    if (p_regs->sw44.dec_avsp_ena) {
        if (p_syn->pp.picCodingType == PFRAME
            || p_syn->pp.picCodingType == IFRAME) {
            p_regs->sw03.write_mvs_e = 1;
        }
    } else {
        if (p_syn->pp.picCodingType == PFRAME
            || (p_syn->pp.picCodingType == IFRAME && !p_hal->first_field)) {
            p_regs->sw03.write_mvs_e = 1;
        }
    }
    //!< AVS Plus end
    //!< set mv base
    if (p_hal->first_field ||
        (p_syn->pp.picCodingType == BFRAME && p_hal->prev_pic_structure)) {
        p_regs->sw41.dir_mv_base = mpp_buffer_get_fd(p_hal->mv_buf);
    } else {
        RK_U32 frame_width = 0, frame_height = 0, offset = 0;
        frame_width = (p_syn->pp.horizontalSize + 15) >> 4;
        if (p_syn->pp.progressiveFrame)
            frame_height = (p_syn->pp.verticalSize + 15) >> 4;
        else
            frame_height = 2 * ((p_syn->pp.verticalSize + 31) >> 5);
        offset = MPP_ALIGN(frame_width * frame_height / 2, 2) * 16;
        p_regs->sw41.dir_mv_base = mpp_buffer_get_fd(p_hal->mv_buf);
        mpp_dev_set_reg_offset(p_hal->dev, 41, offset);
    }
    //!< AVS Plus stuff
    if (p_regs->sw44.dec_avsp_ena) {
        p_regs->sw45.dir_mv_base2 = mpp_buffer_get_fd(p_hal->mv_buf);
    }
    //!< AVS Plus end
    {
        RK_U32 pic_type = 0;
        RK_U32 prev_anc_type = 0;

        if (p_hal->work0 >= 0) {
            pic_type = p_hal->pic[p_hal->work0].pic_type;
        }
        prev_anc_type = !pic_type || (!p_hal->first_field && !p_hal->prev_pic_structure);
        p_regs->sw18.prev_anc_type = prev_anc_type;
    }
    //!< b-picture needs to know if future reference is field or frame coded
    // p_regs->sw16.refer2_field_e = (!p_hal->prev_pic_structure) ? 1 : 0;
    // p_regs->sw17.refer3_field_e = (!p_hal->prev_pic_structure) ? 1 : 0;
    if (!p_hal->prev_pic_structure) {
        mpp_dev_set_reg_offset(p_hal->dev, 16, 2);
        mpp_dev_set_reg_offset(p_hal->dev, 17, 3);
    }

    p_regs->sw03.dec_out_dis = 0;
    p_regs->sw01.dec_e = 1;

    return MPP_OK;
__FAILED:
    return ret;
}

static MPP_RET update_parameters(AvsdHalCtx_t *p_hal)
{
    AvsdSyntax_t *p_syn = &p_hal->syn;

    if (p_syn->pp.pictureStructure == FRAMEPICTURE || !p_hal->first_field) {
        p_hal->first_field = 1;
        if (p_syn->pp.picCodingType != BFRAME) {
            RK_S32 temp = p_hal->work1;

            p_hal->work1 =  p_hal->work0;
            p_hal->work0 =  p_hal->work_out;

            if (p_hal->work_out >= 0)
                p_hal->pic[p_hal->work_out].pic_type = p_syn->pp.picCodingType == IFRAME;
            p_hal->work_out = temp;
            p_hal->prev_pic_structure = p_syn->pp.pictureStructure;
        }
        p_hal->prev_pic_code_type = p_syn->pp.picCodingType;
    } else {
        p_hal->first_field = 0;
    }

    return MPP_OK;
}

static MPP_RET repeat_other_field(AvsdHalCtx_t *p_hal, HalTaskInfo *task)
{
    RK_U32 i = 0;
    RK_U8 *pdata = NULL;
    MppBuffer mbuffer = NULL;
    MPP_RET ret = MPP_ERR_UNKNOW;
    AvsdPlusRegs_t *p_regs = (AvsdPlusRegs_t *)p_hal->p_regs;
    RK_U32 stream_remain = 0;
    RK_U8 *ptr = NULL;

    //!< re-find start code and calculate offset
    p_hal->data_offset = p_regs->sw12.rlc_vlc_base >> 10;
    p_hal->data_offset += p_hal->syn.bitstream_offset;
    p_hal->data_offset -= MPP_MIN(p_hal->data_offset, 8);

    mpp_buf_slot_get_prop(p_hal->packet_slots, task->dec.input, SLOT_BUFFER, &mbuffer);
    pdata = (RK_U8 *)mpp_buffer_get_ptr(mbuffer) + p_hal->data_offset;
    stream_remain = p_hal->syn.bitstream_size - p_hal->data_offset;

    AVSD_HAL_DBG(AVSD_HAL_DBG_OFFSET, "frame_no=%d, poc %d, stream %d, offset %d, remain %d\n",
                 p_hal->frame_no, p_hal->syn.pp.pictureDistance, p_hal->syn.bitstream_size,
                 p_hal->data_offset, stream_remain);

    while (stream_remain > 3) {
        ptr = memchr(pdata, 1, stream_remain);

        if (!ptr)
            break;

        stream_remain = stream_remain - (ptr - pdata + 1);

        if (!ptr[-1] && !ptr[-2]) {
            p_hal->data_offset = p_hal->syn.bitstream_size - stream_remain - 3;
            break;
        } else {
            pdata = ptr + 1;
            ptr = NULL;
        }
    }
    AVSD_HAL_DBG(AVSD_HAL_DBG_OFFSET, "frame_no=%d, i=%d, offset=%d\n",
                 p_hal->frame_no, i, p_hal->data_offset);
    //!< re-generate register
    p_hal->frame_no++;
    FUN_CHECK(ret = set_regs_parameters(p_hal, &task->dec));
    hal_avsd_plus_start((void *)p_hal, task);
    hal_avsd_plus_wait((void *)p_hal, task);

    return ret = MPP_OK;
__FAILED:
    return ret;
}

/*!
 ***********************************************************************
 * \brief
 *    init
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_init(void *decoder, MppHalCfg *cfg)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    RK_U32 buf_size = 0;
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("AVS_plus In.");

    buf_size = (1920 * 1088) * 2;
    FUN_CHECK(ret = mpp_buffer_get(p_hal->buf_group, &p_hal->mv_buf, buf_size));

    p_hal->p_regs = mpp_calloc_size(RK_U32, sizeof(AvsdPlusRegs_t));
    MEM_CHECK(ret, p_hal->p_regs);

    mpp_slots_set_prop(p_hal->frame_slots, SLOTS_HOR_ALIGN, avsd_hor_align);
    mpp_slots_set_prop(p_hal->frame_slots, SLOTS_VER_ALIGN, avsd_ver_align);
    mpp_slots_set_prop(p_hal->frame_slots, SLOTS_LEN_ALIGN, avsd_len_align);

    p_hal->regs_num = 60;
    //!< initial for control
    p_hal->first_field = 1;
    p_hal->prev_pic_structure = 0; //!< field

    memset(p_hal->pic, 0, sizeof(p_hal->pic));
    p_hal->work_out = -1;
    p_hal->work0 = -1;
    p_hal->work1 = -1;
    p_hal->cfg = cfg;

    AVSD_HAL_TRACE("Out.");
    return ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
 ***********************************************************************
 * \brief
 *    deinit
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_deinit(void *decoder)
{
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("In.");
    if (p_hal->mv_buf) {
        mpp_buffer_put(p_hal->mv_buf);
        p_hal->mv_buf = NULL;
    }
    MPP_FREE(p_hal->p_regs);

    AVSD_HAL_TRACE("Out.");
    return MPP_OK;
}
/*!
 ***********************************************************************
 * \brief
 *    generate register
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_gen_regs(void *decoder, HalTaskInfo *task)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("In.");
    if ((task->dec.flags.parse_err || task->dec.flags.ref_err) &&
        !p_hal->dec_cfg->base.disable_error) {
        goto __RETURN;
    }
    p_hal->data_offset = p_hal->syn.bitstream_offset;

    FUN_CHECK(ret = set_regs_parameters(p_hal, &task->dec));
__RETURN:
    AVSD_HAL_TRACE("Out.");
    return ret = MPP_OK;
__FAILED:
    return ret;
}
/*!
 ***********************************************************************
 * \brief h
 *    start hard
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_start(void *decoder, HalTaskInfo *task)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("In.");
    INP_CHECK(ret, NULL == decoder);

    if ((task->dec.flags.parse_err || task->dec.flags.ref_err) &&
        !p_hal->dec_cfg->base.disable_error) {
        goto __RETURN;
    }

    do {
        MppDevRegWrCfg wr_cfg;
        MppDevRegRdCfg rd_cfg;

        wr_cfg.reg = p_hal->p_regs;
        wr_cfg.size = AVSD_REGISTERS * sizeof(RK_U32);
        wr_cfg.offset = 0;

        if (avsd_hal_debug & AVSD_HAL_DBG_ERROR) {
            static RK_U32 frame_no = 0;
            static FILE *fp = NULL;
            RK_U32 i;

            if (!fp)
                fp = fopen("regs.txt", "w+b");

            if (fp) {
                fprintf(fp, "frame %d\n", frame_no);

                for (i = 0; i < AVSD_REGISTERS; i++)
                    fprintf(fp, "reg[%03d]: %08x\n", i, p_hal->p_regs[i]);

                frame_no++;

                fflush(fp);
            }
        }

        ret = mpp_dev_ioctl(p_hal->dev, MPP_DEV_REG_WR, &wr_cfg);
        if (ret) {
            mpp_err_f("set register write failed %d\n", ret);
            break;
        }

        rd_cfg.reg = p_hal->p_regs;
        rd_cfg.size = AVSD_REGISTERS * sizeof(RK_U32);
        rd_cfg.offset = 0;

        ret = mpp_dev_ioctl(p_hal->dev, MPP_DEV_REG_RD, &rd_cfg);
        if (ret) {
            mpp_err_f("set register read failed %d\n", ret);
            break;
        }

        ret = mpp_dev_ioctl(p_hal->dev, MPP_DEV_CMD_SEND, NULL);
        if (ret) {
            mpp_err_f("send cmd failed %d\n", ret);
            break;
        }
    } while (0);

__RETURN:
    AVSD_HAL_TRACE("Out.");

    return ret;
}
/*!
 ***********************************************************************
 * \brief
 *    wait hard
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_wait(void *decoder, HalTaskInfo *task)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("In.");
    INP_CHECK(ret, NULL == decoder);

    if ((task->dec.flags.parse_err || task->dec.flags.ref_err) &&
        !p_hal->dec_cfg->base.disable_error) {
        goto __SKIP_HARD;
    }

    ret = mpp_dev_ioctl(p_hal->dev, MPP_DEV_CMD_POLL, NULL);
    if (ret)
        mpp_err_f("poll cmd failed %d\n", ret);

__SKIP_HARD:
    if (p_hal->dec_cb) {
        DecCbHalDone param;

        param.task = (void *)&task->dec;
        param.regs = (RK_U32 *)p_hal->p_regs;
        param.hard_err = (!((AvsdPlusRegs_t *)p_hal->p_regs)->sw01.dec_rdy_int) ||
                         ((AvsdPlusRegs_t *)p_hal->p_regs)->sw01.dec_error_int;

        mpp_callback(p_hal->dec_cb, &param);
    }

    AVSD_HAL_DBG(AVSD_HAL_DBG_WAIT, "first_field %d, irq 0x%08x, parse err %d, ref err %d\n",
                 p_hal->first_field, p_hal->p_regs[1], task->dec.flags.parse_err, task->dec.flags.ref_err);

    update_parameters(p_hal);
    if (!p_hal->first_field && p_hal->syn.pp.pictureStructure == FIELDPICTURE &&
        ((!task->dec.flags.parse_err && !task->dec.flags.ref_err) ||
         p_hal->dec_cfg->base.disable_error)) {
        if (((AvsdPlusRegs_t *)p_hal->p_regs)->sw01.dec_rdy_int &&
            !((AvsdPlusRegs_t *)p_hal->p_regs)->sw01.dec_error_int) {
            memset(&p_hal->p_regs[1], 0, sizeof(RK_U32));
            repeat_other_field(p_hal, task);
        } else {
            AVSD_HAL_DBG(AVSD_HAL_DBG_WAIT, "last field error, skip decoding");
        }
    }
    memset(&p_hal->p_regs[1], 0, sizeof(RK_U32));

__RETURN:
    AVSD_HAL_TRACE("Out.");

    return ret;
}
/*!
 ***********************************************************************
 * \brief
 *    reset
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_reset(void *decoder)
{
    MPP_RET ret = MPP_ERR_UNKNOW;
    AvsdHalCtx_t *p_hal = (AvsdHalCtx_t *)decoder;

    AVSD_HAL_TRACE("In.");

    p_hal->first_field = 1;
    p_hal->prev_pic_structure = 0; //!< field

    memset(p_hal->pic, 0, sizeof(p_hal->pic));
    p_hal->work_out = -1;
    p_hal->work0 = -1;
    p_hal->work1 = -1;

    AVSD_HAL_TRACE("Out.");

    return ret = MPP_OK;
}
/*!
 ***********************************************************************
 * \brief
 *    flush
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_flush(void *decoder)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    AVSD_HAL_TRACE("In.");

    (void)decoder;

    AVSD_HAL_TRACE("Out.");

    return ret = MPP_OK;
}
/*!
 ***********************************************************************
 * \brief
 *    control
 ***********************************************************************
 */
//extern "C"
MPP_RET hal_avsd_plus_control(void *decoder, MpiCmd cmd_type, void *param)
{
    MPP_RET ret = MPP_ERR_UNKNOW;

    AVSD_HAL_TRACE("In.");

    (void)decoder;
    (void)cmd_type;
    (void)param;

    AVSD_HAL_TRACE("Out.");

    return ret = MPP_OK;
}
