All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Jernej Škrabec" <jernej.skrabec@gmail.com>
To: linux-media@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
	linux-staging@lists.linux.dev,
	Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Cc: Andrzej Pietrasiewicz <andrzej.p@collabora.com>,
	Benjamin Gaignard <benjamin.gaignard@collabora.com>,
	Boris Brezillon <boris.brezillon@collabora.com>,
	Ezequiel Garcia <elezegarcia@gmail.com>,
	Fabio Estevam <festevam@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Hans Verkuil <hverkuil-cisco@xs4all.nl>,
	Heiko Stuebner <heiko@sntech.de>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Nicolas Dufresne <nicolas.dufresne@collabora.com>,
	NXP Linux Team <linux-imx@nxp.com>,
	Pengutronix Kernel Team <kernel@pengutronix.de>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Shawn Guo <shawnguo@kernel.org>,
	kernel@collabora.com
Subject: Re: [PATCH v3 09/10] media: hantro: Support VP9 on the G2 core
Date: Mon, 09 Aug 2021 19:07:35 +0200	[thread overview]
Message-ID: <1771240.sZXMzDhkfG@kista> (raw)
In-Reply-To: <20210805144246.11998-10-andrzej.p@collabora.com>

Hi Andrzej!

Dne četrtek, 05. avgust 2021 ob 16:42:45 CEST je Andrzej Pietrasiewicz 
napisal(a):
> VeriSilicon Hantro G2 core supports VP9 codec.
> 
> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
> ---
>  drivers/staging/media/hantro/Kconfig          |   1 +
>  drivers/staging/media/hantro/Makefile         |   6 +-
>  drivers/staging/media/hantro/hantro.h         |  26 +
>  drivers/staging/media/hantro/hantro_drv.c     |  18 +-
>  drivers/staging/media/hantro/hantro_g2_regs.h |  97 ++
>  .../staging/media/hantro/hantro_g2_vp9_dec.c  | 978 ++++++++++++++++++
>  drivers/staging/media/hantro/hantro_hw.h      |  67 ++
>  drivers/staging/media/hantro/hantro_v4l2.c    |   6 +
>  drivers/staging/media/hantro/hantro_vp9.c     | 240 +++++
>  drivers/staging/media/hantro/hantro_vp9.h     | 103 ++
>  drivers/staging/media/hantro/imx8m_vpu_hw.c   |  22 +-
>  11 files changed, 1560 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/staging/media/hantro/hantro_g2_vp9_dec.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.h

<snip>

> diff --git a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c b/drivers/
staging/media/hantro/hantro_g2_vp9_dec.c
> new file mode 100644
> index 000000000000..45a7be4a43fa
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_g2_vp9_dec.c
> @@ -0,0 +1,978 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +#include "media/videobuf2-core.h"
> +#include "media/videobuf2-dma-contig.h"
> +#include "media/videobuf2-v4l2.h"
> +#include <linux/kernel.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-vp9.h>
> +
> +#include "hantro.h"
> +#include "hantro_vp9.h"
> +#include "hantro_g2_regs.h"
> +
> +#define G2_ALIGN 16
> +
> +enum hantro_ref_frames {
> +	INTRA_FRAME = 0,
> +	LAST_FRAME = 1,
> +	GOLDEN_FRAME = 2,
> +	ALTREF_FRAME = 3,
> +	MAX_REF_FRAMES = 4
> +};
> +
> +static int start_prepare_run(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame **dec_params)
> +{
> +	const struct v4l2_ctrl_vp9_compressed_hdr_probs *prob_updates;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct v4l2_ctrl *ctrl;
> +	unsigned int fctx_idx;
> +
> +	/* v4l2-specific stuff */
> +	hantro_start_prepare_run(ctx);
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_FRAME);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	*dec_params = ctrl->p_cur.p;
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_COMPRESSED_HDR_PROBS);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	prob_updates = ctrl->p_cur.p;
> +
> +	/*
> +	 * vp9 stuff
> +	 *
> +	 * by this point the userspace has done all parts of 6.2 
uncompressed_header()
> +	 * except this fragment:
> +	 * if ( FrameIsIntra || error_resilient_mode ) {
> +	 *	setup_past_independence ( )
> +	 *	if ( frame_type == KEY_FRAME || error_resilient_mode == 
1 ||
> +	 *	     reset_frame_context == 3 ) {
> +	 *		for ( i = 0; i < 4; i ++ ) {
> +	 *			save_probs( i )
> +	 *		}
> +	 *	} else if ( reset_frame_context == 2 ) {
> +	 *		save_probs( frame_context_idx )
> +	 *	}
> +	 *	frame_context_idx = 0
> +	 * }
> +	 */
> +	fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx-
>frame_context);
> +	vp9_ctx->cur.frame_context_idx = fctx_idx;
> +
> +	/* 6.1 frame(sz): load_probs() and load_probs2() */
> +	vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx];
> +
> +	/*
> +	 * The userspace has also performed 6.3 compressed_header(), but 
handling the
> +	 * probs in a special way. All probs which need updating, except 
MV-related,
> +	 * have been read from the bitstream and translated through 
inv_map_table[],
> +	 * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The 
values passed
> +	 * by userspace are either translated values (there are no 0 values 
in
> +	 * inv_map_table[]), or zero to indicate no update. All MV-related 
probs which need
> +	 * updating have been read from the bitstream and (mv_prob << 1) | 
1 has been
> +	 * performed. The values passed by userspace are either new values
> +	 * to replace old ones (the above mentioned shift and bitwise or 
never result in
> +	 * a zero) or zero to indicate no update.
> +	 * fw_update_probs() performs actual probs updates or leaves probs 
as-is
> +	 * for values for which a zero was passed from userspace.
> +	 */
> +	v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, 
prob_updates, *dec_params);
> +
> +	return 0;
> +}
> +
> +static size_t chroma_offset(const struct hantro_ctx *ctx,
> +			    const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2;
> +
> +	return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel;
> +}
> +
> +static size_t mv_offset(const struct hantro_ctx *ctx,
> +			const struct v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	size_t cr_offset = chroma_offset(ctx, dec_params);
> +
> +	return ALIGN((cr_offset * 3) / 2, G2_ALIGN);
> +}
> +
> +static struct hantro_decoded_buffer *
> +get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 
timestamp)
> +{
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
> +	int buf_idx;
> +
> +	/*
> +	 * If a ref is unused or invalid, address of current destination
> +	 * buffer is returned.
> +	 */
> +	buf_idx = vb2_find_timestamp(cap_q, timestamp, 0);
> +	if (buf_idx < 0)
> +		return vb2_to_hantro_decoded_buf(&dst->vb2_buf);
> +
> +	return vb2_to_hantro_decoded_buf(vb2_get_buffer(cap_q, buf_idx));
> +}
> +
> +static void update_dec_buf_info(struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	buf->vp9.width = dec_params->frame_width_minus_1 + 1;
> +	buf->vp9.height = dec_params->frame_height_minus_1 + 1;
> +	buf->vp9.bit_depth = dec_params->bit_depth;
> +}
> +
> +static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +				struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	vp9_ctx->cur.valid = true;
> +	vp9_ctx->cur.reference_mode = dec_params->reference_mode;
> +	vp9_ctx->cur.tx_mode = dec_params->tx_mode;
> +	vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter;
> +	vp9_ctx->cur.flags = dec_params->flags;
> +	vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp;
> +}
> +
> +static void config_output(struct hantro_ctx *ctx,
> +			  struct hantro_decoded_buffer *dst,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	dma_addr_t luma_addr, chroma_addr, mv_addr;
> +
> +	hantro_reg_write(ctx->dev, &g2_out_dis, 0);
> +	hantro_reg_write(ctx->dev, &g2_output_format, 0);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_CHR, chroma_addr);
> +
> +	mv_addr = luma_addr + mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_MV, mv_addr);
> +}
> +
> +struct hantro_vp9_ref_reg {
> +	const struct hantro_reg width;
> +	const struct hantro_reg height;
> +	const struct hantro_reg hor_scale;
> +	const struct hantro_reg ver_scale;
> +	u32 y_base;
> +	u32 c_base;
> +};
> +
> +static void config_ref(struct hantro_ctx *ctx,
> +		       struct hantro_decoded_buffer *dst,
> +		       const struct hantro_vp9_ref_reg *ref_reg,
> +		       const struct v4l2_ctrl_vp9_frame *dec_params,
> +		       u64 ref_ts)
> +{
> +	struct hantro_decoded_buffer *buf;
> +	dma_addr_t luma_addr, chroma_addr;
> +	u32 refw, refh;
> +
> +	buf = get_ref_buf(ctx, &dst->base.vb, ref_ts);
> +	refw = buf->vp9.width;
> +	refh = buf->vp9.height;
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->width, refw);
> +	hantro_reg_write(ctx->dev, &ref_reg->height, refh);
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst-
>vp9.width);
> +	hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst-
>vp9.height);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr);
> +}
> +
> +static void config_ref_registers(struct hantro_ctx *ctx,
> +				 const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +				 struct hantro_decoded_buffer *dst,
> +				 struct hantro_decoded_buffer 
*mv_ref)
> +{
> +	static const struct hantro_vp9_ref_reg ref_regs[] = {
> +		{
> +			/* Last */
> +			.width = vp9_lref_width,
> +			.height = vp9_lref_height,
> +			.hor_scale = vp9_lref_hor_scale,
> +			.ver_scale = vp9_lref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(0),
> +			.c_base = G2_REG_CHR_REF(0),
> +		}, {
> +			/* Golden */
> +			.width = vp9_gref_width,
> +			.height = vp9_gref_height,
> +			.hor_scale = vp9_gref_hor_scale,
> +			.ver_scale = vp9_gref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(4),
> +			.c_base = G2_REG_CHR_REF(4),
> +		}, {
> +			/* Altref */
> +			.width = vp9_aref_width,
> +			.height = vp9_aref_height,
> +			.hor_scale = vp9_aref_hor_scale,
> +			.ver_scale = vp9_aref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(5),
> +			.c_base = G2_REG_CHR_REF(5),
> +		},
> +	};
> +	dma_addr_t mv_addr;
> +
> +	config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params-
>last_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params-
>golden_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params-
>alt_frame_ts);
> +
> +	mv_addr = vb2_dma_contig_plane_dma_addr(&mv_ref->base.vb.vb2_buf, 
0) +
> +		  mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_REG_DMV_REF(0), mv_addr);
> +
> +	hantro_reg_write(ctx->dev, &vp9_last_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_gref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_aref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0);

Not sure if I'm missing something, but V4L2_VP9_SIGN_BIAS_LAST, 
V4L2_VP9_SIGN_BIAS_GOLDEN and V4L2_VP9_SIGN_BIAS_ALT are defined in ordinary 
enum in patch 5, without any value, so they would have values 0, 1 and 2.

I guess you actually want (1 << 0), (1 << 1) and (1 << 2) in patch 5. Second 
and third flag still work by chance and first flag is always applied.

Best regards,
Jernej


> +}
> +
> +static void recompute_tile_info(unsigned short *tile_info, unsigned int 
tiles, unsigned int sbs)
> +{
> +	int i;
> +	unsigned int accumulated = 0;
> +	unsigned int next_accumulated;
> +
> +	for (i = 1; i <= tiles; ++i) {
> +		next_accumulated = i * sbs / tiles;
> +		*tile_info++ = next_accumulated - accumulated;
> +		accumulated = next_accumulated;
> +	}
> +}
> +
> +static void
> +recompute_tile_rc_info(struct hantro_ctx *ctx,
> +		       unsigned int tile_r, unsigned int tile_c,
> +		       unsigned int sbs_r, unsigned int sbs_c)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r);
> +	recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c);
> +
> +	vp9_ctx->last_tile_r = tile_r;
> +	vp9_ctx->last_tile_c = tile_c;
> +	vp9_ctx->last_sbs_r = sbs_r;
> +	vp9_ctx->last_sbs_c = sbs_c;
> +}
> +
> +static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int 
sbs_r)
> +{
> +	if (tile_r == sbs_r + 1)
> +		return 1;
> +
> +	if (tile_r == sbs_r + 2)
> +		return 2;
> +
> +	return 0;
> +}
> +
> +static void
> +fill_tile_info(struct hantro_ctx *ctx,
> +	       unsigned int tile_r, unsigned int tile_c,
> +	       unsigned int sbs_r, unsigned int sbs_c,
> +	       unsigned short *tile_mem)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int i, j;
> +	bool first = true;
> +
> +	for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) {
> +		unsigned short r_info = vp9_ctx->tile_r_info[i];
> +
> +		if (first) {
> +			if (i > 0)
> +				r_info += vp9_ctx->tile_r_info[0];
> +			if (i == 2)
> +				r_info += vp9_ctx->tile_r_info[1];
> +			first = false;
> +		}
> +		for (j = 0; j < tile_c; ++j) {
> +			*tile_mem++ = vp9_ctx->tile_c_info[j];
> +			*tile_mem++ = r_info;
> +		}
> +	}
> +}
> +
> +static void
> +config_tiles(struct hantro_ctx *ctx,
> +	     const struct v4l2_ctrl_vp9_frame *dec_params,
> +	     struct hantro_decoded_buffer *dst)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge;
> +	dma_addr_t addr;
> +	unsigned short *tile_mem;
> +
> +	addr = misc->dma + vp9_ctx->tile_info_offset;
> +	hantro_write_addr(ctx->dev, G2_ADDR_TILE_SIZE, addr);
> +
> +	tile_mem = misc->cpu + vp9_ctx->tile_info_offset;
> +	if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) {
> +		unsigned int tile_r = (1 << dec_params->tile_rows_log2);
> +		unsigned int tile_c = (1 << dec_params->tile_cols_log2);
> +		unsigned int sbs_r = hantro_vp9_num_sbs(dst-
>vp9.height);
> +		unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width);
> +
> +		if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx-
>last_tile_c ||
> +		    sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx-
>last_sbs_c)
> +			recompute_tile_rc_info(ctx, tile_r, tile_c, 
sbs_r, sbs_c);
> +
> +		fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, 
tile_mem);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, tile_c);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, tile_r);
> +
> +		addr = tile_edge->dma;
> +		hantro_write_addr(ctx->dev, G2_TILE_FILTER, addr);
> +
> +		addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset;
> +		hantro_write_addr(ctx->dev, G2_TILE_BSD, addr);
> +	} else {
> +		tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width);
> +		tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 0);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, 1);
> +	}
> +}
> +
> +static void
> +update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +		     const struct v4l2_vp9_segmentation *seg,
> +		     enum v4l2_vp9_segment_feature feature,
> +		     unsigned int segid)
> +{
> +	u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature);
> +
> +	vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid]
[feature];
> +	vp9_ctx->feature_enabled[segid] &= ~mask;
> +	vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & 
mask);
> +}
> +
> +static inline s16 clip3(s16 x, s16 y, s16 z)
> +{
> +	return (z < x) ? x : (z > y) ? y : z;
> +}
> +
> +static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 
clip)
> +{
> +	if (absolute)
> +		return feature_data;
> +
> +	return clip3(0, 255, feat_val + feature_data);
> +}
> +
> +static void config_segment(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	s16 feat_val;
> +	unsigned char feat_id;
> +	unsigned int segid;
> +	bool segment_enabled, absolute, update_data;
> +
> +	static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = 
{
> +		{ vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, 
vp9_skip_seg0 },
> +		{ vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, 
vp9_skip_seg1 },
> +		{ vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, 
vp9_skip_seg2 },
> +		{ vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, 
vp9_skip_seg3 },
> +		{ vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, 
vp9_skip_seg4 },
> +		{ vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, 
vp9_skip_seg5 },
> +		{ vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, 
vp9_skip_seg6 },
> +		{ vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, 
vp9_skip_seg7 },
> +	};
> +
> +	segment_enabled = !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_ENABLED);
> +	hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled);
> +	hantro_reg_write(ctx->dev, &vp9_segment_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP));
> +	hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE));
> +
> +	seg = &dec_params->seg;
> +	absolute = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
> +	update_data = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA);
> +
> +	for (segid = 0; segid < 8; ++segid) {
> +		/* Quantizer segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_Q;
> +		feat_val = dec_params->quant.base_q_idx;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 255);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Loop filter segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_L;
> +		feat_val = dec_params->lf.level;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 63);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Reference frame segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_REF_FRAME;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			    v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = vp9_ctx-
>feature_data[segid][feat_id] + 1;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Skip segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_SKIP;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled,
> +							     
feat_id, segid) ? 1 : 0;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +	}
> +}
> +
> +static void config_loop_filter(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	bool d = dec_params->lf.flags & 
V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED;
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level);
> +	hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params-
>lf.level == 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params-
>lf.sharpness);
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params-
>lf.ref_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params-
>lf.ref_deltas[1] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params-
>lf.ref_deltas[2] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params-
>lf.ref_deltas[3] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params-
>lf.mode_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params-
>lf.mode_deltas[1] : 0);
> +}
> +
> +static void config_picture_dimensions(struct hantro_ctx *ctx, struct 
hantro_decoded_buffer *dst)
> +{
> +	u32 pic_w_4x4, pic_h_4x4;
> +
> +	hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 
7) / 8);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height 
+ 7) / 8);
> +	pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2;
> +	pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2;
> +	hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4);
> +}
> +
> +static void
> +config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params-
>bit_depth - 8);
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params-
>bit_depth - 8);
> +}
> +
> +static inline bool is_lossless(const struct v4l2_vp9_quantization *quant)
> +{
> +	return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 &&
> +	       quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0;
> +}
> +
> +static void
> +config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params-
>quant.delta_q_y_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params-
>quant.delta_q_uv_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params-
>quant.delta_q_uv_ac);
> +	hantro_reg_write(ctx->dev, &vp9_lossless_e, 
is_lossless(&dec_params->quant));
> +}
> +
> +static u32
> +hantro_interp_filter_from_v4l2(enum v4l2_vp9_interpolation_filter 
interpolation_filter)
> +{
> +	switch (interpolation_filter) {
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP:
> +		return 0x1;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH:
> +		return 0;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP:
> +		return 0x2;
> +	case V4L2_VP9_INTERP_FILTER_BILINEAR:
> +		return 0x3;
> +	case V4L2_VP9_INTERP_FILTER_SWITCHABLE:
> +		return 0x4;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      bool intra_only, bool resolution_change)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only);
> +
> +	hantro_reg_write(ctx->dev, &vp9_transform_mode, dec_params-
>tx_mode);
> +
> +	hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ?
> +		0 : hantro_interp_filter_from_v4l2(dec_params-
>interpolation_filter));
> +
> +	hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e,
> +			 !!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV));
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params-
>reference_mode);
> +
> +	hantro_reg_write(ctx->dev, &g2_tempor_mvp_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY) &&
> +			 !resolution_change &&
> +			 vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_SHOW_FRAME
> +	);
> +
> +	hantro_reg_write(ctx->dev, &g2_write_mvs_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME));
> +}
> +
> +static void
> +config_compound_reference(struct hantro_ctx *ctx,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	u32 comp_fixed_ref, comp_var_ref[2];
> +	bool last_ref_frame_sign_bias;
> +	bool golden_ref_frame_sign_bias;
> +	bool alt_ref_frame_sign_bias;
> +	bool comp_ref_allowed = 0;
> +
> +	comp_fixed_ref = 0;
> +	comp_var_ref[0] = 0;
> +	comp_var_ref[1] = 0;
> +
> +	last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST;
> +	golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN;
> +	alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT;
> +
> +	/* 6.3.12 Frame reference mode syntax */
> +	comp_ref_allowed |= golden_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +	comp_ref_allowed |= alt_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +
> +	if (comp_ref_allowed) {
> +		if (last_ref_frame_sign_bias ==
> +		    golden_ref_frame_sign_bias) {
> +			comp_fixed_ref = ALTREF_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = GOLDEN_FRAME;
> +		} else if (last_ref_frame_sign_bias ==
> +			   alt_ref_frame_sign_bias) {
> +			comp_fixed_ref = GOLDEN_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		} else {
> +			comp_fixed_ref = LAST_FRAME;
> +			comp_var_ref[0] = GOLDEN_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		}
> +	}
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, 
comp_var_ref[0]);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, 
comp_var_ref[1]);
> +}
> +
> +#define INNER_LOOP \
> +do {								
	\
> +	for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) {	\
> +		memcpy(adaptive->coef[i][j][k][l][m],			
\
> +		       probs->coef[i][j][k][l][m],			
\
> +		       sizeof(probs->coef[i][j][k][l][m]));		
\
> +								
	\
> +		adaptive->coef[i][j][k][l][m][3] = 0;			
\
> +	}							
	\
> +} while (0)
> +
> +static void config_probs(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_g2_all_probs *all_probs = misc->cpu;
> +	struct hantro_g2_probs *adaptive;
> +	struct hantro_g2_mv_probs *mv;
> +	const struct v4l2_vp9_segmentation *seg = &dec_params->seg;
> +	const struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +	int i, j, k, l, m;
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i)
> +		for (j = 0; j < ARRAY_SIZE(all_probs-
>kf_y_mode_prob[0]); ++j) {
> +			memcpy(all_probs->kf_y_mode_prob[i][j],
> +			       v4l2_vp9_kf_y_mode_prob[i][j],
> +			       ARRAY_SIZE(all_probs-
>kf_y_mode_prob[i][j]));
> +
> +			all_probs->kf_y_mode_prob_tail[i][j][0] =
> +				v4l2_vp9_kf_y_mode_prob[i][j][8];
> +		}
> +
> +	memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs,
> +	       sizeof(all_probs->mb_segment_tree_probs));
> +
> +	memcpy(all_probs->segment_pred_probs, seg->pred_probs,
> +	       sizeof(all_probs->segment_pred_probs));
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) {
> +		memcpy(all_probs->kf_uv_mode_prob[i], 
v4l2_vp9_kf_uv_mode_prob[i],
> +		       ARRAY_SIZE(all_probs->kf_uv_mode_prob[i]));
> +
> +		all_probs->kf_uv_mode_prob_tail[i][0] = 
v4l2_vp9_kf_uv_mode_prob[i][8];
> +	}
> +
> +	adaptive = &all_probs->probs;
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) {
> +		memcpy(adaptive->inter_mode[i], probs->inter_mode[i],
> +		       sizeof(probs->inter_mode));
> +
> +		adaptive->inter_mode[i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive-
>is_inter));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) {
> +		memcpy(adaptive->uv_mode[i], probs->uv_mode[i],
> +		       sizeof(adaptive->uv_mode[i]));
> +		adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8];
> +	}
> +
> +	memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8));
> +	memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16));
> +	memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) {
> +		memcpy(adaptive->y_mode[i], probs->y_mode[i],
> +		       ARRAY_SIZE(adaptive->y_mode[i]));
> +
> +		adaptive->y_mode_tail[i][0] = probs->y_mode[i][8];
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) {
> +		memcpy(adaptive->partition[0][i], 
v4l2_vp9_kf_partition_probs[i],
> +		       sizeof(v4l2_vp9_kf_partition_probs[i]));
> +
> +		adaptive->partition[0][i][3] = 0;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) {
> +		memcpy(adaptive->partition[1][i], probs->partition[i],
> +		       sizeof(probs->partition[i]));
> +
> +		adaptive->partition[1][i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->interp_filter, probs->interp_filter,
> +	       sizeof(adaptive->interp_filter));
> +
> +	memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive-
>comp_mode));
> +
> +	memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip));
> +
> +	mv = &adaptive->mv;
> +
> +	memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint));
> +	memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign));
> +	memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv-
>class0_bit));
> +	memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr));
> +	memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp));
> +	memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp));
> +	memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes));
> +	memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr));
> +	memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits));
> +
> +	memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive-
>single_ref));
> +
> +	memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive-
>comp_ref));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i)
> +		for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(adaptive->coef[0]
[0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(adaptive->coef[0][0][0]); ++l)
> +					INNER_LOOP;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_PROBS, misc->dma);
> +}
> +
> +static void config_counts(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_CTR, addr);
> +}
> +
> +static void config_seg_map(struct hantro_ctx *ctx,
> +			   const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +			   bool intra_only, bool update_map)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map;
> +	dma_addr_t addr;
> +
> +	if (intra_only ||
> +	    (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) {
> +		memset(segment_map->cpu, 0, segment_map->size);
> +		memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx-
>feature_data));
> +		memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx-
>feature_enabled));
> +	}
> +
> +	addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_READ, addr);
> +
> +	addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_WRITE, addr);
> +
> +	if (update_map)
> +		vp9_ctx->active_segment = 1 - vp9_ctx->active_segment;
> +}
> +
> +static void
> +config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      struct vb2_v4l2_buffer *vb2_src)
> +{
> +	dma_addr_t stream_base, tmp_addr;
> +	unsigned int headres_size;
> +	u32 src_len, start_bit, src_buf_len;
> +
> +	headres_size = dec_params->uncompressed_header_size
> +		     + dec_params->compressed_header_size;
> +
> +	stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_STR, stream_base);
> +
> +	tmp_addr = stream_base + headres_size;
> +	start_bit = (tmp_addr & 0xf) * 8;
> +	hantro_reg_write(ctx->dev, &g2_start_bit, start_bit);
> +
> +	src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0);
> +	src_len += start_bit / 8 - headres_size;
> +	hantro_reg_write(ctx->dev, &g2_stream_len, src_len);
> +
> +	tmp_addr &= ~0xf;
> +	hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - 
stream_base);
> +	src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0);
> +	hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len);
> +}
> +
> +static void
> +config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +		 struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer 
*vb2_dst)
> +{
> +	struct hantro_decoded_buffer *dst, *last, *mv_ref;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	bool intra_only, resolution_change;
> +
> +	/* vp9 stuff */
> +	dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
> +
> +	if (vp9_ctx->last.valid)
> +		last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx-
>last.timestamp);
> +	else
> +		last = dst;
> +
> +	update_dec_buf_info(dst, dec_params);
> +	update_ctx_cur_info(vp9_ctx, dst, dec_params);
> +	seg = &dec_params->seg;
> +
> +	intra_only = !!(dec_params->flags &
> +			(V4L2_VP9_FRAME_FLAG_KEY_FRAME |
> +			V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
> +
> +	if (!intra_only &&
> +	    !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +	    vp9_ctx->last.valid)
> +		mv_ref = last;
> +	else
> +		mv_ref = dst;
> +
> +	resolution_change = dst->vp9.width != last->vp9.width ||
> +			    dst->vp9.height != last->vp9.height;
> +
> +	/* configure basic registers */
> +	hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE);
> +	hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128);
> +	hantro_reg_write(ctx->dev, &g2_max_burst, 16);
> +	hantro_reg_write(ctx->dev, &g2_apf_threshold, 8);
> +	hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1);
> +	hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1);
> +	hantro_reg_write(ctx->dev, &g2_max_cb_size, 6);
> +	hantro_reg_write(ctx->dev, &g2_min_cb_size, 3);
> +
> +	config_output(ctx, dst, dec_params);
> +
> +	if (!intra_only)
> +		config_ref_registers(ctx, dec_params, dst, mv_ref);
> +
> +	config_tiles(ctx, dec_params, dst);
> +	config_segment(ctx, dec_params);
> +	config_loop_filter(ctx, dec_params);
> +	config_picture_dimensions(ctx, dst);
> +	config_bit_depth(ctx, dec_params);
> +	config_quant(ctx, dec_params);
> +	config_others(ctx, dec_params, intra_only, resolution_change);
> +	config_compound_reference(ctx, dec_params);
> +	config_probs(ctx, dec_params);
> +	config_counts(ctx);
> +	config_seg_map(ctx, dec_params, intra_only,
> +		       seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP);
> +	config_source(ctx, dec_params, vb2_src);
> +}
> +
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx)
> +{
> +	const struct v4l2_ctrl_vp9_frame *decode_params;
> +	struct vb2_v4l2_buffer *src;
> +	struct vb2_v4l2_buffer *dst;
> +	int ret;
> +
> +	hantro_g2_check_idle(ctx->dev);
> +
> +	ret = start_prepare_run(ctx, &decode_params);
> +	if (ret) {
> +		hantro_end_prepare_run(ctx);
> +		return ret;
> +	}
> +
> +	src = hantro_get_src_buf(ctx);
> +	dst = hantro_get_dst_buf(ctx);
> +
> +	config_registers(ctx, decode_params, src, dst);
> +
> +	hantro_end_prepare_run(ctx);
> +
> +	vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT);
> +
> +	return 0;
> +}
> +
> +#define copy_tx_and_skip(p1, p2)				\
> +do {								
\
> +	memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8));	\
> +	memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16));	\
> +	memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32));	\
> +	memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip));	\
> +} while (0)
> +
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int fctx_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX))
> +		goto out_update_last;
> +
> +	fctx_idx = vp9_ctx->cur.frame_context_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) {
> +		/* error_resilient_mode == 0 && 
frame_parallel_decoding_mode == 0 */
> +		struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +		bool frame_is_intra = vp9_ctx->cur.flags &
> +		    (V4L2_VP9_FRAME_FLAG_KEY_FRAME | 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY);
> +		struct tx_and_skip {
> +			u8 tx8[2][1];
> +			u8 tx16[2][2];
> +			u8 tx32[2][3];
> +			u8 skip[3];
> +		} _tx_skip, *tx_skip = &_tx_skip;
> +		struct v4l2_vp9_frame_symbol_counts *counts;
> +		struct symbol_counts *hantro_cnts;
> +		u32 tx16p[2][4];
> +		int i;
> +
> +		/* buffer the forward-updated TX and skip probs */
> +		if (frame_is_intra)
> +			copy_tx_and_skip(tx_skip, probs);
> +
> +		/* 6.1.2 refresh_probs(): load_probs() and load_probs2() 
*/
> +		*probs = vp9_ctx->frame_context[fctx_idx];
> +
> +		/* if FrameIsIntra then undo the effect of load_probs2() 
*/
> +		if (frame_is_intra)
> +			copy_tx_and_skip(probs, tx_skip);
> +
> +		counts = &vp9_ctx->cnts;
> +		hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +		for (i = 0; i < ARRAY_SIZE(tx16p); ++i) {
> +			memcpy(tx16p[i],
> +			       hantro_cnts->tx16x16_count[i],
> +			       sizeof(hantro_cnts-
>tx16x16_count[0]));
> +			tx16p[i][3] = 0;
> +		}
> +		counts->tx16p = &tx16p;
> +
> +		v4l2_vp9_adapt_coef_probs(probs, counts,
> +					  !vp9_ctx->last.valid 
||
> +					  vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME,
> +					  frame_is_intra);
> +
> +		if (!frame_is_intra) {
> +			/* load_probs2() already done */
> +			u32 mv_mode[7][4];
> +
> +			for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) {
> +				mv_mode[i][0] = hantro_cnts-
>inter_mode_counts[i][1][0];
> +				mv_mode[i][1] = hantro_cnts-
>inter_mode_counts[i][2][0];
> +				mv_mode[i][2] = hantro_cnts-
>inter_mode_counts[i][0][0];
> +				mv_mode[i][3] = hantro_cnts-
>inter_mode_counts[i][2][1];
> +			}
> +			counts->mv_mode = &mv_mode;
> +			v4l2_vp9_adapt_noncoef_probs(&vp9_ctx-
>probability_tables, counts,
> +						     
vp9_ctx->cur.reference_mode,
> +						     
vp9_ctx->cur.interpolation_filter,
> +						     
vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags);
> +		}
> +	}
> +
> +	vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables;
> +
> +out_update_last:
> +	vp9_ctx->last = vp9_ctx->cur;
> +}
> diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/
media/hantro/hantro_hw.h
> index 42b3f3961f75..2961d399fd60 100644
> --- a/drivers/staging/media/hantro/hantro_hw.h
> +++ b/drivers/staging/media/hantro/hantro_hw.h
> @@ -12,6 +12,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/v4l2-controls.h>
>  #include <media/v4l2-ctrls.h>
> +#include <media/v4l2-vp9.h>
>  #include <media/videobuf2-core.h>
>  
>  #define DEC_8190_ALIGN_MASK	0x07U
> @@ -161,6 +162,50 @@ struct hantro_vp8_dec_hw_ctx {
>  	struct hantro_aux_buf prob_tbl;
>  };
>  
> +struct hantro_vp9_frame_info {
> +	u32 valid : 1;
> +	u32 frame_context_idx : 2;
> +	u32 reference_mode : 2;
> +	u32 tx_mode : 3;
> +	u32 interpolation_filter : 3;
> +	u32 flags;
> +	u64 timestamp;
> +};
> +
> +#define MAX_SB_COLS	64
> +#define MAX_SB_ROWS	34
> +
> +/**
> + * struct hantro_vp9_dec_hw_ctx
> + *
> + */
> +struct hantro_vp9_dec_hw_ctx {
> +	struct hantro_aux_buf tile_edge;
> +	struct hantro_aux_buf segment_map;
> +	struct hantro_aux_buf misc;
> +	struct v4l2_vp9_frame_symbol_counts cnts;
> +	struct v4l2_vp9_frame_context probability_tables;
> +	struct v4l2_vp9_frame_context frame_context[4];
> +	struct hantro_vp9_frame_info cur;
> +	struct hantro_vp9_frame_info last;
> +
> +	unsigned int bsd_ctrl_offset;
> +	unsigned int segment_map_size;
> +	unsigned int ctx_counters_offset;
> +	unsigned int tile_info_offset;
> +
> +	unsigned short tile_r_info[MAX_SB_ROWS];
> +	unsigned short tile_c_info[MAX_SB_COLS];
> +	unsigned int last_tile_r;
> +	unsigned int last_tile_c;
> +	unsigned int last_sbs_r;
> +	unsigned int last_sbs_c;
> +
> +	unsigned int active_segment;
> +	u8 feature_enabled[8];
> +	s16 feature_data[8][4];
> +};
> +
>  /**
>   * struct hantro_postproc_ctx
>   *
> @@ -267,6 +312,24 @@ void hantro_hevc_ref_remove_unused(struct hantro_ctx 
*ctx);
>  size_t hantro_hevc_chroma_offset(const struct v4l2_ctrl_hevc_sps *sps);
>  size_t hantro_hevc_motion_vectors_offset(const struct v4l2_ctrl_hevc_sps 
*sps);
>  
> +static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension)
> +{
> +	return (dimension + 63) / 64;
> +}
> +
> +static inline size_t
> +hantro_vp9_mv_size(unsigned int width, unsigned int height)
> +{
> +	int num_ctbs;
> +
> +	/*
> +	 * There can be up to (CTBs x 64) number of blocks,
> +	 * and the motion vector for each block needs 16 bytes.
> +	 */
> +	num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height);
> +	return (num_ctbs * 64) * 16;
> +}
> +
>  static inline size_t
>  hantro_h264_mv_size(unsigned int width, unsigned int height)
>  {
> @@ -308,6 +371,10 @@ void hantro_vp8_dec_exit(struct hantro_ctx *ctx);
>  void hantro_vp8_prob_update(struct hantro_ctx *ctx,
>  			    const struct v4l2_ctrl_vp8_frame *hdr);
>  
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx);
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx);
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx);
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx);
>  void hantro_g2_check_idle(struct hantro_dev *vpu);
>  
>  #endif /* HANTRO_HW_H_ */
> diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/
media/hantro/hantro_v4l2.c
> index d1f060c55fed..e4b0645ba6fc 100644
> --- a/drivers/staging/media/hantro/hantro_v4l2.c
> +++ b/drivers/staging/media/hantro/hantro_v4l2.c
> @@ -299,6 +299,11 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx,
>  			pix_mp->plane_fmt[0].sizeimage +=
>  				hantro_h264_mv_size(pix_mp-
>width,
>  						    pix_mp-
>height);
> +		else if (ctx->vpu_src_fmt->fourcc == 
V4L2_PIX_FMT_VP9_FRAME &&
> +			 !hantro_needs_postproc(ctx, fmt))
> +			pix_mp->plane_fmt[0].sizeimage +=
> +				hantro_vp9_mv_size(pix_mp->width,
> +						   pix_mp-
>height);
>  	} else if (!pix_mp->plane_fmt[0].sizeimage) {
>  		/*
>  		 * For coded formats the application can specify
> @@ -407,6 +412,7 @@ hantro_update_requires_request(struct hantro_ctx *ctx, 
u32 fourcc)
>  	case V4L2_PIX_FMT_VP8_FRAME:
>  	case V4L2_PIX_FMT_H264_SLICE:
>  	case V4L2_PIX_FMT_HEVC_SLICE:
> +	case V4L2_PIX_FMT_VP9_FRAME:
>  		ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true;
>  		break;
>  	default:
> diff --git a/drivers/staging/media/hantro/hantro_vp9.c b/drivers/staging/
media/hantro/hantro_vp9.c
> new file mode 100644
> index 000000000000..566cd376c097
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.c
> @@ -0,0 +1,240 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +#include <linux/types.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "hantro.h"
> +#include "hantro_hw.h"
> +#include "hantro_vp9.h"
> +
> +#define POW2(x) (1 << (x))
> +
> +#define MAX_LOG2_TILE_COLUMNS 6
> +#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS)
> +#define MAX_TILE_COLS 20
> +#define MAX_TILE_ROWS 22
> +
> +static size_t hantro_vp9_tile_filter_size(unsigned int height)
> +{
> +	u32 h, height32, size;
> +
> +	h = roundup(height, 8);
> +
> +	height32 = roundup(h, 64);
> +	size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 
8 + 8 */
> +
> +	return size;
> +}
> +
> +static size_t hantro_vp9_bsd_control_size(unsigned int height)
> +{
> +	u32 h, height32;
> +
> +	h = roundup(height, 8);
> +	height32 = roundup(h, 64);
> +
> +	return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1);
> +}
> +
> +static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int 
height)
> +{
> +	u32 w, h;
> +	int num_ctbs;
> +
> +	w = roundup(width, 8);
> +	h = roundup(height, 8);
> +	num_ctbs = ((w + 63) / 64) * ((h + 63) / 64);
> +
> +	return num_ctbs * 32;
> +}
> +
> +static inline size_t hantro_vp9_prob_tab_size(void)
> +{
> +	return roundup(sizeof(struct hantro_g2_all_probs), 16);
> +}
> +
> +static inline size_t hantro_vp9_count_tab_size(void)
> +{
> +	return roundup(sizeof(struct symbol_counts), 16);
> +}
> +
> +static inline size_t hantro_vp9_tile_info_size(void)
> +{
> +	return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 
15 + 16) & ~0xf, 16);
> +}
> +
> +static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, 
int l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m];
> +
> +	return NULL;
> +}
> +
> +static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int 
l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m][3];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m][3];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m][3];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m][3];
> +
> +	return NULL;
> +}
> +
> +#define INNER_LOOP \
> +	do {							
			\
> +		for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]
[0]); ++m) {	\
> +			vp9_ctx->cnts.coeff[i][j][k][l][m] =		
		\
> +				get_coeffs_arr(cnts, i, j, k, l, 
m);			\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][0] =		
		\
> +				&cnts->count_eobs[i][j][k][l][m];	
		\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][1] =		
		\
> +				get_eobs1(cnts, i, j, k, l, m);	
			\
> +		}						
			\
> +	} while (0)
> +
> +static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +	int i, j, k, l, m;
> +
> +	vp9_ctx->cnts.partition = &cnts->partition_counts;
> +	vp9_ctx->cnts.skip = &cnts->mbskip_count;
> +	vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count;
> +	vp9_ctx->cnts.tx32p = &cnts->tx32x32_count;
> +	/*
> +	 * g2 hardware uses tx16x16_count[2][3], while the api
> +	 * expects tx16p[2][4], so this must be explicitly copied
> +	 * into vp9_ctx->cnts.tx16p when passing the data to the
> +	 * vp9 library function
> +	 */
> +	vp9_ctx->cnts.tx8p = &cnts->tx8x8_count;
> +
> +	vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts;
> +	vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts;
> +	vp9_ctx->cnts.comp = &cnts->comp_inter_count;
> +	vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count;
> +	vp9_ctx->cnts.single_ref = &cnts->single_ref_count;
> +	vp9_ctx->cnts.filter = &cnts->switchable_interp_counts;
> +	vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints;
> +	vp9_ctx->cnts.sign = &cnts->mv_counts.sign;
> +	vp9_ctx->cnts.classes = &cnts->mv_counts.classes;
> +	vp9_ctx->cnts.class0 = &cnts->mv_counts.class0;
> +	vp9_ctx->cnts.bits = &cnts->mv_counts.bits;
> +	vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp;
> +	vp9_ctx->cnts.fp = &cnts->mv_counts.fp;
> +	vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp;
> +	vp9_ctx->cnts.hp = &cnts->mv_counts.hp;
> +
> +	for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i)
> +		for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(vp9_ctx-
>cnts.coeff[i][0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l)
> +					INNER_LOOP;
> +}
> +
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	const struct hantro_variant *variant = vpu->variant;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	u32 i, max_width, max_height, size;
> +
> +	if (variant->num_dec_fmts < 1)
> +		return -EINVAL;
> +
> +	for (i = 0; i < variant->num_dec_fmts; ++i)
> +		if (variant->dec_fmts[i].fourcc == 
V4L2_PIX_FMT_VP9_FRAME)
> +			break;
> +
> +	if (i == variant->num_dec_fmts)
> +		return -EINVAL;
> +
> +	max_width = vpu->variant->dec_fmts[i].frmsize.max_width;
> +	max_height = vpu->variant->dec_fmts[i].frmsize.max_height;
> +
> +	size = hantro_vp9_tile_filter_size(max_height);
> +	vp9_dec->bsd_ctrl_offset = size;
> +	size += hantro_vp9_bsd_control_size(max_height);
> +
> +	tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge-
>dma, GFP_KERNEL);
> +	if (!tile_edge->cpu)
> +		return -ENOMEM;
> +
> +	tile_edge->size = size;
> +	memset(tile_edge->cpu, 0, size);
> +
> +	size = hantro_vp9_segment_map_size(max_width, max_height);
> +	vp9_dec->segment_map_size = size;
> +	size *= 2; /* we need two areas of this size, used alternately */
> +
> +	segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map-
>dma, GFP_KERNEL);
> +	if (!segment_map->cpu)
> +		goto err_segment_map;
> +
> +	segment_map->size = size;
> +	memset(segment_map->cpu, 0, size);
> +
> +	size = hantro_vp9_prob_tab_size();
> +	vp9_dec->ctx_counters_offset = size;
> +	size += hantro_vp9_count_tab_size();
> +	vp9_dec->tile_info_offset = size;
> +	size += hantro_vp9_tile_info_size();
> +
> +	misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, 
GFP_KERNEL);
> +	if (!misc->cpu)
> +		goto err_misc;
> +
> +	misc->size = size;
> +	memset(misc->cpu, 0, size);
> +
> +	init_v4l2_vp9_count_tbl(ctx);
> +
> +	return 0;
> +
> +err_misc:
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +
> +err_segment_map:
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +
> +	return -ENOMEM;
> +}
> +
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +
> +	dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma);
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +}
> diff --git a/drivers/staging/media/hantro/hantro_vp9.h b/drivers/staging/
media/hantro/hantro_vp9.h
> new file mode 100644
> index 000000000000..c7f4bd3ff8dd
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +struct hantro_g2_mv_probs {
> +	u8 joint[3];
> +	u8 sign[2];
> +	u8 class0_bit[2][1];
> +	u8 fr[2][3];
> +	u8 class0_hp[2];
> +	u8 hp[2];
> +	u8 classes[2][10];
> +	u8 class0_fr[2][2][3];
> +	u8 bits[2][10];
> +};
> +
> +struct hantro_g2_probs {
> +	u8 inter_mode[7][4];
> +	u8 is_inter[4];
> +	u8 uv_mode[10][8];
> +	u8 tx8[2][1];
> +	u8 tx16[2][2];
> +	u8 tx32[2][3];
> +	u8 y_mode_tail[4][1];
> +	u8 y_mode[4][8];
> +	u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */
> +	u8 uv_mode_tail[10][1];
> +	u8 interp_filter[4][2];
> +	u8 comp_mode[5];
> +	u8 skip[3];
> +
> +	u8 pad1[1];
> +
> +	struct hantro_g2_mv_probs mv;
> +
> +	u8 single_ref[5][2];
> +	u8 comp_ref[5];
> +
> +	u8 pad2[17];
> +
> +	u8 coef[4][2][2][6][6][4];
> +};
> +
> +struct hantro_g2_all_probs {
> +	u8 kf_y_mode_prob[10][10][8];
> +
> +	u8 kf_y_mode_prob_tail[10][10][1];
> +	u8 ref_pred_probs[3];
> +	u8 mb_segment_tree_probs[7];
> +	u8 segment_pred_probs[3];
> +	u8 ref_scores[4];
> +	u8 prob_comppred[2];
> +
> +	u8 pad1[9];
> +
> +	u8 kf_uv_mode_prob[10][8];
> +	u8 kf_uv_mode_prob_tail[10][1];
> +
> +	u8 pad2[6];
> +
> +	struct hantro_g2_probs probs;
> +};
> +
> +struct mv_counts {
> +	u32 joints[4];
> +	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 symbol_counts {
> +	u32 inter_mode_counts[7][3][2];
> +	u32 sb_ymode_counts[4][10];
> +	u32 uv_mode_counts[10][10];
> +	u32 partition_counts[16][4];
> +	u32 switchable_interp_counts[4][3];
> +	u32 intra_inter_count[4][2];
> +	u32 comp_inter_count[5][2];
> +	u32 single_ref_count[5][2][2];
> +	u32 comp_ref_count[5][2];
> +	u32 tx32x32_count[2][4];
> +	u32 tx16x16_count[2][3];
> +	u32 tx8x8_count[2][2];
> +	u32 mbskip_count[3][2];
> +
> +	struct mv_counts mv_counts;
> +
> +	u32 count_coeffs[2][2][6][6][4];
> +	u32 count_coeffs8x8[2][2][6][6][4];
> +	u32 count_coeffs16x16[2][2][6][6][4];
> +	u32 count_coeffs32x32[2][2][6][6][4];
> +
> +	u32 count_eobs[4][2][2][6][6];
> +};
> +
> diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/
media/hantro/imx8m_vpu_hw.c
> index a40b161e5956..455a107ffb02 100644
> --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c
> +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c
> @@ -150,6 +150,19 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] 
= {
>  			.step_height = MB_DIM,
>  		},
>  	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_VP9_FRAME,
> +		.codec_mode = HANTRO_MODE_VP9_DEC,
> +		.max_depth = 2,
> +		.frmsize = {
> +			.min_width = 48,
> +			.max_width = 3840,
> +			.step_width = MB_DIM,
> +			.min_height = 48,
> +			.max_height = 2160,
> +			.step_height = MB_DIM,
> +		},
> +	},
>  };
>  
>  static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id)
> @@ -241,6 +254,13 @@ static const struct hantro_codec_ops 
imx8mq_vpu_g2_codec_ops[] = {
>  		.init = hantro_hevc_dec_init,
>  		.exit = hantro_hevc_dec_exit,
>  	},
> +	[HANTRO_MODE_VP9_DEC] = {
> +		.run = hantro_g2_vp9_dec_run,
> +		.done = hantro_g2_vp9_dec_done,
> +		.reset = imx8m_vpu_g2_reset,
> +		.init = hantro_vp9_dec_init,
> +		.exit = hantro_vp9_dec_exit,
> +	},
>  };
>  
>  /*
> @@ -281,7 +301,7 @@ const struct hantro_variant imx8mq_vpu_g2_variant = {
>  	.dec_offset = 0x0,
>  	.dec_fmts = imx8m_vpu_g2_dec_fmts,
>  	.num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts),
> -	.codec = HANTRO_HEVC_DECODER,
> +	.codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER,
>  	.codec_ops = imx8mq_vpu_g2_codec_ops,
>  	.init = imx8mq_vpu_hw_init,
>  	.runtime_resume = imx8mq_runtime_resume,
> -- 
> 2.17.1
> 
> 



WARNING: multiple messages have this Message-ID (diff)
From: "Jernej Škrabec" <jernej.skrabec@gmail.com>
To: linux-media@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
	linux-staging@lists.linux.dev,
	Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Cc: Andrzej Pietrasiewicz <andrzej.p@collabora.com>,
	Benjamin Gaignard <benjamin.gaignard@collabora.com>,
	Boris Brezillon <boris.brezillon@collabora.com>,
	Ezequiel Garcia <elezegarcia@gmail.com>,
	Fabio Estevam <festevam@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Hans Verkuil <hverkuil-cisco@xs4all.nl>,
	Heiko Stuebner <heiko@sntech.de>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Nicolas Dufresne <nicolas.dufresne@collabora.com>,
	NXP Linux Team <linux-imx@nxp.com>,
	Pengutronix Kernel Team <kernel@pengutronix.de>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Shawn Guo <shawnguo@kernel.org>,
	kernel@collabora.com
Subject: Re: [PATCH v3 09/10] media: hantro: Support VP9 on the G2 core
Date: Mon, 09 Aug 2021 19:07:35 +0200	[thread overview]
Message-ID: <1771240.sZXMzDhkfG@kista> (raw)
In-Reply-To: <20210805144246.11998-10-andrzej.p@collabora.com>

Hi Andrzej!

Dne četrtek, 05. avgust 2021 ob 16:42:45 CEST je Andrzej Pietrasiewicz 
napisal(a):
> VeriSilicon Hantro G2 core supports VP9 codec.
> 
> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
> ---
>  drivers/staging/media/hantro/Kconfig          |   1 +
>  drivers/staging/media/hantro/Makefile         |   6 +-
>  drivers/staging/media/hantro/hantro.h         |  26 +
>  drivers/staging/media/hantro/hantro_drv.c     |  18 +-
>  drivers/staging/media/hantro/hantro_g2_regs.h |  97 ++
>  .../staging/media/hantro/hantro_g2_vp9_dec.c  | 978 ++++++++++++++++++
>  drivers/staging/media/hantro/hantro_hw.h      |  67 ++
>  drivers/staging/media/hantro/hantro_v4l2.c    |   6 +
>  drivers/staging/media/hantro/hantro_vp9.c     | 240 +++++
>  drivers/staging/media/hantro/hantro_vp9.h     | 103 ++
>  drivers/staging/media/hantro/imx8m_vpu_hw.c   |  22 +-
>  11 files changed, 1560 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/staging/media/hantro/hantro_g2_vp9_dec.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.h

<snip>

> diff --git a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c b/drivers/
staging/media/hantro/hantro_g2_vp9_dec.c
> new file mode 100644
> index 000000000000..45a7be4a43fa
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_g2_vp9_dec.c
> @@ -0,0 +1,978 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +#include "media/videobuf2-core.h"
> +#include "media/videobuf2-dma-contig.h"
> +#include "media/videobuf2-v4l2.h"
> +#include <linux/kernel.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-vp9.h>
> +
> +#include "hantro.h"
> +#include "hantro_vp9.h"
> +#include "hantro_g2_regs.h"
> +
> +#define G2_ALIGN 16
> +
> +enum hantro_ref_frames {
> +	INTRA_FRAME = 0,
> +	LAST_FRAME = 1,
> +	GOLDEN_FRAME = 2,
> +	ALTREF_FRAME = 3,
> +	MAX_REF_FRAMES = 4
> +};
> +
> +static int start_prepare_run(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame **dec_params)
> +{
> +	const struct v4l2_ctrl_vp9_compressed_hdr_probs *prob_updates;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct v4l2_ctrl *ctrl;
> +	unsigned int fctx_idx;
> +
> +	/* v4l2-specific stuff */
> +	hantro_start_prepare_run(ctx);
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_FRAME);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	*dec_params = ctrl->p_cur.p;
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_COMPRESSED_HDR_PROBS);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	prob_updates = ctrl->p_cur.p;
> +
> +	/*
> +	 * vp9 stuff
> +	 *
> +	 * by this point the userspace has done all parts of 6.2 
uncompressed_header()
> +	 * except this fragment:
> +	 * if ( FrameIsIntra || error_resilient_mode ) {
> +	 *	setup_past_independence ( )
> +	 *	if ( frame_type == KEY_FRAME || error_resilient_mode == 
1 ||
> +	 *	     reset_frame_context == 3 ) {
> +	 *		for ( i = 0; i < 4; i ++ ) {
> +	 *			save_probs( i )
> +	 *		}
> +	 *	} else if ( reset_frame_context == 2 ) {
> +	 *		save_probs( frame_context_idx )
> +	 *	}
> +	 *	frame_context_idx = 0
> +	 * }
> +	 */
> +	fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx-
>frame_context);
> +	vp9_ctx->cur.frame_context_idx = fctx_idx;
> +
> +	/* 6.1 frame(sz): load_probs() and load_probs2() */
> +	vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx];
> +
> +	/*
> +	 * The userspace has also performed 6.3 compressed_header(), but 
handling the
> +	 * probs in a special way. All probs which need updating, except 
MV-related,
> +	 * have been read from the bitstream and translated through 
inv_map_table[],
> +	 * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The 
values passed
> +	 * by userspace are either translated values (there are no 0 values 
in
> +	 * inv_map_table[]), or zero to indicate no update. All MV-related 
probs which need
> +	 * updating have been read from the bitstream and (mv_prob << 1) | 
1 has been
> +	 * performed. The values passed by userspace are either new values
> +	 * to replace old ones (the above mentioned shift and bitwise or 
never result in
> +	 * a zero) or zero to indicate no update.
> +	 * fw_update_probs() performs actual probs updates or leaves probs 
as-is
> +	 * for values for which a zero was passed from userspace.
> +	 */
> +	v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, 
prob_updates, *dec_params);
> +
> +	return 0;
> +}
> +
> +static size_t chroma_offset(const struct hantro_ctx *ctx,
> +			    const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2;
> +
> +	return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel;
> +}
> +
> +static size_t mv_offset(const struct hantro_ctx *ctx,
> +			const struct v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	size_t cr_offset = chroma_offset(ctx, dec_params);
> +
> +	return ALIGN((cr_offset * 3) / 2, G2_ALIGN);
> +}
> +
> +static struct hantro_decoded_buffer *
> +get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 
timestamp)
> +{
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
> +	int buf_idx;
> +
> +	/*
> +	 * If a ref is unused or invalid, address of current destination
> +	 * buffer is returned.
> +	 */
> +	buf_idx = vb2_find_timestamp(cap_q, timestamp, 0);
> +	if (buf_idx < 0)
> +		return vb2_to_hantro_decoded_buf(&dst->vb2_buf);
> +
> +	return vb2_to_hantro_decoded_buf(vb2_get_buffer(cap_q, buf_idx));
> +}
> +
> +static void update_dec_buf_info(struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	buf->vp9.width = dec_params->frame_width_minus_1 + 1;
> +	buf->vp9.height = dec_params->frame_height_minus_1 + 1;
> +	buf->vp9.bit_depth = dec_params->bit_depth;
> +}
> +
> +static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +				struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	vp9_ctx->cur.valid = true;
> +	vp9_ctx->cur.reference_mode = dec_params->reference_mode;
> +	vp9_ctx->cur.tx_mode = dec_params->tx_mode;
> +	vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter;
> +	vp9_ctx->cur.flags = dec_params->flags;
> +	vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp;
> +}
> +
> +static void config_output(struct hantro_ctx *ctx,
> +			  struct hantro_decoded_buffer *dst,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	dma_addr_t luma_addr, chroma_addr, mv_addr;
> +
> +	hantro_reg_write(ctx->dev, &g2_out_dis, 0);
> +	hantro_reg_write(ctx->dev, &g2_output_format, 0);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_CHR, chroma_addr);
> +
> +	mv_addr = luma_addr + mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_MV, mv_addr);
> +}
> +
> +struct hantro_vp9_ref_reg {
> +	const struct hantro_reg width;
> +	const struct hantro_reg height;
> +	const struct hantro_reg hor_scale;
> +	const struct hantro_reg ver_scale;
> +	u32 y_base;
> +	u32 c_base;
> +};
> +
> +static void config_ref(struct hantro_ctx *ctx,
> +		       struct hantro_decoded_buffer *dst,
> +		       const struct hantro_vp9_ref_reg *ref_reg,
> +		       const struct v4l2_ctrl_vp9_frame *dec_params,
> +		       u64 ref_ts)
> +{
> +	struct hantro_decoded_buffer *buf;
> +	dma_addr_t luma_addr, chroma_addr;
> +	u32 refw, refh;
> +
> +	buf = get_ref_buf(ctx, &dst->base.vb, ref_ts);
> +	refw = buf->vp9.width;
> +	refh = buf->vp9.height;
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->width, refw);
> +	hantro_reg_write(ctx->dev, &ref_reg->height, refh);
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst-
>vp9.width);
> +	hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst-
>vp9.height);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr);
> +}
> +
> +static void config_ref_registers(struct hantro_ctx *ctx,
> +				 const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +				 struct hantro_decoded_buffer *dst,
> +				 struct hantro_decoded_buffer 
*mv_ref)
> +{
> +	static const struct hantro_vp9_ref_reg ref_regs[] = {
> +		{
> +			/* Last */
> +			.width = vp9_lref_width,
> +			.height = vp9_lref_height,
> +			.hor_scale = vp9_lref_hor_scale,
> +			.ver_scale = vp9_lref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(0),
> +			.c_base = G2_REG_CHR_REF(0),
> +		}, {
> +			/* Golden */
> +			.width = vp9_gref_width,
> +			.height = vp9_gref_height,
> +			.hor_scale = vp9_gref_hor_scale,
> +			.ver_scale = vp9_gref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(4),
> +			.c_base = G2_REG_CHR_REF(4),
> +		}, {
> +			/* Altref */
> +			.width = vp9_aref_width,
> +			.height = vp9_aref_height,
> +			.hor_scale = vp9_aref_hor_scale,
> +			.ver_scale = vp9_aref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(5),
> +			.c_base = G2_REG_CHR_REF(5),
> +		},
> +	};
> +	dma_addr_t mv_addr;
> +
> +	config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params-
>last_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params-
>golden_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params-
>alt_frame_ts);
> +
> +	mv_addr = vb2_dma_contig_plane_dma_addr(&mv_ref->base.vb.vb2_buf, 
0) +
> +		  mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_REG_DMV_REF(0), mv_addr);
> +
> +	hantro_reg_write(ctx->dev, &vp9_last_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_gref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_aref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0);

Not sure if I'm missing something, but V4L2_VP9_SIGN_BIAS_LAST, 
V4L2_VP9_SIGN_BIAS_GOLDEN and V4L2_VP9_SIGN_BIAS_ALT are defined in ordinary 
enum in patch 5, without any value, so they would have values 0, 1 and 2.

I guess you actually want (1 << 0), (1 << 1) and (1 << 2) in patch 5. Second 
and third flag still work by chance and first flag is always applied.

Best regards,
Jernej


> +}
> +
> +static void recompute_tile_info(unsigned short *tile_info, unsigned int 
tiles, unsigned int sbs)
> +{
> +	int i;
> +	unsigned int accumulated = 0;
> +	unsigned int next_accumulated;
> +
> +	for (i = 1; i <= tiles; ++i) {
> +		next_accumulated = i * sbs / tiles;
> +		*tile_info++ = next_accumulated - accumulated;
> +		accumulated = next_accumulated;
> +	}
> +}
> +
> +static void
> +recompute_tile_rc_info(struct hantro_ctx *ctx,
> +		       unsigned int tile_r, unsigned int tile_c,
> +		       unsigned int sbs_r, unsigned int sbs_c)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r);
> +	recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c);
> +
> +	vp9_ctx->last_tile_r = tile_r;
> +	vp9_ctx->last_tile_c = tile_c;
> +	vp9_ctx->last_sbs_r = sbs_r;
> +	vp9_ctx->last_sbs_c = sbs_c;
> +}
> +
> +static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int 
sbs_r)
> +{
> +	if (tile_r == sbs_r + 1)
> +		return 1;
> +
> +	if (tile_r == sbs_r + 2)
> +		return 2;
> +
> +	return 0;
> +}
> +
> +static void
> +fill_tile_info(struct hantro_ctx *ctx,
> +	       unsigned int tile_r, unsigned int tile_c,
> +	       unsigned int sbs_r, unsigned int sbs_c,
> +	       unsigned short *tile_mem)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int i, j;
> +	bool first = true;
> +
> +	for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) {
> +		unsigned short r_info = vp9_ctx->tile_r_info[i];
> +
> +		if (first) {
> +			if (i > 0)
> +				r_info += vp9_ctx->tile_r_info[0];
> +			if (i == 2)
> +				r_info += vp9_ctx->tile_r_info[1];
> +			first = false;
> +		}
> +		for (j = 0; j < tile_c; ++j) {
> +			*tile_mem++ = vp9_ctx->tile_c_info[j];
> +			*tile_mem++ = r_info;
> +		}
> +	}
> +}
> +
> +static void
> +config_tiles(struct hantro_ctx *ctx,
> +	     const struct v4l2_ctrl_vp9_frame *dec_params,
> +	     struct hantro_decoded_buffer *dst)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge;
> +	dma_addr_t addr;
> +	unsigned short *tile_mem;
> +
> +	addr = misc->dma + vp9_ctx->tile_info_offset;
> +	hantro_write_addr(ctx->dev, G2_ADDR_TILE_SIZE, addr);
> +
> +	tile_mem = misc->cpu + vp9_ctx->tile_info_offset;
> +	if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) {
> +		unsigned int tile_r = (1 << dec_params->tile_rows_log2);
> +		unsigned int tile_c = (1 << dec_params->tile_cols_log2);
> +		unsigned int sbs_r = hantro_vp9_num_sbs(dst-
>vp9.height);
> +		unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width);
> +
> +		if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx-
>last_tile_c ||
> +		    sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx-
>last_sbs_c)
> +			recompute_tile_rc_info(ctx, tile_r, tile_c, 
sbs_r, sbs_c);
> +
> +		fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, 
tile_mem);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, tile_c);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, tile_r);
> +
> +		addr = tile_edge->dma;
> +		hantro_write_addr(ctx->dev, G2_TILE_FILTER, addr);
> +
> +		addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset;
> +		hantro_write_addr(ctx->dev, G2_TILE_BSD, addr);
> +	} else {
> +		tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width);
> +		tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 0);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, 1);
> +	}
> +}
> +
> +static void
> +update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +		     const struct v4l2_vp9_segmentation *seg,
> +		     enum v4l2_vp9_segment_feature feature,
> +		     unsigned int segid)
> +{
> +	u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature);
> +
> +	vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid]
[feature];
> +	vp9_ctx->feature_enabled[segid] &= ~mask;
> +	vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & 
mask);
> +}
> +
> +static inline s16 clip3(s16 x, s16 y, s16 z)
> +{
> +	return (z < x) ? x : (z > y) ? y : z;
> +}
> +
> +static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 
clip)
> +{
> +	if (absolute)
> +		return feature_data;
> +
> +	return clip3(0, 255, feat_val + feature_data);
> +}
> +
> +static void config_segment(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	s16 feat_val;
> +	unsigned char feat_id;
> +	unsigned int segid;
> +	bool segment_enabled, absolute, update_data;
> +
> +	static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = 
{
> +		{ vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, 
vp9_skip_seg0 },
> +		{ vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, 
vp9_skip_seg1 },
> +		{ vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, 
vp9_skip_seg2 },
> +		{ vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, 
vp9_skip_seg3 },
> +		{ vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, 
vp9_skip_seg4 },
> +		{ vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, 
vp9_skip_seg5 },
> +		{ vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, 
vp9_skip_seg6 },
> +		{ vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, 
vp9_skip_seg7 },
> +	};
> +
> +	segment_enabled = !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_ENABLED);
> +	hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled);
> +	hantro_reg_write(ctx->dev, &vp9_segment_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP));
> +	hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE));
> +
> +	seg = &dec_params->seg;
> +	absolute = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
> +	update_data = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA);
> +
> +	for (segid = 0; segid < 8; ++segid) {
> +		/* Quantizer segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_Q;
> +		feat_val = dec_params->quant.base_q_idx;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 255);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Loop filter segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_L;
> +		feat_val = dec_params->lf.level;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 63);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Reference frame segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_REF_FRAME;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			    v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = vp9_ctx-
>feature_data[segid][feat_id] + 1;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Skip segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_SKIP;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled,
> +							     
feat_id, segid) ? 1 : 0;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +	}
> +}
> +
> +static void config_loop_filter(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	bool d = dec_params->lf.flags & 
V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED;
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level);
> +	hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params-
>lf.level == 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params-
>lf.sharpness);
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params-
>lf.ref_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params-
>lf.ref_deltas[1] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params-
>lf.ref_deltas[2] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params-
>lf.ref_deltas[3] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params-
>lf.mode_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params-
>lf.mode_deltas[1] : 0);
> +}
> +
> +static void config_picture_dimensions(struct hantro_ctx *ctx, struct 
hantro_decoded_buffer *dst)
> +{
> +	u32 pic_w_4x4, pic_h_4x4;
> +
> +	hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 
7) / 8);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height 
+ 7) / 8);
> +	pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2;
> +	pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2;
> +	hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4);
> +}
> +
> +static void
> +config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params-
>bit_depth - 8);
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params-
>bit_depth - 8);
> +}
> +
> +static inline bool is_lossless(const struct v4l2_vp9_quantization *quant)
> +{
> +	return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 &&
> +	       quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0;
> +}
> +
> +static void
> +config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params-
>quant.delta_q_y_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params-
>quant.delta_q_uv_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params-
>quant.delta_q_uv_ac);
> +	hantro_reg_write(ctx->dev, &vp9_lossless_e, 
is_lossless(&dec_params->quant));
> +}
> +
> +static u32
> +hantro_interp_filter_from_v4l2(enum v4l2_vp9_interpolation_filter 
interpolation_filter)
> +{
> +	switch (interpolation_filter) {
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP:
> +		return 0x1;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH:
> +		return 0;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP:
> +		return 0x2;
> +	case V4L2_VP9_INTERP_FILTER_BILINEAR:
> +		return 0x3;
> +	case V4L2_VP9_INTERP_FILTER_SWITCHABLE:
> +		return 0x4;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      bool intra_only, bool resolution_change)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only);
> +
> +	hantro_reg_write(ctx->dev, &vp9_transform_mode, dec_params-
>tx_mode);
> +
> +	hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ?
> +		0 : hantro_interp_filter_from_v4l2(dec_params-
>interpolation_filter));
> +
> +	hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e,
> +			 !!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV));
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params-
>reference_mode);
> +
> +	hantro_reg_write(ctx->dev, &g2_tempor_mvp_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY) &&
> +			 !resolution_change &&
> +			 vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_SHOW_FRAME
> +	);
> +
> +	hantro_reg_write(ctx->dev, &g2_write_mvs_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME));
> +}
> +
> +static void
> +config_compound_reference(struct hantro_ctx *ctx,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	u32 comp_fixed_ref, comp_var_ref[2];
> +	bool last_ref_frame_sign_bias;
> +	bool golden_ref_frame_sign_bias;
> +	bool alt_ref_frame_sign_bias;
> +	bool comp_ref_allowed = 0;
> +
> +	comp_fixed_ref = 0;
> +	comp_var_ref[0] = 0;
> +	comp_var_ref[1] = 0;
> +
> +	last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST;
> +	golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN;
> +	alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT;
> +
> +	/* 6.3.12 Frame reference mode syntax */
> +	comp_ref_allowed |= golden_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +	comp_ref_allowed |= alt_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +
> +	if (comp_ref_allowed) {
> +		if (last_ref_frame_sign_bias ==
> +		    golden_ref_frame_sign_bias) {
> +			comp_fixed_ref = ALTREF_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = GOLDEN_FRAME;
> +		} else if (last_ref_frame_sign_bias ==
> +			   alt_ref_frame_sign_bias) {
> +			comp_fixed_ref = GOLDEN_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		} else {
> +			comp_fixed_ref = LAST_FRAME;
> +			comp_var_ref[0] = GOLDEN_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		}
> +	}
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, 
comp_var_ref[0]);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, 
comp_var_ref[1]);
> +}
> +
> +#define INNER_LOOP \
> +do {								
	\
> +	for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) {	\
> +		memcpy(adaptive->coef[i][j][k][l][m],			
\
> +		       probs->coef[i][j][k][l][m],			
\
> +		       sizeof(probs->coef[i][j][k][l][m]));		
\
> +								
	\
> +		adaptive->coef[i][j][k][l][m][3] = 0;			
\
> +	}							
	\
> +} while (0)
> +
> +static void config_probs(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_g2_all_probs *all_probs = misc->cpu;
> +	struct hantro_g2_probs *adaptive;
> +	struct hantro_g2_mv_probs *mv;
> +	const struct v4l2_vp9_segmentation *seg = &dec_params->seg;
> +	const struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +	int i, j, k, l, m;
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i)
> +		for (j = 0; j < ARRAY_SIZE(all_probs-
>kf_y_mode_prob[0]); ++j) {
> +			memcpy(all_probs->kf_y_mode_prob[i][j],
> +			       v4l2_vp9_kf_y_mode_prob[i][j],
> +			       ARRAY_SIZE(all_probs-
>kf_y_mode_prob[i][j]));
> +
> +			all_probs->kf_y_mode_prob_tail[i][j][0] =
> +				v4l2_vp9_kf_y_mode_prob[i][j][8];
> +		}
> +
> +	memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs,
> +	       sizeof(all_probs->mb_segment_tree_probs));
> +
> +	memcpy(all_probs->segment_pred_probs, seg->pred_probs,
> +	       sizeof(all_probs->segment_pred_probs));
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) {
> +		memcpy(all_probs->kf_uv_mode_prob[i], 
v4l2_vp9_kf_uv_mode_prob[i],
> +		       ARRAY_SIZE(all_probs->kf_uv_mode_prob[i]));
> +
> +		all_probs->kf_uv_mode_prob_tail[i][0] = 
v4l2_vp9_kf_uv_mode_prob[i][8];
> +	}
> +
> +	adaptive = &all_probs->probs;
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) {
> +		memcpy(adaptive->inter_mode[i], probs->inter_mode[i],
> +		       sizeof(probs->inter_mode));
> +
> +		adaptive->inter_mode[i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive-
>is_inter));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) {
> +		memcpy(adaptive->uv_mode[i], probs->uv_mode[i],
> +		       sizeof(adaptive->uv_mode[i]));
> +		adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8];
> +	}
> +
> +	memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8));
> +	memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16));
> +	memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) {
> +		memcpy(adaptive->y_mode[i], probs->y_mode[i],
> +		       ARRAY_SIZE(adaptive->y_mode[i]));
> +
> +		adaptive->y_mode_tail[i][0] = probs->y_mode[i][8];
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) {
> +		memcpy(adaptive->partition[0][i], 
v4l2_vp9_kf_partition_probs[i],
> +		       sizeof(v4l2_vp9_kf_partition_probs[i]));
> +
> +		adaptive->partition[0][i][3] = 0;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) {
> +		memcpy(adaptive->partition[1][i], probs->partition[i],
> +		       sizeof(probs->partition[i]));
> +
> +		adaptive->partition[1][i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->interp_filter, probs->interp_filter,
> +	       sizeof(adaptive->interp_filter));
> +
> +	memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive-
>comp_mode));
> +
> +	memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip));
> +
> +	mv = &adaptive->mv;
> +
> +	memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint));
> +	memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign));
> +	memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv-
>class0_bit));
> +	memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr));
> +	memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp));
> +	memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp));
> +	memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes));
> +	memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr));
> +	memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits));
> +
> +	memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive-
>single_ref));
> +
> +	memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive-
>comp_ref));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i)
> +		for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(adaptive->coef[0]
[0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(adaptive->coef[0][0][0]); ++l)
> +					INNER_LOOP;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_PROBS, misc->dma);
> +}
> +
> +static void config_counts(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_CTR, addr);
> +}
> +
> +static void config_seg_map(struct hantro_ctx *ctx,
> +			   const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +			   bool intra_only, bool update_map)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map;
> +	dma_addr_t addr;
> +
> +	if (intra_only ||
> +	    (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) {
> +		memset(segment_map->cpu, 0, segment_map->size);
> +		memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx-
>feature_data));
> +		memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx-
>feature_enabled));
> +	}
> +
> +	addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_READ, addr);
> +
> +	addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_WRITE, addr);
> +
> +	if (update_map)
> +		vp9_ctx->active_segment = 1 - vp9_ctx->active_segment;
> +}
> +
> +static void
> +config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      struct vb2_v4l2_buffer *vb2_src)
> +{
> +	dma_addr_t stream_base, tmp_addr;
> +	unsigned int headres_size;
> +	u32 src_len, start_bit, src_buf_len;
> +
> +	headres_size = dec_params->uncompressed_header_size
> +		     + dec_params->compressed_header_size;
> +
> +	stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_STR, stream_base);
> +
> +	tmp_addr = stream_base + headres_size;
> +	start_bit = (tmp_addr & 0xf) * 8;
> +	hantro_reg_write(ctx->dev, &g2_start_bit, start_bit);
> +
> +	src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0);
> +	src_len += start_bit / 8 - headres_size;
> +	hantro_reg_write(ctx->dev, &g2_stream_len, src_len);
> +
> +	tmp_addr &= ~0xf;
> +	hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - 
stream_base);
> +	src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0);
> +	hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len);
> +}
> +
> +static void
> +config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +		 struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer 
*vb2_dst)
> +{
> +	struct hantro_decoded_buffer *dst, *last, *mv_ref;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	bool intra_only, resolution_change;
> +
> +	/* vp9 stuff */
> +	dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
> +
> +	if (vp9_ctx->last.valid)
> +		last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx-
>last.timestamp);
> +	else
> +		last = dst;
> +
> +	update_dec_buf_info(dst, dec_params);
> +	update_ctx_cur_info(vp9_ctx, dst, dec_params);
> +	seg = &dec_params->seg;
> +
> +	intra_only = !!(dec_params->flags &
> +			(V4L2_VP9_FRAME_FLAG_KEY_FRAME |
> +			V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
> +
> +	if (!intra_only &&
> +	    !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +	    vp9_ctx->last.valid)
> +		mv_ref = last;
> +	else
> +		mv_ref = dst;
> +
> +	resolution_change = dst->vp9.width != last->vp9.width ||
> +			    dst->vp9.height != last->vp9.height;
> +
> +	/* configure basic registers */
> +	hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE);
> +	hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128);
> +	hantro_reg_write(ctx->dev, &g2_max_burst, 16);
> +	hantro_reg_write(ctx->dev, &g2_apf_threshold, 8);
> +	hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1);
> +	hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1);
> +	hantro_reg_write(ctx->dev, &g2_max_cb_size, 6);
> +	hantro_reg_write(ctx->dev, &g2_min_cb_size, 3);
> +
> +	config_output(ctx, dst, dec_params);
> +
> +	if (!intra_only)
> +		config_ref_registers(ctx, dec_params, dst, mv_ref);
> +
> +	config_tiles(ctx, dec_params, dst);
> +	config_segment(ctx, dec_params);
> +	config_loop_filter(ctx, dec_params);
> +	config_picture_dimensions(ctx, dst);
> +	config_bit_depth(ctx, dec_params);
> +	config_quant(ctx, dec_params);
> +	config_others(ctx, dec_params, intra_only, resolution_change);
> +	config_compound_reference(ctx, dec_params);
> +	config_probs(ctx, dec_params);
> +	config_counts(ctx);
> +	config_seg_map(ctx, dec_params, intra_only,
> +		       seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP);
> +	config_source(ctx, dec_params, vb2_src);
> +}
> +
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx)
> +{
> +	const struct v4l2_ctrl_vp9_frame *decode_params;
> +	struct vb2_v4l2_buffer *src;
> +	struct vb2_v4l2_buffer *dst;
> +	int ret;
> +
> +	hantro_g2_check_idle(ctx->dev);
> +
> +	ret = start_prepare_run(ctx, &decode_params);
> +	if (ret) {
> +		hantro_end_prepare_run(ctx);
> +		return ret;
> +	}
> +
> +	src = hantro_get_src_buf(ctx);
> +	dst = hantro_get_dst_buf(ctx);
> +
> +	config_registers(ctx, decode_params, src, dst);
> +
> +	hantro_end_prepare_run(ctx);
> +
> +	vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT);
> +
> +	return 0;
> +}
> +
> +#define copy_tx_and_skip(p1, p2)				\
> +do {								
\
> +	memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8));	\
> +	memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16));	\
> +	memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32));	\
> +	memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip));	\
> +} while (0)
> +
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int fctx_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX))
> +		goto out_update_last;
> +
> +	fctx_idx = vp9_ctx->cur.frame_context_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) {
> +		/* error_resilient_mode == 0 && 
frame_parallel_decoding_mode == 0 */
> +		struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +		bool frame_is_intra = vp9_ctx->cur.flags &
> +		    (V4L2_VP9_FRAME_FLAG_KEY_FRAME | 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY);
> +		struct tx_and_skip {
> +			u8 tx8[2][1];
> +			u8 tx16[2][2];
> +			u8 tx32[2][3];
> +			u8 skip[3];
> +		} _tx_skip, *tx_skip = &_tx_skip;
> +		struct v4l2_vp9_frame_symbol_counts *counts;
> +		struct symbol_counts *hantro_cnts;
> +		u32 tx16p[2][4];
> +		int i;
> +
> +		/* buffer the forward-updated TX and skip probs */
> +		if (frame_is_intra)
> +			copy_tx_and_skip(tx_skip, probs);
> +
> +		/* 6.1.2 refresh_probs(): load_probs() and load_probs2() 
*/
> +		*probs = vp9_ctx->frame_context[fctx_idx];
> +
> +		/* if FrameIsIntra then undo the effect of load_probs2() 
*/
> +		if (frame_is_intra)
> +			copy_tx_and_skip(probs, tx_skip);
> +
> +		counts = &vp9_ctx->cnts;
> +		hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +		for (i = 0; i < ARRAY_SIZE(tx16p); ++i) {
> +			memcpy(tx16p[i],
> +			       hantro_cnts->tx16x16_count[i],
> +			       sizeof(hantro_cnts-
>tx16x16_count[0]));
> +			tx16p[i][3] = 0;
> +		}
> +		counts->tx16p = &tx16p;
> +
> +		v4l2_vp9_adapt_coef_probs(probs, counts,
> +					  !vp9_ctx->last.valid 
||
> +					  vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME,
> +					  frame_is_intra);
> +
> +		if (!frame_is_intra) {
> +			/* load_probs2() already done */
> +			u32 mv_mode[7][4];
> +
> +			for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) {
> +				mv_mode[i][0] = hantro_cnts-
>inter_mode_counts[i][1][0];
> +				mv_mode[i][1] = hantro_cnts-
>inter_mode_counts[i][2][0];
> +				mv_mode[i][2] = hantro_cnts-
>inter_mode_counts[i][0][0];
> +				mv_mode[i][3] = hantro_cnts-
>inter_mode_counts[i][2][1];
> +			}
> +			counts->mv_mode = &mv_mode;
> +			v4l2_vp9_adapt_noncoef_probs(&vp9_ctx-
>probability_tables, counts,
> +						     
vp9_ctx->cur.reference_mode,
> +						     
vp9_ctx->cur.interpolation_filter,
> +						     
vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags);
> +		}
> +	}
> +
> +	vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables;
> +
> +out_update_last:
> +	vp9_ctx->last = vp9_ctx->cur;
> +}
> diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/
media/hantro/hantro_hw.h
> index 42b3f3961f75..2961d399fd60 100644
> --- a/drivers/staging/media/hantro/hantro_hw.h
> +++ b/drivers/staging/media/hantro/hantro_hw.h
> @@ -12,6 +12,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/v4l2-controls.h>
>  #include <media/v4l2-ctrls.h>
> +#include <media/v4l2-vp9.h>
>  #include <media/videobuf2-core.h>
>  
>  #define DEC_8190_ALIGN_MASK	0x07U
> @@ -161,6 +162,50 @@ struct hantro_vp8_dec_hw_ctx {
>  	struct hantro_aux_buf prob_tbl;
>  };
>  
> +struct hantro_vp9_frame_info {
> +	u32 valid : 1;
> +	u32 frame_context_idx : 2;
> +	u32 reference_mode : 2;
> +	u32 tx_mode : 3;
> +	u32 interpolation_filter : 3;
> +	u32 flags;
> +	u64 timestamp;
> +};
> +
> +#define MAX_SB_COLS	64
> +#define MAX_SB_ROWS	34
> +
> +/**
> + * struct hantro_vp9_dec_hw_ctx
> + *
> + */
> +struct hantro_vp9_dec_hw_ctx {
> +	struct hantro_aux_buf tile_edge;
> +	struct hantro_aux_buf segment_map;
> +	struct hantro_aux_buf misc;
> +	struct v4l2_vp9_frame_symbol_counts cnts;
> +	struct v4l2_vp9_frame_context probability_tables;
> +	struct v4l2_vp9_frame_context frame_context[4];
> +	struct hantro_vp9_frame_info cur;
> +	struct hantro_vp9_frame_info last;
> +
> +	unsigned int bsd_ctrl_offset;
> +	unsigned int segment_map_size;
> +	unsigned int ctx_counters_offset;
> +	unsigned int tile_info_offset;
> +
> +	unsigned short tile_r_info[MAX_SB_ROWS];
> +	unsigned short tile_c_info[MAX_SB_COLS];
> +	unsigned int last_tile_r;
> +	unsigned int last_tile_c;
> +	unsigned int last_sbs_r;
> +	unsigned int last_sbs_c;
> +
> +	unsigned int active_segment;
> +	u8 feature_enabled[8];
> +	s16 feature_data[8][4];
> +};
> +
>  /**
>   * struct hantro_postproc_ctx
>   *
> @@ -267,6 +312,24 @@ void hantro_hevc_ref_remove_unused(struct hantro_ctx 
*ctx);
>  size_t hantro_hevc_chroma_offset(const struct v4l2_ctrl_hevc_sps *sps);
>  size_t hantro_hevc_motion_vectors_offset(const struct v4l2_ctrl_hevc_sps 
*sps);
>  
> +static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension)
> +{
> +	return (dimension + 63) / 64;
> +}
> +
> +static inline size_t
> +hantro_vp9_mv_size(unsigned int width, unsigned int height)
> +{
> +	int num_ctbs;
> +
> +	/*
> +	 * There can be up to (CTBs x 64) number of blocks,
> +	 * and the motion vector for each block needs 16 bytes.
> +	 */
> +	num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height);
> +	return (num_ctbs * 64) * 16;
> +}
> +
>  static inline size_t
>  hantro_h264_mv_size(unsigned int width, unsigned int height)
>  {
> @@ -308,6 +371,10 @@ void hantro_vp8_dec_exit(struct hantro_ctx *ctx);
>  void hantro_vp8_prob_update(struct hantro_ctx *ctx,
>  			    const struct v4l2_ctrl_vp8_frame *hdr);
>  
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx);
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx);
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx);
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx);
>  void hantro_g2_check_idle(struct hantro_dev *vpu);
>  
>  #endif /* HANTRO_HW_H_ */
> diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/
media/hantro/hantro_v4l2.c
> index d1f060c55fed..e4b0645ba6fc 100644
> --- a/drivers/staging/media/hantro/hantro_v4l2.c
> +++ b/drivers/staging/media/hantro/hantro_v4l2.c
> @@ -299,6 +299,11 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx,
>  			pix_mp->plane_fmt[0].sizeimage +=
>  				hantro_h264_mv_size(pix_mp-
>width,
>  						    pix_mp-
>height);
> +		else if (ctx->vpu_src_fmt->fourcc == 
V4L2_PIX_FMT_VP9_FRAME &&
> +			 !hantro_needs_postproc(ctx, fmt))
> +			pix_mp->plane_fmt[0].sizeimage +=
> +				hantro_vp9_mv_size(pix_mp->width,
> +						   pix_mp-
>height);
>  	} else if (!pix_mp->plane_fmt[0].sizeimage) {
>  		/*
>  		 * For coded formats the application can specify
> @@ -407,6 +412,7 @@ hantro_update_requires_request(struct hantro_ctx *ctx, 
u32 fourcc)
>  	case V4L2_PIX_FMT_VP8_FRAME:
>  	case V4L2_PIX_FMT_H264_SLICE:
>  	case V4L2_PIX_FMT_HEVC_SLICE:
> +	case V4L2_PIX_FMT_VP9_FRAME:
>  		ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true;
>  		break;
>  	default:
> diff --git a/drivers/staging/media/hantro/hantro_vp9.c b/drivers/staging/
media/hantro/hantro_vp9.c
> new file mode 100644
> index 000000000000..566cd376c097
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.c
> @@ -0,0 +1,240 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +#include <linux/types.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "hantro.h"
> +#include "hantro_hw.h"
> +#include "hantro_vp9.h"
> +
> +#define POW2(x) (1 << (x))
> +
> +#define MAX_LOG2_TILE_COLUMNS 6
> +#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS)
> +#define MAX_TILE_COLS 20
> +#define MAX_TILE_ROWS 22
> +
> +static size_t hantro_vp9_tile_filter_size(unsigned int height)
> +{
> +	u32 h, height32, size;
> +
> +	h = roundup(height, 8);
> +
> +	height32 = roundup(h, 64);
> +	size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 
8 + 8 */
> +
> +	return size;
> +}
> +
> +static size_t hantro_vp9_bsd_control_size(unsigned int height)
> +{
> +	u32 h, height32;
> +
> +	h = roundup(height, 8);
> +	height32 = roundup(h, 64);
> +
> +	return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1);
> +}
> +
> +static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int 
height)
> +{
> +	u32 w, h;
> +	int num_ctbs;
> +
> +	w = roundup(width, 8);
> +	h = roundup(height, 8);
> +	num_ctbs = ((w + 63) / 64) * ((h + 63) / 64);
> +
> +	return num_ctbs * 32;
> +}
> +
> +static inline size_t hantro_vp9_prob_tab_size(void)
> +{
> +	return roundup(sizeof(struct hantro_g2_all_probs), 16);
> +}
> +
> +static inline size_t hantro_vp9_count_tab_size(void)
> +{
> +	return roundup(sizeof(struct symbol_counts), 16);
> +}
> +
> +static inline size_t hantro_vp9_tile_info_size(void)
> +{
> +	return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 
15 + 16) & ~0xf, 16);
> +}
> +
> +static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, 
int l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m];
> +
> +	return NULL;
> +}
> +
> +static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int 
l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m][3];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m][3];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m][3];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m][3];
> +
> +	return NULL;
> +}
> +
> +#define INNER_LOOP \
> +	do {							
			\
> +		for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]
[0]); ++m) {	\
> +			vp9_ctx->cnts.coeff[i][j][k][l][m] =		
		\
> +				get_coeffs_arr(cnts, i, j, k, l, 
m);			\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][0] =		
		\
> +				&cnts->count_eobs[i][j][k][l][m];	
		\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][1] =		
		\
> +				get_eobs1(cnts, i, j, k, l, m);	
			\
> +		}						
			\
> +	} while (0)
> +
> +static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +	int i, j, k, l, m;
> +
> +	vp9_ctx->cnts.partition = &cnts->partition_counts;
> +	vp9_ctx->cnts.skip = &cnts->mbskip_count;
> +	vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count;
> +	vp9_ctx->cnts.tx32p = &cnts->tx32x32_count;
> +	/*
> +	 * g2 hardware uses tx16x16_count[2][3], while the api
> +	 * expects tx16p[2][4], so this must be explicitly copied
> +	 * into vp9_ctx->cnts.tx16p when passing the data to the
> +	 * vp9 library function
> +	 */
> +	vp9_ctx->cnts.tx8p = &cnts->tx8x8_count;
> +
> +	vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts;
> +	vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts;
> +	vp9_ctx->cnts.comp = &cnts->comp_inter_count;
> +	vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count;
> +	vp9_ctx->cnts.single_ref = &cnts->single_ref_count;
> +	vp9_ctx->cnts.filter = &cnts->switchable_interp_counts;
> +	vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints;
> +	vp9_ctx->cnts.sign = &cnts->mv_counts.sign;
> +	vp9_ctx->cnts.classes = &cnts->mv_counts.classes;
> +	vp9_ctx->cnts.class0 = &cnts->mv_counts.class0;
> +	vp9_ctx->cnts.bits = &cnts->mv_counts.bits;
> +	vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp;
> +	vp9_ctx->cnts.fp = &cnts->mv_counts.fp;
> +	vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp;
> +	vp9_ctx->cnts.hp = &cnts->mv_counts.hp;
> +
> +	for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i)
> +		for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(vp9_ctx-
>cnts.coeff[i][0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l)
> +					INNER_LOOP;
> +}
> +
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	const struct hantro_variant *variant = vpu->variant;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	u32 i, max_width, max_height, size;
> +
> +	if (variant->num_dec_fmts < 1)
> +		return -EINVAL;
> +
> +	for (i = 0; i < variant->num_dec_fmts; ++i)
> +		if (variant->dec_fmts[i].fourcc == 
V4L2_PIX_FMT_VP9_FRAME)
> +			break;
> +
> +	if (i == variant->num_dec_fmts)
> +		return -EINVAL;
> +
> +	max_width = vpu->variant->dec_fmts[i].frmsize.max_width;
> +	max_height = vpu->variant->dec_fmts[i].frmsize.max_height;
> +
> +	size = hantro_vp9_tile_filter_size(max_height);
> +	vp9_dec->bsd_ctrl_offset = size;
> +	size += hantro_vp9_bsd_control_size(max_height);
> +
> +	tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge-
>dma, GFP_KERNEL);
> +	if (!tile_edge->cpu)
> +		return -ENOMEM;
> +
> +	tile_edge->size = size;
> +	memset(tile_edge->cpu, 0, size);
> +
> +	size = hantro_vp9_segment_map_size(max_width, max_height);
> +	vp9_dec->segment_map_size = size;
> +	size *= 2; /* we need two areas of this size, used alternately */
> +
> +	segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map-
>dma, GFP_KERNEL);
> +	if (!segment_map->cpu)
> +		goto err_segment_map;
> +
> +	segment_map->size = size;
> +	memset(segment_map->cpu, 0, size);
> +
> +	size = hantro_vp9_prob_tab_size();
> +	vp9_dec->ctx_counters_offset = size;
> +	size += hantro_vp9_count_tab_size();
> +	vp9_dec->tile_info_offset = size;
> +	size += hantro_vp9_tile_info_size();
> +
> +	misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, 
GFP_KERNEL);
> +	if (!misc->cpu)
> +		goto err_misc;
> +
> +	misc->size = size;
> +	memset(misc->cpu, 0, size);
> +
> +	init_v4l2_vp9_count_tbl(ctx);
> +
> +	return 0;
> +
> +err_misc:
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +
> +err_segment_map:
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +
> +	return -ENOMEM;
> +}
> +
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +
> +	dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma);
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +}
> diff --git a/drivers/staging/media/hantro/hantro_vp9.h b/drivers/staging/
media/hantro/hantro_vp9.h
> new file mode 100644
> index 000000000000..c7f4bd3ff8dd
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +struct hantro_g2_mv_probs {
> +	u8 joint[3];
> +	u8 sign[2];
> +	u8 class0_bit[2][1];
> +	u8 fr[2][3];
> +	u8 class0_hp[2];
> +	u8 hp[2];
> +	u8 classes[2][10];
> +	u8 class0_fr[2][2][3];
> +	u8 bits[2][10];
> +};
> +
> +struct hantro_g2_probs {
> +	u8 inter_mode[7][4];
> +	u8 is_inter[4];
> +	u8 uv_mode[10][8];
> +	u8 tx8[2][1];
> +	u8 tx16[2][2];
> +	u8 tx32[2][3];
> +	u8 y_mode_tail[4][1];
> +	u8 y_mode[4][8];
> +	u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */
> +	u8 uv_mode_tail[10][1];
> +	u8 interp_filter[4][2];
> +	u8 comp_mode[5];
> +	u8 skip[3];
> +
> +	u8 pad1[1];
> +
> +	struct hantro_g2_mv_probs mv;
> +
> +	u8 single_ref[5][2];
> +	u8 comp_ref[5];
> +
> +	u8 pad2[17];
> +
> +	u8 coef[4][2][2][6][6][4];
> +};
> +
> +struct hantro_g2_all_probs {
> +	u8 kf_y_mode_prob[10][10][8];
> +
> +	u8 kf_y_mode_prob_tail[10][10][1];
> +	u8 ref_pred_probs[3];
> +	u8 mb_segment_tree_probs[7];
> +	u8 segment_pred_probs[3];
> +	u8 ref_scores[4];
> +	u8 prob_comppred[2];
> +
> +	u8 pad1[9];
> +
> +	u8 kf_uv_mode_prob[10][8];
> +	u8 kf_uv_mode_prob_tail[10][1];
> +
> +	u8 pad2[6];
> +
> +	struct hantro_g2_probs probs;
> +};
> +
> +struct mv_counts {
> +	u32 joints[4];
> +	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 symbol_counts {
> +	u32 inter_mode_counts[7][3][2];
> +	u32 sb_ymode_counts[4][10];
> +	u32 uv_mode_counts[10][10];
> +	u32 partition_counts[16][4];
> +	u32 switchable_interp_counts[4][3];
> +	u32 intra_inter_count[4][2];
> +	u32 comp_inter_count[5][2];
> +	u32 single_ref_count[5][2][2];
> +	u32 comp_ref_count[5][2];
> +	u32 tx32x32_count[2][4];
> +	u32 tx16x16_count[2][3];
> +	u32 tx8x8_count[2][2];
> +	u32 mbskip_count[3][2];
> +
> +	struct mv_counts mv_counts;
> +
> +	u32 count_coeffs[2][2][6][6][4];
> +	u32 count_coeffs8x8[2][2][6][6][4];
> +	u32 count_coeffs16x16[2][2][6][6][4];
> +	u32 count_coeffs32x32[2][2][6][6][4];
> +
> +	u32 count_eobs[4][2][2][6][6];
> +};
> +
> diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/
media/hantro/imx8m_vpu_hw.c
> index a40b161e5956..455a107ffb02 100644
> --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c
> +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c
> @@ -150,6 +150,19 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] 
= {
>  			.step_height = MB_DIM,
>  		},
>  	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_VP9_FRAME,
> +		.codec_mode = HANTRO_MODE_VP9_DEC,
> +		.max_depth = 2,
> +		.frmsize = {
> +			.min_width = 48,
> +			.max_width = 3840,
> +			.step_width = MB_DIM,
> +			.min_height = 48,
> +			.max_height = 2160,
> +			.step_height = MB_DIM,
> +		},
> +	},
>  };
>  
>  static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id)
> @@ -241,6 +254,13 @@ static const struct hantro_codec_ops 
imx8mq_vpu_g2_codec_ops[] = {
>  		.init = hantro_hevc_dec_init,
>  		.exit = hantro_hevc_dec_exit,
>  	},
> +	[HANTRO_MODE_VP9_DEC] = {
> +		.run = hantro_g2_vp9_dec_run,
> +		.done = hantro_g2_vp9_dec_done,
> +		.reset = imx8m_vpu_g2_reset,
> +		.init = hantro_vp9_dec_init,
> +		.exit = hantro_vp9_dec_exit,
> +	},
>  };
>  
>  /*
> @@ -281,7 +301,7 @@ const struct hantro_variant imx8mq_vpu_g2_variant = {
>  	.dec_offset = 0x0,
>  	.dec_fmts = imx8m_vpu_g2_dec_fmts,
>  	.num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts),
> -	.codec = HANTRO_HEVC_DECODER,
> +	.codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER,
>  	.codec_ops = imx8mq_vpu_g2_codec_ops,
>  	.init = imx8mq_vpu_hw_init,
>  	.runtime_resume = imx8mq_runtime_resume,
> -- 
> 2.17.1
> 
> 



_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

WARNING: multiple messages have this Message-ID (diff)
From: "Jernej Škrabec" <jernej.skrabec@gmail.com>
To: linux-media@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org,
	linux-staging@lists.linux.dev,
	Andrzej Pietrasiewicz <andrzej.p@collabora.com>
Cc: Andrzej Pietrasiewicz <andrzej.p@collabora.com>,
	Benjamin Gaignard <benjamin.gaignard@collabora.com>,
	Boris Brezillon <boris.brezillon@collabora.com>,
	Ezequiel Garcia <elezegarcia@gmail.com>,
	Fabio Estevam <festevam@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Hans Verkuil <hverkuil-cisco@xs4all.nl>,
	Heiko Stuebner <heiko@sntech.de>,
	Mauro Carvalho Chehab <mchehab@kernel.org>,
	Nicolas Dufresne <nicolas.dufresne@collabora.com>,
	NXP Linux Team <linux-imx@nxp.com>,
	Pengutronix Kernel Team <kernel@pengutronix.de>,
	Philipp Zabel <p.zabel@pengutronix.de>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Shawn Guo <shawnguo@kernel.org>,
	kernel@collabora.com
Subject: Re: [PATCH v3 09/10] media: hantro: Support VP9 on the G2 core
Date: Mon, 09 Aug 2021 19:07:35 +0200	[thread overview]
Message-ID: <1771240.sZXMzDhkfG@kista> (raw)
In-Reply-To: <20210805144246.11998-10-andrzej.p@collabora.com>

Hi Andrzej!

Dne četrtek, 05. avgust 2021 ob 16:42:45 CEST je Andrzej Pietrasiewicz 
napisal(a):
> VeriSilicon Hantro G2 core supports VP9 codec.
> 
> Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@collabora.com>
> ---
>  drivers/staging/media/hantro/Kconfig          |   1 +
>  drivers/staging/media/hantro/Makefile         |   6 +-
>  drivers/staging/media/hantro/hantro.h         |  26 +
>  drivers/staging/media/hantro/hantro_drv.c     |  18 +-
>  drivers/staging/media/hantro/hantro_g2_regs.h |  97 ++
>  .../staging/media/hantro/hantro_g2_vp9_dec.c  | 978 ++++++++++++++++++
>  drivers/staging/media/hantro/hantro_hw.h      |  67 ++
>  drivers/staging/media/hantro/hantro_v4l2.c    |   6 +
>  drivers/staging/media/hantro/hantro_vp9.c     | 240 +++++
>  drivers/staging/media/hantro/hantro_vp9.h     | 103 ++
>  drivers/staging/media/hantro/imx8m_vpu_hw.c   |  22 +-
>  11 files changed, 1560 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/staging/media/hantro/hantro_g2_vp9_dec.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.c
>  create mode 100644 drivers/staging/media/hantro/hantro_vp9.h

<snip>

> diff --git a/drivers/staging/media/hantro/hantro_g2_vp9_dec.c b/drivers/
staging/media/hantro/hantro_g2_vp9_dec.c
> new file mode 100644
> index 000000000000..45a7be4a43fa
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_g2_vp9_dec.c
> @@ -0,0 +1,978 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +#include "media/videobuf2-core.h"
> +#include "media/videobuf2-dma-contig.h"
> +#include "media/videobuf2-v4l2.h"
> +#include <linux/kernel.h>
> +#include <linux/vmalloc.h>
> +#include <media/v4l2-mem2mem.h>
> +#include <media/v4l2-vp9.h>
> +
> +#include "hantro.h"
> +#include "hantro_vp9.h"
> +#include "hantro_g2_regs.h"
> +
> +#define G2_ALIGN 16
> +
> +enum hantro_ref_frames {
> +	INTRA_FRAME = 0,
> +	LAST_FRAME = 1,
> +	GOLDEN_FRAME = 2,
> +	ALTREF_FRAME = 3,
> +	MAX_REF_FRAMES = 4
> +};
> +
> +static int start_prepare_run(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame **dec_params)
> +{
> +	const struct v4l2_ctrl_vp9_compressed_hdr_probs *prob_updates;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct v4l2_ctrl *ctrl;
> +	unsigned int fctx_idx;
> +
> +	/* v4l2-specific stuff */
> +	hantro_start_prepare_run(ctx);
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_FRAME);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	*dec_params = ctrl->p_cur.p;
> +
> +	ctrl = v4l2_ctrl_find(&ctx->ctrl_handler, 
V4L2_CID_STATELESS_VP9_COMPRESSED_HDR_PROBS);
> +	if (WARN_ON(!ctrl))
> +		return -EINVAL;
> +	prob_updates = ctrl->p_cur.p;
> +
> +	/*
> +	 * vp9 stuff
> +	 *
> +	 * by this point the userspace has done all parts of 6.2 
uncompressed_header()
> +	 * except this fragment:
> +	 * if ( FrameIsIntra || error_resilient_mode ) {
> +	 *	setup_past_independence ( )
> +	 *	if ( frame_type == KEY_FRAME || error_resilient_mode == 
1 ||
> +	 *	     reset_frame_context == 3 ) {
> +	 *		for ( i = 0; i < 4; i ++ ) {
> +	 *			save_probs( i )
> +	 *		}
> +	 *	} else if ( reset_frame_context == 2 ) {
> +	 *		save_probs( frame_context_idx )
> +	 *	}
> +	 *	frame_context_idx = 0
> +	 * }
> +	 */
> +	fctx_idx = v4l2_vp9_reset_frame_ctx(*dec_params, vp9_ctx-
>frame_context);
> +	vp9_ctx->cur.frame_context_idx = fctx_idx;
> +
> +	/* 6.1 frame(sz): load_probs() and load_probs2() */
> +	vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx];
> +
> +	/*
> +	 * The userspace has also performed 6.3 compressed_header(), but 
handling the
> +	 * probs in a special way. All probs which need updating, except 
MV-related,
> +	 * have been read from the bitstream and translated through 
inv_map_table[],
> +	 * but no 6.3.6 inv_recenter_nonneg(v, m) has been performed. The 
values passed
> +	 * by userspace are either translated values (there are no 0 values 
in
> +	 * inv_map_table[]), or zero to indicate no update. All MV-related 
probs which need
> +	 * updating have been read from the bitstream and (mv_prob << 1) | 
1 has been
> +	 * performed. The values passed by userspace are either new values
> +	 * to replace old ones (the above mentioned shift and bitwise or 
never result in
> +	 * a zero) or zero to indicate no update.
> +	 * fw_update_probs() performs actual probs updates or leaves probs 
as-is
> +	 * for values for which a zero was passed from userspace.
> +	 */
> +	v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables, 
prob_updates, *dec_params);
> +
> +	return 0;
> +}
> +
> +static size_t chroma_offset(const struct hantro_ctx *ctx,
> +			    const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	int bytes_per_pixel = dec_params->bit_depth == 8 ? 1 : 2;
> +
> +	return ctx->src_fmt.width * ctx->src_fmt.height * bytes_per_pixel;
> +}
> +
> +static size_t mv_offset(const struct hantro_ctx *ctx,
> +			const struct v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	size_t cr_offset = chroma_offset(ctx, dec_params);
> +
> +	return ALIGN((cr_offset * 3) / 2, G2_ALIGN);
> +}
> +
> +static struct hantro_decoded_buffer *
> +get_ref_buf(struct hantro_ctx *ctx, struct vb2_v4l2_buffer *dst, u64 
timestamp)
> +{
> +	struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
> +	struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
> +	int buf_idx;
> +
> +	/*
> +	 * If a ref is unused or invalid, address of current destination
> +	 * buffer is returned.
> +	 */
> +	buf_idx = vb2_find_timestamp(cap_q, timestamp, 0);
> +	if (buf_idx < 0)
> +		return vb2_to_hantro_decoded_buf(&dst->vb2_buf);
> +
> +	return vb2_to_hantro_decoded_buf(vb2_get_buffer(cap_q, buf_idx));
> +}
> +
> +static void update_dec_buf_info(struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	buf->vp9.width = dec_params->frame_width_minus_1 + 1;
> +	buf->vp9.height = dec_params->frame_height_minus_1 + 1;
> +	buf->vp9.bit_depth = dec_params->bit_depth;
> +}
> +
> +static void update_ctx_cur_info(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +				struct hantro_decoded_buffer *buf,
> +				const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	vp9_ctx->cur.valid = true;
> +	vp9_ctx->cur.reference_mode = dec_params->reference_mode;
> +	vp9_ctx->cur.tx_mode = dec_params->tx_mode;
> +	vp9_ctx->cur.interpolation_filter = dec_params->interpolation_filter;
> +	vp9_ctx->cur.flags = dec_params->flags;
> +	vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp;
> +}
> +
> +static void config_output(struct hantro_ctx *ctx,
> +			  struct hantro_decoded_buffer *dst,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	dma_addr_t luma_addr, chroma_addr, mv_addr;
> +
> +	hantro_reg_write(ctx->dev, &g2_out_dis, 0);
> +	hantro_reg_write(ctx->dev, &g2_output_format, 0);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&dst->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_CHR, chroma_addr);
> +
> +	mv_addr = luma_addr + mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_ADDR_DST_MV, mv_addr);
> +}
> +
> +struct hantro_vp9_ref_reg {
> +	const struct hantro_reg width;
> +	const struct hantro_reg height;
> +	const struct hantro_reg hor_scale;
> +	const struct hantro_reg ver_scale;
> +	u32 y_base;
> +	u32 c_base;
> +};
> +
> +static void config_ref(struct hantro_ctx *ctx,
> +		       struct hantro_decoded_buffer *dst,
> +		       const struct hantro_vp9_ref_reg *ref_reg,
> +		       const struct v4l2_ctrl_vp9_frame *dec_params,
> +		       u64 ref_ts)
> +{
> +	struct hantro_decoded_buffer *buf;
> +	dma_addr_t luma_addr, chroma_addr;
> +	u32 refw, refh;
> +
> +	buf = get_ref_buf(ctx, &dst->base.vb, ref_ts);
> +	refw = buf->vp9.width;
> +	refh = buf->vp9.height;
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->width, refw);
> +	hantro_reg_write(ctx->dev, &ref_reg->height, refh);
> +
> +	hantro_reg_write(ctx->dev, &ref_reg->hor_scale, (refw << 14) / dst-
>vp9.width);
> +	hantro_reg_write(ctx->dev, &ref_reg->ver_scale, (refh << 14) / dst-
>vp9.height);
> +
> +	luma_addr = vb2_dma_contig_plane_dma_addr(&buf->base.vb.vb2_buf, 
0);
> +	hantro_write_addr(ctx->dev, ref_reg->y_base, luma_addr);
> +
> +	chroma_addr = luma_addr + chroma_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, ref_reg->c_base, chroma_addr);
> +}
> +
> +static void config_ref_registers(struct hantro_ctx *ctx,
> +				 const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +				 struct hantro_decoded_buffer *dst,
> +				 struct hantro_decoded_buffer 
*mv_ref)
> +{
> +	static const struct hantro_vp9_ref_reg ref_regs[] = {
> +		{
> +			/* Last */
> +			.width = vp9_lref_width,
> +			.height = vp9_lref_height,
> +			.hor_scale = vp9_lref_hor_scale,
> +			.ver_scale = vp9_lref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(0),
> +			.c_base = G2_REG_CHR_REF(0),
> +		}, {
> +			/* Golden */
> +			.width = vp9_gref_width,
> +			.height = vp9_gref_height,
> +			.hor_scale = vp9_gref_hor_scale,
> +			.ver_scale = vp9_gref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(4),
> +			.c_base = G2_REG_CHR_REF(4),
> +		}, {
> +			/* Altref */
> +			.width = vp9_aref_width,
> +			.height = vp9_aref_height,
> +			.hor_scale = vp9_aref_hor_scale,
> +			.ver_scale = vp9_aref_ver_scale,
> +			.y_base = G2_REG_ADDR_REF(5),
> +			.c_base = G2_REG_CHR_REF(5),
> +		},
> +	};
> +	dma_addr_t mv_addr;
> +
> +	config_ref(ctx, dst, &ref_regs[0], dec_params, dec_params-
>last_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[1], dec_params, dec_params-
>golden_frame_ts);
> +	config_ref(ctx, dst, &ref_regs[2], dec_params, dec_params-
>alt_frame_ts);
> +
> +	mv_addr = vb2_dma_contig_plane_dma_addr(&mv_ref->base.vb.vb2_buf, 
0) +
> +		  mv_offset(ctx, dec_params);
> +	hantro_write_addr(ctx->dev, G2_REG_DMV_REF(0), mv_addr);
> +
> +	hantro_reg_write(ctx->dev, &vp9_last_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_gref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN ? 1 : 0);
> +
> +	hantro_reg_write(ctx->dev, &vp9_aref_sign_bias,
> +			 dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT ? 1 : 0);

Not sure if I'm missing something, but V4L2_VP9_SIGN_BIAS_LAST, 
V4L2_VP9_SIGN_BIAS_GOLDEN and V4L2_VP9_SIGN_BIAS_ALT are defined in ordinary 
enum in patch 5, without any value, so they would have values 0, 1 and 2.

I guess you actually want (1 << 0), (1 << 1) and (1 << 2) in patch 5. Second 
and third flag still work by chance and first flag is always applied.

Best regards,
Jernej


> +}
> +
> +static void recompute_tile_info(unsigned short *tile_info, unsigned int 
tiles, unsigned int sbs)
> +{
> +	int i;
> +	unsigned int accumulated = 0;
> +	unsigned int next_accumulated;
> +
> +	for (i = 1; i <= tiles; ++i) {
> +		next_accumulated = i * sbs / tiles;
> +		*tile_info++ = next_accumulated - accumulated;
> +		accumulated = next_accumulated;
> +	}
> +}
> +
> +static void
> +recompute_tile_rc_info(struct hantro_ctx *ctx,
> +		       unsigned int tile_r, unsigned int tile_c,
> +		       unsigned int sbs_r, unsigned int sbs_c)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	recompute_tile_info(vp9_ctx->tile_r_info, tile_r, sbs_r);
> +	recompute_tile_info(vp9_ctx->tile_c_info, tile_c, sbs_c);
> +
> +	vp9_ctx->last_tile_r = tile_r;
> +	vp9_ctx->last_tile_c = tile_c;
> +	vp9_ctx->last_sbs_r = sbs_r;
> +	vp9_ctx->last_sbs_c = sbs_c;
> +}
> +
> +static inline unsigned int first_tile_row(unsigned int tile_r, unsigned int 
sbs_r)
> +{
> +	if (tile_r == sbs_r + 1)
> +		return 1;
> +
> +	if (tile_r == sbs_r + 2)
> +		return 2;
> +
> +	return 0;
> +}
> +
> +static void
> +fill_tile_info(struct hantro_ctx *ctx,
> +	       unsigned int tile_r, unsigned int tile_c,
> +	       unsigned int sbs_r, unsigned int sbs_c,
> +	       unsigned short *tile_mem)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int i, j;
> +	bool first = true;
> +
> +	for (i = first_tile_row(tile_r, sbs_r); i < tile_r; ++i) {
> +		unsigned short r_info = vp9_ctx->tile_r_info[i];
> +
> +		if (first) {
> +			if (i > 0)
> +				r_info += vp9_ctx->tile_r_info[0];
> +			if (i == 2)
> +				r_info += vp9_ctx->tile_r_info[1];
> +			first = false;
> +		}
> +		for (j = 0; j < tile_c; ++j) {
> +			*tile_mem++ = vp9_ctx->tile_c_info[j];
> +			*tile_mem++ = r_info;
> +		}
> +	}
> +}
> +
> +static void
> +config_tiles(struct hantro_ctx *ctx,
> +	     const struct v4l2_ctrl_vp9_frame *dec_params,
> +	     struct hantro_decoded_buffer *dst)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_aux_buf *tile_edge = &vp9_ctx->tile_edge;
> +	dma_addr_t addr;
> +	unsigned short *tile_mem;
> +
> +	addr = misc->dma + vp9_ctx->tile_info_offset;
> +	hantro_write_addr(ctx->dev, G2_ADDR_TILE_SIZE, addr);
> +
> +	tile_mem = misc->cpu + vp9_ctx->tile_info_offset;
> +	if (dec_params->tile_cols_log2 || dec_params->tile_rows_log2) {
> +		unsigned int tile_r = (1 << dec_params->tile_rows_log2);
> +		unsigned int tile_c = (1 << dec_params->tile_cols_log2);
> +		unsigned int sbs_r = hantro_vp9_num_sbs(dst-
>vp9.height);
> +		unsigned int sbs_c = hantro_vp9_num_sbs(dst->vp9.width);
> +
> +		if (tile_r != vp9_ctx->last_tile_r || tile_c != vp9_ctx-
>last_tile_c ||
> +		    sbs_r != vp9_ctx->last_sbs_r || sbs_c != vp9_ctx-
>last_sbs_c)
> +			recompute_tile_rc_info(ctx, tile_r, tile_c, 
sbs_r, sbs_c);
> +
> +		fill_tile_info(ctx, tile_r, tile_c, sbs_r, sbs_c, 
tile_mem);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, tile_c);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, tile_r);
> +
> +		addr = tile_edge->dma;
> +		hantro_write_addr(ctx->dev, G2_TILE_FILTER, addr);
> +
> +		addr = tile_edge->dma + vp9_ctx->bsd_ctrl_offset;
> +		hantro_write_addr(ctx->dev, G2_TILE_BSD, addr);
> +	} else {
> +		tile_mem[0] = hantro_vp9_num_sbs(dst->vp9.width);
> +		tile_mem[1] = hantro_vp9_num_sbs(dst->vp9.height);
> +
> +		hantro_reg_write(ctx->dev, &g2_tile_e, 0);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_cols, 1);
> +		hantro_reg_write(ctx->dev, &g2_num_tile_rows, 1);
> +	}
> +}
> +
> +static void
> +update_feat_and_flag(struct hantro_vp9_dec_hw_ctx *vp9_ctx,
> +		     const struct v4l2_vp9_segmentation *seg,
> +		     enum v4l2_vp9_segment_feature feature,
> +		     unsigned int segid)
> +{
> +	u8 mask = V4L2_VP9_SEGMENT_FEATURE_ENABLED(feature);
> +
> +	vp9_ctx->feature_data[segid][feature] = seg->feature_data[segid]
[feature];
> +	vp9_ctx->feature_enabled[segid] &= ~mask;
> +	vp9_ctx->feature_enabled[segid] |= (seg->feature_enabled[segid] & 
mask);
> +}
> +
> +static inline s16 clip3(s16 x, s16 y, s16 z)
> +{
> +	return (z < x) ? x : (z > y) ? y : z;
> +}
> +
> +static s16 feat_val_clip3(s16 feat_val, s16 feature_data, bool absolute, u8 
clip)
> +{
> +	if (absolute)
> +		return feature_data;
> +
> +	return clip3(0, 255, feat_val + feature_data);
> +}
> +
> +static void config_segment(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	s16 feat_val;
> +	unsigned char feat_id;
> +	unsigned int segid;
> +	bool segment_enabled, absolute, update_data;
> +
> +	static const struct hantro_reg seg_regs[8][V4L2_VP9_SEG_LVL_MAX] = 
{
> +		{ vp9_quant_seg0, vp9_filt_level_seg0, vp9_refpic_seg0, 
vp9_skip_seg0 },
> +		{ vp9_quant_seg1, vp9_filt_level_seg1, vp9_refpic_seg1, 
vp9_skip_seg1 },
> +		{ vp9_quant_seg2, vp9_filt_level_seg2, vp9_refpic_seg2, 
vp9_skip_seg2 },
> +		{ vp9_quant_seg3, vp9_filt_level_seg3, vp9_refpic_seg3, 
vp9_skip_seg3 },
> +		{ vp9_quant_seg4, vp9_filt_level_seg4, vp9_refpic_seg4, 
vp9_skip_seg4 },
> +		{ vp9_quant_seg5, vp9_filt_level_seg5, vp9_refpic_seg5, 
vp9_skip_seg5 },
> +		{ vp9_quant_seg6, vp9_filt_level_seg6, vp9_refpic_seg6, 
vp9_skip_seg6 },
> +		{ vp9_quant_seg7, vp9_filt_level_seg7, vp9_refpic_seg7, 
vp9_skip_seg7 },
> +	};
> +
> +	segment_enabled = !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_ENABLED);
> +	hantro_reg_write(ctx->dev, &vp9_segment_e, segment_enabled);
> +	hantro_reg_write(ctx->dev, &vp9_segment_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP));
> +	hantro_reg_write(ctx->dev, &vp9_segment_temp_upd_e,
> +			 !!(dec_params->seg.flags & 
V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE));
> +
> +	seg = &dec_params->seg;
> +	absolute = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
> +	update_data = !!(seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA);
> +
> +	for (segid = 0; segid < 8; ++segid) {
> +		/* Quantizer segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_Q;
> +		feat_val = dec_params->quant.base_q_idx;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 255);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Loop filter segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_ALT_L;
> +		feat_val = dec_params->lf.level;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = 
feat_val_clip3(feat_val,
> +							  
vp9_ctx->feature_data[segid][feat_id],
> +							  
absolute, 63);
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Reference frame segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_REF_FRAME;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			if (!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			    v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled, feat_id, segid))
> +				feat_val = vp9_ctx-
>feature_data[segid][feat_id] + 1;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +
> +		/* Skip segment feature */
> +		feat_id = V4L2_VP9_SEG_LVL_SKIP;
> +		feat_val = 0;
> +		if (segment_enabled) {
> +			if (update_data)
> +				update_feat_and_flag(vp9_ctx, seg, 
feat_id, segid);
> +			feat_val = v4l2_vp9_seg_feat_enabled(vp9_ctx-
>feature_enabled,
> +							     
feat_id, segid) ? 1 : 0;
> +		}
> +		hantro_reg_write(ctx->dev, &seg_regs[segid][feat_id], 
feat_val);
> +	}
> +}
> +
> +static void config_loop_filter(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	bool d = dec_params->lf.flags & 
V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED;
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_level, dec_params->lf.level);
> +	hantro_reg_write(ctx->dev, &g2_out_filtering_dis, dec_params-
>lf.level == 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_sharpness, dec_params-
>lf.sharpness);
> +
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_0, d ? dec_params-
>lf.ref_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_1, d ? dec_params-
>lf.ref_deltas[1] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_2, d ? dec_params-
>lf.ref_deltas[2] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_ref_adj_3, d ? dec_params-
>lf.ref_deltas[3] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_0, d ? dec_params-
>lf.mode_deltas[0] : 0);
> +	hantro_reg_write(ctx->dev, &vp9_filt_mb_adj_1, d ? dec_params-
>lf.mode_deltas[1] : 0);
> +}
> +
> +static void config_picture_dimensions(struct hantro_ctx *ctx, struct 
hantro_decoded_buffer *dst)
> +{
> +	u32 pic_w_4x4, pic_h_4x4;
> +
> +	hantro_reg_write(ctx->dev, &g2_pic_width_in_cbs, (dst->vp9.width + 
7) / 8);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_in_cbs, (dst->vp9.height 
+ 7) / 8);
> +	pic_w_4x4 = roundup(dst->vp9.width, 8) >> 2;
> +	pic_h_4x4 = roundup(dst->vp9.height, 8) >> 2;
> +	hantro_reg_write(ctx->dev, &g2_pic_width_4x4, pic_w_4x4);
> +	hantro_reg_write(ctx->dev, &g2_pic_height_4x4, pic_h_4x4);
> +}
> +
> +static void
> +config_bit_depth(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_y_minus8, dec_params-
>bit_depth - 8);
> +	hantro_reg_write(ctx->dev, &g2_bit_depth_c_minus8, dec_params-
>bit_depth - 8);
> +}
> +
> +static inline bool is_lossless(const struct v4l2_vp9_quantization *quant)
> +{
> +	return quant->base_q_idx == 0 && quant->delta_q_uv_ac == 0 &&
> +	       quant->delta_q_uv_dc == 0 && quant->delta_q_y_dc == 0;
> +}
> +
> +static void
> +config_quant(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_y_dc, dec_params-
>quant.delta_q_y_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_dc, dec_params-
>quant.delta_q_uv_dc);
> +	hantro_reg_write(ctx->dev, &vp9_qp_delta_ch_ac, dec_params-
>quant.delta_q_uv_ac);
> +	hantro_reg_write(ctx->dev, &vp9_lossless_e, 
is_lossless(&dec_params->quant));
> +}
> +
> +static u32
> +hantro_interp_filter_from_v4l2(enum v4l2_vp9_interpolation_filter 
interpolation_filter)
> +{
> +	switch (interpolation_filter) {
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP:
> +		return 0x1;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SMOOTH:
> +		return 0;
> +	case V4L2_VP9_INTERP_FILTER_EIGHTTAP_SHARP:
> +		return 0x2;
> +	case V4L2_VP9_INTERP_FILTER_BILINEAR:
> +		return 0x3;
> +	case V4L2_VP9_INTERP_FILTER_SWITCHABLE:
> +		return 0x4;
> +	}
> +
> +	return 0;
> +}
> +
> +static void
> +config_others(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      bool intra_only, bool resolution_change)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +
> +	hantro_reg_write(ctx->dev, &g2_idr_pic_e, intra_only);
> +
> +	hantro_reg_write(ctx->dev, &vp9_transform_mode, dec_params-
>tx_mode);
> +
> +	hantro_reg_write(ctx->dev, &vp9_mcomp_filt_type, intra_only ?
> +		0 : hantro_interp_filter_from_v4l2(dec_params-
>interpolation_filter));
> +
> +	hantro_reg_write(ctx->dev, &vp9_high_prec_mv_e,
> +			 !!(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV));
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_mode, dec_params-
>reference_mode);
> +
> +	hantro_reg_write(ctx->dev, &g2_tempor_mvp_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY) &&
> +			 !resolution_change &&
> +			 vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_SHOW_FRAME
> +	);
> +
> +	hantro_reg_write(ctx->dev, &g2_write_mvs_e,
> +			 !(dec_params->flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME));
> +}
> +
> +static void
> +config_compound_reference(struct hantro_ctx *ctx,
> +			  const struct v4l2_ctrl_vp9_frame 
*dec_params)
> +{
> +	u32 comp_fixed_ref, comp_var_ref[2];
> +	bool last_ref_frame_sign_bias;
> +	bool golden_ref_frame_sign_bias;
> +	bool alt_ref_frame_sign_bias;
> +	bool comp_ref_allowed = 0;
> +
> +	comp_fixed_ref = 0;
> +	comp_var_ref[0] = 0;
> +	comp_var_ref[1] = 0;
> +
> +	last_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_LAST;
> +	golden_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_GOLDEN;
> +	alt_ref_frame_sign_bias = dec_params->ref_frame_sign_bias & 
V4L2_VP9_SIGN_BIAS_ALT;
> +
> +	/* 6.3.12 Frame reference mode syntax */
> +	comp_ref_allowed |= golden_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +	comp_ref_allowed |= alt_ref_frame_sign_bias != 
last_ref_frame_sign_bias;
> +
> +	if (comp_ref_allowed) {
> +		if (last_ref_frame_sign_bias ==
> +		    golden_ref_frame_sign_bias) {
> +			comp_fixed_ref = ALTREF_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = GOLDEN_FRAME;
> +		} else if (last_ref_frame_sign_bias ==
> +			   alt_ref_frame_sign_bias) {
> +			comp_fixed_ref = GOLDEN_FRAME;
> +			comp_var_ref[0] = LAST_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		} else {
> +			comp_fixed_ref = LAST_FRAME;
> +			comp_var_ref[0] = GOLDEN_FRAME;
> +			comp_var_ref[1] = ALTREF_FRAME;
> +		}
> +	}
> +
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_fixed_ref, comp_fixed_ref);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref0, 
comp_var_ref[0]);
> +	hantro_reg_write(ctx->dev, &vp9_comp_pred_var_ref1, 
comp_var_ref[1]);
> +}
> +
> +#define INNER_LOOP \
> +do {								
	\
> +	for (m = 0; m < ARRAY_SIZE(adaptive->coef[0][0][0][0]); ++m) {	\
> +		memcpy(adaptive->coef[i][j][k][l][m],			
\
> +		       probs->coef[i][j][k][l][m],			
\
> +		       sizeof(probs->coef[i][j][k][l][m]));		
\
> +								
	\
> +		adaptive->coef[i][j][k][l][m][3] = 0;			
\
> +	}							
	\
> +} while (0)
> +
> +static void config_probs(struct hantro_ctx *ctx, const struct 
v4l2_ctrl_vp9_frame *dec_params)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_ctx->misc;
> +	struct hantro_g2_all_probs *all_probs = misc->cpu;
> +	struct hantro_g2_probs *adaptive;
> +	struct hantro_g2_mv_probs *mv;
> +	const struct v4l2_vp9_segmentation *seg = &dec_params->seg;
> +	const struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +	int i, j, k, l, m;
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_y_mode_prob); ++i)
> +		for (j = 0; j < ARRAY_SIZE(all_probs-
>kf_y_mode_prob[0]); ++j) {
> +			memcpy(all_probs->kf_y_mode_prob[i][j],
> +			       v4l2_vp9_kf_y_mode_prob[i][j],
> +			       ARRAY_SIZE(all_probs-
>kf_y_mode_prob[i][j]));
> +
> +			all_probs->kf_y_mode_prob_tail[i][j][0] =
> +				v4l2_vp9_kf_y_mode_prob[i][j][8];
> +		}
> +
> +	memcpy(all_probs->mb_segment_tree_probs, seg->tree_probs,
> +	       sizeof(all_probs->mb_segment_tree_probs));
> +
> +	memcpy(all_probs->segment_pred_probs, seg->pred_probs,
> +	       sizeof(all_probs->segment_pred_probs));
> +
> +	for (i = 0; i < ARRAY_SIZE(all_probs->kf_uv_mode_prob); ++i) {
> +		memcpy(all_probs->kf_uv_mode_prob[i], 
v4l2_vp9_kf_uv_mode_prob[i],
> +		       ARRAY_SIZE(all_probs->kf_uv_mode_prob[i]));
> +
> +		all_probs->kf_uv_mode_prob_tail[i][0] = 
v4l2_vp9_kf_uv_mode_prob[i][8];
> +	}
> +
> +	adaptive = &all_probs->probs;
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->inter_mode); ++i) {
> +		memcpy(adaptive->inter_mode[i], probs->inter_mode[i],
> +		       sizeof(probs->inter_mode));
> +
> +		adaptive->inter_mode[i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->is_inter, probs->is_inter, sizeof(adaptive-
>is_inter));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->uv_mode); ++i) {
> +		memcpy(adaptive->uv_mode[i], probs->uv_mode[i],
> +		       sizeof(adaptive->uv_mode[i]));
> +		adaptive->uv_mode_tail[i][0] = probs->uv_mode[i][8];
> +	}
> +
> +	memcpy(adaptive->tx8, probs->tx8, sizeof(adaptive->tx8));
> +	memcpy(adaptive->tx16, probs->tx16, sizeof(adaptive->tx16));
> +	memcpy(adaptive->tx32, probs->tx32, sizeof(adaptive->tx32));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->y_mode); ++i) {
> +		memcpy(adaptive->y_mode[i], probs->y_mode[i],
> +		       ARRAY_SIZE(adaptive->y_mode[i]));
> +
> +		adaptive->y_mode_tail[i][0] = probs->y_mode[i][8];
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[0]); ++i) {
> +		memcpy(adaptive->partition[0][i], 
v4l2_vp9_kf_partition_probs[i],
> +		       sizeof(v4l2_vp9_kf_partition_probs[i]));
> +
> +		adaptive->partition[0][i][3] = 0;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->partition[1]); ++i) {
> +		memcpy(adaptive->partition[1][i], probs->partition[i],
> +		       sizeof(probs->partition[i]));
> +
> +		adaptive->partition[1][i][3] = 0;
> +	}
> +
> +	memcpy(adaptive->interp_filter, probs->interp_filter,
> +	       sizeof(adaptive->interp_filter));
> +
> +	memcpy(adaptive->comp_mode, probs->comp_mode, sizeof(adaptive-
>comp_mode));
> +
> +	memcpy(adaptive->skip, probs->skip, sizeof(adaptive->skip));
> +
> +	mv = &adaptive->mv;
> +
> +	memcpy(mv->joint, probs->mv.joint, sizeof(mv->joint));
> +	memcpy(mv->sign, probs->mv.sign, sizeof(mv->sign));
> +	memcpy(mv->class0_bit, probs->mv.class0_bit, sizeof(mv-
>class0_bit));
> +	memcpy(mv->fr, probs->mv.fr, sizeof(mv->fr));
> +	memcpy(mv->class0_hp, probs->mv.class0_hp, sizeof(mv->class0_hp));
> +	memcpy(mv->hp, probs->mv.hp, sizeof(mv->hp));
> +	memcpy(mv->classes, probs->mv.classes, sizeof(mv->classes));
> +	memcpy(mv->class0_fr, probs->mv.class0_fr, sizeof(mv->class0_fr));
> +	memcpy(mv->bits, probs->mv.bits, sizeof(mv->bits));
> +
> +	memcpy(adaptive->single_ref, probs->single_ref, sizeof(adaptive-
>single_ref));
> +
> +	memcpy(adaptive->comp_ref, probs->comp_ref, sizeof(adaptive-
>comp_ref));
> +
> +	for (i = 0; i < ARRAY_SIZE(adaptive->coef); ++i)
> +		for (j = 0; j < ARRAY_SIZE(adaptive->coef[0]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(adaptive->coef[0]
[0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(adaptive->coef[0][0][0]); ++l)
> +					INNER_LOOP;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_PROBS, misc->dma);
> +}
> +
> +static void config_counts(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	dma_addr_t addr = misc->dma + vp9_dec->ctx_counters_offset;
> +
> +	hantro_write_addr(ctx->dev, VP9_ADDR_CTR, addr);
> +}
> +
> +static void config_seg_map(struct hantro_ctx *ctx,
> +			   const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +			   bool intra_only, bool update_map)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct hantro_aux_buf *segment_map = &vp9_ctx->segment_map;
> +	dma_addr_t addr;
> +
> +	if (intra_only ||
> +	    (dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT)) {
> +		memset(segment_map->cpu, 0, segment_map->size);
> +		memset(vp9_ctx->feature_data, 0, sizeof(vp9_ctx-
>feature_data));
> +		memset(vp9_ctx->feature_enabled, 0, sizeof(vp9_ctx-
>feature_enabled));
> +	}
> +
> +	addr = segment_map->dma + vp9_ctx->active_segment * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_READ, addr);
> +
> +	addr = segment_map->dma + (1 - vp9_ctx->active_segment) * vp9_ctx-
>segment_map_size;
> +	hantro_write_addr(ctx->dev, VP9_ADDR_SEGMENT_WRITE, addr);
> +
> +	if (update_map)
> +		vp9_ctx->active_segment = 1 - vp9_ctx->active_segment;
> +}
> +
> +static void
> +config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +	      struct vb2_v4l2_buffer *vb2_src)
> +{
> +	dma_addr_t stream_base, tmp_addr;
> +	unsigned int headres_size;
> +	u32 src_len, start_bit, src_buf_len;
> +
> +	headres_size = dec_params->uncompressed_header_size
> +		     + dec_params->compressed_header_size;
> +
> +	stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0);
> +	hantro_write_addr(ctx->dev, G2_ADDR_STR, stream_base);
> +
> +	tmp_addr = stream_base + headres_size;
> +	start_bit = (tmp_addr & 0xf) * 8;
> +	hantro_reg_write(ctx->dev, &g2_start_bit, start_bit);
> +
> +	src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0);
> +	src_len += start_bit / 8 - headres_size;
> +	hantro_reg_write(ctx->dev, &g2_stream_len, src_len);
> +
> +	tmp_addr &= ~0xf;
> +	hantro_reg_write(ctx->dev, &g2_strm_start_offset, tmp_addr - 
stream_base);
> +	src_buf_len = vb2_plane_size(&vb2_src->vb2_buf, 0);
> +	hantro_reg_write(ctx->dev, &g2_strm_buffer_len, src_buf_len);
> +}
> +
> +static void
> +config_registers(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame 
*dec_params,
> +		 struct vb2_v4l2_buffer *vb2_src, struct vb2_v4l2_buffer 
*vb2_dst)
> +{
> +	struct hantro_decoded_buffer *dst, *last, *mv_ref;
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	const struct v4l2_vp9_segmentation *seg;
> +	bool intra_only, resolution_change;
> +
> +	/* vp9 stuff */
> +	dst = vb2_to_hantro_decoded_buf(&vb2_dst->vb2_buf);
> +
> +	if (vp9_ctx->last.valid)
> +		last = get_ref_buf(ctx, &dst->base.vb, vp9_ctx-
>last.timestamp);
> +	else
> +		last = dst;
> +
> +	update_dec_buf_info(dst, dec_params);
> +	update_ctx_cur_info(vp9_ctx, dst, dec_params);
> +	seg = &dec_params->seg;
> +
> +	intra_only = !!(dec_params->flags &
> +			(V4L2_VP9_FRAME_FLAG_KEY_FRAME |
> +			V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
> +
> +	if (!intra_only &&
> +	    !(dec_params->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
> +	    vp9_ctx->last.valid)
> +		mv_ref = last;
> +	else
> +		mv_ref = dst;
> +
> +	resolution_change = dst->vp9.width != last->vp9.width ||
> +			    dst->vp9.height != last->vp9.height;
> +
> +	/* configure basic registers */
> +	hantro_reg_write(ctx->dev, &g2_mode, VP9_DEC_MODE);
> +	hantro_reg_write(ctx->dev, &g2_strm_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_dirmv_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_compress_swap, 0xf);
> +	hantro_reg_write(ctx->dev, &g2_buswidth, BUS_WIDTH_128);
> +	hantro_reg_write(ctx->dev, &g2_max_burst, 16);
> +	hantro_reg_write(ctx->dev, &g2_apf_threshold, 8);
> +	hantro_reg_write(ctx->dev, &g2_ref_compress_bypass, 1);
> +	hantro_reg_write(ctx->dev, &g2_clk_gate_e, 1);
> +	hantro_reg_write(ctx->dev, &g2_max_cb_size, 6);
> +	hantro_reg_write(ctx->dev, &g2_min_cb_size, 3);
> +
> +	config_output(ctx, dst, dec_params);
> +
> +	if (!intra_only)
> +		config_ref_registers(ctx, dec_params, dst, mv_ref);
> +
> +	config_tiles(ctx, dec_params, dst);
> +	config_segment(ctx, dec_params);
> +	config_loop_filter(ctx, dec_params);
> +	config_picture_dimensions(ctx, dst);
> +	config_bit_depth(ctx, dec_params);
> +	config_quant(ctx, dec_params);
> +	config_others(ctx, dec_params, intra_only, resolution_change);
> +	config_compound_reference(ctx, dec_params);
> +	config_probs(ctx, dec_params);
> +	config_counts(ctx);
> +	config_seg_map(ctx, dec_params, intra_only,
> +		       seg->flags & 
V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP);
> +	config_source(ctx, dec_params, vb2_src);
> +}
> +
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx)
> +{
> +	const struct v4l2_ctrl_vp9_frame *decode_params;
> +	struct vb2_v4l2_buffer *src;
> +	struct vb2_v4l2_buffer *dst;
> +	int ret;
> +
> +	hantro_g2_check_idle(ctx->dev);
> +
> +	ret = start_prepare_run(ctx, &decode_params);
> +	if (ret) {
> +		hantro_end_prepare_run(ctx);
> +		return ret;
> +	}
> +
> +	src = hantro_get_src_buf(ctx);
> +	dst = hantro_get_dst_buf(ctx);
> +
> +	config_registers(ctx, decode_params, src, dst);
> +
> +	hantro_end_prepare_run(ctx);
> +
> +	vdpu_write(ctx->dev, G2_REG_INTERRUPT_DEC_E, G2_REG_INTERRUPT);
> +
> +	return 0;
> +}
> +
> +#define copy_tx_and_skip(p1, p2)				\
> +do {								
\
> +	memcpy((p1)->tx8, (p2)->tx8, sizeof((p1)->tx8));	\
> +	memcpy((p1)->tx16, (p2)->tx16, sizeof((p1)->tx16));	\
> +	memcpy((p1)->tx32, (p2)->tx32, sizeof((p1)->tx32));	\
> +	memcpy((p1)->skip, (p2)->skip, sizeof((p1)->skip));	\
> +} while (0)
> +
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	unsigned int fctx_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX))
> +		goto out_update_last;
> +
> +	fctx_idx = vp9_ctx->cur.frame_context_idx;
> +
> +	if (!(vp9_ctx->cur.flags & V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE)) {
> +		/* error_resilient_mode == 0 && 
frame_parallel_decoding_mode == 0 */
> +		struct v4l2_vp9_frame_context *probs = &vp9_ctx-
>probability_tables;
> +		bool frame_is_intra = vp9_ctx->cur.flags &
> +		    (V4L2_VP9_FRAME_FLAG_KEY_FRAME | 
V4L2_VP9_FRAME_FLAG_INTRA_ONLY);
> +		struct tx_and_skip {
> +			u8 tx8[2][1];
> +			u8 tx16[2][2];
> +			u8 tx32[2][3];
> +			u8 skip[3];
> +		} _tx_skip, *tx_skip = &_tx_skip;
> +		struct v4l2_vp9_frame_symbol_counts *counts;
> +		struct symbol_counts *hantro_cnts;
> +		u32 tx16p[2][4];
> +		int i;
> +
> +		/* buffer the forward-updated TX and skip probs */
> +		if (frame_is_intra)
> +			copy_tx_and_skip(tx_skip, probs);
> +
> +		/* 6.1.2 refresh_probs(): load_probs() and load_probs2() 
*/
> +		*probs = vp9_ctx->frame_context[fctx_idx];
> +
> +		/* if FrameIsIntra then undo the effect of load_probs2() 
*/
> +		if (frame_is_intra)
> +			copy_tx_and_skip(probs, tx_skip);
> +
> +		counts = &vp9_ctx->cnts;
> +		hantro_cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +		for (i = 0; i < ARRAY_SIZE(tx16p); ++i) {
> +			memcpy(tx16p[i],
> +			       hantro_cnts->tx16x16_count[i],
> +			       sizeof(hantro_cnts-
>tx16x16_count[0]));
> +			tx16p[i][3] = 0;
> +		}
> +		counts->tx16p = &tx16p;
> +
> +		v4l2_vp9_adapt_coef_probs(probs, counts,
> +					  !vp9_ctx->last.valid 
||
> +					  vp9_ctx->last.flags & 
V4L2_VP9_FRAME_FLAG_KEY_FRAME,
> +					  frame_is_intra);
> +
> +		if (!frame_is_intra) {
> +			/* load_probs2() already done */
> +			u32 mv_mode[7][4];
> +
> +			for (i = 0; i < ARRAY_SIZE(mv_mode); ++i) {
> +				mv_mode[i][0] = hantro_cnts-
>inter_mode_counts[i][1][0];
> +				mv_mode[i][1] = hantro_cnts-
>inter_mode_counts[i][2][0];
> +				mv_mode[i][2] = hantro_cnts-
>inter_mode_counts[i][0][0];
> +				mv_mode[i][3] = hantro_cnts-
>inter_mode_counts[i][2][1];
> +			}
> +			counts->mv_mode = &mv_mode;
> +			v4l2_vp9_adapt_noncoef_probs(&vp9_ctx-
>probability_tables, counts,
> +						     
vp9_ctx->cur.reference_mode,
> +						     
vp9_ctx->cur.interpolation_filter,
> +						     
vp9_ctx->cur.tx_mode, vp9_ctx->cur.flags);
> +		}
> +	}
> +
> +	vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables;
> +
> +out_update_last:
> +	vp9_ctx->last = vp9_ctx->cur;
> +}
> diff --git a/drivers/staging/media/hantro/hantro_hw.h b/drivers/staging/
media/hantro/hantro_hw.h
> index 42b3f3961f75..2961d399fd60 100644
> --- a/drivers/staging/media/hantro/hantro_hw.h
> +++ b/drivers/staging/media/hantro/hantro_hw.h
> @@ -12,6 +12,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/v4l2-controls.h>
>  #include <media/v4l2-ctrls.h>
> +#include <media/v4l2-vp9.h>
>  #include <media/videobuf2-core.h>
>  
>  #define DEC_8190_ALIGN_MASK	0x07U
> @@ -161,6 +162,50 @@ struct hantro_vp8_dec_hw_ctx {
>  	struct hantro_aux_buf prob_tbl;
>  };
>  
> +struct hantro_vp9_frame_info {
> +	u32 valid : 1;
> +	u32 frame_context_idx : 2;
> +	u32 reference_mode : 2;
> +	u32 tx_mode : 3;
> +	u32 interpolation_filter : 3;
> +	u32 flags;
> +	u64 timestamp;
> +};
> +
> +#define MAX_SB_COLS	64
> +#define MAX_SB_ROWS	34
> +
> +/**
> + * struct hantro_vp9_dec_hw_ctx
> + *
> + */
> +struct hantro_vp9_dec_hw_ctx {
> +	struct hantro_aux_buf tile_edge;
> +	struct hantro_aux_buf segment_map;
> +	struct hantro_aux_buf misc;
> +	struct v4l2_vp9_frame_symbol_counts cnts;
> +	struct v4l2_vp9_frame_context probability_tables;
> +	struct v4l2_vp9_frame_context frame_context[4];
> +	struct hantro_vp9_frame_info cur;
> +	struct hantro_vp9_frame_info last;
> +
> +	unsigned int bsd_ctrl_offset;
> +	unsigned int segment_map_size;
> +	unsigned int ctx_counters_offset;
> +	unsigned int tile_info_offset;
> +
> +	unsigned short tile_r_info[MAX_SB_ROWS];
> +	unsigned short tile_c_info[MAX_SB_COLS];
> +	unsigned int last_tile_r;
> +	unsigned int last_tile_c;
> +	unsigned int last_sbs_r;
> +	unsigned int last_sbs_c;
> +
> +	unsigned int active_segment;
> +	u8 feature_enabled[8];
> +	s16 feature_data[8][4];
> +};
> +
>  /**
>   * struct hantro_postproc_ctx
>   *
> @@ -267,6 +312,24 @@ void hantro_hevc_ref_remove_unused(struct hantro_ctx 
*ctx);
>  size_t hantro_hevc_chroma_offset(const struct v4l2_ctrl_hevc_sps *sps);
>  size_t hantro_hevc_motion_vectors_offset(const struct v4l2_ctrl_hevc_sps 
*sps);
>  
> +static inline unsigned short hantro_vp9_num_sbs(unsigned short dimension)
> +{
> +	return (dimension + 63) / 64;
> +}
> +
> +static inline size_t
> +hantro_vp9_mv_size(unsigned int width, unsigned int height)
> +{
> +	int num_ctbs;
> +
> +	/*
> +	 * There can be up to (CTBs x 64) number of blocks,
> +	 * and the motion vector for each block needs 16 bytes.
> +	 */
> +	num_ctbs = hantro_vp9_num_sbs(width) * hantro_vp9_num_sbs(height);
> +	return (num_ctbs * 64) * 16;
> +}
> +
>  static inline size_t
>  hantro_h264_mv_size(unsigned int width, unsigned int height)
>  {
> @@ -308,6 +371,10 @@ void hantro_vp8_dec_exit(struct hantro_ctx *ctx);
>  void hantro_vp8_prob_update(struct hantro_ctx *ctx,
>  			    const struct v4l2_ctrl_vp8_frame *hdr);
>  
> +int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx);
> +void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx);
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx);
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx);
>  void hantro_g2_check_idle(struct hantro_dev *vpu);
>  
>  #endif /* HANTRO_HW_H_ */
> diff --git a/drivers/staging/media/hantro/hantro_v4l2.c b/drivers/staging/
media/hantro/hantro_v4l2.c
> index d1f060c55fed..e4b0645ba6fc 100644
> --- a/drivers/staging/media/hantro/hantro_v4l2.c
> +++ b/drivers/staging/media/hantro/hantro_v4l2.c
> @@ -299,6 +299,11 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx,
>  			pix_mp->plane_fmt[0].sizeimage +=
>  				hantro_h264_mv_size(pix_mp-
>width,
>  						    pix_mp-
>height);
> +		else if (ctx->vpu_src_fmt->fourcc == 
V4L2_PIX_FMT_VP9_FRAME &&
> +			 !hantro_needs_postproc(ctx, fmt))
> +			pix_mp->plane_fmt[0].sizeimage +=
> +				hantro_vp9_mv_size(pix_mp->width,
> +						   pix_mp-
>height);
>  	} else if (!pix_mp->plane_fmt[0].sizeimage) {
>  		/*
>  		 * For coded formats the application can specify
> @@ -407,6 +412,7 @@ hantro_update_requires_request(struct hantro_ctx *ctx, 
u32 fourcc)
>  	case V4L2_PIX_FMT_VP8_FRAME:
>  	case V4L2_PIX_FMT_H264_SLICE:
>  	case V4L2_PIX_FMT_HEVC_SLICE:
> +	case V4L2_PIX_FMT_VP9_FRAME:
>  		ctx->fh.m2m_ctx->out_q_ctx.q.requires_requests = true;
>  		break;
>  	default:
> diff --git a/drivers/staging/media/hantro/hantro_vp9.c b/drivers/staging/
media/hantro/hantro_vp9.c
> new file mode 100644
> index 000000000000..566cd376c097
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.c
> @@ -0,0 +1,240 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +#include <linux/types.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "hantro.h"
> +#include "hantro_hw.h"
> +#include "hantro_vp9.h"
> +
> +#define POW2(x) (1 << (x))
> +
> +#define MAX_LOG2_TILE_COLUMNS 6
> +#define MAX_NUM_TILE_COLS POW2(MAX_LOG2_TILE_COLUMNS)
> +#define MAX_TILE_COLS 20
> +#define MAX_TILE_ROWS 22
> +
> +static size_t hantro_vp9_tile_filter_size(unsigned int height)
> +{
> +	u32 h, height32, size;
> +
> +	h = roundup(height, 8);
> +
> +	height32 = roundup(h, 64);
> +	size = 24 * height32 * (MAX_NUM_TILE_COLS - 1); /* luma: 8, chroma: 
8 + 8 */
> +
> +	return size;
> +}
> +
> +static size_t hantro_vp9_bsd_control_size(unsigned int height)
> +{
> +	u32 h, height32;
> +
> +	h = roundup(height, 8);
> +	height32 = roundup(h, 64);
> +
> +	return 16 * (height32 / 4) * (MAX_NUM_TILE_COLS - 1);
> +}
> +
> +static size_t hantro_vp9_segment_map_size(unsigned int width, unsigned int 
height)
> +{
> +	u32 w, h;
> +	int num_ctbs;
> +
> +	w = roundup(width, 8);
> +	h = roundup(height, 8);
> +	num_ctbs = ((w + 63) / 64) * ((h + 63) / 64);
> +
> +	return num_ctbs * 32;
> +}
> +
> +static inline size_t hantro_vp9_prob_tab_size(void)
> +{
> +	return roundup(sizeof(struct hantro_g2_all_probs), 16);
> +}
> +
> +static inline size_t hantro_vp9_count_tab_size(void)
> +{
> +	return roundup(sizeof(struct symbol_counts), 16);
> +}
> +
> +static inline size_t hantro_vp9_tile_info_size(void)
> +{
> +	return roundup((MAX_TILE_COLS * MAX_TILE_ROWS * 4 * sizeof(u16) + 
15 + 16) & ~0xf, 16);
> +}
> +
> +static void *get_coeffs_arr(struct symbol_counts *cnts, int i, int j, int k, 
int l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m];
> +
> +	return NULL;
> +}
> +
> +static void *get_eobs1(struct symbol_counts *cnts, int i, int j, int k, int 
l, int m)
> +{
> +	if (i == 0)
> +		return &cnts->count_coeffs[j][k][l][m][3];
> +
> +	if (i == 1)
> +		return &cnts->count_coeffs8x8[j][k][l][m][3];
> +
> +	if (i == 2)
> +		return &cnts->count_coeffs16x16[j][k][l][m][3];
> +
> +	if (i == 3)
> +		return &cnts->count_coeffs32x32[j][k][l][m][3];
> +
> +	return NULL;
> +}
> +
> +#define INNER_LOOP \
> +	do {							
			\
> +		for (m = 0; m < ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]
[0]); ++m) {	\
> +			vp9_ctx->cnts.coeff[i][j][k][l][m] =		
		\
> +				get_coeffs_arr(cnts, i, j, k, l, 
m);			\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][0] =		
		\
> +				&cnts->count_eobs[i][j][k][l][m];	
		\
> +			vp9_ctx->cnts.eob[i][j][k][l][m][1] =		
		\
> +				get_eobs1(cnts, i, j, k, l, m);	
			\
> +		}						
			\
> +	} while (0)
> +
> +static void init_v4l2_vp9_count_tbl(struct hantro_ctx *ctx)
> +{
> +	struct hantro_vp9_dec_hw_ctx *vp9_ctx = &ctx->vp9_dec;
> +	struct symbol_counts *cnts = vp9_ctx->misc.cpu + vp9_ctx-
>ctx_counters_offset;
> +	int i, j, k, l, m;
> +
> +	vp9_ctx->cnts.partition = &cnts->partition_counts;
> +	vp9_ctx->cnts.skip = &cnts->mbskip_count;
> +	vp9_ctx->cnts.intra_inter = &cnts->intra_inter_count;
> +	vp9_ctx->cnts.tx32p = &cnts->tx32x32_count;
> +	/*
> +	 * g2 hardware uses tx16x16_count[2][3], while the api
> +	 * expects tx16p[2][4], so this must be explicitly copied
> +	 * into vp9_ctx->cnts.tx16p when passing the data to the
> +	 * vp9 library function
> +	 */
> +	vp9_ctx->cnts.tx8p = &cnts->tx8x8_count;
> +
> +	vp9_ctx->cnts.y_mode = &cnts->sb_ymode_counts;
> +	vp9_ctx->cnts.uv_mode = &cnts->uv_mode_counts;
> +	vp9_ctx->cnts.comp = &cnts->comp_inter_count;
> +	vp9_ctx->cnts.comp_ref = &cnts->comp_ref_count;
> +	vp9_ctx->cnts.single_ref = &cnts->single_ref_count;
> +	vp9_ctx->cnts.filter = &cnts->switchable_interp_counts;
> +	vp9_ctx->cnts.mv_joint = &cnts->mv_counts.joints;
> +	vp9_ctx->cnts.sign = &cnts->mv_counts.sign;
> +	vp9_ctx->cnts.classes = &cnts->mv_counts.classes;
> +	vp9_ctx->cnts.class0 = &cnts->mv_counts.class0;
> +	vp9_ctx->cnts.bits = &cnts->mv_counts.bits;
> +	vp9_ctx->cnts.class0_fp = &cnts->mv_counts.class0_fp;
> +	vp9_ctx->cnts.fp = &cnts->mv_counts.fp;
> +	vp9_ctx->cnts.class0_hp = &cnts->mv_counts.class0_hp;
> +	vp9_ctx->cnts.hp = &cnts->mv_counts.hp;
> +
> +	for (i = 0; i < ARRAY_SIZE(vp9_ctx->cnts.coeff); ++i)
> +		for (j = 0; j < ARRAY_SIZE(vp9_ctx->cnts.coeff[i]); ++j)
> +			for (k = 0; k < ARRAY_SIZE(vp9_ctx-
>cnts.coeff[i][0]); ++k)
> +				for (l = 0; l < 
ARRAY_SIZE(vp9_ctx->cnts.coeff[i][0][0]); ++l)
> +					INNER_LOOP;
> +}
> +
> +int hantro_vp9_dec_init(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	const struct hantro_variant *variant = vpu->variant;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +	u32 i, max_width, max_height, size;
> +
> +	if (variant->num_dec_fmts < 1)
> +		return -EINVAL;
> +
> +	for (i = 0; i < variant->num_dec_fmts; ++i)
> +		if (variant->dec_fmts[i].fourcc == 
V4L2_PIX_FMT_VP9_FRAME)
> +			break;
> +
> +	if (i == variant->num_dec_fmts)
> +		return -EINVAL;
> +
> +	max_width = vpu->variant->dec_fmts[i].frmsize.max_width;
> +	max_height = vpu->variant->dec_fmts[i].frmsize.max_height;
> +
> +	size = hantro_vp9_tile_filter_size(max_height);
> +	vp9_dec->bsd_ctrl_offset = size;
> +	size += hantro_vp9_bsd_control_size(max_height);
> +
> +	tile_edge->cpu = dma_alloc_coherent(vpu->dev, size, &tile_edge-
>dma, GFP_KERNEL);
> +	if (!tile_edge->cpu)
> +		return -ENOMEM;
> +
> +	tile_edge->size = size;
> +	memset(tile_edge->cpu, 0, size);
> +
> +	size = hantro_vp9_segment_map_size(max_width, max_height);
> +	vp9_dec->segment_map_size = size;
> +	size *= 2; /* we need two areas of this size, used alternately */
> +
> +	segment_map->cpu = dma_alloc_coherent(vpu->dev, size, &segment_map-
>dma, GFP_KERNEL);
> +	if (!segment_map->cpu)
> +		goto err_segment_map;
> +
> +	segment_map->size = size;
> +	memset(segment_map->cpu, 0, size);
> +
> +	size = hantro_vp9_prob_tab_size();
> +	vp9_dec->ctx_counters_offset = size;
> +	size += hantro_vp9_count_tab_size();
> +	vp9_dec->tile_info_offset = size;
> +	size += hantro_vp9_tile_info_size();
> +
> +	misc->cpu = dma_alloc_coherent(vpu->dev, size, &misc->dma, 
GFP_KERNEL);
> +	if (!misc->cpu)
> +		goto err_misc;
> +
> +	misc->size = size;
> +	memset(misc->cpu, 0, size);
> +
> +	init_v4l2_vp9_count_tbl(ctx);
> +
> +	return 0;
> +
> +err_misc:
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +
> +err_segment_map:
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +
> +	return -ENOMEM;
> +}
> +
> +void hantro_vp9_dec_exit(struct hantro_ctx *ctx)
> +{
> +	struct hantro_dev *vpu = ctx->dev;
> +	struct hantro_vp9_dec_hw_ctx *vp9_dec = &ctx->vp9_dec;
> +	struct hantro_aux_buf *tile_edge = &vp9_dec->tile_edge;
> +	struct hantro_aux_buf *segment_map = &vp9_dec->segment_map;
> +	struct hantro_aux_buf *misc = &vp9_dec->misc;
> +
> +	dma_free_coherent(vpu->dev, misc->size, misc->cpu, misc->dma);
> +	dma_free_coherent(vpu->dev, segment_map->size, segment_map->cpu, 
segment_map->dma);
> +	dma_free_coherent(vpu->dev, tile_edge->size, tile_edge->cpu, 
tile_edge->dma);
> +}
> diff --git a/drivers/staging/media/hantro/hantro_vp9.h b/drivers/staging/
media/hantro/hantro_vp9.h
> new file mode 100644
> index 000000000000..c7f4bd3ff8dd
> --- /dev/null
> +++ b/drivers/staging/media/hantro/hantro_vp9.h
> @@ -0,0 +1,103 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Hantro VP9 codec driver
> + *
> + * Copyright (C) 2021 Collabora Ltd.
> + */
> +
> +struct hantro_g2_mv_probs {
> +	u8 joint[3];
> +	u8 sign[2];
> +	u8 class0_bit[2][1];
> +	u8 fr[2][3];
> +	u8 class0_hp[2];
> +	u8 hp[2];
> +	u8 classes[2][10];
> +	u8 class0_fr[2][2][3];
> +	u8 bits[2][10];
> +};
> +
> +struct hantro_g2_probs {
> +	u8 inter_mode[7][4];
> +	u8 is_inter[4];
> +	u8 uv_mode[10][8];
> +	u8 tx8[2][1];
> +	u8 tx16[2][2];
> +	u8 tx32[2][3];
> +	u8 y_mode_tail[4][1];
> +	u8 y_mode[4][8];
> +	u8 partition[2][16][4]; /* [keyframe][][], [inter][][] */
> +	u8 uv_mode_tail[10][1];
> +	u8 interp_filter[4][2];
> +	u8 comp_mode[5];
> +	u8 skip[3];
> +
> +	u8 pad1[1];
> +
> +	struct hantro_g2_mv_probs mv;
> +
> +	u8 single_ref[5][2];
> +	u8 comp_ref[5];
> +
> +	u8 pad2[17];
> +
> +	u8 coef[4][2][2][6][6][4];
> +};
> +
> +struct hantro_g2_all_probs {
> +	u8 kf_y_mode_prob[10][10][8];
> +
> +	u8 kf_y_mode_prob_tail[10][10][1];
> +	u8 ref_pred_probs[3];
> +	u8 mb_segment_tree_probs[7];
> +	u8 segment_pred_probs[3];
> +	u8 ref_scores[4];
> +	u8 prob_comppred[2];
> +
> +	u8 pad1[9];
> +
> +	u8 kf_uv_mode_prob[10][8];
> +	u8 kf_uv_mode_prob_tail[10][1];
> +
> +	u8 pad2[6];
> +
> +	struct hantro_g2_probs probs;
> +};
> +
> +struct mv_counts {
> +	u32 joints[4];
> +	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 symbol_counts {
> +	u32 inter_mode_counts[7][3][2];
> +	u32 sb_ymode_counts[4][10];
> +	u32 uv_mode_counts[10][10];
> +	u32 partition_counts[16][4];
> +	u32 switchable_interp_counts[4][3];
> +	u32 intra_inter_count[4][2];
> +	u32 comp_inter_count[5][2];
> +	u32 single_ref_count[5][2][2];
> +	u32 comp_ref_count[5][2];
> +	u32 tx32x32_count[2][4];
> +	u32 tx16x16_count[2][3];
> +	u32 tx8x8_count[2][2];
> +	u32 mbskip_count[3][2];
> +
> +	struct mv_counts mv_counts;
> +
> +	u32 count_coeffs[2][2][6][6][4];
> +	u32 count_coeffs8x8[2][2][6][6][4];
> +	u32 count_coeffs16x16[2][2][6][6][4];
> +	u32 count_coeffs32x32[2][2][6][6][4];
> +
> +	u32 count_eobs[4][2][2][6][6];
> +};
> +
> diff --git a/drivers/staging/media/hantro/imx8m_vpu_hw.c b/drivers/staging/
media/hantro/imx8m_vpu_hw.c
> index a40b161e5956..455a107ffb02 100644
> --- a/drivers/staging/media/hantro/imx8m_vpu_hw.c
> +++ b/drivers/staging/media/hantro/imx8m_vpu_hw.c
> @@ -150,6 +150,19 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] 
= {
>  			.step_height = MB_DIM,
>  		},
>  	},
> +	{
> +		.fourcc = V4L2_PIX_FMT_VP9_FRAME,
> +		.codec_mode = HANTRO_MODE_VP9_DEC,
> +		.max_depth = 2,
> +		.frmsize = {
> +			.min_width = 48,
> +			.max_width = 3840,
> +			.step_width = MB_DIM,
> +			.min_height = 48,
> +			.max_height = 2160,
> +			.step_height = MB_DIM,
> +		},
> +	},
>  };
>  
>  static irqreturn_t imx8m_vpu_g1_irq(int irq, void *dev_id)
> @@ -241,6 +254,13 @@ static const struct hantro_codec_ops 
imx8mq_vpu_g2_codec_ops[] = {
>  		.init = hantro_hevc_dec_init,
>  		.exit = hantro_hevc_dec_exit,
>  	},
> +	[HANTRO_MODE_VP9_DEC] = {
> +		.run = hantro_g2_vp9_dec_run,
> +		.done = hantro_g2_vp9_dec_done,
> +		.reset = imx8m_vpu_g2_reset,
> +		.init = hantro_vp9_dec_init,
> +		.exit = hantro_vp9_dec_exit,
> +	},
>  };
>  
>  /*
> @@ -281,7 +301,7 @@ const struct hantro_variant imx8mq_vpu_g2_variant = {
>  	.dec_offset = 0x0,
>  	.dec_fmts = imx8m_vpu_g2_dec_fmts,
>  	.num_dec_fmts = ARRAY_SIZE(imx8m_vpu_g2_dec_fmts),
> -	.codec = HANTRO_HEVC_DECODER,
> +	.codec = HANTRO_HEVC_DECODER | HANTRO_VP9_DECODER,
>  	.codec_ops = imx8mq_vpu_g2_codec_ops,
>  	.init = imx8mq_vpu_hw_init,
>  	.runtime_resume = imx8mq_runtime_resume,
> -- 
> 2.17.1
> 
> 



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2021-08-09 17:07 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-05 14:42 [PATCH v3 00/10] VP9 codec V4L2 control interface Andrzej Pietrasiewicz
2021-08-05 14:42 ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 01/10] hantro: postproc: Fix motion vector space size Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 02/10] hantro: postproc: Introduce struct hantro_postproc_ops Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 03/10] hantro: Simplify postprocessor Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 04/10] hantro: Add quirk for NV12/NV12_4L4 capture format Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 19:29   ` kernel test robot
2021-08-05 19:29     ` kernel test robot
2021-08-05 19:29     ` kernel test robot
2021-08-05 19:29     ` kernel test robot
2021-08-05 14:42 ` [PATCH v3 05/10] media: uapi: Add VP9 stateless decoder controls Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-09 18:04   ` Jernej Škrabec
2021-08-09 18:04     ` Jernej Škrabec
2021-08-09 18:04     ` Jernej Škrabec
2021-08-09 18:36     ` Ezequiel Garcia
2021-08-09 18:36       ` Ezequiel Garcia
2021-08-09 18:36       ` Ezequiel Garcia
2021-08-09 18:36       ` Ezequiel Garcia
2021-08-17 17:49   ` Nicolas Dufresne
2021-08-17 17:49     ` Nicolas Dufresne
2021-08-17 17:49     ` Nicolas Dufresne
2021-08-17 17:49     ` Nicolas Dufresne
2021-08-05 14:42 ` [PATCH v3 06/10] media: Add VP9 v4l2 library Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 07/10] media: rkvdec: Add the VP9 backend Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 20:14   ` kernel test robot
2021-08-05 14:42 ` [PATCH v3 08/10] media: hantro: Prepare for other G2 codecs Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42 ` [PATCH v3 09/10] media: hantro: Support VP9 on the G2 core Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-09 17:07   ` Jernej Škrabec [this message]
2021-08-09 17:07     ` Jernej Škrabec
2021-08-09 17:07     ` Jernej Škrabec
2021-08-05 14:42 ` [PATCH v3 10/10] media: hantro: Support NV12 " Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz
2021-08-05 14:42   ` Andrzej Pietrasiewicz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1771240.sZXMzDhkfG@kista \
    --to=jernej.skrabec@gmail.com \
    --cc=andrzej.p@collabora.com \
    --cc=benjamin.gaignard@collabora.com \
    --cc=boris.brezillon@collabora.com \
    --cc=elezegarcia@gmail.com \
    --cc=festevam@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=heiko@sntech.de \
    --cc=hverkuil-cisco@xs4all.nl \
    --cc=kernel@collabora.com \
    --cc=kernel@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=linux-staging@lists.linux.dev \
    --cc=mchehab@kernel.org \
    --cc=nicolas.dufresne@collabora.com \
    --cc=p.zabel@pengutronix.de \
    --cc=s.hauer@pengutronix.de \
    --cc=shawnguo@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.