[PATCH 12/14] media: apple: avd: add vp9 support
From: Sofus Forstreuter
Date: Fri Sep 18 2026 - 09:22:03 EST
The fluster scores is 216/305 for VP9-TEST-VECTORS and 1/6 for
VP9-TEST-VECTORS-HIGH.
Signed-off-by: Sofus Forstreuter <sofus.c@xxxxxxxxxx>
---
drivers/media/platform/apple/avd/Kconfig | 1 +
drivers/media/platform/apple/avd/Makefile | 2 +-
drivers/media/platform/apple/avd/avd-v4l2.c | 37 +
drivers/media/platform/apple/avd/avd-vp9.c | 1072 +++++++++++++++++++++++++++
drivers/media/platform/apple/avd/avd.h | 10 +
5 files changed, 1121 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/apple/avd/Kconfig b/drivers/media/platform/apple/avd/Kconfig
index 9e2e4e3fa56e..6d31f9285973 100644
--- a/drivers/media/platform/apple/avd/Kconfig
+++ b/drivers/media/platform/apple/avd/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_APPLE_AVD
select V4L2_MEM2MEM_DEV
select VIDEOBUF2_DMA_CONTIG
select V4L2_H264
+ select V4L2_VP9
help
Support for hardware video decoding on Apple Silicon devices using
the Apple Video Decoder (AVD).
diff --git a/drivers/media/platform/apple/avd/Makefile b/drivers/media/platform/apple/avd/Makefile
index 837182588e2a..54ea67a589cd 100644
--- a/drivers/media/platform/apple/avd/Makefile
+++ b/drivers/media/platform/apple/avd/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-apple-avd-y := avd-drv.o avd-v4l2.o avd-hw.o avd-h264.o
+apple-avd-y := avd-drv.o avd-v4l2.o avd-hw.o avd-h264.o avd-vp9.o
obj-$(CONFIG_VIDEO_APPLE_AVD) += apple-avd.o
diff --git a/drivers/media/platform/apple/avd/avd-v4l2.c b/drivers/media/platform/apple/avd/avd-v4l2.c
index 36117fd5fbff..52dcb70b340c 100644
--- a/drivers/media/platform/apple/avd/avd-v4l2.c
+++ b/drivers/media/platform/apple/avd/avd-v4l2.c
@@ -227,6 +227,29 @@ static const struct avd_ctrls avd_h264_ctrls = {
.num_ctrls = ARRAY_SIZE(avd_h264_ctrl_descs),
};
+static const struct avd_ctrl_desc avd_vp9_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_VP9_FRAME,
+ .cfg.ops = &avd_ctrl_ops,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_VP9_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .cfg.max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
+ .cfg.menu_skip_mask =
+ BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_1),
+ .cfg.def = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ },
+};
+
+static const struct avd_ctrls avd_vp9_ctrls = {
+ .ctrls = avd_vp9_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(avd_vp9_ctrl_descs),
+};
+
static const struct avd_coded_fmt_desc avd_coded_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_H264_SLICE,
@@ -243,6 +266,20 @@ static const struct avd_coded_fmt_desc avd_coded_fmts[] = {
.subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
.capability = AVD_CAPABILITY_H264,
},
+ {
+ .fourcc = V4L2_PIX_FMT_VP9_FRAME,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 16384,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 16384,
+ .step_height = 16,
+ },
+ .ctrls = &avd_vp9_ctrls,
+ .ops = &avd_vp9_fmt_ops,
+ .capability = AVD_CAPABILITY_VP9,
+ },
};
static bool avd_is_capable(struct avd_ctx *ctx, unsigned int capability)
diff --git a/drivers/media/platform/apple/avd/avd-vp9.c b/drivers/media/platform/apple/avd/avd-vp9.c
new file mode 100644
index 000000000000..76599a1a3981
--- /dev/null
+++ b/drivers/media/platform/apple/avd/avd-vp9.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Apple Video Decoder VP9 driver
+ *
+ * Copyright (C) 2026 The Asahi Linux Contributors
+ * Copyright (C) 2026 Sofus Forstreuter <sofus.c@xxxxxxxxxx>
+ * Copyright (C) 2023 Eileen Yoon <eyn@xxxxxxx>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@xxxxxxxxxxxxx>
+ * Copyright (C) 2021 Collabora, Ltd.
+ * Andrzej Pietrasiewicz <andrzej.p@xxxxxxxxxxxxx>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Alpha Lin <Alpha.Lin@xxxxxxxxxxxxxx>
+ */
+
+#include <linux/dev_printk.h>
+#include <linux/unaligned.h>
+
+#include <media/v4l2-vp9.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "avd.h"
+#include "avd-inst.h"
+
+#define VP9_Q_IDX(v) FIELD_PREP(GENMASK(31, 15), v)
+#define VP9_Q_DC_Y(v) FIELD_PREP(GENMASK(14, 10), v)
+#define VP9_Q_DC_UV(v) FIELD_PREP(GENMASK(9, 5), v)
+#define VP9_Q_AC_UV(v) FIELD_PREP(GENMASK(4, 0), v)
+
+#define VP9_LF_SHARPNESS(v) FIELD_PREP(GENMASK(31, 28), v)
+#define VP9_LF_REF0(v) FIELD_PREP(GENMASK(27, 21), v)
+#define VP9_LF_REF1(v) FIELD_PREP(GENMASK(20, 14), v)
+#define VP9_LF_REF2(v) FIELD_PREP(GENMASK(13, 7), v)
+#define VP9_LF_REF3(v) FIELD_PREP(GENMASK(6, 0), v)
+
+#define VP9_LF_LV(v) FIELD_PREP(GENMASK(31, 14), v)
+#define VP9_LF_MODE0(v) FIELD_PREP(GENMASK(13, 7), v)
+#define VP9_LF_MODE1(v) FIELD_PREP(GENMASK(6, 0), v)
+
+#define VP9_FEAT_LVL_ALT_Q_EN(v) FIELD_PREP(BIT(21), !!(v))
+#define VP9_FEAT_LVL_ALT_Q(v) FIELD_PREP(GENMASK(20, 12), v)
+#define VP9_FEAT_LVL_ALT_L_EN(v) FIELD_PREP(BIT(11), !!(v))
+#define VP9_FEAT_LVL_ALT_L(v) FIELD_PREP(GENMASK(10, 4), v)
+#define VP9_FEAT_LVL_REF_FRAME_EN(v) FIELD_PREP(BIT(3), !!(v))
+#define VP9_FEAT_LVL_REF_FRAME(v) FIELD_PREP(GENMASK(2, 1), v)
+#define VP9_FEAT_LVL_SKIP_EN(v) FIELD_PREP(BIT(0), !!(v))
+#define VP9_FEAT_LVL_SKIP(v)
+
+#define VP9_REF_SEL_LAST(v) FIELD_PREP(GENMASK(2, 0), v)
+#define VP9_REF_BIAS_LAST(v) FIELD_PREP(BIT(3), !!(v))
+#define VP9_REF_SEL_GOLDEN(v) FIELD_PREP(GENMASK(6, 4), v)
+#define VP9_REF_BIAS_GOLDEN(v) FIELD_PREP(BIT(7), !!(v))
+#define VP9_REF_SEL_ALT(v) FIELD_PREP(GENMASK(10, 8), v)
+#define VP9_REF_BIAS_ALT(v) FIELD_PREP(BIT(11), !!(v))
+#define VP9_REFERENCE_MODE(v) FIELD_PREP(GENMASK(13, 12), v)
+#define VP9_PARALLEL_DEC(v) FIELD_PREP(BIT(14), !!(v))
+#define VP9_REFRESH_CTX(v) FIELD_PREP(BIT(15), !!(v))
+#define VP9_INTERP_FILTER(v) FIELD_PREP(GENMASK(18, 16), v)
+#define VP9_HIGH_PREC_MV(v) FIELD_PREP(BIT(19), !!(v))
+#define VP9_ERR_RES(v) FIELD_PREP(BIT(20), !!(v))
+#define VP9_HAS_REF(v) FIELD_PREP(BIT(21), !!(v))
+#define VP9_SEG_ABS(v) FIELD_PREP(BIT(22), !!(v))
+#define VP9_SEG_UDATA_TEMP(v) FIELD_PREP(BIT(23), !!(v))
+#define VP9_SEG_UPDATE_MAP(v) FIELD_PREP(BIT(24), !!(v))
+#define VP9_SEG_ENABLED(v) FIELD_PREP(BIT(25), !!(v))
+#define VP9_SEG_RESET(v) FIELD_PREP(BIT(26), !!(v))
+
+#define VP9_MAX_TILE_COLS (1 << 4)
+#define VP9_REF_SCALE_SHIFT 14
+#define VP9_LAST_FRAME 1
+#define VP9_GOLDEN_FRAME 2
+#define VP9_ALTREF_FRAME 3
+
+struct avd_vp9_seg_probs {
+ u8 tree_probs[7];
+ u8 pred_probs[3];
+};
+
+struct avd_vp9_probs {
+ struct avd_vp9_seg_probs seg;
+ u8 tx8[2][1];
+ u8 tx16[2][2];
+ u8 tx32[2][3];
+ /* [4][2][2][k=6][(k == 0) ? 3 : 6][3] */
+ u8 coef[1584];
+ u8 skip[3];
+ u8 inter_mode[7][3];
+ u8 interp_filter[4][2];
+ u8 is_inter[4];
+ u8 comp_mode[5];
+ u8 single_ref[5][2];
+ u8 comp_ref[5];
+ u8 y_mode[4][9];
+ u8 uv_mode[10][9];
+ u8 partition[16][3];
+ u8 joint[3];
+ struct mv_comp {
+ u8 sign;
+ u8 classes[10];
+ u8 class0_bit;
+ u8 bits[10];
+ } mv_comp[2];
+ struct mv_fr {
+ u8 class0_fr[2][3];
+ u8 fr[3];
+ } mv_fr[2];
+ struct mv_hp {
+ u8 class0_hp;
+ u8 hp;
+ } mv_hp[2];
+};
+
+struct avd_vp9_frame_symbol_counts {
+ u32 padding;
+ u32 tx8p[2][2];
+ u32 tx16p[2][3];
+ u32 tx32p[2][4];
+ /* [4][2][2][k=6][(k == 0) ? 3 : 6] */
+ u32 eob_0[528];
+ /*
+ * struct ref_cnt { u32 coef[3]; u32 eob_1; }
+ * struct ref_cnt cef_counts[4][2][2][k=6][(k == 0) ? 3 : 6];
+ */
+ u32 ref_cnt[2112];
+ u32 skip[3][2];
+ u32 mv_mode[7][4];
+ u32 filter[4][3];
+ u32 intra_inter[4][2];
+ u32 comp[5][2];
+ u32 single_ref[5][2][2];
+ u32 comp_ref[5][2];
+ u32 y_mode[4][10];
+ u32 uv_mode[10][10];
+ u32 partition[16][4];
+ u32 mv_joint[4];
+ struct mv_comp_ctn {
+ u32 sign[2];
+ u32 classes[11];
+ u32 class0[2];
+ u32 bits[10][2];
+ } mv_comp[2];
+ struct mv_fr_cnt {
+ u32 class0_fr[2][4];
+ u32 fr[4];
+ } mv_fr[2];
+ struct mv_hp_cnt {
+ u32 class0_hp[2];
+ u32 hp[2];
+ } mv_hp[2];
+};
+
+struct avd_vp9_frame_info {
+ u32 valid : 1;
+ u32 segmapid : 1;
+ u32 frame_context_idx : 2;
+ u32 reference_mode : 2;
+ u32 tx_mode : 3;
+ u32 interpolation_filter : 3;
+ u32 flags;
+ u64 timestamp;
+ struct v4l2_vp9_segmentation seg;
+ struct v4l2_vp9_loop_filter lf;
+};
+
+struct avd_vp9_run {
+ struct avd_run base;
+
+ const struct v4l2_ctrl_vp9_frame *decode_params;
+ const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates;
+};
+
+struct avd_vp9_ctx {
+ struct v4l2_vp9_frame_symbol_counts cnts;
+ struct v4l2_vp9_frame_context probability_tables;
+ struct v4l2_vp9_frame_context frame_context[4];
+ struct avd_vp9_bufs {
+ struct avd_buf az_left;
+ struct avd_buf above_info;
+ struct avd_buf ip_above;
+ struct avd_buf lf_above;
+ struct avd_buf lf_left_info;
+ struct avd_buf lf_left;
+ struct avd_buf color;
+ struct avd_buf seg;
+ struct avd_buf counts;
+ struct avd_buf probs;
+ } bufs;
+ struct avd_vp9_frame_info cur;
+ struct avd_vp9_frame_info last;
+ u8 submit_num;
+};
+
+static void set_refs(struct avd_ctx *ctx, struct avd_vp9_run *run)
+{
+ const struct v4l2_ctrl_vp9_frame *frame = run->decode_params;
+ struct avd_decoded_buffer *dst, *ref_buf[4];
+ dma_addr_t addr;
+ int xscale, yscale;
+
+ dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf);
+
+ ref_buf[0] = avd_get_ref_buf(ctx, &dst->base.vb, frame->last_frame_ts);
+ ref_buf[1] =
+ avd_get_ref_buf(ctx, &dst->base.vb, frame->golden_frame_ts);
+ ref_buf[2] = avd_get_ref_buf(ctx, &dst->base.vb, frame->alt_frame_ts);
+
+ push(0, "");
+ push(0, "");
+ push(0, "");
+
+ for (int i = 0; i < V4L2_VP9_NUM_FRAME_CTX - 1; i++) {
+ addr = vb2_dma_contig_plane_dma_addr(
+ &ref_buf[i]->base.vb.vb2_buf, 0) +
+ ref_buf[i]->comp.start_offset;
+
+ push(AVD_REF_FLAG_CONST, "hdr_9c_ref_100");
+ push(AVD_HDR_HEIGHT(ref_buf[i]->vp9.height - 1) |
+ AVD_HDR_WIDTH(ref_buf[i]->vp9.width - 1),
+ "hdr_70_ref_height_width");
+
+ xscale = (ref_buf[i]->vp9.width << VP9_REF_SCALE_SHIFT) /
+ dst->vp9.width;
+ yscale = (ref_buf[i]->vp9.height << VP9_REF_SCALE_SHIFT) /
+ dst->vp9.height;
+ push(AVD_HDR_HEIGHT(xscale) | AVD_HDR_WIDTH(yscale),
+ "ref_scale");
+
+ push_comp(ctx, addr, ref_buf[i]->comp.offsets);
+ }
+}
+
+static u32 make_flags1(struct avd_ctx *ctx, struct avd_vp9_run *run)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ const struct v4l2_ctrl_vp9_frame *frame = run->decode_params;
+ struct avd_decoded_buffer *dst, *last, *golden, *alt;
+
+ dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf);
+ last = avd_get_ref_buf(ctx, &dst->base.vb, frame->last_frame_ts);
+ golden = avd_get_ref_buf(ctx, &dst->base.vb, frame->golden_frame_ts);
+ alt = avd_get_ref_buf(ctx, &dst->base.vb, frame->alt_frame_ts);
+ u32 flags;
+
+ /*
+ * i have only seen V4L2_VP9_SIGN_BIAS_ALT, im simply guessing the
+ * same applies for last and golden
+ */
+ flags = VP9_REF_SEL_LAST(VP9_LAST_FRAME);
+
+ flags |= VP9_REF_BIAS_LAST(frame->ref_frame_sign_bias &
+ V4L2_VP9_SIGN_BIAS_LAST);
+ flags |= VP9_REF_SEL_GOLDEN(golden == last ? VP9_LAST_FRAME :
+ VP9_GOLDEN_FRAME);
+ flags |= VP9_REF_BIAS_GOLDEN(frame->ref_frame_sign_bias &
+ V4L2_VP9_SIGN_BIAS_GOLDEN);
+ flags |= VP9_REF_SEL_ALT(alt != golden ? VP9_ALTREF_FRAME :
+ alt != last ? VP9_GOLDEN_FRAME :
+ VP9_LAST_FRAME);
+ flags |= VP9_REF_BIAS_ALT(frame->ref_frame_sign_bias &
+ V4L2_VP9_SIGN_BIAS_ALT);
+
+ flags |= VP9_REFERENCE_MODE(frame->reference_mode);
+
+ flags |= VP9_PARALLEL_DEC(frame->flags &
+ V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE);
+ flags |= VP9_REFRESH_CTX(frame->flags &
+ V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX);
+ flags |= VP9_INTERP_FILTER(frame->interpolation_filter);
+ flags |= VP9_HIGH_PREC_MV(frame->flags &
+ V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV);
+
+ flags |=
+ VP9_ERR_RES(frame->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT);
+
+ flags |= VP9_SEG_ABS(frame->seg.flags &
+ V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE);
+ flags |= VP9_SEG_UDATA_TEMP(
+ frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA &&
+ frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE);
+ flags |= VP9_SEG_UPDATE_MAP(frame->seg.flags &
+ V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP);
+ flags |= VP9_SEG_ENABLED(frame->seg.flags &
+ V4L2_VP9_SEGMENTATION_FLAG_ENABLED);
+
+ flags |= VP9_HAS_REF(
+ !(frame->flags & V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT) &&
+ !(frame->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME) &&
+ vp9_ctx->last.valid &&
+ vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_SHOW_FRAME &&
+ !(vp9_ctx->last.flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME));
+
+ flags |= VP9_SEG_RESET(
+ frame->seg.flags & V4L2_VP9_SEGMENTATION_FLAG_ENABLED &&
+ (frame->flags & V4L2_VP9_FRAME_FLAG_KEY_FRAME ||
+ (vp9_ctx->last.valid &&
+ !(vp9_ctx->last.seg.flags &
+ V4L2_VP9_SEGMENTATION_FLAG_ENABLED) &&
+ vp9_ctx->last.flags & (V4L2_VP9_FRAME_FLAG_KEY_FRAME |
+ V4L2_VP9_FRAME_FLAG_INTRA_ONLY))));
+
+ return flags;
+}
+
+static u32 seg_features(struct avd_ctx *ctx, struct avd_vp9_run *run,
+ unsigned int segid)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ const struct v4l2_vp9_segmentation *seg = &vp9_ctx->cur.seg;
+ u32 feature_val = 0;
+ int feature_id = 0;
+
+ feature_id = V4L2_VP9_SEG_LVL_ALT_Q;
+ feature_val |= VP9_FEAT_LVL_ALT_Q_EN(v4l2_vp9_seg_feat_enabled(
+ seg->feature_enabled, feature_id, segid));
+ feature_val |= VP9_FEAT_LVL_ALT_Q(seg->feature_data[segid][feature_id]);
+
+ feature_id = V4L2_VP9_SEG_LVL_ALT_L;
+ feature_val |= VP9_FEAT_LVL_ALT_L_EN(v4l2_vp9_seg_feat_enabled(
+ seg->feature_enabled, feature_id, segid));
+ feature_val |= VP9_FEAT_LVL_ALT_L(seg->feature_data[segid][feature_id]);
+
+ feature_id = V4L2_VP9_SEG_LVL_REF_FRAME;
+ feature_val |= VP9_FEAT_LVL_REF_FRAME_EN(v4l2_vp9_seg_feat_enabled(
+ seg->feature_enabled, feature_id, segid));
+ feature_val |=
+ VP9_FEAT_LVL_REF_FRAME(seg->feature_data[segid][feature_id]);
+
+ feature_id = V4L2_VP9_SEG_LVL_SKIP;
+ feature_val |= VP9_FEAT_LVL_SKIP_EN(v4l2_vp9_seg_feat_enabled(
+ seg->feature_enabled, feature_id, segid));
+
+ return feature_val;
+}
+
+static void set_header(struct avd_ctx *ctx, struct avd_vp9_run *run)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ const struct v4l2_ctrl_vp9_compressed_hdr *prob_updates =
+ run->prob_updates;
+ const struct v4l2_ctrl_vp9_frame *frame = run->decode_params;
+ struct avd_dev *avd = ctx->dev;
+ u32 bytesperline;
+
+ bool intra_only = !!(frame->flags & (V4L2_VP9_FRAME_FLAG_KEY_FRAME |
+ V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
+
+ push(AVD_OP_HDR | AVD_OP_HDR_FLAG_DECOMP(ctx->decomp) |
+ AVD_OP_HDR_FLAG_INTRA(intra_only) | AVD_OP_HDR_CONST |
+ AVD_OP_HDR_FLAG_PIPE_STATE(
+ !(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE)),
+ "hdr_34_start_hdr");
+
+ push(AVD_HDR_CODEC_MODE(AVD_CODEC_VP9), "hdr_38_mode");
+
+ push(AVD_HDR_HEIGHT(frame->frame_height_minus_1) |
+ AVD_HDR_WIDTH(frame->frame_width_minus_1),
+ "hdr_28_height_width_shift3");
+ push(0, "");
+ push(AVD_HDR_HEIGHT(frame->frame_height_minus_1) |
+ AVD_HDR_WIDTH(frame->frame_width_minus_1),
+ "hdr_38_height_width_shift3");
+
+ push(AVD_HDR_COMMON_CHROMA_FORMAT(1) |
+ AVD_HDR_COMMON_BIT_DEPTH_C(frame->bit_depth - 8) |
+ AVD_HDR_COMMON_BIT_DEPTH_L(frame->bit_depth - 8) |
+ AVD_HDR_COMMON_LUMA_CBS(3) |
+ AVD_HDR_COMMON_LUMA_TBS(min(prob_updates->tx_mode, 3)) |
+ AVD_HDR_COMMON_FLAG0(prob_updates->tx_mode &
+ V4L2_VP9_TX_MODE_SELECT),
+ "hdr_2c_txfm_mode");
+
+ push(make_flags1(ctx, run), "hdr_40_flags1_pt1");
+
+ for (int i = 0; i < 8; i++)
+ push(seg_features(ctx, run, i), "seg");
+
+ push(AVD_HDR_FEAT_VP9, "unk_const");
+ push(0, "");
+ push(0, "");
+
+ pusha(vp9_ctx->bufs.counts.addr, "counts", 0);
+ pusha(vp9_ctx->bufs.probs.addr, "probs", 0);
+ pusha(vp9_ctx->bufs.above_info.addr, "above_info", 0);
+ pusha(vp9_ctx->bufs.seg.addr, "seg", 1);
+ pusha(vp9_ctx->bufs.seg.addr, "seg", 2);
+ pusha(vp9_ctx->bufs.color.addr, "color", 3);
+ pusha(vp9_ctx->bufs.color.addr, "color", 4);
+
+ push(VP9_Q_IDX(frame->quant.base_q_idx) |
+ VP9_Q_DC_Y(frame->quant.delta_q_y_dc) |
+ VP9_Q_DC_UV(frame->quant.delta_q_uv_dc) |
+ VP9_Q_AC_UV(frame->quant.delta_q_uv_ac),
+ "hdr_4c_base_q_idx");
+ /* filter related flags? */
+ push(VP9_LF_SHARPNESS(frame->lf.sharpness) |
+ (frame->lf.flags &
+ V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED ?
+ VP9_LF_REF0(frame->lf.ref_deltas[0]) |
+ VP9_LF_REF1(frame->lf.ref_deltas[1]) |
+ VP9_LF_REF2(frame->lf.ref_deltas[2]) |
+ VP9_LF_REF3(frame->lf.ref_deltas[3]) :
+ 0),
+ "hdr_44_flags1_pt2");
+
+ push(VP9_LF_LV(frame->lf.level) |
+ (frame->lf.flags &
+ V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED ?
+ VP9_LF_MODE0(frame->lf.mode_deltas[0]) |
+ VP9_LF_MODE1(frame->lf.mode_deltas[1]) :
+ 0),
+ "hdr_48_loop_filter_level");
+
+ push(0, "");
+ push(0, "");
+
+ if (avd->variant->revision == 3)
+ push(0, "");
+ if (!(avd->variant->quirks & AVD_QUIRK_NO_PIPE_STATE))
+ pusha(ctx->pipe_state.addr, "pipe_state", 0);
+
+ pusha(vp9_ctx->bufs.ip_above.addr, "ip_above", 0);
+ pusha(vp9_ctx->bufs.lf_above.addr, "lf_above", 0);
+ /* no lf_above_info? */
+ pusha((u64)0, "hdr_e8_sps0_tile_addr_lsb8", 0);
+ pusha(vp9_ctx->bufs.lf_left.addr, "lf_left", 0);
+ pusha(vp9_ctx->bufs.lf_left_info.addr, "lf_left_info", 0);
+ pusha(vp9_ctx->bufs.az_left.addr, "az_left", 0);
+
+ push(0, "");
+
+ push_comp(ctx, run->base.comp_out, ctx->comp.offsets);
+
+ pusha((u64)0, "packed_fmt_scratch", 0);
+
+ bytesperline = ctx->decoded_fmt.fmt.pix_mp.plane_fmt[0].bytesperline;
+ if (avd->variant->quirks & AVD_QUIRK_LSR)
+ bytesperline = bytesperline >> 4;
+
+ pusha(run->base.y_out, "hdr_168_y_addr_lsb8", 0);
+ push(bytesperline, "hdr_170_width_align");
+ pusha(run->base.uv_out, "hdr_16c_uv_addr_lsb8", 0);
+ push(bytesperline, "hdr_174_width_align");
+ push(0, "");
+ push(AVD_HDR_HEIGHT(frame->frame_height_minus_1) |
+ AVD_HDR_WIDTH(frame->frame_width_minus_1),
+ "cm3_height_width");
+
+ if (!(intra_only))
+ set_refs(ctx, run);
+}
+
+static void set_tiles(struct avd_ctx *ctx, struct avd_vp9_run *run)
+{
+ const struct v4l2_ctrl_vp9_frame *frame = run->decode_params;
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ const u8 *data = vb2_plane_vaddr(&src->vb2_buf, 0);
+
+ u32 offset =
+ frame->uncompressed_header_size + frame->compressed_header_size;
+ u32 size = vb2_get_plane_payload(&src->vb2_buf, 0) - offset;
+ u32 num_tile_rows = 1 << frame->tile_rows_log2;
+ u32 num_tile_cols = 1 << frame->tile_cols_log2;
+ u32 tile_size;
+
+ /* 6.2.6 Compute image size syntax */
+ u32 sb_64_cols = (((frame->frame_width_minus_1 + 8) >> 3) + 7) >> 3;
+ u32 sb_64_rows = (((frame->frame_height_minus_1 + 8) >> 3) + 7) >> 3;
+
+ for (int row = 0; row < num_tile_rows; row++)
+ for (int col = 0; col < num_tile_cols; col++) {
+ ctx->job.num++;
+ if (row == num_tile_rows - 1 &&
+ col == num_tile_cols - 1) {
+ tile_size = size;
+ } else {
+ tile_size = get_unaligned_be32(&data[offset]);
+ /* i have crashed my computer because of this */
+ if (tile_size > size - 4)
+ return;
+ offset += 4;
+ size -= 4;
+ }
+ push(AVD_OP_CODED_DATA |
+ AVD_OP_CODED_DATA_ADDR(
+ run->base.coded_in >> 32),
+ "cm3_cmd_set_slice_data");
+ push((u32)((run->base.coded_in + offset) & 0xffffffff),
+ "til_ab4_tile_addr_low");
+ push(tile_size, "til_ab8_tile_size");
+ push(AVD_OP_SL_DIM_START |
+ AVD_OP_SL_DIM_START_Y((row * sb_64_rows) /
+ num_tile_rows) |
+ AVD_OP_SL_DIM_START_X((col * sb_64_cols) /
+ num_tile_cols),
+ "i");
+
+ push(AVD_SL_DIM_END_COL(col) |
+ AVD_SL_DIM_END_Y(((row + 1) * sb_64_rows) /
+ num_tile_rows - 1) |
+ AVD_SL_DIM_END_X(((col + 1) * sb_64_cols) /
+ num_tile_cols - 1),
+ "til_ac0_tile_dims");
+
+ offset += tile_size;
+ size -= tile_size;
+ vp9_ctx->submit_num++;
+ }
+}
+
+static void update_dec_buf_info(struct avd_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 avd_vp9_ctx *vp9_ctx,
+ struct avd_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.interpolation_filter = dec_params->interpolation_filter;
+ vp9_ctx->cur.flags = dec_params->flags;
+ vp9_ctx->cur.timestamp = buf->base.vb.vb2_buf.timestamp;
+ vp9_ctx->cur.seg = dec_params->seg;
+ vp9_ctx->cur.lf = dec_params->lf;
+}
+
+static void update_ctx_last_info(struct avd_vp9_ctx *vp9_ctx)
+{
+ vp9_ctx->last = vp9_ctx->cur;
+}
+
+static void copy_vp9_frame_mv(struct avd_vp9_probs *avd_probs,
+ const struct v4l2_vp9_frame_context *probs)
+{
+ memcpy(avd_probs->joint, probs->mv.joint, sizeof(avd_probs->joint));
+ for (int i = 0; i < 2; i++) {
+ avd_probs->mv_comp[i].sign = probs->mv.sign[i];
+ memcpy(avd_probs->mv_comp[i].bits, probs->mv.bits[i],
+ sizeof(avd_probs->mv_comp[i].bits));
+ avd_probs->mv_comp[i].class0_bit = probs->mv.class0_bit[i];
+ memcpy(avd_probs->mv_comp[i].classes, probs->mv.classes[i],
+ sizeof(avd_probs->mv_comp[i].bits));
+
+ memcpy(avd_probs->mv_fr[i].class0_fr, probs->mv.class0_fr[i],
+ sizeof(avd_probs->mv_fr[i].class0_fr));
+ memcpy(avd_probs->mv_fr[i].fr, probs->mv.fr[i],
+ sizeof(avd_probs->mv_fr[i].fr));
+
+ avd_probs->mv_hp[i].class0_hp = probs->mv.class0_hp[i];
+ avd_probs->mv_hp[i].hp = probs->mv.hp[i];
+ }
+}
+
+static void init_probs(struct avd_ctx *ctx, const struct avd_vp9_run *run)
+{
+ const struct v4l2_ctrl_vp9_frame *dec_params;
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ struct avd_vp9_probs *avd_probs = vp9_ctx->bufs.probs.cpu;
+ const struct v4l2_vp9_segmentation *seg;
+ const struct v4l2_vp9_frame_context *probs;
+ bool intra_only;
+ int count = 0;
+
+ dec_params = run->decode_params;
+ probs = &vp9_ctx->probability_tables;
+ seg = &dec_params->seg;
+
+ memset(avd_probs, 0, sizeof(*avd_probs));
+
+ intra_only = !!(dec_params->flags & (V4L2_VP9_FRAME_FLAG_KEY_FRAME |
+ V4L2_VP9_FRAME_FLAG_INTRA_ONLY));
+
+ memcpy(avd_probs->tx8, probs->tx8, sizeof(avd_probs->tx8));
+ memcpy(avd_probs->tx16, probs->tx16, sizeof(avd_probs->tx16));
+ memcpy(avd_probs->tx32, probs->tx32, sizeof(avd_probs->tx32));
+ memcpy(avd_probs->skip, probs->skip, sizeof(avd_probs->skip));
+ memcpy(avd_probs->inter_mode, probs->inter_mode,
+ sizeof(avd_probs->inter_mode));
+ memcpy(avd_probs->interp_filter, probs->interp_filter,
+ sizeof(avd_probs->interp_filter));
+ memcpy(avd_probs->is_inter, probs->is_inter,
+ sizeof(avd_probs->is_inter));
+ memcpy(avd_probs->comp_mode, probs->comp_mode,
+ sizeof(avd_probs->comp_mode));
+ memcpy(avd_probs->single_ref, probs->single_ref,
+ sizeof(avd_probs->single_ref));
+ memcpy(avd_probs->comp_ref, probs->comp_ref,
+ sizeof(avd_probs->comp_ref));
+ memcpy(avd_probs->y_mode, probs->y_mode, sizeof(avd_probs->y_mode));
+
+ memcpy(avd_probs->partition,
+ intra_only ? v4l2_vp9_kf_partition_probs : probs->partition,
+ sizeof(avd_probs->partition));
+ memcpy(avd_probs->uv_mode,
+ intra_only ? v4l2_vp9_kf_uv_mode_prob : probs->uv_mode,
+ sizeof(avd_probs->uv_mode));
+
+ copy_vp9_frame_mv(avd_probs, probs);
+
+ for (int t = 0; t < 4; t++)
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ for (int k = 0; k < 6; k++) {
+ int max_l = (k == 0) ? 3 : 6;
+ for (int l = 0; l < max_l; l++) {
+ for (int n = 0; n < 3; n++)
+ avd_probs->coef[count++] =
+ probs->coef[t][i][j][k][l][n];
+ }
+ }
+
+ memcpy(avd_probs->seg.pred_probs, seg->pred_probs,
+ sizeof(avd_probs->seg.pred_probs));
+ memcpy(avd_probs->seg.tree_probs, seg->tree_probs,
+ sizeof(avd_probs->seg.tree_probs));
+}
+
+static int validate_dec_params(struct avd_ctx *ctx,
+ const struct v4l2_ctrl_vp9_frame *dec_params)
+{
+ unsigned int aligned_width, aligned_height;
+
+ if (dec_params->bit_depth > 10)
+ /* not implemented */
+ return -EINVAL;
+
+ if (dec_params->profile == 1 || dec_params->profile > 2)
+ return -EINVAL;
+
+ if (dec_params->frame_height_minus_1 + 1 < 64 ||
+ dec_params->frame_width_minus_1 + 1 < 64)
+ return -EINVAL;
+
+ aligned_width = round_up(dec_params->frame_width_minus_1 + 1, 64);
+ aligned_height = round_up(dec_params->frame_height_minus_1 + 1, 16);
+
+ /*
+ * Userspace should update the capture/decoded format when the
+ * resolution changes.
+ */
+ if (aligned_width != ctx->decoded_fmt.fmt.pix_mp.width ||
+ aligned_height != ctx->decoded_fmt.fmt.pix_mp.height) {
+ dev_err(ctx->dev->dev,
+ "unexpected bitstream resolution %dx%d\n",
+ aligned_width, aligned_height);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int avd_vp9_alloc_bufs(struct avd_ctx *ctx)
+{
+ struct avd_dev *avd = ctx->dev;
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ int ret, w, h, bit_depth;
+
+ w = fmt_width(ctx);
+ h = fmt_height(ctx);
+ bit_depth = (ctx->image_fmt == AVD_IMG_FMT_420_10BIT ||
+ ctx->image_fmt == AVD_IMG_FMT_422_10BIT) ?
+ 10 :
+ 8;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.probs,
+ sizeof(struct avd_vp9_probs));
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.counts,
+ sizeof(struct avd_vp9_frame_symbol_counts));
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.color,
+ DIV_ROUND_UP(w, 16) * DIV_ROUND_UP(h, 64) * 144);
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.ip_above,
+ DIV_ROUND_UP(w, 16) * 4 * bit_depth +
+ (VP9_MAX_TILE_COLS - 1) * 128);
+ if (ret)
+ return ret;
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.lf_above,
+ DIV_ROUND_UP(w, 8) * 16 * bit_depth);
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.seg, DIV_ROUND_UP(w, 8) * 24);
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.lf_left,
+ DIV_ROUND_UP(h, 8) * 16 * bit_depth);
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.lf_left_info,
+ DIV_ROUND_UP(h, 64) * 16);
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.az_left,
+ bit_depth * 36 * DIV_ROUND_UP(h, 8) +
+ bit_depth * 18 * DIV_ROUND_UP(h, 16));
+ if (ret)
+ return ret;
+
+ ret = avd_buf_alloc(avd, &vp9_ctx->bufs.above_info,
+ DIV_ROUND_UP(w, 64) * 288 +
+ (VP9_MAX_TILE_COLS - 1) * 128);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int avd_vp9_run_preamble(struct avd_ctx *ctx, struct avd_vp9_run *run)
+{
+ struct v4l2_ctrl *ctrl;
+ const struct v4l2_ctrl_vp9_frame *dec_params;
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ unsigned int fctx_idx;
+ int ret;
+
+ avd_run_preamble(ctx, &run->base);
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_VP9_COMPRESSED_HDR);
+ if (WARN_ON(!ctrl))
+ return -EINVAL;
+ run->prob_updates = ctrl->p_cur.p;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, V4L2_CID_STATELESS_VP9_FRAME);
+ if (WARN_ON(!ctrl))
+ return -EINVAL;
+ dec_params = ctrl->p_cur.p;
+
+ ret = validate_dec_params(ctx, dec_params);
+ if (ret)
+ return ret;
+
+ run->decode_params = dec_params;
+
+ vp9_ctx->cur.tx_mode = run->prob_updates->tx_mode;
+
+ fctx_idx = v4l2_vp9_reset_frame_ctx(dec_params, vp9_ctx->frame_context);
+ vp9_ctx->cur.frame_context_idx = fctx_idx;
+
+ vp9_ctx->probability_tables = vp9_ctx->frame_context[fctx_idx];
+ v4l2_vp9_fw_update_probs(&vp9_ctx->probability_tables,
+ run->prob_updates, dec_params);
+
+ return 0;
+}
+
+static int avd_vp9_run(struct avd_ctx *ctx)
+{
+ struct avd_vp9_run run;
+ struct avd_vp9_ctx *vp9_ctx;
+ struct avd_decoded_buffer *dst;
+ int ret;
+
+ ret = avd_vp9_run_preamble(ctx, &run);
+ if (ret) {
+ avd_run_postamble(ctx, &run.base);
+ return ret;
+ }
+
+ ret = avd_init_job(
+ ctx, AVD_CODEC_VP9,
+ (1 << run.decode_params->tile_rows_log2) *
+ (1 << run.decode_params->tile_cols_log2) +
+ 1);
+ if (ret)
+ return ret;
+
+ init_probs(ctx, &run);
+
+ vp9_ctx = ctx->priv;
+ dst = vb2_to_avd_decoded_buf(&run.base.bufs.dst->vb2_buf);
+ update_dec_buf_info(dst, run.decode_params);
+ update_ctx_cur_info(vp9_ctx, dst, run.decode_params);
+
+ set_header(ctx, &run);
+ vp9_ctx->submit_num = 0;
+ set_tiles(ctx, &run);
+ avd_run_postamble(ctx, &run.base);
+
+ return avd_submit_job(ctx);
+}
+
+#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)
+
+static void avd_vp9_done(struct avd_ctx *ctx, struct vb2_v4l2_buffer *src_buf,
+ struct vb2_v4l2_buffer *dst_buf,
+ enum vb2_buffer_state result)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ unsigned int fctx_idx;
+
+ /* v4l2-specific stuff */
+ if (result == VB2_BUF_STATE_ERROR)
+ goto out_update_last;
+
+ /*
+ * vp9 stuff
+ *
+ * 6.1.2 refresh_probs()
+ *
+ * In the spec a complementary condition goes last in 6.1.2 refresh_probs(),
+ * but it makes no sense to perform all the activities from the first "if"
+ * there if we actually are not refreshing the frame context. On top of that,
+ * because of 6.2 uncompressed_header() whenever error_resilient_mode == 1,
+ * refresh_frame_context == 0. Consequently, if we don't jump to out_update_last
+ * it means error_resilient_mode must be 0.
+ */
+ 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;
+
+ /* 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;
+
+ 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) {
+ const struct avd_vp9_frame_symbol_counts *cnts;
+ int i;
+ u32 tx16p[2][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];
+
+ cnts = vp9_ctx->bufs.counts.cpu;
+
+ for (i = 0; i < ARRAY_SIZE(cnts->tx16p); ++i)
+ memcpy(tx16p[i], cnts->tx16p[i],
+ sizeof(tx16p[0]));
+
+ for (i = 0; i < 2; i++) {
+ memcpy(sign[i], cnts->mv_comp[i].sign,
+ sizeof(sign[0]));
+ memcpy(classes[i], cnts->mv_comp[i].classes,
+ sizeof(classes[0]));
+ memcpy(class0[i], cnts->mv_comp[i].class0,
+ sizeof(class0[0]));
+ memcpy(bits[i], cnts->mv_comp[i].bits,
+ sizeof(bits[0]));
+ memcpy(class0_fp[i], cnts->mv_fr[i].class0_fr,
+ sizeof(class0_fp[0]));
+ memcpy(fp[i], cnts->mv_fr[i].fr, sizeof(fp[0]));
+ memcpy(class0_hp[i], cnts->mv_hp[i].class0_hp,
+ sizeof(class0_hp[0]));
+ memcpy(hp[i], cnts->mv_hp[i].hp, sizeof(hp[0]));
+ }
+
+ counts->tx16p = &tx16p;
+ counts->sign = &sign;
+ counts->classes = &classes;
+ counts->class0 = &class0;
+ counts->bits = &bits;
+ counts->class0_fp = &class0_fp;
+ counts->fp = &fp;
+ counts->class0_hp = &class0_hp;
+ counts->hp = &hp;
+
+ /* load_probs2() already done */
+ 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);
+ }
+ }
+
+ /* 6.1.2 refresh_probs(): save_probs(fctx_idx) */
+ vp9_ctx->frame_context[fctx_idx] = vp9_ctx->probability_tables;
+
+out_update_last:
+ update_ctx_last_info(vp9_ctx);
+}
+
+static noinline_for_stack void
+avd_init_v4l2_vp9_count_tbl(struct avd_ctx *ctx)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ struct avd_vp9_frame_symbol_counts *cnts = vp9_ctx->bufs.counts.cpu;
+ int coeff_cnts = 0, eob_cnts = 0;
+
+ vp9_ctx->cnts.intra_inter = &cnts->intra_inter;
+ vp9_ctx->cnts.y_mode = &cnts->y_mode;
+ vp9_ctx->cnts.uv_mode = &cnts->uv_mode;
+ vp9_ctx->cnts.comp = &cnts->comp;
+ vp9_ctx->cnts.comp_ref = &cnts->comp_ref;
+ vp9_ctx->cnts.single_ref = &cnts->single_ref;
+ vp9_ctx->cnts.filter = &cnts->filter;
+ vp9_ctx->cnts.mv_mode = &cnts->mv_mode;
+ vp9_ctx->cnts.mv_joint = &cnts->mv_joint;
+ /* all of the mv use a different structure, so they must all be copied */
+ vp9_ctx->cnts.tx8p = &cnts->tx8p;
+ /*
+ * AVD also uses "u32 tx16p[2][3]" instead of "u32 tx16p[2][4]", so
+ * they must also be copied
+ */
+ vp9_ctx->cnts.tx32p = &cnts->tx32p;
+ vp9_ctx->cnts.partition = &cnts->partition;
+ vp9_ctx->cnts.skip = &cnts->skip;
+
+ for (int t = 0; t < 4; t++)
+ for (int i = 0; i < 2; i++)
+ for (int j = 0; j < 2; j++)
+ for (int k = 0; k < 6; k++) {
+ int max_l = (k == 0) ? 3 : 6;
+ for (int l = 0; l < max_l; l++) {
+ vp9_ctx->cnts.coeff[t][i][j][k][l] =
+ (u32 (*)[3])&cnts->ref_cnt[coeff_cnts];
+ coeff_cnts += 3;
+ vp9_ctx->cnts.eob[t][i][j][k][l][0] =
+ &cnts->eob_0[eob_cnts++];
+ vp9_ctx->cnts.eob[t][i][j][k][l][1] =
+ &cnts->ref_cnt[coeff_cnts++];
+ }
+ }
+}
+
+static int avd_vp9_start(struct avd_ctx *ctx)
+{
+ struct avd_vp9_ctx *vp9_ctx;
+ int ret;
+
+ vp9_ctx = kzalloc_obj(*vp9_ctx, GFP_KERNEL);
+ if (!vp9_ctx)
+ return -ENOMEM;
+
+ ctx->priv = vp9_ctx;
+ ret = avd_vp9_alloc_bufs(ctx);
+ if (ret)
+ goto err_free_ctx;
+
+ avd_init_v4l2_vp9_count_tbl(ctx);
+
+ return 0;
+
+err_free_ctx:
+ kfree(vp9_ctx);
+ ctx->priv = NULL;
+ return ret;
+}
+
+static void avd_vp9_stop(struct avd_ctx *ctx)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ struct avd_dev *avd = ctx->dev;
+
+ avd_buf_free(avd, &vp9_ctx->bufs.probs);
+ avd_buf_free(avd, &vp9_ctx->bufs.counts);
+ avd_buf_free(avd, &vp9_ctx->bufs.seg);
+ avd_buf_free(avd, &vp9_ctx->bufs.color);
+ avd_buf_free(avd, &vp9_ctx->bufs.ip_above);
+ avd_buf_free(avd, &vp9_ctx->bufs.lf_above);
+ avd_buf_free(avd, &vp9_ctx->bufs.above_info);
+ avd_buf_free(avd, &vp9_ctx->bufs.az_left);
+ avd_buf_free(avd, &vp9_ctx->bufs.lf_left_info);
+ avd_buf_free(avd, &vp9_ctx->bufs.lf_left);
+
+ kfree(vp9_ctx);
+}
+
+static enum avd_image_fmt avd_vp9_get_image_fmt(struct avd_ctx *ctx,
+ struct v4l2_ctrl *ctrl)
+{
+#define BIT_DEPTH(chroma) \
+ (frame->bit_depth == 8 ? AVD_IMG_FMT_##chroma##_8BIT : \
+ AVD_IMG_FMT_##chroma##_10BIT)
+ const struct v4l2_ctrl_vp9_frame *frame = ctrl->p_new.p_vp9_frame;
+
+ if (ctrl->id != V4L2_CID_STATELESS_VP9_FRAME)
+ return AVD_IMG_FMT_ANY;
+
+ /* 7.2.2 Color config semantics */
+ if (frame->flags & V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING) {
+ if (frame->flags & V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING)
+ return BIT_DEPTH(420);
+ else
+ return BIT_DEPTH(422);
+ }
+
+ return AVD_IMG_FMT_ANY;
+#undef BIT_DEPTH
+}
+
+static void avd_vp9_submit(struct avd_ctx *ctx)
+{
+ struct avd_vp9_ctx *vp9_ctx = ctx->priv;
+ struct avd_dev *avd = ctx->dev;
+ u32 submit_mask = ctx->dev->variant->revision == 3 ?
+ AVD_OP_EXEC_REV3_VP9_MASK :
+ 0;
+
+ writel(AVD_OP_EXEC | submit_mask |
+ AVD_OP_EXEC_FLAG_START_REV4(avd->variant->revision ==
+ 4) |
+ AVD_OP_EXEC_FLAG_START_REV3(avd->variant->revision ==
+ 3) |
+ AVD_OP_EXEC_FIFO_IDX(ctx->fifo_idx) |
+ AVD_OP_EXEC_FIFO_MASK(avd->variant->fifo_slots),
+ avd->ctrl + avd->variant->submit_offset);
+ for (int i = 0; i < vp9_ctx->submit_num - 1; i++)
+ writel(AVD_OP_EXEC | submit_mask |
+ AVD_OP_EXEC_FIFO_IDX(ctx->fifo_idx) |
+ AVD_OP_EXEC_FIFO_MASK(avd->variant->fifo_slots),
+ avd->ctrl + avd->variant->submit_offset);
+}
+
+const struct avd_coded_fmt_ops avd_vp9_fmt_ops = {
+ .start = avd_vp9_start,
+ .stop = avd_vp9_stop,
+ .run = avd_vp9_run,
+ .done = avd_vp9_done,
+ .submit = avd_vp9_submit,
+ .get_image_fmt = avd_vp9_get_image_fmt,
+};
diff --git a/drivers/media/platform/apple/avd/avd.h b/drivers/media/platform/apple/avd/avd.h
index 5dab6cb077fa..facf23d151c0 100644
--- a/drivers/media/platform/apple/avd/avd.h
+++ b/drivers/media/platform/apple/avd/avd.h
@@ -91,6 +91,12 @@ struct avd_ctrls {
unsigned int num_ctrls;
};
+struct avd_vp9_decoded_buffer_info {
+ unsigned short width;
+ unsigned short height;
+ unsigned int bit_depth : 4;
+};
+
struct avd_comp {
u32 size;
/* offset to start of compressed data */
@@ -103,6 +109,9 @@ struct avd_decoded_buffer {
/* Must be the first field in this struct. */
struct v4l2_m2m_buffer base;
struct avd_comp comp;
+ union {
+ struct avd_vp9_decoded_buffer_info vp9;
+ };
};
static inline struct avd_decoded_buffer *
@@ -238,6 +247,7 @@ void avd_run_preamble(struct avd_ctx *ctx, struct avd_run *run);
void avd_run_postamble(struct avd_ctx *ctx, struct avd_run *run);
extern const struct avd_coded_fmt_ops avd_h264_fmt_ops;
+extern const struct avd_coded_fmt_ops avd_vp9_fmt_ops;
extern const struct v4l2_ctrl_ops avd_ctrl_ops;
extern const struct v4l2_ioctl_ops avd_ioctl_ops;
--
2.55.0