root/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2021 MediaTek Inc.
 * Author: George Sun <george.sun@mediatek.com>
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <media/videobuf2-dma-contig.h>
#include <media/v4l2-vp9.h>

#include "../mtk_vcodec_dec.h"
#include "../../common/mtk_vcodec_intr.h"
#include "../vdec_drv_base.h"
#include "../vdec_drv_if.h"
#include "../vdec_vpu_if.h"

/* reset_frame_context defined in VP9 spec */
#define VP9_RESET_FRAME_CONTEXT_NONE0 0
#define VP9_RESET_FRAME_CONTEXT_NONE1 1
#define VP9_RESET_FRAME_CONTEXT_SPEC 2
#define VP9_RESET_FRAME_CONTEXT_ALL 3

#define VP9_TILE_BUF_SIZE 4096
#define VP9_PROB_BUF_SIZE 2560
#define VP9_COUNTS_BUF_SIZE 16384

#define HDR_FLAG(x) (!!((hdr)->flags & V4L2_VP9_FRAME_FLAG_##x))
#define LF_FLAG(x) (!!((lf)->flags & V4L2_VP9_LOOP_FILTER_FLAG_##x))
#define SEG_FLAG(x) (!!((seg)->flags & V4L2_VP9_SEGMENTATION_FLAG_##x))
#define VP9_BAND_6(band) ((band) == 0 ? 3 : 6)

/*
 * struct vdec_vp9_slice_frame_ctx - vp9 prob tables footprint
 */
struct vdec_vp9_slice_frame_ctx {
        struct {
                u8 probs[6][3];
                u8 padding[2];
        } coef_probs[4][2][2][6];

        u8 y_mode_prob[4][16];
        u8 switch_interp_prob[4][16];
        u8 seg[32];  /* ignore */
        u8 comp_inter_prob[16];
        u8 comp_ref_prob[16];
        u8 single_ref_prob[5][2];
        u8 single_ref_prob_padding[6];

        u8 joint[3];
        u8 joint_padding[13];
        struct {
                u8 sign;
                u8 classes[10];
                u8 padding[5];
        } sign_classes[2];
        struct {
                u8 class0[1];
                u8 bits[10];
                u8 padding[5];
        } class0_bits[2];
        struct {
                u8 class0_fp[2][3];
                u8 fp[3];
                u8 class0_hp;
                u8 hp;
                u8 padding[5];
        } class0_fp_hp[2];

        u8 uv_mode_prob[10][16];
        u8 uv_mode_prob_padding[2][16];

        u8 partition_prob[16][4];

        u8 inter_mode_probs[7][4];
        u8 skip_probs[4];

        u8 tx_p8x8[2][4];
        u8 tx_p16x16[2][4];
        u8 tx_p32x32[2][4];
        u8 intra_inter_prob[8];
};

/*
 * struct vdec_vp9_slice_frame_counts - vp9 counts tables footprint
 */
struct vdec_vp9_slice_frame_counts {
        union {
                struct {
                        u32 band_0[3];
                        u32 padding0[1];
                        u32 band_1_5[5][6];
                        u32 padding1[2];
                } eob_branch[4][2][2];
                u32 eob_branch_space[256 * 4];
        };

        struct {
                u32 band_0[3][4];
                u32 band_1_5[5][6][4];
        } coef_probs[4][2][2];

        u32 intra_inter[4][2];
        u32 comp_inter[5][2];
        u32 comp_inter_padding[2];
        u32 comp_ref[5][2];
        u32 comp_ref_padding[2];
        u32 single_ref[5][2][2];
        u32 inter_mode[7][4];
        u32 y_mode[4][12];
        u32 uv_mode[10][10];
        u32 partition[16][4];
        u32 switchable_interp[4][4];

        u32 tx_p8x8[2][2];
        u32 tx_p16x16[2][4];
        u32 tx_p32x32[2][4];

        u32 skip[3][4];

        u32 joint[4];

        struct {
                u32 sign[2];
                u32 class0[2];
                u32 classes[12];
                u32 bits[10][2];
                u32 padding[4];
                u32 class0_fp[2][4];
                u32 fp[4];
                u32 class0_hp[2];
                u32 hp[2];
        } mvcomp[2];

        u32 reserved[126][4];
};

/**
 * struct vdec_vp9_slice_counts_map - vp9 counts tables to map
 *                                    v4l2_vp9_frame_symbol_counts
 * @skip:       skip counts.
 * @y_mode:     Y prediction mode counts.
 * @filter:     interpolation filter counts.
 * @sign:       motion vector sign counts.
 * @classes:    motion vector class counts.
 * @class0:     motion vector class0 bit counts.
 * @bits:       motion vector bits counts.
 * @class0_fp:  motion vector class0 fractional bit counts.
 * @fp: motion vector fractional bit counts.
 * @class0_hp:  motion vector class0 high precision fractional bit counts.
 * @hp: motion vector high precision fractional bit counts.
 */
struct vdec_vp9_slice_counts_map {
        u32 skip[3][2];
        u32 y_mode[4][10];
        u32 filter[4][3];
        u32 sign[2][2];
        u32 classes[2][11];
        u32 class0[2][2];
        u32 bits[2][10][2];
        u32 class0_fp[2][2][4];
        u32 fp[2][4];
        u32 class0_hp[2][2];
        u32 hp[2][2];
};

/*
 * struct vdec_vp9_slice_uncompressed_header - vp9 uncompressed header syntax
 *                                             used for decoding
 */
struct vdec_vp9_slice_uncompressed_header {
        u8 profile;
        u8 last_frame_type;
        u8 frame_type;

        u8 last_show_frame;
        u8 show_frame;
        u8 error_resilient_mode;

        u8 bit_depth;
        u8 padding0[1];
        u16 last_frame_width;
        u16 last_frame_height;
        u16 frame_width;
        u16 frame_height;

        u8 intra_only;
        u8 reset_frame_context;
        u8 ref_frame_sign_bias[4];
        u8 allow_high_precision_mv;
        u8 interpolation_filter;

        u8 refresh_frame_context;
        u8 frame_parallel_decoding_mode;
        u8 frame_context_idx;

        /* loop_filter_params */
        u8 loop_filter_level;
        u8 loop_filter_sharpness;
        u8 loop_filter_delta_enabled;
        s8 loop_filter_ref_deltas[4];
        s8 loop_filter_mode_deltas[2];

        /* quantization_params */
        u8 base_q_idx;
        s8 delta_q_y_dc;
        s8 delta_q_uv_dc;
        s8 delta_q_uv_ac;

        /* segmentation_params */
        u8 segmentation_enabled;
        u8 segmentation_update_map;
        u8 segmentation_tree_probs[7];
        u8 padding1[1];
        u8 segmentation_temporal_udpate;
        u8 segmentation_pred_prob[3];
        u8 segmentation_update_data;
        u8 segmentation_abs_or_delta_update;
        u8 feature_enabled[8];
        s16 feature_value[8][4];

        /* tile_info */
        u8 tile_cols_log2;
        u8 tile_rows_log2;
        u8 padding2[2];

        u16 uncompressed_header_size;
        u16 header_size_in_bytes;

        /* LAT OUT, CORE IN */
        u32 dequant[8][4];
};

/*
 * struct vdec_vp9_slice_compressed_header - vp9 compressed header syntax
 *                                           used for decoding.
 */
struct vdec_vp9_slice_compressed_header {
        u8 tx_mode;
        u8 ref_mode;
        u8 comp_fixed_ref;
        u8 comp_var_ref[2];
        u8 padding[3];
};

/*
 * struct vdec_vp9_slice_tiles - vp9 tile syntax
 */
struct vdec_vp9_slice_tiles {
        u32 size[4][64];
        u32 mi_rows[4];
        u32 mi_cols[64];
        u8 actual_rows;
        u8 padding[7];
};

/*
 * struct vdec_vp9_slice_reference - vp9 reference frame information
 */
struct vdec_vp9_slice_reference {
        u16 frame_width;
        u16 frame_height;
        u8 bit_depth;
        u8 subsampling_x;
        u8 subsampling_y;
        u8 padding;
};

/*
 * struct vdec_vp9_slice_frame - vp9 syntax used for decoding
 */
struct vdec_vp9_slice_frame {
        struct vdec_vp9_slice_uncompressed_header uh;
        struct vdec_vp9_slice_compressed_header ch;
        struct vdec_vp9_slice_tiles tiles;
        struct vdec_vp9_slice_reference ref[3];
};

/*
 * struct vdec_vp9_slice_init_vsi - VSI used to initialize instance
 */
struct vdec_vp9_slice_init_vsi {
        unsigned int architecture;
        unsigned int reserved;
        u64 core_vsi;
        /* default frame context's position in MicroP */
        u64 default_frame_ctx;
};

/*
 * struct vdec_vp9_slice_mem - memory address and size
 */
struct vdec_vp9_slice_mem {
        union {
                u64 buf;
                dma_addr_t dma_addr;
        };
        union {
                size_t size;
                dma_addr_t dma_addr_end;
                u64 padding;
        };
};

/*
 * struct vdec_vp9_slice_bs - input buffer for decoding
 */
struct vdec_vp9_slice_bs {
        struct vdec_vp9_slice_mem buf;
        struct vdec_vp9_slice_mem frame;
};

/*
 * struct vdec_vp9_slice_fb - frame buffer for decoding
 */
struct vdec_vp9_slice_fb {
        struct vdec_vp9_slice_mem y;
        struct vdec_vp9_slice_mem c;
};

/*
 * struct vdec_vp9_slice_state - decoding state
 */
struct vdec_vp9_slice_state {
        int err;
        unsigned int full;
        unsigned int timeout;
        unsigned int perf;

        unsigned int crc[12];
};

/**
 * struct vdec_vp9_slice_vsi - exchange decoding information
 *                             between Main CPU and MicroP
 *
 * @bs: input buffer
 * @fb: output buffer
 * @ref:        3 reference buffers
 * @mv: mv working buffer
 * @seg:        segmentation working buffer
 * @tile:       tile buffer
 * @prob:       prob table buffer, used to set/update prob table
 * @counts:     counts table buffer, used to update prob table
 * @ube:        general buffer
 * @trans:      trans buffer position in general buffer
 * @err_map:    error buffer
 * @row_info:   row info buffer
 * @frame:      decoding syntax
 * @state:      decoding state
 */
struct vdec_vp9_slice_vsi {
        /* used in LAT stage */
        struct vdec_vp9_slice_bs bs;
        /* used in Core stage */
        struct vdec_vp9_slice_fb fb;
        struct vdec_vp9_slice_fb ref[3];

        struct vdec_vp9_slice_mem mv[2];
        struct vdec_vp9_slice_mem seg[2];
        struct vdec_vp9_slice_mem tile;
        struct vdec_vp9_slice_mem prob;
        struct vdec_vp9_slice_mem counts;

        /* LAT stage's output, Core stage's input */
        struct vdec_vp9_slice_mem ube;
        struct vdec_vp9_slice_mem trans;
        struct vdec_vp9_slice_mem err_map;
        struct vdec_vp9_slice_mem row_info;

        /* decoding parameters */
        struct vdec_vp9_slice_frame frame;

        struct vdec_vp9_slice_state state;
};

/**
 * struct vdec_vp9_slice_pfc - per-frame context that contains a local vsi.
 *                             pass it from lat to core
 *
 * @vsi:        local vsi. copy to/from remote vsi before/after decoding
 * @ref_idx:    reference buffer index
 * @seq:        picture sequence
 * @state:      decoding state
 */
struct vdec_vp9_slice_pfc {
        struct vdec_vp9_slice_vsi vsi;

        u64 ref_idx[3];

        int seq;

        /* LAT/Core CRC */
        struct vdec_vp9_slice_state state[2];
};

/*
 * enum vdec_vp9_slice_resolution_level
 */
enum vdec_vp9_slice_resolution_level {
        VP9_RES_NONE,
        VP9_RES_FHD,
        VP9_RES_4K,
        VP9_RES_8K,
};

/*
 * struct vdec_vp9_slice_ref - picture's width & height should kept
 *                             for later decoding as reference picture
 */
struct vdec_vp9_slice_ref {
        unsigned int width;
        unsigned int height;
};

/**
 * struct vdec_vp9_slice_instance - represent one vp9 instance
 *
 * @ctx:                pointer to codec's context
 * @vpu:                VPU instance
 * @seq:                global picture sequence
 * @level:              level of current resolution
 * @width:              width of last picture
 * @height:             height of last picture
 * @frame_type: frame_type of last picture
 * @irq:                irq to Main CPU or MicroP
 * @show_frame: show_frame of last picture
 * @dpb:                picture information (width/height) for reference
 * @mv:         mv working buffer
 * @seg:                segmentation working buffer
 * @tile:               tile buffer
 * @prob:               prob table buffer, used to set/update prob table
 * @counts:             counts table buffer, used to update prob table
 * @frame_ctx:          4 frame context according to VP9 Spec
 * @frame_ctx_helper:   4 frame context according to newest kernel spec
 * @dirty:              state of each frame context
 * @init_vsi:           vsi used for initialized VP9 instance
 * @vsi:                vsi used for decoding/flush ...
 * @core_vsi:           vsi used for Core stage
 *
 * @sc_pfc:             per frame context single core
 * @counts_map: used map to counts_helper
 * @counts_helper:      counts table according to newest kernel spec
 */
struct vdec_vp9_slice_instance {
        struct mtk_vcodec_dec_ctx *ctx;
        struct vdec_vpu_inst vpu;

        int seq;

        enum vdec_vp9_slice_resolution_level level;

        /* for resolution change and get_pic_info */
        unsigned int width;
        unsigned int height;

        /* for last_frame_type */
        unsigned int frame_type;
        unsigned int irq;

        unsigned int show_frame;

        /* maintain vp9 reference frame state */
        struct vdec_vp9_slice_ref dpb[VB2_MAX_FRAME];

        /*
         * normal working buffers
         * mv[0]/seg[0]/tile/prob/counts is used for LAT
         * mv[1]/seg[1] is used for CORE
         */
        struct mtk_vcodec_mem mv[2];
        struct mtk_vcodec_mem seg[2];
        struct mtk_vcodec_mem tile;
        struct mtk_vcodec_mem prob;
        struct mtk_vcodec_mem counts;

        /* 4 prob tables */
        struct vdec_vp9_slice_frame_ctx frame_ctx[4];
        /*4 helper tables */
        struct v4l2_vp9_frame_context frame_ctx_helper;
        unsigned char dirty[4];

        /* MicroP vsi */
        union {
                struct vdec_vp9_slice_init_vsi *init_vsi;
                struct vdec_vp9_slice_vsi *vsi;
        };
        struct vdec_vp9_slice_vsi *core_vsi;

        struct vdec_vp9_slice_pfc sc_pfc;
        struct vdec_vp9_slice_counts_map counts_map;
        struct v4l2_vp9_frame_symbol_counts counts_helper;
};

/*
 * all VP9 instances could share this default frame context.
 */
static struct vdec_vp9_slice_frame_ctx *vdec_vp9_slice_default_frame_ctx;
static DEFINE_MUTEX(vdec_vp9_slice_frame_ctx_lock);

static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf);

static int vdec_vp9_slice_init_default_frame_ctx(struct vdec_vp9_slice_instance *instance)
{
        struct vdec_vp9_slice_frame_ctx *remote_frame_ctx;
        struct vdec_vp9_slice_frame_ctx *frame_ctx;
        struct mtk_vcodec_dec_ctx *ctx;
        struct vdec_vp9_slice_init_vsi *vsi;
        int ret = 0;

        ctx = instance->ctx;
        vsi = instance->vpu.vsi;
        if (!ctx || !vsi)
                return -EINVAL;

        remote_frame_ctx = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
                                                     (u32)vsi->default_frame_ctx);
        if (!remote_frame_ctx) {
                mtk_vdec_err(ctx, "failed to map default frame ctx\n");
                return -EINVAL;
        }

        mutex_lock(&vdec_vp9_slice_frame_ctx_lock);
        if (vdec_vp9_slice_default_frame_ctx)
                goto out;

        frame_ctx = kmemdup(remote_frame_ctx, sizeof(*frame_ctx), GFP_KERNEL);
        if (!frame_ctx) {
                ret = -ENOMEM;
                goto out;
        }

        vdec_vp9_slice_default_frame_ctx = frame_ctx;

out:
        mutex_unlock(&vdec_vp9_slice_frame_ctx_lock);

        return ret;
}

static int vdec_vp9_slice_alloc_working_buffer(struct vdec_vp9_slice_instance *instance,
                                               struct vdec_vp9_slice_vsi *vsi)
{
        struct mtk_vcodec_dec_ctx *ctx = instance->ctx;
        enum vdec_vp9_slice_resolution_level level;
        /* super blocks */
        unsigned int max_sb_w;
        unsigned int max_sb_h;
        unsigned int max_w;
        unsigned int max_h;
        unsigned int w;
        unsigned int h;
        size_t size;
        int ret;
        int i;

        w = vsi->frame.uh.frame_width;
        h = vsi->frame.uh.frame_height;

        if (w > VCODEC_DEC_4K_CODED_WIDTH ||
            h > VCODEC_DEC_4K_CODED_HEIGHT) {
                return -EINVAL;
        } else if (w > MTK_VDEC_MAX_W || h > MTK_VDEC_MAX_H) {
                /* 4K */
                level = VP9_RES_4K;
                max_w = VCODEC_DEC_4K_CODED_WIDTH;
                max_h = VCODEC_DEC_4K_CODED_HEIGHT;
        } else {
                /* FHD */
                level = VP9_RES_FHD;
                max_w = MTK_VDEC_MAX_W;
                max_h = MTK_VDEC_MAX_H;
        }

        if (level == instance->level)
                return 0;

        mtk_vdec_debug(ctx, "resolution level changed, from %u to %u, %ux%u",
                       instance->level, level, w, h);

        max_sb_w = DIV_ROUND_UP(max_w, 64);
        max_sb_h = DIV_ROUND_UP(max_h, 64);
        ret = -ENOMEM;

        /*
         * Lat-flush must wait core idle, otherwise core will
         * use released buffers
         */

        size = (max_sb_w * max_sb_h + 2) * 576;
        for (i = 0; i < 2; i++) {
                if (instance->mv[i].va)
                        mtk_vcodec_mem_free(ctx, &instance->mv[i]);
                instance->mv[i].size = size;
                if (mtk_vcodec_mem_alloc(ctx, &instance->mv[i]))
                        goto err;
        }

        size = (max_sb_w * max_sb_h * 32) + 256;
        for (i = 0; i < 2; i++) {
                if (instance->seg[i].va)
                        mtk_vcodec_mem_free(ctx, &instance->seg[i]);
                instance->seg[i].size = size;
                if (mtk_vcodec_mem_alloc(ctx, &instance->seg[i]))
                        goto err;
        }

        if (!instance->tile.va) {
                instance->tile.size = VP9_TILE_BUF_SIZE;
                if (mtk_vcodec_mem_alloc(ctx, &instance->tile))
                        goto err;
        }

        if (!instance->prob.va) {
                instance->prob.size = VP9_PROB_BUF_SIZE;
                if (mtk_vcodec_mem_alloc(ctx, &instance->prob))
                        goto err;
        }

        if (!instance->counts.va) {
                instance->counts.size = VP9_COUNTS_BUF_SIZE;
                if (mtk_vcodec_mem_alloc(ctx, &instance->counts))
                        goto err;
        }

        instance->level = level;
        return 0;

err:
        instance->level = VP9_RES_NONE;
        return ret;
}

static void vdec_vp9_slice_free_working_buffer(struct vdec_vp9_slice_instance *instance)
{
        struct mtk_vcodec_dec_ctx *ctx = instance->ctx;
        int i;

        for (i = 0; i < ARRAY_SIZE(instance->mv); i++) {
                if (instance->mv[i].va)
                        mtk_vcodec_mem_free(ctx, &instance->mv[i]);
        }
        for (i = 0; i < ARRAY_SIZE(instance->seg); i++) {
                if (instance->seg[i].va)
                        mtk_vcodec_mem_free(ctx, &instance->seg[i]);
        }
        if (instance->tile.va)
                mtk_vcodec_mem_free(ctx, &instance->tile);
        if (instance->prob.va)
                mtk_vcodec_mem_free(ctx, &instance->prob);
        if (instance->counts.va)
                mtk_vcodec_mem_free(ctx, &instance->counts);

        instance->level = VP9_RES_NONE;
}

static void vdec_vp9_slice_vsi_from_remote(struct vdec_vp9_slice_vsi *vsi,
                                           struct vdec_vp9_slice_vsi *remote_vsi,
                                           int skip)
{
        struct vdec_vp9_slice_frame *rf;
        struct vdec_vp9_slice_frame *f;

        /*
         * compressed header
         * dequant
         * buffer position
         * decode state
         */
        if (!skip) {
                rf = &remote_vsi->frame;
                f = &vsi->frame;
                memcpy(&f->ch, &rf->ch, sizeof(f->ch));
                memcpy(&f->uh.dequant, &rf->uh.dequant, sizeof(f->uh.dequant));
                memcpy(&vsi->trans, &remote_vsi->trans, sizeof(vsi->trans));
        }

        memcpy(&vsi->state, &remote_vsi->state, sizeof(vsi->state));
}

static void vdec_vp9_slice_vsi_to_remote(struct vdec_vp9_slice_vsi *vsi,
                                         struct vdec_vp9_slice_vsi *remote_vsi)
{
        memcpy(remote_vsi, vsi, sizeof(*vsi));
}

static int vdec_vp9_slice_tile_offset(int idx, int mi_num, int tile_log2)
{
        int sbs = (mi_num + 7) >> 3;
        int offset = ((idx * sbs) >> tile_log2) << 3;

        return min(offset, mi_num);
}

static
int vdec_vp9_slice_setup_single_from_src_to_dst(struct vdec_vp9_slice_instance *instance)
{
        struct vb2_v4l2_buffer *src;
        struct vb2_v4l2_buffer *dst;

        src = v4l2_m2m_next_src_buf(instance->ctx->m2m_ctx);
        if (!src)
                return -EINVAL;

        dst = v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx);
        if (!dst)
                return -EINVAL;

        v4l2_m2m_buf_copy_metadata(src, dst);

        return 0;
}

static int vdec_vp9_slice_setup_lat_from_src_buf(struct vdec_vp9_slice_instance *instance,
                                                 struct vdec_lat_buf *lat_buf)
{
        struct vb2_v4l2_buffer *src;
        struct vb2_v4l2_buffer *dst;

        src = v4l2_m2m_next_src_buf(instance->ctx->m2m_ctx);
        if (!src)
                return -EINVAL;

        lat_buf->src_buf_req = src->vb2_buf.req_obj.req;

        dst = &lat_buf->ts_info;
        v4l2_m2m_buf_copy_metadata(src, dst);
        return 0;
}

static void vdec_vp9_slice_setup_hdr(struct vdec_vp9_slice_instance *instance,
                                     struct vdec_vp9_slice_uncompressed_header *uh,
                                     struct v4l2_ctrl_vp9_frame *hdr)
{
        int i;

        uh->profile = hdr->profile;
        uh->last_frame_type = instance->frame_type;
        uh->frame_type = !HDR_FLAG(KEY_FRAME);
        uh->last_show_frame = instance->show_frame;
        uh->show_frame = HDR_FLAG(SHOW_FRAME);
        uh->error_resilient_mode = HDR_FLAG(ERROR_RESILIENT);
        uh->bit_depth = hdr->bit_depth;
        uh->last_frame_width = instance->width;
        uh->last_frame_height = instance->height;
        uh->frame_width = hdr->frame_width_minus_1 + 1;
        uh->frame_height = hdr->frame_height_minus_1 + 1;
        uh->intra_only = HDR_FLAG(INTRA_ONLY);
        /* map v4l2 enum to values defined in VP9 spec for firmware */
        switch (hdr->reset_frame_context) {
        case V4L2_VP9_RESET_FRAME_CTX_NONE:
                uh->reset_frame_context = VP9_RESET_FRAME_CONTEXT_NONE0;
                break;
        case V4L2_VP9_RESET_FRAME_CTX_SPEC:
                uh->reset_frame_context = VP9_RESET_FRAME_CONTEXT_SPEC;
                break;
        case V4L2_VP9_RESET_FRAME_CTX_ALL:
                uh->reset_frame_context = VP9_RESET_FRAME_CONTEXT_ALL;
                break;
        default:
                uh->reset_frame_context = VP9_RESET_FRAME_CONTEXT_NONE0;
                break;
        }
        /*
         * ref_frame_sign_bias specifies the intended direction
         * of the motion vector in time for each reference frame.
         * - INTRA_FRAME = 0,
         * - LAST_FRAME = 1,
         * - GOLDEN_FRAME = 2,
         * - ALTREF_FRAME = 3,
         * ref_frame_sign_bias[INTRA_FRAME] is always 0
         * and VDA only passes another 3 directions
         */
        uh->ref_frame_sign_bias[0] = 0;
        for (i = 0; i < 3; i++)
                uh->ref_frame_sign_bias[i + 1] =
                        !!(hdr->ref_frame_sign_bias & (1 << i));
        uh->allow_high_precision_mv = HDR_FLAG(ALLOW_HIGH_PREC_MV);
        uh->interpolation_filter = hdr->interpolation_filter;
        uh->refresh_frame_context = HDR_FLAG(REFRESH_FRAME_CTX);
        uh->frame_parallel_decoding_mode = HDR_FLAG(PARALLEL_DEC_MODE);
        uh->frame_context_idx = hdr->frame_context_idx;

        /* tile info */
        uh->tile_cols_log2 = hdr->tile_cols_log2;
        uh->tile_rows_log2 = hdr->tile_rows_log2;

        uh->uncompressed_header_size = hdr->uncompressed_header_size;
        uh->header_size_in_bytes = hdr->compressed_header_size;
}

static void vdec_vp9_slice_setup_frame_ctx(struct vdec_vp9_slice_instance *instance,
                                           struct vdec_vp9_slice_uncompressed_header *uh,
                                           struct v4l2_ctrl_vp9_frame *hdr)
{
        int error_resilient_mode;
        int reset_frame_context;
        int key_frame;
        int intra_only;
        int i;

        key_frame = HDR_FLAG(KEY_FRAME);
        intra_only = HDR_FLAG(INTRA_ONLY);
        error_resilient_mode = HDR_FLAG(ERROR_RESILIENT);
        reset_frame_context = uh->reset_frame_context;

        /*
         * according to "6.2 Uncompressed header syntax" in
         * "VP9 Bitstream & Decoding Process Specification",
         * reset @frame_context_idx when (FrameIsIntra || error_resilient_mode)
         */
        if (key_frame || intra_only || error_resilient_mode) {
                /*
                 * @reset_frame_context specifies
                 * whether the frame context should be
                 * reset to default values:
                 * 0 or 1 means do not reset any frame context
                 * 2 resets just the context specified in the frame header
                 * 3 resets all contexts
                 */
                if (key_frame || error_resilient_mode ||
                    reset_frame_context == 3) {
                        /* use default table */
                        for (i = 0; i < 4; i++)
                                instance->dirty[i] = 0;
                } else if (reset_frame_context == 2) {
                        instance->dirty[uh->frame_context_idx] = 0;
                }
                uh->frame_context_idx = 0;
        }
}

static void vdec_vp9_slice_setup_loop_filter(struct vdec_vp9_slice_uncompressed_header *uh,
                                             struct v4l2_vp9_loop_filter *lf)
{
        int i;

        uh->loop_filter_level = lf->level;
        uh->loop_filter_sharpness = lf->sharpness;
        uh->loop_filter_delta_enabled = LF_FLAG(DELTA_ENABLED);
        for (i = 0; i < 4; i++)
                uh->loop_filter_ref_deltas[i] = lf->ref_deltas[i];
        for (i = 0; i < 2; i++)
                uh->loop_filter_mode_deltas[i] = lf->mode_deltas[i];
}

static void vdec_vp9_slice_setup_quantization(struct vdec_vp9_slice_uncompressed_header *uh,
                                              struct v4l2_vp9_quantization *quant)
{
        uh->base_q_idx = quant->base_q_idx;
        uh->delta_q_y_dc = quant->delta_q_y_dc;
        uh->delta_q_uv_dc = quant->delta_q_uv_dc;
        uh->delta_q_uv_ac = quant->delta_q_uv_ac;
}

static void vdec_vp9_slice_setup_segmentation(struct vdec_vp9_slice_uncompressed_header *uh,
                                              struct v4l2_vp9_segmentation *seg)
{
        int i;
        int j;

        uh->segmentation_enabled = SEG_FLAG(ENABLED);
        uh->segmentation_update_map = SEG_FLAG(UPDATE_MAP);
        for (i = 0; i < 7; i++)
                uh->segmentation_tree_probs[i] = seg->tree_probs[i];
        uh->segmentation_temporal_udpate = SEG_FLAG(TEMPORAL_UPDATE);
        for (i = 0; i < 3; i++)
                uh->segmentation_pred_prob[i] = seg->pred_probs[i];
        uh->segmentation_update_data = SEG_FLAG(UPDATE_DATA);
        uh->segmentation_abs_or_delta_update = SEG_FLAG(ABS_OR_DELTA_UPDATE);
        for (i = 0; i < 8; i++) {
                uh->feature_enabled[i] = seg->feature_enabled[i];
                for (j = 0; j < 4; j++)
                        uh->feature_value[i][j] = seg->feature_data[i][j];
        }
}

static int vdec_vp9_slice_setup_tile(struct vdec_vp9_slice_vsi *vsi,
                                     struct v4l2_ctrl_vp9_frame *hdr)
{
        unsigned int rows_log2;
        unsigned int cols_log2;
        unsigned int rows;
        unsigned int cols;
        unsigned int mi_rows;
        unsigned int mi_cols;
        struct vdec_vp9_slice_tiles *tiles;
        int offset;
        int start;
        int end;
        int i;

        rows_log2 = hdr->tile_rows_log2;
        cols_log2 = hdr->tile_cols_log2;
        rows = 1 << rows_log2;
        cols = 1 << cols_log2;
        tiles = &vsi->frame.tiles;
        tiles->actual_rows = 0;

        if (rows > 4 || cols > 64)
                return -EINVAL;

        /* setup mi rows/cols information */
        mi_rows = (hdr->frame_height_minus_1 + 1 + 7) >> 3;
        mi_cols = (hdr->frame_width_minus_1 + 1 + 7) >> 3;

        for (i = 0; i < rows; i++) {
                start = vdec_vp9_slice_tile_offset(i, mi_rows, rows_log2);
                end = vdec_vp9_slice_tile_offset(i + 1, mi_rows, rows_log2);
                offset = end - start;
                tiles->mi_rows[i] = (offset + 7) >> 3;
                if (tiles->mi_rows[i])
                        tiles->actual_rows++;
        }

        for (i = 0; i < cols; i++) {
                start = vdec_vp9_slice_tile_offset(i, mi_cols, cols_log2);
                end = vdec_vp9_slice_tile_offset(i + 1, mi_cols, cols_log2);
                offset = end - start;
                tiles->mi_cols[i] = (offset + 7) >> 3;
        }

        return 0;
}

static void vdec_vp9_slice_setup_state(struct vdec_vp9_slice_vsi *vsi)
{
        memset(&vsi->state, 0, sizeof(vsi->state));
}

static void vdec_vp9_slice_setup_ref_idx(struct vdec_vp9_slice_pfc *pfc,
                                         struct v4l2_ctrl_vp9_frame *hdr)
{
        pfc->ref_idx[0] = hdr->last_frame_ts;
        pfc->ref_idx[1] = hdr->golden_frame_ts;
        pfc->ref_idx[2] = hdr->alt_frame_ts;
}

static int vdec_vp9_slice_setup_pfc(struct vdec_vp9_slice_instance *instance,
                                    struct vdec_vp9_slice_pfc *pfc)
{
        struct v4l2_ctrl_vp9_frame *hdr;
        struct vdec_vp9_slice_uncompressed_header *uh;
        struct v4l2_ctrl *hdr_ctrl;
        struct vdec_vp9_slice_vsi *vsi;
        int ret;

        /* frame header */
        hdr_ctrl = v4l2_ctrl_find(&instance->ctx->ctrl_hdl, V4L2_CID_STATELESS_VP9_FRAME);
        if (!hdr_ctrl || !hdr_ctrl->p_cur.p)
                return -EINVAL;

        hdr = hdr_ctrl->p_cur.p;
        vsi = &pfc->vsi;
        uh = &vsi->frame.uh;

        /* setup vsi information */
        vdec_vp9_slice_setup_hdr(instance, uh, hdr);
        vdec_vp9_slice_setup_frame_ctx(instance, uh, hdr);
        vdec_vp9_slice_setup_loop_filter(uh, &hdr->lf);
        vdec_vp9_slice_setup_quantization(uh, &hdr->quant);
        vdec_vp9_slice_setup_segmentation(uh, &hdr->seg);
        ret = vdec_vp9_slice_setup_tile(vsi, hdr);
        if (ret)
                return ret;
        vdec_vp9_slice_setup_state(vsi);

        /* core stage needs buffer index to get ref y/c ... */
        vdec_vp9_slice_setup_ref_idx(pfc, hdr);

        pfc->seq = instance->seq;
        instance->seq++;

        return 0;
}

static int vdec_vp9_slice_setup_lat_buffer(struct vdec_vp9_slice_instance *instance,
                                           struct vdec_vp9_slice_vsi *vsi,
                                           struct mtk_vcodec_mem *bs,
                                           struct vdec_lat_buf *lat_buf)
{
        int i;

        vsi->bs.buf.dma_addr = bs->dma_addr;
        vsi->bs.buf.size = bs->size;
        vsi->bs.frame.dma_addr = bs->dma_addr;
        vsi->bs.frame.size = bs->size;

        for (i = 0; i < 2; i++) {
                vsi->mv[i].dma_addr = instance->mv[i].dma_addr;
                vsi->mv[i].size = instance->mv[i].size;
        }
        for (i = 0; i < 2; i++) {
                vsi->seg[i].dma_addr = instance->seg[i].dma_addr;
                vsi->seg[i].size = instance->seg[i].size;
        }
        vsi->tile.dma_addr = instance->tile.dma_addr;
        vsi->tile.size = instance->tile.size;
        vsi->prob.dma_addr = instance->prob.dma_addr;
        vsi->prob.size = instance->prob.size;
        vsi->counts.dma_addr = instance->counts.dma_addr;
        vsi->counts.size = instance->counts.size;

        vsi->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
        vsi->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size;
        vsi->trans.dma_addr = lat_buf->ctx->msg_queue.wdma_wptr_addr;
        /* used to store trans end */
        vsi->trans.dma_addr_end = lat_buf->ctx->msg_queue.wdma_rptr_addr;
        vsi->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr;
        vsi->err_map.size = lat_buf->wdma_err_addr.size;

        vsi->row_info.buf = 0;
        vsi->row_info.size = 0;

        return 0;
}

static int vdec_vp9_slice_setup_prob_buffer(struct vdec_vp9_slice_instance *instance,
                                            struct vdec_vp9_slice_vsi *vsi)
{
        struct vdec_vp9_slice_frame_ctx *frame_ctx;
        struct vdec_vp9_slice_uncompressed_header *uh;

        uh = &vsi->frame.uh;

        mtk_vdec_debug(instance->ctx, "ctx dirty %u idx %d\n",
                       instance->dirty[uh->frame_context_idx],
                       uh->frame_context_idx);

        if (instance->dirty[uh->frame_context_idx])
                frame_ctx = &instance->frame_ctx[uh->frame_context_idx];
        else
                frame_ctx = vdec_vp9_slice_default_frame_ctx;
        memcpy(instance->prob.va, frame_ctx, sizeof(*frame_ctx));

        return 0;
}

static void vdec_vp9_slice_setup_seg_buffer(struct vdec_vp9_slice_instance *instance,
                                            struct vdec_vp9_slice_vsi *vsi,
                                            struct mtk_vcodec_mem *buf)
{
        struct vdec_vp9_slice_uncompressed_header *uh;

        /* reset segment buffer */
        uh = &vsi->frame.uh;
        if (uh->frame_type == 0 ||
            uh->intra_only ||
            uh->error_resilient_mode ||
            uh->frame_width != instance->width ||
            uh->frame_height != instance->height) {
                mtk_vdec_debug(instance->ctx, "reset seg\n");
                memset(buf->va, 0, buf->size);
        }
}

/*
 * parse tiles according to `6.4 Decode tiles syntax`
 * in "vp9-bitstream-specification"
 *
 * frame contains uncompress header, compressed header and several tiles.
 * this function parses tiles' position and size, stores them to tile buffer
 * for decoding.
 */
static int vdec_vp9_slice_setup_tile_buffer(struct vdec_vp9_slice_instance *instance,
                                            struct vdec_vp9_slice_vsi *vsi,
                                            struct mtk_vcodec_mem *bs)
{
        struct vdec_vp9_slice_uncompressed_header *uh;
        unsigned int rows_log2;
        unsigned int cols_log2;
        unsigned int rows;
        unsigned int cols;
        unsigned int mi_row;
        unsigned int mi_col;
        unsigned int offset;
        dma_addr_t pa;
        unsigned int size;
        struct vdec_vp9_slice_tiles *tiles;
        unsigned char *pos;
        unsigned char *end;
        unsigned char *va;
        unsigned int *tb;
        int i;
        int j;

        uh = &vsi->frame.uh;
        rows_log2 = uh->tile_rows_log2;
        cols_log2 = uh->tile_cols_log2;
        rows = 1 << rows_log2;
        cols = 1 << cols_log2;

        if (rows > 4 || cols > 64) {
                mtk_vdec_err(instance->ctx, "tile_rows %u tile_cols %u\n", rows, cols);
                return -EINVAL;
        }

        offset = uh->uncompressed_header_size +
                uh->header_size_in_bytes;
        if (bs->size <= offset) {
                mtk_vdec_err(instance->ctx, "bs size %zu tile offset %u\n", bs->size, offset);
                return -EINVAL;
        }

        tiles = &vsi->frame.tiles;
        /* setup tile buffer */

        va = (unsigned char *)bs->va;
        pos = va + offset;
        end = va + bs->size;
        /* truncated */
        pa = bs->dma_addr + offset;
        tb = instance->tile.va;
        for (i = 0; i < rows; i++) {
                for (j = 0; j < cols; j++) {
                        if (i == rows - 1 &&
                            j == cols - 1) {
                                size = (unsigned int)(end - pos);
                        } else {
                                if (end - pos < 4)
                                        return -EINVAL;

                                size = (pos[0] << 24) | (pos[1] << 16) |
                                        (pos[2] << 8) | pos[3];
                                pos += 4;
                                pa += 4;
                                offset += 4;
                                if (end - pos < size)
                                        return -EINVAL;
                        }
                        tiles->size[i][j] = size;
                        if (tiles->mi_rows[i]) {
                                *tb++ = (size << 3) + ((offset << 3) & 0x7f);
                                *tb++ = pa & ~0xf;
                                *tb++ = (pa << 3) & 0x7f;
                                mi_row = (tiles->mi_rows[i] - 1) & 0x1ff;
                                mi_col = (tiles->mi_cols[j] - 1) & 0x3f;
                                *tb++ = (mi_row << 6) + mi_col;
                        }
                        pos += size;
                        pa += size;
                        offset += size;
                }
        }

        return 0;
}

static int vdec_vp9_slice_setup_lat(struct vdec_vp9_slice_instance *instance,
                                    struct mtk_vcodec_mem *bs,
                                    struct vdec_lat_buf *lat_buf,
                                    struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi = &pfc->vsi;
        int ret;

        ret = vdec_vp9_slice_setup_lat_from_src_buf(instance, lat_buf);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_pfc(instance, pfc);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_alloc_working_buffer(instance, vsi);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_lat_buffer(instance, vsi, bs, lat_buf);
        if (ret)
                goto err;

        vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]);

        /* setup prob/tile buffers for LAT */

        ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs);
        if (ret)
                goto err;

        return 0;

err:
        return ret;
}

/* clang stack usage explodes if this is inlined */
static noinline_for_stack
void vdec_vp9_slice_map_counts_eob_coef(unsigned int i, unsigned int j, unsigned int k,
                                        struct vdec_vp9_slice_frame_counts *counts,
                                        struct v4l2_vp9_frame_symbol_counts *counts_helper)
{
        u32 l = 0, m;

        /*
         * helper eo -> mtk eo
         * helpre e1 -> mtk c3
         * helper c0 -> c0
         * helper c1 -> c1
         * helper c2 -> c2
         */
        for (m = 0; m < 3; m++) {
                counts_helper->coeff[i][j][k][l][m] =
                        (u32 (*)[3]) & counts->coef_probs[i][j][k].band_0[m];
                counts_helper->eob[i][j][k][l][m][0] =
                        &counts->eob_branch[i][j][k].band_0[m];
                counts_helper->eob[i][j][k][l][m][1] =
                        &counts->coef_probs[i][j][k].band_0[m][3];
        }

        for (l = 1; l < 6; l++) {
                for (m = 0; m < 6; m++) {
                        counts_helper->coeff[i][j][k][l][m] =
                                (u32 (*)[3]) & counts->coef_probs[i][j][k].band_1_5[l - 1][m];
                        counts_helper->eob[i][j][k][l][m][0] =
                                &counts->eob_branch[i][j][k].band_1_5[l - 1][m];
                        counts_helper->eob[i][j][k][l][m][1] =
                                &counts->coef_probs[i][j][k].band_1_5[l - 1][m][3];
                }
        }
}

static void vdec_vp9_slice_counts_map_helper(struct vdec_vp9_slice_counts_map *counts_map,
                                             struct vdec_vp9_slice_frame_counts *counts,
                                             struct v4l2_vp9_frame_symbol_counts *counts_helper)
{
        int i, j, k;

        counts_helper->partition = &counts->partition;
        counts_helper->intra_inter = &counts->intra_inter;
        counts_helper->tx32p = &counts->tx_p32x32;
        counts_helper->tx16p = &counts->tx_p16x16;
        counts_helper->tx8p = &counts->tx_p8x8;
        counts_helper->uv_mode = &counts->uv_mode;

        counts_helper->comp = &counts->comp_inter;
        counts_helper->comp_ref = &counts->comp_ref;
        counts_helper->single_ref = &counts->single_ref;
        counts_helper->mv_mode = &counts->inter_mode;
        counts_helper->mv_joint = &counts->joint;

        for (i = 0; i < ARRAY_SIZE(counts_map->skip); i++)
                memcpy(counts_map->skip[i], counts->skip[i],
                       sizeof(counts_map->skip[0]));
        counts_helper->skip = &counts_map->skip;

        for (i = 0; i < ARRAY_SIZE(counts_map->y_mode); i++)
                memcpy(counts_map->y_mode[i], counts->y_mode[i],
                       sizeof(counts_map->y_mode[0]));
        counts_helper->y_mode = &counts_map->y_mode;

        for (i = 0; i < ARRAY_SIZE(counts_map->filter); i++)
                memcpy(counts_map->filter[i], counts->switchable_interp[i],
                       sizeof(counts_map->filter[0]));
        counts_helper->filter = &counts_map->filter;

        for (i = 0; i < ARRAY_SIZE(counts_map->sign); i++)
                memcpy(counts_map->sign[i], counts->mvcomp[i].sign,
                       sizeof(counts_map->sign[0]));
        counts_helper->sign = &counts_map->sign;

        for (i = 0; i < ARRAY_SIZE(counts_map->classes); i++)
                memcpy(counts_map->classes[i], counts->mvcomp[i].classes,
                       sizeof(counts_map->classes[0]));
        counts_helper->classes = &counts_map->classes;

        for (i = 0; i < ARRAY_SIZE(counts_map->class0); i++)
                memcpy(counts_map->class0[i], counts->mvcomp[i].class0,
                       sizeof(counts_map->class0[0]));
        counts_helper->class0 = &counts_map->class0;

        for (i = 0; i < ARRAY_SIZE(counts_map->bits); i++)
                for (j = 0; j < ARRAY_SIZE(counts_map->bits[0]); j++)
                        memcpy(counts_map->bits[i][j], counts->mvcomp[i].bits[j],
                               sizeof(counts_map->bits[0][0]));
        counts_helper->bits = &counts_map->bits;

        for (i = 0; i < ARRAY_SIZE(counts_map->class0_fp); i++)
                for (j = 0; j < ARRAY_SIZE(counts_map->class0_fp[0]); j++)
                        memcpy(counts_map->class0_fp[i][j], counts->mvcomp[i].class0_fp[j],
                               sizeof(counts_map->class0_fp[0][0]));
        counts_helper->class0_fp = &counts_map->class0_fp;

        for (i = 0; i < ARRAY_SIZE(counts_map->fp); i++)
                memcpy(counts_map->fp[i], counts->mvcomp[i].fp,
                       sizeof(counts_map->fp[0]));
        counts_helper->fp = &counts_map->fp;

        for (i = 0; i < ARRAY_SIZE(counts_map->class0_hp); i++)
                memcpy(counts_map->class0_hp[i], counts->mvcomp[i].class0_hp,
                       sizeof(counts_map->class0_hp[0]));
        counts_helper->class0_hp = &counts_map->class0_hp;

        for (i = 0; i < ARRAY_SIZE(counts_map->hp); i++)
                memcpy(counts_map->hp[i], counts->mvcomp[i].hp, sizeof(counts_map->hp[0]));

        counts_helper->hp = &counts_map->hp;

        for (i = 0; i < 4; i++)
                for (j = 0; j < 2; j++)
                        for (k = 0; k < 2; k++)
                                vdec_vp9_slice_map_counts_eob_coef(i, j, k, counts, counts_helper);
}

static void vdec_vp9_slice_map_to_coef(unsigned int i, unsigned int j, unsigned int k,
                                       struct vdec_vp9_slice_frame_ctx *frame_ctx,
                                       struct v4l2_vp9_frame_context *frame_ctx_helper)
{
        u32 l, m;

        for (l = 0; l < ARRAY_SIZE(frame_ctx_helper->coef[0][0][0]); l++) {
                for (m = 0; m < VP9_BAND_6(l); m++) {
                        memcpy(frame_ctx_helper->coef[i][j][k][l][m],
                               frame_ctx->coef_probs[i][j][k][l].probs[m],
                               sizeof(frame_ctx_helper->coef[i][j][k][l][0]));
                }
        }
}

static void vdec_vp9_slice_map_from_coef(unsigned int i, unsigned int j, unsigned int k,
                                         struct vdec_vp9_slice_frame_ctx *frame_ctx,
                                         struct v4l2_vp9_frame_context *frame_ctx_helper)
{
        u32 l, m;

        for (l = 0; l < ARRAY_SIZE(frame_ctx_helper->coef[0][0][0]); l++) {
                for (m = 0; m < VP9_BAND_6(l); m++) {
                        memcpy(frame_ctx->coef_probs[i][j][k][l].probs[m],
                               frame_ctx_helper->coef[i][j][k][l][m],
                               sizeof(frame_ctx_helper->coef[i][j][k][l][0]));
                }
        }
}

static
void vdec_vp9_slice_framectx_map_helper(bool frame_is_intra,
                                        struct vdec_vp9_slice_frame_ctx *pre_frame_ctx,
                                        struct vdec_vp9_slice_frame_ctx *frame_ctx,
                                        struct v4l2_vp9_frame_context *frame_ctx_helper)
{
        struct v4l2_vp9_frame_mv_context *mv = &frame_ctx_helper->mv;
        u32 i, j, k;

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->coef); i++)
                for (j = 0; j < ARRAY_SIZE(frame_ctx_helper->coef[0]); j++)
                        for (k = 0; k < ARRAY_SIZE(frame_ctx_helper->coef[0][0]); k++)
                                vdec_vp9_slice_map_to_coef(i, j, k, pre_frame_ctx,
                                                           frame_ctx_helper);

        /*
         * use previous prob when frame is not intra or
         * we should use the prob updated by the compressed header parse
         */
        if (!frame_is_intra)
                frame_ctx = pre_frame_ctx;

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx8); i++)
                memcpy(frame_ctx_helper->tx8[i], frame_ctx->tx_p8x8[i],
                       sizeof(frame_ctx_helper->tx8[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx16); i++)
                memcpy(frame_ctx_helper->tx16[i], frame_ctx->tx_p16x16[i],
                       sizeof(frame_ctx_helper->tx16[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx32); i++)
                memcpy(frame_ctx_helper->tx32[i], frame_ctx->tx_p32x32[i],
                       sizeof(frame_ctx_helper->tx32[0]));

        memcpy(frame_ctx_helper->skip, frame_ctx->skip_probs, sizeof(frame_ctx_helper->skip));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->inter_mode); i++)
                memcpy(frame_ctx_helper->inter_mode[i], frame_ctx->inter_mode_probs[i],
                       sizeof(frame_ctx_helper->inter_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->interp_filter); i++)
                memcpy(frame_ctx_helper->interp_filter[i], frame_ctx->switch_interp_prob[i],
                       sizeof(frame_ctx_helper->interp_filter[0]));

        memcpy(frame_ctx_helper->is_inter, frame_ctx->intra_inter_prob,
               sizeof(frame_ctx_helper->is_inter));

        memcpy(frame_ctx_helper->comp_mode, frame_ctx->comp_inter_prob,
               sizeof(frame_ctx_helper->comp_mode));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->single_ref); i++)
                memcpy(frame_ctx_helper->single_ref[i], frame_ctx->single_ref_prob[i],
                       sizeof(frame_ctx_helper->single_ref[0]));

        memcpy(frame_ctx_helper->comp_ref, frame_ctx->comp_ref_prob,
               sizeof(frame_ctx_helper->comp_ref));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->y_mode); i++)
                memcpy(frame_ctx_helper->y_mode[i], frame_ctx->y_mode_prob[i],
                       sizeof(frame_ctx_helper->y_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->uv_mode); i++)
                memcpy(frame_ctx_helper->uv_mode[i], frame_ctx->uv_mode_prob[i],
                       sizeof(frame_ctx_helper->uv_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->partition); i++)
                memcpy(frame_ctx_helper->partition[i], frame_ctx->partition_prob[i],
                       sizeof(frame_ctx_helper->partition[0]));

        memcpy(mv->joint, frame_ctx->joint, sizeof(mv->joint));

        for (i = 0; i < ARRAY_SIZE(mv->sign); i++)
                mv->sign[i] = frame_ctx->sign_classes[i].sign;

        for (i = 0; i < ARRAY_SIZE(mv->classes); i++)
                memcpy(mv->classes[i], frame_ctx->sign_classes[i].classes,
                       sizeof(mv->classes[i]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_bit); i++)
                mv->class0_bit[i] = frame_ctx->class0_bits[i].class0[0];

        for (i = 0; i < ARRAY_SIZE(mv->bits); i++)
                memcpy(mv->bits[i], frame_ctx->class0_bits[i].bits, sizeof(mv->bits[0]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_fr); i++)
                for (j = 0; j < ARRAY_SIZE(mv->class0_fr[0]); j++)
                        memcpy(mv->class0_fr[i][j], frame_ctx->class0_fp_hp[i].class0_fp[j],
                               sizeof(mv->class0_fr[0][0]));

        for (i = 0; i < ARRAY_SIZE(mv->fr); i++)
                memcpy(mv->fr[i], frame_ctx->class0_fp_hp[i].fp, sizeof(mv->fr[0]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_hp); i++)
                mv->class0_hp[i] = frame_ctx->class0_fp_hp[i].class0_hp;

        for (i = 0; i < ARRAY_SIZE(mv->hp); i++)
                mv->hp[i] = frame_ctx->class0_fp_hp[i].hp;
}

static void vdec_vp9_slice_helper_map_framectx(struct v4l2_vp9_frame_context *frame_ctx_helper,
                                               struct vdec_vp9_slice_frame_ctx *frame_ctx)
{
        struct v4l2_vp9_frame_mv_context *mv = &frame_ctx_helper->mv;
        u32 i, j, k;

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx8); i++)
                memcpy(frame_ctx->tx_p8x8[i], frame_ctx_helper->tx8[i],
                       sizeof(frame_ctx_helper->tx8[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx16); i++)
                memcpy(frame_ctx->tx_p16x16[i], frame_ctx_helper->tx16[i],
                       sizeof(frame_ctx_helper->tx16[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->tx32); i++)
                memcpy(frame_ctx->tx_p32x32[i], frame_ctx_helper->tx32[i],
                       sizeof(frame_ctx_helper->tx32[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->coef); i++)
                for (j = 0; j < ARRAY_SIZE(frame_ctx_helper->coef[0]); j++)
                        for (k = 0; k < ARRAY_SIZE(frame_ctx_helper->coef[0][0]); k++)
                                vdec_vp9_slice_map_from_coef(i, j, k, frame_ctx,
                                                             frame_ctx_helper);

        memcpy(frame_ctx->skip_probs, frame_ctx_helper->skip, sizeof(frame_ctx_helper->skip));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->inter_mode); i++)
                memcpy(frame_ctx->inter_mode_probs[i], frame_ctx_helper->inter_mode[i],
                       sizeof(frame_ctx_helper->inter_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->interp_filter); i++)
                memcpy(frame_ctx->switch_interp_prob[i], frame_ctx_helper->interp_filter[i],
                       sizeof(frame_ctx_helper->interp_filter[0]));

        memcpy(frame_ctx->intra_inter_prob, frame_ctx_helper->is_inter,
               sizeof(frame_ctx_helper->is_inter));

        memcpy(frame_ctx->comp_inter_prob, frame_ctx_helper->comp_mode,
               sizeof(frame_ctx_helper->comp_mode));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->single_ref); i++)
                memcpy(frame_ctx->single_ref_prob[i], frame_ctx_helper->single_ref[i],
                       sizeof(frame_ctx_helper->single_ref[0]));

        memcpy(frame_ctx->comp_ref_prob, frame_ctx_helper->comp_ref,
               sizeof(frame_ctx_helper->comp_ref));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->y_mode); i++)
                memcpy(frame_ctx->y_mode_prob[i], frame_ctx_helper->y_mode[i],
                       sizeof(frame_ctx_helper->y_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->uv_mode); i++)
                memcpy(frame_ctx->uv_mode_prob[i], frame_ctx_helper->uv_mode[i],
                       sizeof(frame_ctx_helper->uv_mode[0]));

        for (i = 0; i < ARRAY_SIZE(frame_ctx_helper->partition); i++)
                memcpy(frame_ctx->partition_prob[i], frame_ctx_helper->partition[i],
                       sizeof(frame_ctx_helper->partition[0]));

        memcpy(frame_ctx->joint, mv->joint, sizeof(mv->joint));

        for (i = 0; i < ARRAY_SIZE(mv->sign); i++)
                frame_ctx->sign_classes[i].sign = mv->sign[i];

        for (i = 0; i < ARRAY_SIZE(mv->classes); i++)
                memcpy(frame_ctx->sign_classes[i].classes, mv->classes[i],
                       sizeof(mv->classes[i]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_bit); i++)
                frame_ctx->class0_bits[i].class0[0] = mv->class0_bit[i];

        for (i = 0; i < ARRAY_SIZE(mv->bits); i++)
                memcpy(frame_ctx->class0_bits[i].bits, mv->bits[i], sizeof(mv->bits[0]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_fr); i++)
                for (j = 0; j < ARRAY_SIZE(mv->class0_fr[0]); j++)
                        memcpy(frame_ctx->class0_fp_hp[i].class0_fp[j], mv->class0_fr[i][j],
                               sizeof(mv->class0_fr[0][0]));

        for (i = 0; i < ARRAY_SIZE(mv->fr); i++)
                memcpy(frame_ctx->class0_fp_hp[i].fp, mv->fr[i], sizeof(mv->fr[0]));

        for (i = 0; i < ARRAY_SIZE(mv->class0_hp); i++)
                frame_ctx->class0_fp_hp[i].class0_hp = mv->class0_hp[i];

        for (i = 0; i < ARRAY_SIZE(mv->hp); i++)
                frame_ctx->class0_fp_hp[i].hp = mv->hp[i];
}

static int vdec_vp9_slice_update_prob(struct vdec_vp9_slice_instance *instance,
                                      struct vdec_vp9_slice_vsi *vsi)
{
        struct vdec_vp9_slice_frame_ctx *pre_frame_ctx;
        struct v4l2_vp9_frame_context *pre_frame_ctx_helper;
        struct vdec_vp9_slice_frame_ctx *frame_ctx;
        struct vdec_vp9_slice_frame_counts *counts;
        struct v4l2_vp9_frame_symbol_counts *counts_helper;
        struct vdec_vp9_slice_uncompressed_header *uh;
        bool frame_is_intra;
        bool use_128;

        uh = &vsi->frame.uh;
        pre_frame_ctx = &instance->frame_ctx[uh->frame_context_idx];
        pre_frame_ctx_helper = &instance->frame_ctx_helper;
        frame_ctx = (struct vdec_vp9_slice_frame_ctx *)instance->prob.va;
        counts = (struct vdec_vp9_slice_frame_counts *)instance->counts.va;
        counts_helper = &instance->counts_helper;

        if (!uh->refresh_frame_context)
                return 0;

        if (!uh->frame_parallel_decoding_mode) {
                vdec_vp9_slice_counts_map_helper(&instance->counts_map, counts, counts_helper);

                frame_is_intra = !vsi->frame.uh.frame_type || vsi->frame.uh.intra_only;
                /* check default prob */
                if (!instance->dirty[uh->frame_context_idx])
                        vdec_vp9_slice_framectx_map_helper(frame_is_intra,
                                                           vdec_vp9_slice_default_frame_ctx,
                                                           frame_ctx,
                                                           pre_frame_ctx_helper);
                else
                        vdec_vp9_slice_framectx_map_helper(frame_is_intra,
                                                           pre_frame_ctx,
                                                           frame_ctx,
                                                           pre_frame_ctx_helper);

                use_128 = !frame_is_intra && !vsi->frame.uh.last_frame_type;
                v4l2_vp9_adapt_coef_probs(pre_frame_ctx_helper,
                                          counts_helper,
                                          use_128,
                                          frame_is_intra);
                if (!frame_is_intra)
                        v4l2_vp9_adapt_noncoef_probs(pre_frame_ctx_helper,
                                                     counts_helper,
                                                     V4L2_VP9_REFERENCE_MODE_SINGLE_REFERENCE,
                                                     vsi->frame.uh.interpolation_filter,
                                                     vsi->frame.ch.tx_mode,
                                                     vsi->frame.uh.allow_high_precision_mv ?
                                                     V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV : 0);
                vdec_vp9_slice_helper_map_framectx(pre_frame_ctx_helper, pre_frame_ctx);
        } else {
                memcpy(pre_frame_ctx, frame_ctx, sizeof(*frame_ctx));
        }

        instance->dirty[uh->frame_context_idx] = 1;

        return 0;
}

static int vdec_vp9_slice_update_single(struct vdec_vp9_slice_instance *instance,
                                        struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi;

        vsi = &pfc->vsi;
        memcpy(&pfc->state[0], &vsi->state, sizeof(vsi->state));

        mtk_vdec_debug(instance->ctx, "Frame %u Y_CRC %08x %08x %08x %08x\n",
                       pfc->seq, vsi->state.crc[0], vsi->state.crc[1],
                       vsi->state.crc[2], vsi->state.crc[3]);
        mtk_vdec_debug(instance->ctx, "Frame %u C_CRC %08x %08x %08x %08x\n",
                       pfc->seq, vsi->state.crc[4], vsi->state.crc[5],
                       vsi->state.crc[6], vsi->state.crc[7]);

        vdec_vp9_slice_update_prob(instance, vsi);

        instance->width = vsi->frame.uh.frame_width;
        instance->height = vsi->frame.uh.frame_height;
        instance->frame_type = vsi->frame.uh.frame_type;
        instance->show_frame = vsi->frame.uh.show_frame;

        return 0;
}

static int vdec_vp9_slice_update_lat(struct vdec_vp9_slice_instance *instance,
                                     struct vdec_lat_buf *lat_buf,
                                     struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi;

        vsi = &pfc->vsi;
        memcpy(&pfc->state[0], &vsi->state, sizeof(vsi->state));

        mtk_vdec_debug(instance->ctx, "Frame %u LAT CRC 0x%08x %lx %lx\n",
                       pfc->seq, vsi->state.crc[0],
                       (unsigned long)vsi->trans.dma_addr,
                       (unsigned long)vsi->trans.dma_addr_end);

        /* buffer full, need to re-decode */
        if (vsi->state.full) {
                /* buffer not enough */
                if (vsi->trans.dma_addr_end - vsi->trans.dma_addr ==
                        vsi->ube.size)
                        return -ENOMEM;
                return -EAGAIN;
        }

        vdec_vp9_slice_update_prob(instance, vsi);

        instance->width = vsi->frame.uh.frame_width;
        instance->height = vsi->frame.uh.frame_height;
        instance->frame_type = vsi->frame.uh.frame_type;
        instance->show_frame = vsi->frame.uh.show_frame;

        return 0;
}

static int vdec_vp9_slice_setup_core_to_dst_buf(struct vdec_vp9_slice_instance *instance,
                                                struct vdec_lat_buf *lat_buf)
{
        struct vb2_v4l2_buffer *dst;

        dst = v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx);
        if (!dst)
                return -EINVAL;

        v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst);
        return 0;
}

static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *instance,
                                            struct vdec_vp9_slice_pfc *pfc,
                                            struct vdec_vp9_slice_vsi *vsi,
                                            struct vdec_fb *fb,
                                            struct vdec_lat_buf *lat_buf)
{
        struct vb2_buffer *vb;
        struct vb2_queue *vq;
        struct vdec_vp9_slice_reference *ref;
        int plane;
        int size;
        int w;
        int h;
        int i;

        plane = instance->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes;
        w = vsi->frame.uh.frame_width;
        h = vsi->frame.uh.frame_height;
        size = ALIGN(w, 64) * ALIGN(h, 64);

        /* frame buffer */
        vsi->fb.y.dma_addr = fb->base_y.dma_addr;
        if (plane == 1)
                vsi->fb.c.dma_addr = fb->base_y.dma_addr + size;
        else
                vsi->fb.c.dma_addr = fb->base_c.dma_addr;

        /* reference buffers */
        vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx,
                             V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);

        /* get current output buffer */
        vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf;
        if (!vb)
                return -EINVAL;

        /* update internal buffer's width/height */
        instance->dpb[vb->index].width = w;
        instance->dpb[vb->index].height = h;

        /*
         * get buffer's width/height from instance
         * get buffer address from vb2buf
         */
        for (i = 0; i < 3; i++) {
                ref = &vsi->frame.ref[i];
                vb = vb2_find_buffer(vq, pfc->ref_idx[i]);
                if (!vb) {
                        ref->frame_width = w;
                        ref->frame_height = h;
                        memset(&vsi->ref[i], 0, sizeof(vsi->ref[i]));
                } else {
                        int idx = vb->index;

                        ref->frame_width = instance->dpb[idx].width;
                        ref->frame_height = instance->dpb[idx].height;
                        vsi->ref[i].y.dma_addr =
                                vb2_dma_contig_plane_dma_addr(vb, 0);
                        if (plane == 1)
                                vsi->ref[i].c.dma_addr =
                                        vsi->ref[i].y.dma_addr + size;
                        else
                                vsi->ref[i].c.dma_addr =
                                        vb2_dma_contig_plane_dma_addr(vb, 1);
                }
        }

        return 0;
}

static void vdec_vp9_slice_setup_single_buffer(struct vdec_vp9_slice_instance *instance,
                                               struct vdec_vp9_slice_pfc *pfc,
                                               struct vdec_vp9_slice_vsi *vsi,
                                               struct mtk_vcodec_mem *bs,
                                               struct vdec_fb *fb)
{
        int i;

        vsi->bs.buf.dma_addr = bs->dma_addr;
        vsi->bs.buf.size = bs->size;
        vsi->bs.frame.dma_addr = bs->dma_addr;
        vsi->bs.frame.size = bs->size;

        for (i = 0; i < 2; i++) {
                vsi->mv[i].dma_addr = instance->mv[i].dma_addr;
                vsi->mv[i].size = instance->mv[i].size;
        }
        for (i = 0; i < 2; i++) {
                vsi->seg[i].dma_addr = instance->seg[i].dma_addr;
                vsi->seg[i].size = instance->seg[i].size;
        }
        vsi->tile.dma_addr = instance->tile.dma_addr;
        vsi->tile.size = instance->tile.size;
        vsi->prob.dma_addr = instance->prob.dma_addr;
        vsi->prob.size = instance->prob.size;
        vsi->counts.dma_addr = instance->counts.dma_addr;
        vsi->counts.size = instance->counts.size;

        vsi->row_info.buf = 0;
        vsi->row_info.size = 0;

        vdec_vp9_slice_setup_core_buffer(instance, pfc, vsi, fb, NULL);
}

static int vdec_vp9_slice_setup_core(struct vdec_vp9_slice_instance *instance,
                                     struct vdec_fb *fb,
                                     struct vdec_lat_buf *lat_buf,
                                     struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi = &pfc->vsi;
        int ret;

        vdec_vp9_slice_setup_state(vsi);

        ret = vdec_vp9_slice_setup_core_to_dst_buf(instance, lat_buf);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_core_buffer(instance, pfc, vsi, fb, lat_buf);
        if (ret)
                goto err;

        vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[1]);

        return 0;

err:
        return ret;
}

static int vdec_vp9_slice_setup_single(struct vdec_vp9_slice_instance *instance,
                                       struct mtk_vcodec_mem *bs,
                                       struct vdec_fb *fb,
                                       struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi = &pfc->vsi;
        int ret;

        ret = vdec_vp9_slice_setup_single_from_src_to_dst(instance);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_pfc(instance, pfc);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_alloc_working_buffer(instance, vsi);
        if (ret)
                goto err;

        vdec_vp9_slice_setup_single_buffer(instance, pfc, vsi, bs, fb);
        vdec_vp9_slice_setup_seg_buffer(instance, vsi, &instance->seg[0]);

        ret = vdec_vp9_slice_setup_prob_buffer(instance, vsi);
        if (ret)
                goto err;

        ret = vdec_vp9_slice_setup_tile_buffer(instance, vsi, bs);
        if (ret)
                goto err;

        return 0;

err:
        return ret;
}

static int vdec_vp9_slice_update_core(struct vdec_vp9_slice_instance *instance,
                                      struct vdec_lat_buf *lat_buf,
                                      struct vdec_vp9_slice_pfc *pfc)
{
        struct vdec_vp9_slice_vsi *vsi;

        vsi = &pfc->vsi;
        memcpy(&pfc->state[1], &vsi->state, sizeof(vsi->state));

        mtk_vdec_debug(instance->ctx, "Frame %u Y_CRC %08x %08x %08x %08x\n",
                       pfc->seq, vsi->state.crc[0], vsi->state.crc[1],
                       vsi->state.crc[2], vsi->state.crc[3]);
        mtk_vdec_debug(instance->ctx, "Frame %u C_CRC %08x %08x %08x %08x\n",
                       pfc->seq, vsi->state.crc[4], vsi->state.crc[5],
                       vsi->state.crc[6], vsi->state.crc[7]);

        return 0;
}

static int vdec_vp9_slice_init(struct mtk_vcodec_dec_ctx *ctx)
{
        struct vdec_vp9_slice_instance *instance;
        struct vdec_vp9_slice_init_vsi *vsi;
        int ret;

        instance = kzalloc_obj(*instance);
        if (!instance)
                return -ENOMEM;

        instance->ctx = ctx;
        instance->vpu.id = SCP_IPI_VDEC_LAT;
        instance->vpu.core_id = SCP_IPI_VDEC_CORE;
        instance->vpu.ctx = ctx;
        instance->vpu.codec_type = ctx->current_codec;

        ret = vpu_dec_init(&instance->vpu);
        if (ret) {
                mtk_vdec_err(ctx, "failed to init vpu dec, ret %d\n", ret);
                goto error_vpu_init;
        }

        /* init vsi and global flags */

        vsi = instance->vpu.vsi;
        if (!vsi) {
                mtk_vdec_err(ctx, "failed to get VP9 vsi\n");
                ret = -EINVAL;
                goto error_vsi;
        }
        instance->init_vsi = vsi;
        instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler,
                                                       (u32)vsi->core_vsi);
        if (!instance->core_vsi) {
                mtk_vdec_err(ctx, "failed to get VP9 core vsi\n");
                ret = -EINVAL;
                goto error_vsi;
        }

        instance->irq = 1;

        ret = vdec_vp9_slice_init_default_frame_ctx(instance);
        if (ret)
                goto error_default_frame_ctx;

        ctx->drv_handle = instance;

        return 0;

error_default_frame_ctx:
error_vsi:
        vpu_dec_deinit(&instance->vpu);
error_vpu_init:
        kfree(instance);
        return ret;
}

static void vdec_vp9_slice_deinit(void *h_vdec)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;

        if (!instance)
                return;

        vpu_dec_deinit(&instance->vpu);
        vdec_vp9_slice_free_working_buffer(instance);
        vdec_msg_queue_deinit(&instance->ctx->msg_queue, instance->ctx);
        kfree(instance);
}

static int vdec_vp9_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs,
                                struct vdec_fb *fb, bool *res_chg)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;

        mtk_vdec_debug(instance->ctx, "flush ...\n");
        if (instance->ctx->dev->vdec_pdata->hw_arch != MTK_VDEC_PURE_SINGLE_CORE)
                vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue);
        return vpu_dec_reset(&instance->vpu);
}

static void vdec_vp9_slice_get_pic_info(struct vdec_vp9_slice_instance *instance)
{
        struct mtk_vcodec_dec_ctx *ctx = instance->ctx;
        unsigned int data[3];

        mtk_vdec_debug(instance->ctx, "w %u h %u\n", ctx->picinfo.pic_w, ctx->picinfo.pic_h);

        data[0] = ctx->picinfo.pic_w;
        data[1] = ctx->picinfo.pic_h;
        data[2] = ctx->capture_fourcc;
        vpu_dec_get_param(&instance->vpu, data, 3, GET_PARAM_PIC_INFO);

        ctx->picinfo.buf_w = ALIGN(ctx->picinfo.pic_w, 64);
        ctx->picinfo.buf_h = ALIGN(ctx->picinfo.pic_h, 64);
        ctx->picinfo.fb_sz[0] = instance->vpu.fb_sz[0];
        ctx->picinfo.fb_sz[1] = instance->vpu.fb_sz[1];
}

static void vdec_vp9_slice_get_dpb_size(struct vdec_vp9_slice_instance *instance,
                                        unsigned int *dpb_sz)
{
        /* refer VP9 specification */
        *dpb_sz = 9;
}

static int vdec_vp9_slice_get_param(void *h_vdec, enum vdec_get_param_type type, void *out)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;

        switch (type) {
        case GET_PARAM_PIC_INFO:
                vdec_vp9_slice_get_pic_info(instance);
                break;
        case GET_PARAM_DPB_SIZE:
                vdec_vp9_slice_get_dpb_size(instance, out);
                break;
        case GET_PARAM_CROP_INFO:
                mtk_vdec_debug(instance->ctx, "No need to get vp9 crop information.");
                break;
        default:
                mtk_vdec_err(instance->ctx, "invalid get parameter type=%d\n", type);
                return -EINVAL;
        }

        return 0;
}

static int vdec_vp9_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
                                        struct vdec_fb *fb, bool *res_chg)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;
        struct vdec_vp9_slice_pfc *pfc = &instance->sc_pfc;
        struct vdec_vp9_slice_vsi *vsi;
        struct mtk_vcodec_dec_ctx *ctx;
        int ret;

        if (!instance || !instance->ctx)
                return -EINVAL;
        ctx = instance->ctx;

        /* bs NULL means flush decoder */
        if (!bs)
                return vdec_vp9_slice_flush(h_vdec, bs, fb, res_chg);

        fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
        if (!fb)
                return -EBUSY;

        vsi = &pfc->vsi;

        ret = vdec_vp9_slice_setup_single(instance, bs, fb, pfc);
        if (ret) {
                mtk_vdec_err(ctx, "Failed to setup VP9 single ret %d\n", ret);
                return ret;
        }
        vdec_vp9_slice_vsi_to_remote(vsi, instance->vsi);

        ret = vpu_dec_start(&instance->vpu, NULL, 0);
        if (ret) {
                mtk_vdec_err(ctx, "Failed to dec VP9 ret %d\n", ret);
                return ret;
        }

        ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
                                           WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
        /* update remote vsi if decode timeout */
        if (ret) {
                mtk_vdec_err(ctx, "VP9 decode timeout %d\n", ret);
                WRITE_ONCE(instance->vsi->state.timeout, 1);
        }

        vpu_dec_end(&instance->vpu);

        vdec_vp9_slice_vsi_from_remote(vsi, instance->vsi, 0);
        ret = vdec_vp9_slice_update_single(instance, pfc);
        if (ret) {
                mtk_vdec_err(ctx, "VP9 decode error: %d\n", ret);
                return ret;
        }

        instance->ctx->decoded_frame_cnt++;
        return 0;
}

static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
                                     struct vdec_fb *fb, bool *res_chg)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;
        struct vdec_lat_buf *lat_buf;
        struct vdec_vp9_slice_pfc *pfc;
        struct vdec_vp9_slice_vsi *vsi;
        struct mtk_vcodec_dec_ctx *ctx;
        int ret;

        if (!instance || !instance->ctx)
                return -EINVAL;
        ctx = instance->ctx;

        /* init msgQ for the first time */
        if (vdec_msg_queue_init(&ctx->msg_queue, ctx,
                                vdec_vp9_slice_core_decode,
                                sizeof(*pfc)))
                return -ENOMEM;

        /* bs NULL means flush decoder */
        if (!bs)
                return vdec_vp9_slice_flush(h_vdec, bs, fb, res_chg);

        lat_buf = vdec_msg_queue_dqbuf(&instance->ctx->msg_queue.lat_ctx);
        if (!lat_buf) {
                mtk_vdec_debug(ctx, "Failed to get VP9 lat buf\n");
                return -EAGAIN;
        }
        pfc = (struct vdec_vp9_slice_pfc *)lat_buf->private_data;
        if (!pfc) {
                ret = -EINVAL;
                goto err_free_fb_out;
        }
        vsi = &pfc->vsi;

        ret = vdec_vp9_slice_setup_lat(instance, bs, lat_buf, pfc);
        if (ret) {
                mtk_vdec_err(ctx, "Failed to setup VP9 lat ret %d\n", ret);
                goto err_free_fb_out;
        }
        vdec_vp9_slice_vsi_to_remote(vsi, instance->vsi);

        ret = vpu_dec_start(&instance->vpu, NULL, 0);
        if (ret) {
                mtk_vdec_err(ctx, "Failed to dec VP9 ret %d\n", ret);
                goto err_free_fb_out;
        }

        if (instance->irq) {
                ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
                                                   WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0);
                /* update remote vsi if decode timeout */
                if (ret) {
                        mtk_vdec_err(ctx, "VP9 decode timeout %d pic %d\n", ret, pfc->seq);
                        WRITE_ONCE(instance->vsi->state.timeout, 1);
                }
                vpu_dec_end(&instance->vpu);
        }

        vdec_vp9_slice_vsi_from_remote(vsi, instance->vsi, 0);
        ret = vdec_vp9_slice_update_lat(instance, lat_buf, pfc);

        /* LAT trans full, no more UBE or decode timeout */
        if (ret) {
                mtk_vdec_err(ctx, "VP9 decode error: %d\n", ret);
                goto err_free_fb_out;
        }

        mtk_vdec_debug(ctx, "lat dma addr: 0x%lx 0x%lx\n",
                       (unsigned long)pfc->vsi.trans.dma_addr,
                       (unsigned long)pfc->vsi.trans.dma_addr_end);

        vdec_msg_queue_update_ube_wptr(&ctx->msg_queue,
                                       vsi->trans.dma_addr_end +
                                       ctx->msg_queue.wdma_addr.dma_addr);
        vdec_msg_queue_qbuf(&ctx->msg_queue.core_ctx, lat_buf);

        return 0;
err_free_fb_out:
        vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf);
        return ret;
}

static int vdec_vp9_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
                                 struct vdec_fb *fb, bool *res_chg)
{
        struct vdec_vp9_slice_instance *instance = h_vdec;
        int ret;

        if (instance->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
                ret = vdec_vp9_slice_single_decode(h_vdec, bs, fb, res_chg);
        else
                ret = vdec_vp9_slice_lat_decode(h_vdec, bs, fb, res_chg);

        return ret;
}

static int vdec_vp9_slice_core_decode(struct vdec_lat_buf *lat_buf)
{
        struct vdec_vp9_slice_instance *instance;
        struct vdec_vp9_slice_pfc *pfc;
        struct mtk_vcodec_dec_ctx *ctx = NULL;
        struct vdec_fb *fb = NULL;
        int ret = -EINVAL;

        if (!lat_buf)
                goto err;

        pfc = lat_buf->private_data;
        ctx = lat_buf->ctx;
        if (!pfc || !ctx)
                goto err;

        instance = ctx->drv_handle;
        if (!instance)
                goto err;

        fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
        if (!fb) {
                ret = -EBUSY;
                goto err;
        }

        ret = vdec_vp9_slice_setup_core(instance, fb, lat_buf, pfc);
        if (ret) {
                mtk_vdec_err(ctx, "vdec_vp9_slice_setup_core\n");
                goto err;
        }
        vdec_vp9_slice_vsi_to_remote(&pfc->vsi, instance->core_vsi);

        ret = vpu_dec_core(&instance->vpu);
        if (ret) {
                mtk_vdec_err(ctx, "vpu_dec_core\n");
                goto err;
        }

        if (instance->irq) {
                ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
                                                   WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
                /* update remote vsi if decode timeout */
                if (ret) {
                        mtk_vdec_err(ctx, "VP9 core timeout pic %d\n", pfc->seq);
                        WRITE_ONCE(instance->core_vsi->state.timeout, 1);
                }
                vpu_dec_core_end(&instance->vpu);
        }

        vdec_vp9_slice_vsi_from_remote(&pfc->vsi, instance->core_vsi, 1);
        ret = vdec_vp9_slice_update_core(instance, lat_buf, pfc);
        if (ret) {
                mtk_vdec_err(ctx, "vdec_vp9_slice_update_core\n");
                goto err;
        }

        pfc->vsi.trans.dma_addr_end += ctx->msg_queue.wdma_addr.dma_addr;
        mtk_vdec_debug(ctx, "core dma_addr_end 0x%lx\n",
                       (unsigned long)pfc->vsi.trans.dma_addr_end);
        vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, pfc->vsi.trans.dma_addr_end);
        ctx->dev->vdec_pdata->cap_to_disp(ctx, 0, lat_buf->src_buf_req);

        return 0;

err:
        if (ctx && pfc) {
                /* always update read pointer */
                vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, pfc->vsi.trans.dma_addr_end);

                if (fb)
                        ctx->dev->vdec_pdata->cap_to_disp(ctx, 1, lat_buf->src_buf_req);
        }
        return ret;
}

const struct vdec_common_if vdec_vp9_slice_lat_if = {
        .init           = vdec_vp9_slice_init,
        .decode         = vdec_vp9_slice_decode,
        .get_param      = vdec_vp9_slice_get_param,
        .deinit         = vdec_vp9_slice_deinit,
};