Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
config VIDEO_ROCKCHIP_VDEC
tristate "Rockchip Video Decoder driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
depends on VIDEO_DEV
select MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
select V4L2_H264
select V4L2_VP9
help
Support for the Rockchip Video Decoder IP present on Rockchip SoCs,
which accelerates video decoding.
To compile this driver as a module, choose M here: the module
will be called rockchip-vdec.
@@ -0,0 +1,15 @@
obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o
rockchip-vdec-y += \
rkvdec.o \
rkvdec-cabac.o \
rkvdec-h264.o \
rkvdec-h264-common.o \
rkvdec-hevc.o \
rkvdec-hevc-common.o \
rkvdec-rcb.o \
rkvdec-vdpu381-h264.o \
rkvdec-vdpu381-hevc.o \
rkvdec-vdpu383-h264.o \
rkvdec-vdpu383-hevc.o \
rkvdec-vp9.o
@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Video Decoder bit writer
*
* Copyright (C) 2026 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*/
#ifndef RKVDEC_BIT_WRITER_H_
#define RKVDEC_BIT_WRITER_H_
#include <linux/types.h>
#include <linux/bits.h>
struct rkvdec_bw_field {
u16 offset;
u8 len;
};
#define BW_FIELD(_offset, _len) ((struct rkvdec_bw_field){ _offset, _len })
static inline void rkvdec_set_bw_field(u32 *buf, struct rkvdec_bw_field field, u32 value)
{
u8 bit = field.offset % 32;
u16 word = field.offset / 32;
u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
u64 val = ((u64)value << bit) & mask;
buf[word] &= ~mask;
buf[word] |= val;
if (bit + field.len > 32) {
buf[word + 1] &= ~(mask >> 32);
buf[word + 1] |= val >> 32;
}
}
#endif /* RKVDEC_BIT_WRITER_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Video Decoder CABAC tables
*
* Copyright (C) 2023 Collabora, Ltd.
* Sebastian Fricke <sebastian.fricke@collabora.com>
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*/
#ifndef RKVDEC_CABAC_H_
#define RKVDEC_CABAC_H_
#include <linux/types.h>
#define RKV_HEVC_CABAC_TABLE_SIZE 27456
extern const s8 rkvdec_h264_cabac_table[4][464][2];
extern const u8 rkvdec_hevc_cabac_table[RKV_HEVC_CABAC_TABLE_SIZE];
#endif /* RKVDEC_CABAC_H_ */
@@ -0,0 +1,215 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip video decoder h264 common functions
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <linux/v4l2-common.h>
#include <media/v4l2-h264.h>
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-h264-common.h"
#define RKVDEC_NUM_REFLIST 3
void lookup_ref_buf_idx(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
u32 i;
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb;
struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
struct vb2_buffer *buf = NULL;
if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) {
buf = vb2_find_buffer(cap_q, dpb[i].reference_ts);
if (!buf)
pr_debug("No buffer for reference_ts %llu",
dpb[i].reference_ts);
}
run->ref_buf[i] = buf;
}
}
void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
struct rkvdec_h264_run *run,
struct rkvdec_h264_reflists *reflists,
struct rkvdec_rps *hw_rps)
{
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
u32 i, j;
memset(hw_rps, 0, sizeof(*hw_rps));
/*
* Assign an invalid pic_num if DPB entry at that position is inactive.
* If we assign 0 in that position hardware will treat that as a real
* reference picture with pic_num 0, triggering output picture
* corruption.
*/
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
continue;
rkvdec_set_bw_field(hw_rps->info, RPS_FRAME_NUM(i), builder->refs[i].frame_num);
}
for (j = 0; j < RKVDEC_NUM_REFLIST; j++) {
for (i = 0; i < builder->num_valid; i++) {
struct v4l2_h264_reference *ref;
bool dpb_valid;
bool bottom;
switch (j) {
case 0:
ref = &reflists->p[i];
break;
case 1:
ref = &reflists->b0[i];
break;
case 2:
ref = &reflists->b1[i];
break;
}
if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb)))
continue;
dpb_valid = !!(run->ref_buf[ref->index]);
bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF;
rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_DPB_INFO(j, i),
ref->index | (dpb_valid << 4));
rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_BOTTOM_FLAG(j, i), bottom);
}
}
}
void assemble_hw_scaling_list(struct rkvdec_h264_run *run,
struct rkvdec_h264_scaling_list *scaling_list)
{
const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix;
const struct v4l2_ctrl_h264_pps *pps = run->pps;
if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT))
return;
BUILD_BUG_ON(sizeof(scaling_list->scaling_list_4x4) !=
sizeof(scaling->scaling_list_4x4));
BUILD_BUG_ON(sizeof(scaling_list->scaling_list_8x8) !=
sizeof(scaling->scaling_list_8x8));
memcpy(scaling_list->scaling_list_4x4,
scaling->scaling_list_4x4,
sizeof(scaling->scaling_list_4x4));
memcpy(scaling_list->scaling_list_8x8,
scaling->scaling_list_8x8,
sizeof(scaling->scaling_list_8x8));
}
#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2
int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx,
struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
fmt->num_planes = 1;
if (!fmt->plane_fmt[0].sizeimage)
fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
RKVDEC_H264_MAX_DEPTH_IN_BYTES;
return 0;
}
enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx,
struct v4l2_ctrl *ctrl)
{
const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps;
if (ctrl->id != V4L2_CID_STATELESS_H264_SPS)
return RKVDEC_IMG_FMT_ANY;
if (sps->bit_depth_luma_minus8 == 0) {
if (sps->chroma_format_idc == 2)
return RKVDEC_IMG_FMT_422_8BIT;
else
return RKVDEC_IMG_FMT_420_8BIT;
} else if (sps->bit_depth_luma_minus8 == 2) {
if (sps->chroma_format_idc == 2)
return RKVDEC_IMG_FMT_422_10BIT;
else
return RKVDEC_IMG_FMT_420_10BIT;
}
return RKVDEC_IMG_FMT_ANY;
}
int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx,
const struct v4l2_ctrl_h264_sps *sps)
{
unsigned int width, height;
if (sps->chroma_format_idc > 2)
/* Only 4:0:0, 4:2:0 and 4:2:2 are supported */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
/* Only 8-bit and 10-bit is supported */
return -EINVAL;
width = (sps->pic_width_in_mbs_minus1 + 1) * 16;
height = (sps->pic_height_in_map_units_minus1 + 1) * 16;
/*
* When frame_mbs_only_flag is not set, this is field height,
* which is half the final height (see (7-18) in the
* specification)
*/
if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
height *= 2;
if (width > ctx->coded_fmt.fmt.pix_mp.width ||
height > ctx->coded_fmt.fmt.pix_mp.height)
return -EINVAL;
return 0;
}
void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
struct v4l2_ctrl *ctrl;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_DECODE_PARAMS);
run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_SPS);
run->sps = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_PPS);
run->pps = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_SCALING_MATRIX);
run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
rkvdec_run_preamble(ctx, &run->base);
}
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip video decoder h264 common functions
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <media/v4l2-h264.h>
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-bitwriter.h"
struct rkvdec_h264_scaling_list {
u8 scaling_list_4x4[6][16];
u8 scaling_list_8x8[6][64];
u8 padding[128];
};
struct rkvdec_h264_reflists {
struct v4l2_h264_reference p[V4L2_H264_REF_LIST_LEN];
struct v4l2_h264_reference b0[V4L2_H264_REF_LIST_LEN];
struct v4l2_h264_reference b1[V4L2_H264_REF_LIST_LEN];
};
struct rkvdec_h264_run {
struct rkvdec_run base;
const struct v4l2_ctrl_h264_decode_params *decode_params;
const struct v4l2_ctrl_h264_sps *sps;
const struct v4l2_ctrl_h264_pps *pps;
const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix;
struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES];
};
#define RPS_FRAME_NUM(i) BW_FIELD((i) * 16, 16)
#define RPS_ENTRY_DPB_INFO(l, e) BW_FIELD(288 + (l) * 7 * 32 + (e) * 7, 5) //l: 0-2, e: 0-31
#define RPS_ENTRY_BOTTOM_FLAG(l, e) BW_FIELD(293 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31
#define RPS_ENTRY_VIEW_INDEX_OFF(l, e) BW_FIELD(294 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31
#define RKVDEC_H264_RPS_SIZE ALIGN(288 + 3 * 7 * 32, 128)
struct rkvdec_rps {
u32 info[RKVDEC_H264_RPS_SIZE / 8 / 4];
};
void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run);
void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
struct rkvdec_h264_run *run,
struct rkvdec_h264_reflists *reflists,
struct rkvdec_rps *hw_rps);
void assemble_hw_scaling_list(struct rkvdec_h264_run *run,
struct rkvdec_h264_scaling_list *scaling_list);
int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f);
enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, const struct v4l2_ctrl_h264_sps *sps);
void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run);
@@ -0,0 +1,444 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Video Decoder H264 backend
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <media/v4l2-h264.h>
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-regs.h"
#include "rkvdec-cabac.h"
#include "rkvdec-h264-common.h"
#include "rkvdec-bitwriter.h"
/* Size with u32 units. */
#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128)
#define RKV_ERROR_INFO_SIZE (256 * 144 * 4)
struct rkvdec_sps_pps_packet {
u32 info[8];
};
#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4)
#define PROFILE_IDC BW_FIELD(4, 8)
#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1)
#define CHROMA_FORMAT_IDC BW_FIELD(13, 2)
#define BIT_DEPTH_LUMA BW_FIELD(15, 3)
#define BIT_DEPTH_CHROMA BW_FIELD(18, 3)
#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1)
#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4)
#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5)
#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2)
#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4)
#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1)
#define PIC_WIDTH_IN_MBS BW_FIELD(38, 9)
#define PIC_HEIGHT_IN_MBS BW_FIELD(47, 9)
#define FRAME_MBS_ONLY_FLAG BW_FIELD(56, 1)
#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(57, 1)
#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(58, 1)
#define MVC_EXTENSION_ENABLE BW_FIELD(59, 1)
#define NUM_VIEWS BW_FIELD(60, 2)
#define VIEW_ID(i) BW_FIELD(62 + ((i) * 10), 10)
#define NUM_ANCHOR_REFS_L(i) BW_FIELD(82 + ((i) * 11), 1)
#define ANCHOR_REF_L(i) BW_FIELD(83 + ((i) * 11), 10)
#define NUM_NON_ANCHOR_REFS_L(i) BW_FIELD(104 + ((i) * 11), 1)
#define NON_ANCHOR_REFS_L(i) BW_FIELD(105 + ((i) * 11), 10)
#define PIC_PARAMETER_SET_ID BW_FIELD(128, 8)
#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(136, 5)
#define ENTROPY_CODING_MODE_FLAG BW_FIELD(141, 1)
#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(142, 1)
#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(143 + ((i) * 5), 5)
#define WEIGHTED_PRED_FLAG BW_FIELD(153, 1)
#define WEIGHTED_BIPRED_IDC BW_FIELD(154, 2)
#define PIC_INIT_QP_MINUS26 BW_FIELD(156, 7)
#define PIC_INIT_QS_MINUS26 BW_FIELD(163, 6)
#define CHROMA_QP_INDEX_OFFSET BW_FIELD(169, 5)
#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(174, 1)
#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(175, 1)
#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(176, 1)
#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(177, 1)
#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(178, 5)
#define SCALING_LIST_ENABLE_FLAG BW_FIELD(183, 1)
#define SCALING_LIST_ADDRESS BW_FIELD(184, 32)
#define IS_LONG_TERM(i) BW_FIELD(216 + (i), 1)
/* Data structure describing auxiliary buffer format. */
struct rkvdec_h264_priv_tbl {
s8 cabac_table[4][464][2];
struct rkvdec_h264_scaling_list scaling_list;
struct rkvdec_rps rps;
struct rkvdec_sps_pps_packet param_set[256];
u8 err_info[RKV_ERROR_INFO_SIZE];
};
struct rkvdec_h264_ctx {
struct rkvdec_aux_buf priv_tbl;
struct rkvdec_h264_reflists reflists;
struct rkvdec_regs regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
const struct v4l2_ctrl_h264_sps *sps = run->sps;
const struct v4l2_ctrl_h264_pps *pps = run->pps;
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps_packet *hw_ps;
dma_addr_t scaling_list_address;
u32 scaling_distance;
u32 i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 32 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
/* write sps */
WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
WRITE_PPS(sps->profile_idc, PROFILE_IDC);
WRITE_PPS(!!(sps->constraint_set_flags & (1 << 3)), CONSTRAINT_SET3_FLAG);
WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC);
WRITE_PPS(sps->bit_depth_luma_minus8, BIT_DEPTH_LUMA);
WRITE_PPS(sps->bit_depth_chroma_minus8, BIT_DEPTH_CHROMA);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS),
QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG);
WRITE_PPS(sps->log2_max_frame_num_minus4, LOG2_MAX_FRAME_NUM_MINUS4);
WRITE_PPS(sps->max_num_ref_frames, MAX_NUM_REF_FRAMES);
WRITE_PPS(sps->pic_order_cnt_type, PIC_ORDER_CNT_TYPE);
WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4,
LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO),
DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG);
/*
* Use the SPS values since they are already in macroblocks
* dimensions, height can be field height (halved) if
* V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
* decoding smaller images into larger allocation which can be used
* to implementing SVC spatial layer support.
*/
WRITE_PPS(sps->pic_width_in_mbs_minus1 + 1, PIC_WIDTH_IN_MBS);
WRITE_PPS(sps->pic_height_in_map_units_minus1 + 1, PIC_HEIGHT_IN_MBS);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY),
FRAME_MBS_ONLY_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD),
MB_ADAPTIVE_FRAME_FIELD_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE),
DIRECT_8X8_INFERENCE_FLAG);
/* write pps */
WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID);
WRITE_PPS(pps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE),
ENTROPY_CODING_MODE_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT),
BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG);
WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1,
NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0));
WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1,
NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1));
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED),
WEIGHTED_PRED_FLAG);
WRITE_PPS(pps->weighted_bipred_idc, WEIGHTED_BIPRED_IDC);
WRITE_PPS(pps->pic_init_qp_minus26, PIC_INIT_QP_MINUS26);
WRITE_PPS(pps->pic_init_qs_minus26, PIC_INIT_QS_MINUS26);
WRITE_PPS(pps->chroma_qp_index_offset, CHROMA_QP_INDEX_OFFSET);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT),
DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED),
CONSTRAINED_INTRA_PRED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT),
REDUNDANT_PIC_CNT_PRESENT);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE),
TRANSFORM_8X8_MODE_FLAG);
WRITE_PPS(pps->second_chroma_qp_index_offset,
SECOND_CHROMA_QP_INDEX_OFFSET);
WRITE_PPS(!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT),
SCALING_LIST_ENABLE_FLAG);
/* To be on the safe side, program the scaling matrix address */
scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance;
WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS);
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
u32 is_longterm = 0;
if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
is_longterm = 1;
WRITE_PPS(is_longterm, IS_LONG_TERM(i));
}
}
/*
* Set the ref POC in the correct register.
*
* The 32 registers are spread across 3 regions, each alternating top and bottom ref POCs:
* - 1: ref 0 to 14 contain top 0 to 7 and bottoms 0 to 6
* - 2: ref 15 to 29 contain top 8 to 14 and bottoms 7 to 14
* - 3: ref 30 and 31 which correspond to top 15 and bottom 15 respectively.
*/
static void set_poc_reg(struct rkvdec_regs *regs, uint32_t poc, int id, bool bottom)
{
if (!bottom) {
switch (id) {
case 0 ... 7:
regs->h26x.ref0_14_poc[id * 2] = poc;
break;
case 8 ... 14:
regs->h26x.ref15_29_poc[(id - 8) * 2 + 1] = poc;
break;
case 15:
regs->h26x.ref30_poc = poc;
break;
}
} else {
switch (id) {
case 0 ... 6:
regs->h26x.ref0_14_poc[id * 2 + 1] = poc;
break;
case 7 ... 14:
regs->h26x.ref15_29_poc[(id - 7) * 2] = poc;
break;
case 15:
regs->h26x.ref31_poc = poc;
break;
}
}
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
struct rkvdec_dev *rkvdec = ctx->dev;
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_ctrl_h264_sps *sps = run->sps;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
const struct v4l2_format *f;
struct rkvdec_regs *regs = &h264_ctx->regs;
dma_addr_t rlc_addr;
dma_addr_t refer_addr;
u32 rlc_len;
u32 hor_virstride;
u32 ver_virstride;
u32 y_virstride;
u32 yuv_virstride = 0;
u32 offset;
dma_addr_t dst_addr;
u32 i;
memset(regs, 0, sizeof(*regs));
regs->common.reg02.dec_mode = RKVDEC_MODE_H264;
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
if (sps->chroma_format_idc == 0)
yuv_virstride = y_virstride;
else if (sps->chroma_format_idc == 1)
yuv_virstride = y_virstride + y_virstride / 2;
else if (sps->chroma_format_idc == 2)
yuv_virstride = 2 * y_virstride;
regs->common.reg03.uv_hor_virstride = hor_virstride / 16;
regs->common.reg03.y_hor_virstride = hor_virstride / 16;
regs->common.reg03.slice_num_highbit = 1;
regs->common.reg03.slice_num_lowbits = 0x7ff;
/* config rlc base address */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common.strm_rlc_base = rlc_addr;
regs->h26x.rlcwrite_base = rlc_addr;
rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
regs->common.stream_len = rlc_len;
/* config cabac table */
offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
regs->common.cabactbl_base = priv_start_addr + offset;
/* config output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->common.decout_base = dst_addr;
regs->common.reg08.y_virstride = y_virstride / 16;
regs->common.reg09.yuv_virstride = yuv_virstride / 16;
/* config ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct vb2_buffer *vb_buf = run->ref_buf[i];
struct ref_base *base;
/*
* If a DPB entry is unused or invalid, address of current destination
* buffer is returned.
*/
if (!vb_buf)
vb_buf = &dst_buf->vb2_buf;
refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
if (i < V4L2_H264_NUM_DPB_ENTRIES - 1)
base = &regs->h26x.ref0_14_base[i];
else
base = &regs->h26x.ref15_base;
base->base_addr = refer_addr >> 4;
base->field_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD);
base->colmv_use_flag_ref = !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE);
base->topfield_used_ref = !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF);
base->botfield_used_ref = !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF);
set_poc_reg(regs, dpb[i].top_field_order_cnt, i, false);
set_poc_reg(regs, dpb[i].bottom_field_order_cnt, i, true);
}
regs->h26x.cur_poc = dec_params->top_field_order_cnt;
regs->h26x.cur_poc1 = dec_params->bottom_field_order_cnt;
/* config hw pps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
regs->h26x.pps_base = priv_start_addr + offset;
/* config hw rps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
regs->h26x.rps_base = priv_start_addr + offset;
offset = offsetof(struct rkvdec_h264_priv_tbl, err_info);
regs->h26x.errorinfo_base = priv_start_addr + offset;
rkvdec_memcpy_toio(rkvdec->regs, regs,
MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
}
static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_priv_tbl *priv_tbl;
struct rkvdec_h264_ctx *h264_ctx;
struct v4l2_ctrl *ctrl;
int ret;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_SPS);
if (!ctrl)
return -EINVAL;
ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
if (ret)
return ret;
h264_ctx = kzalloc_obj(*h264_ctx);
if (!h264_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&h264_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
ret = -ENOMEM;
goto err_free_ctx;
}
h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
h264_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
sizeof(rkvdec_h264_cabac_table));
ctx->priv = h264_ctx;
return 0;
err_free_ctx:
kfree(h264_ctx);
return ret;
}
static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
kfree(h264_ctx);
}
static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
{
struct v4l2_h264_reflist_builder reflist_builder;
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_h264_run run;
struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
rkvdec_h264_run_preamble(ctx, &run);
/* Build the P/B{0,1} ref lists. */
v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
run.sps, run.decode_params->dpb);
v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
h264_ctx->reflists.b1);
assemble_hw_scaling_list(&run, &tbl->scaling_list);
assemble_hw_pps(ctx, &run);
lookup_ref_buf_idx(ctx, &run);
assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
/* Start decoding! */
writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
rkvdec->regs + RKVDEC_REG_INTERRUPT);
return 0;
}
static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops = {
.adjust_fmt = rkvdec_h264_adjust_fmt,
.start = rkvdec_h264_start,
.stop = rkvdec_h264_stop,
.run = rkvdec_h264_run,
.try_ctrl = rkvdec_h264_try_ctrl,
.get_image_fmt = rkvdec_h264_get_image_fmt,
};
@@ -0,0 +1,456 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip video decoder hevc common functions
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*
* Copyright (C) 2023 Collabora, Ltd.
* Sebastian Fricke <sebastian.fricke@collabora.com>
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <linux/v4l2-common.h>
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-hevc-common.h"
/* Store the Short term ref pic set calculated values */
struct calculated_rps_st_set {
u8 num_delta_pocs;
u8 num_negative_pics;
u8 num_positive_pics;
u8 used_by_curr_pic_s0[16];
u8 used_by_curr_pic_s1[16];
s32 delta_poc_s0[16];
s32 delta_poc_s1[16];
};
void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
u16 width, u16 height, s32 pic_in_cts_width,
s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
{
const struct v4l2_ctrl_hevc_pps *pps = run->pps;
int i;
for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++)
column_width[i] = ((i + 1) * pic_in_cts_width) /
(pps->num_tile_columns_minus1 + 1) -
(i * pic_in_cts_width) /
(pps->num_tile_columns_minus1 + 1);
for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++)
row_height[i] = ((i + 1) * pic_in_cts_height) /
(pps->num_tile_rows_minus1 + 1) -
(i * pic_in_cts_height) /
(pps->num_tile_rows_minus1 + 1);
}
void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
u16 width, u16 height, s32 pic_in_cts_width,
s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
{
const struct v4l2_ctrl_hevc_pps *pps = run->pps;
s32 sum = 0;
int i;
for (i = 0; i < pps->num_tile_columns_minus1; i++) {
column_width[i] = pps->column_width_minus1[i] + 1;
sum += column_width[i];
}
column_width[i] = pic_in_cts_width - sum;
sum = 0;
for (i = 0; i < pps->num_tile_rows_minus1; i++) {
row_height[i] = pps->row_height_minus1[i] + 1;
sum += row_height[i];
}
row_height[i] = pic_in_cts_height - sum;
}
static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output,
const struct v4l2_ctrl_hevc_scaling_matrix *input)
{
const struct rkvdec_variant *variant = ctx->dev->variant;
int offset = 0;
variant->ops->flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4);
offset = 6 * 16 * sizeof(u8);
variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8);
offset += 6 * 64 * sizeof(u8);
variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_16x16,
6, 8);
offset += 6 * 64 * sizeof(u8);
/* Add a 128 byte padding with 0s between the two 32x32 matrices */
variant->ops->flatten_matrices(output + offset, (const u8 *)input->scaling_list_32x32,
1, 8);
offset += 64 * sizeof(u8);
memset(output + offset, 0, 128);
offset += 128 * sizeof(u8);
variant->ops->flatten_matrices(output + offset,
(const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)),
1, 8);
offset += 64 * sizeof(u8);
memset(output + offset, 0, 128);
}
/*
* Required layout:
* A = scaling_list_dc_coef_16x16
* B = scaling_list_dc_coef_32x32
* 0 = Padding
*
* A, A, A, A, A, A, B, 0, 0, B, 0, 0
*/
static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
{
u8 list_32x32[6] = {0};
memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8));
list_32x32[0] = input->scaling_list_dc_coef_32x32[0];
list_32x32[3] = input->scaling_list_dc_coef_32x32[1];
memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8));
}
static void translate_scaling_list(struct rkvdec_ctx *ctx, struct scaling_factor *output,
const struct v4l2_ctrl_hevc_scaling_matrix *input)
{
assemble_scalingfactor0(ctx, output->scalingfactor0, input);
memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96);
assemble_scalingdc(output->scalingdc, input);
memset(output->reserved, 0, 4 * sizeof(u8));
}
void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run,
struct scaling_factor *scaling_factor,
struct v4l2_ctrl_hevc_scaling_matrix *cache)
{
const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix;
if (!memcmp(cache, scaling,
sizeof(struct v4l2_ctrl_hevc_scaling_matrix)))
return;
translate_scaling_list(ctx, scaling_factor, scaling);
memcpy(cache, scaling,
sizeof(struct v4l2_ctrl_hevc_scaling_matrix));
}
static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps)
{
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
if (!run->ext_sps_lt_rps)
return;
for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
rkvdec_set_bw_field(rps->info, RPS_LT_REF_PIC_POC_LSB(i),
run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps);
rkvdec_set_bw_field(rps->info, RPS_LT_REF_USED_BY_CURR_PIC(i),
!!(run->ext_sps_lt_rps[i].flags &
V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT));
}
}
static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
struct calculated_rps_st_set *calculated_rps_st_sets)
{
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
for (int i = 0; i < sps->num_short_term_ref_pic_sets; i++) {
int poc = 0;
int j = 0;
const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i];
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_NEGATIVE(i),
set->num_negative_pics);
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_POSITIVE(i),
set->num_positive_pics);
for (; j < set->num_negative_pics; j++) {
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, j),
set->delta_poc_s0[j]);
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, j),
set->used_by_curr_pic_s0[j]);
}
poc = j;
for (j = 0; j < set->num_positive_pics; j++) {
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, poc + j),
set->delta_poc_s1[j]);
rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, poc + j),
set->used_by_curr_pic_s1[j]);
}
}
}
/*
* Compute the short term ref pic set parameters based on its reference short term ref pic
*/
static void st_ref_pic_set_prediction(struct rkvdec_hevc_run *run, int idx,
struct calculated_rps_st_set *calculated_rps_st_sets)
{
const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
struct calculated_rps_st_set *ref_rps;
u8 st_rps_idx = idx;
u8 ref_rps_idx = 0;
s16 delta_rps = 0;
u8 use_delta_flag[16] = { 0 };
u8 used_by_curr_pic_flag[16] = { 0 };
int i, j;
int dPoc;
ref_rps_idx = st_rps_idx - (rps_data->delta_idx_minus1 + 1); /* 7-59 */
delta_rps = (1 - 2 * rps_data->delta_rps_sign) *
(rps_data->abs_delta_rps_minus1 + 1); /* 7-60 */
ref_rps = &calculated_rps_st_sets[ref_rps_idx];
for (j = 0; j <= ref_rps->num_delta_pocs; j++) {
used_by_curr_pic_flag[j] = !!(rps_data->used_by_curr_pic & (1 << j));
use_delta_flag[j] = !!(rps_data->use_delta_flag & (1 << j));
}
/* 7-61: calculate num_negative_pics, delta_poc_s0 and used_by_curr_pic_s0 */
i = 0;
for (j = (ref_rps->num_positive_pics - 1); j >= 0; j--) {
dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
if (dPoc < 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
st_rps->delta_poc_s0[i] = dPoc;
st_rps->used_by_curr_pic_s0[i++] =
used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
}
}
if (delta_rps < 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
st_rps->delta_poc_s0[i] = delta_rps;
st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
}
for (j = 0; j < ref_rps->num_negative_pics; j++) {
dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
if (dPoc < 0 && use_delta_flag[j]) {
st_rps->delta_poc_s0[i] = dPoc;
st_rps->used_by_curr_pic_s0[i++] = used_by_curr_pic_flag[j];
}
}
st_rps->num_negative_pics = i;
/* 7-62: calculate num_positive_pics, delta_poc_s1 and used_by_curr_pic_s1 */
i = 0;
for (j = (ref_rps->num_negative_pics - 1); j >= 0; j--) {
dPoc = ref_rps->delta_poc_s0[j] + delta_rps;
if (dPoc > 0 && use_delta_flag[j]) {
st_rps->delta_poc_s1[i] = dPoc;
st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[j];
}
}
if (delta_rps > 0 && use_delta_flag[ref_rps->num_delta_pocs]) {
st_rps->delta_poc_s1[i] = delta_rps;
st_rps->used_by_curr_pic_s1[i++] = used_by_curr_pic_flag[ref_rps->num_delta_pocs];
}
for (j = 0; j < ref_rps->num_positive_pics; j++) {
dPoc = ref_rps->delta_poc_s1[j] + delta_rps;
if (dPoc > 0 && use_delta_flag[ref_rps->num_negative_pics + j]) {
st_rps->delta_poc_s1[i] = dPoc;
st_rps->used_by_curr_pic_s1[i++] =
used_by_curr_pic_flag[ref_rps->num_negative_pics + j];
}
}
st_rps->num_positive_pics = i;
st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
}
/*
* Compute the short term ref pic set parameters based on the control's data.
*/
static void st_ref_pic_set_calculate(struct rkvdec_hevc_run *run, int idx,
struct calculated_rps_st_set *calculated_rps_st_sets)
{
const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
struct calculated_rps_st_set *st_rps = &calculated_rps_st_sets[idx];
int j, i = 0;
/* 7-63 */
st_rps->num_negative_pics = rps_data->num_negative_pics;
/* 7-64 */
st_rps->num_positive_pics = rps_data->num_positive_pics;
for (i = 0; i < st_rps->num_negative_pics; i++) {
/* 7-65 */
st_rps->used_by_curr_pic_s0[i] = !!(rps_data->used_by_curr_pic & (1 << i));
if (i == 0) {
/* 7-67 */
st_rps->delta_poc_s0[i] = -(rps_data->delta_poc_s0_minus1[i] + 1);
} else {
/* 7-69 */
st_rps->delta_poc_s0[i] =
st_rps->delta_poc_s0[i - 1] -
(rps_data->delta_poc_s0_minus1[i] + 1);
}
}
for (j = 0; j < st_rps->num_positive_pics; j++) {
/* 7-66 */
st_rps->used_by_curr_pic_s1[j] = !!(rps_data->used_by_curr_pic & (1 << (i + j)));
if (j == 0) {
/* 7-68 */
st_rps->delta_poc_s1[j] = rps_data->delta_poc_s1_minus1[j] + 1;
} else {
/* 7-70 */
st_rps->delta_poc_s1[j] =
st_rps->delta_poc_s1[j - 1] +
(rps_data->delta_poc_s1_minus1[j] + 1);
}
}
/* 7-71 */
st_rps->num_delta_pocs = st_rps->num_positive_pics + st_rps->num_negative_pics;
}
static void rkvdec_hevc_prepare_hw_st_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
struct v4l2_ctrl_hevc_ext_sps_st_rps *cache)
{
int idx;
if (!run->ext_sps_st_rps)
return;
if (!memcmp(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps)))
return;
struct calculated_rps_st_set *calculated_rps_st_sets =
kzalloc(sizeof(struct calculated_rps_st_set) *
run->sps->num_short_term_ref_pic_sets, GFP_KERNEL);
for (idx = 0; idx < run->sps->num_short_term_ref_pic_sets; idx++) {
const struct v4l2_ctrl_hevc_ext_sps_st_rps *rps_data = &run->ext_sps_st_rps[idx];
if (rps_data->flags & V4L2_HEVC_EXT_SPS_ST_RPS_FLAG_INTER_REF_PIC_SET_PRED)
st_ref_pic_set_prediction(run, idx, calculated_rps_st_sets);
else
st_ref_pic_set_calculate(run, idx, calculated_rps_st_sets);
}
rkvdec_hevc_assemble_hw_st_rps(run, rps, calculated_rps_st_sets);
kfree(calculated_rps_st_sets);
memcpy(cache, run->ext_sps_st_rps, sizeof(struct v4l2_ctrl_hevc_ext_sps_st_rps));
}
void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache)
{
rkvdec_hevc_prepare_hw_st_rps(run, rps, st_cache);
rkvdec_hevc_assemble_hw_lt_rps(run, rps);
}
struct vb2_buffer *
get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run,
unsigned int dpb_idx)
{
struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
struct vb2_buffer *buf = NULL;
if (dpb_idx < decode_params->num_active_dpb_entries)
buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp);
/*
* If a DPB entry is unused or invalid, the address of current destination
* buffer is returned.
*/
if (!buf)
return &run->base.bufs.dst->vb2_buf;
return buf;
}
#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2
int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
fmt->num_planes = 1;
if (!fmt->plane_fmt[0].sizeimage)
fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
RKVDEC_HEVC_MAX_DEPTH_IN_BYTES;
return 0;
}
enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx,
struct v4l2_ctrl *ctrl)
{
const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS)
return RKVDEC_IMG_FMT_ANY;
if (sps->bit_depth_luma_minus8 == 0) {
if (sps->chroma_format_idc == 2)
return RKVDEC_IMG_FMT_422_8BIT;
else
return RKVDEC_IMG_FMT_420_8BIT;
} else if (sps->bit_depth_luma_minus8 == 2) {
if (sps->chroma_format_idc == 2)
return RKVDEC_IMG_FMT_422_10BIT;
else
return RKVDEC_IMG_FMT_420_10BIT;
}
return RKVDEC_IMG_FMT_ANY;
}
void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
struct v4l2_ctrl *ctrl;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_DECODE_PARAMS);
run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_SLICE_PARAMS);
run->slices_params = ctrl ? ctrl->p_cur.p : NULL;
run->num_slices = ctrl ? ctrl->new_elems : 0;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_SPS);
run->sps = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_PPS);
run->pps = ctrl ? ctrl->p_cur.p : NULL;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_SCALING_MATRIX);
run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
if (ctx->has_sps_st_rps) {
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_EXT_SPS_ST_RPS);
run->ext_sps_st_rps = ctrl ? ctrl->p_cur.p : NULL;
} else {
run->ext_sps_st_rps = NULL;
}
if (ctx->has_sps_lt_rps) {
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_EXT_SPS_LT_RPS);
run->ext_sps_lt_rps = ctrl ? ctrl->p_cur.p : NULL;
} else {
run->ext_sps_lt_rps = NULL;
}
rkvdec_run_preamble(ctx, &run->base);
}
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip video decoder hevc common functions
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*
* Copyright (C) 2023 Collabora, Ltd.
* Sebastian Fricke <sebastian.fricke@collabora.com>
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <media/v4l2-mem2mem.h>
#include <linux/types.h>
#include "rkvdec.h"
#include "rkvdec-bitwriter.h"
#define RPS_LT_REF_PIC_POC_LSB(i) BW_FIELD(0 + (i) * 32, 16) // i: 0-31
#define RPS_LT_REF_USED_BY_CURR_PIC(i) BW_FIELD(16 + (i) * 32, 1) // i: 0-31
#define RPS_ST_REF_SET_NUM_NEGATIVE(i) BW_FIELD(1024 + ((i) * 384), 4) // i: 0-63
#define RPS_ST_REF_SET_NUM_POSITIVE(i) BW_FIELD(1028 + ((i) * 384), 4) // i: 0-63
// i: 0-63, j: 0-14
#define RPS_ST_REF_SET_DELTA_POC(i, j) BW_FIELD(1032 + ((i) * 384) + ((j) * 17), 16)
// i: 0-63, j: 0-14
#define RPS_ST_REF_SET_USED(i, j) BW_FIELD(1048 + ((i) * 384) + ((j) * 17), 1)
#define RKVDEC_RPS_HEVC_SIZE ALIGN(1032 + 64 * 384, 128)
struct rkvdec_rps {
u32 info[RKVDEC_RPS_HEVC_SIZE / 8 / 4];
} __packed;
struct rkvdec_hevc_run {
struct rkvdec_run base;
const struct v4l2_ctrl_hevc_slice_params *slices_params;
const struct v4l2_ctrl_hevc_decode_params *decode_params;
const struct v4l2_ctrl_hevc_sps *sps;
const struct v4l2_ctrl_hevc_pps *pps;
const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
const struct v4l2_ctrl_hevc_ext_sps_st_rps *ext_sps_st_rps;
const struct v4l2_ctrl_hevc_ext_sps_lt_rps *ext_sps_lt_rps;
int num_slices;
};
struct scaling_factor {
u8 scalingfactor0[1248];
u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/
u8 scalingdc[12]; /*N1005 Vienna Meeting*/
u8 reserved[4]; /*16Bytes align*/
};
void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
u16 width, u16 height, s32 pic_in_cts_width,
s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
u16 width, u16 height, s32 pic_in_cts_width,
s32 pic_in_cts_height, u16 *column_width, u16 *row_height);
void rkvdec_hevc_assemble_hw_rps(struct rkvdec_hevc_run *run, struct rkvdec_rps *rps,
struct v4l2_ctrl_hevc_ext_sps_st_rps *st_cache);
void rkvdec_hevc_assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run,
struct scaling_factor *scaling_factor,
struct v4l2_ctrl_hevc_scaling_matrix *cache);
struct vb2_buffer *get_ref_buf(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run,
unsigned int dpb_idx);
int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx, struct v4l2_format *f);
enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run);
@@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Video Decoder HEVC backend
*
* Copyright (C) 2023 Collabora, Ltd.
* Sebastian Fricke <sebastian.fricke@collabora.com>
*
* Copyright (C) 2019 Collabora, Ltd.
* Boris Brezillon <boris.brezillon@collabora.com>
*
* Copyright (C) 2016 Rockchip Electronics Co., Ltd.
* Jeffy Chen <jeffy.chen@rock-chips.com>
*/
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-regs.h"
#include "rkvdec-cabac.h"
#include "rkvdec-hevc-common.h"
#include "rkvdec-bitwriter.h"
/* Size in u8/u32 units. */
#define RKV_SCALING_LIST_SIZE 1360
#define RKV_PPS_SIZE (80 / 4)
#define RKV_PPS_LEN 64
#define RKV_RPS_SIZE (32 / 4)
#define RKV_RPS_LEN 600
struct rkvdec_sps_pps_packet {
u32 info[RKV_PPS_SIZE];
};
struct rkvdec_rps_packet {
u32 info[RKV_RPS_SIZE];
};
/* SPS */
#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4)
#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4)
#define CHROMA_FORMAT_IDC BW_FIELD(8, 2)
#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 13)
#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(23, 13)
#define BIT_DEPTH_LUMA BW_FIELD(36, 4)
#define BIT_DEPTH_CHROMA BW_FIELD(40, 4)
#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(44, 5)
#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(49, 2)
#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(51, 3)
#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(54, 3)
#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(57, 2)
#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(59, 3)
#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(62, 3)
#define SCALING_LIST_ENABLED_FLAG BW_FIELD(65, 1)
#define AMP_ENABLED_FLAG BW_FIELD(66, 1)
#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(67, 1)
#define PCM_ENABLED_FLAG BW_FIELD(68, 1)
#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(69, 4)
#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(73, 4)
#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(77, 1)
#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(78, 3)
#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(81, 3)
#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(84, 7)
#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(91, 1)
#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(92, 6)
#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(98, 1)
#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(99, 1)
/* PPS */
#define PIC_PARAMETER_SET_ID BW_FIELD(128, 6)
#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(134, 4)
#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(138, 1)
#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(139, 1)
#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(140, 13)
#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(153, 1)
#define CABAC_INIT_PRESENT_FLAG BW_FIELD(154, 1)
#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(155, 4)
#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(159, 4)
#define INIT_QP_MINUS26 BW_FIELD(163, 7)
#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(170, 1)
#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(171, 1)
#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(172, 1)
#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(173, 3)
#define PPS_CB_QP_OFFSET BW_FIELD(176, 5)
#define PPS_CR_QP_OFFSET BW_FIELD(181, 5)
#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(186, 1)
#define WEIGHTED_PRED_FLAG BW_FIELD(187, 1)
#define WEIGHTED_BIPRED_FLAG BW_FIELD(188, 1)
#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(189, 1)
#define TILES_ENABLED_FLAG BW_FIELD(190, 1)
#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(191, 1)
#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(192, 1)
#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(193, 1)
#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(194, 1)
#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(195, 1)
#define PPS_BETA_OFFSET_DIV2 BW_FIELD(196, 4)
#define PPS_TC_OFFSET_DIV2 BW_FIELD(200, 4)
#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(204, 1)
#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(205, 3)
#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(208, 1)
#define NUM_TILE_COLUMNS BW_FIELD(212, 5)
#define NUM_TILE_ROWS BW_FIELD(217, 5)
#define COLUMN_WIDTH(i) BW_FIELD(256 + ((i) * 8), 8)
#define ROW_HEIGHT(i) BW_FIELD(416 + ((i) * 8), 8)
#define SCALING_LIST_ADDRESS BW_FIELD(592, 32)
/* Data structure describing auxiliary buffer format. */
struct rkvdec_hevc_priv_tbl {
u8 cabac_table[RKV_HEVC_CABAC_TABLE_SIZE];
struct scaling_factor scaling_list;
struct rkvdec_sps_pps_packet param_set[RKV_PPS_LEN];
struct rkvdec_rps_packet rps[RKV_RPS_LEN];
};
struct rkvdec_hevc_ctx {
struct rkvdec_aux_buf priv_tbl;
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
struct rkvdec_regs regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
const struct v4l2_ctrl_hevc_pps *pps = run->pps;
struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps_packet *hw_ps;
u32 min_cb_log2_size_y, ctb_log2_size_y, ctb_size_y;
u32 log2_min_cu_qp_delta_size, scaling_distance;
dma_addr_t scaling_list_address;
int i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 80 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
/* write sps */
WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID);
WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC);
WRITE_PPS(sps->pic_width_in_luma_samples, PIC_WIDTH_IN_LUMA_SAMPLES);
WRITE_PPS(sps->pic_height_in_luma_samples, PIC_HEIGHT_IN_LUMA_SAMPLES);
WRITE_PPS(sps->bit_depth_luma_minus8 + 8, BIT_DEPTH_LUMA);
WRITE_PPS(sps->bit_depth_chroma_minus8 + 8, BIT_DEPTH_CHROMA);
WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
LOG2_MAX_PIC_ORDER_CNT_LSB);
WRITE_PPS(sps->log2_diff_max_min_luma_coding_block_size,
LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE);
WRITE_PPS(sps->log2_min_luma_coding_block_size_minus3 + 3,
LOG2_MIN_LUMA_CODING_BLOCK_SIZE);
WRITE_PPS(sps->log2_min_luma_transform_block_size_minus2 + 2,
LOG2_MIN_TRANSFORM_BLOCK_SIZE);
WRITE_PPS(sps->log2_diff_max_min_luma_transform_block_size,
LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE);
WRITE_PPS(sps->max_transform_hierarchy_depth_inter,
MAX_TRANSFORM_HIERARCHY_DEPTH_INTER);
WRITE_PPS(sps->max_transform_hierarchy_depth_intra,
MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED),
SCALING_LIST_ENABLED_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED),
AMP_ENABLED_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET),
SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG);
if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) {
WRITE_PPS(1, PCM_ENABLED_FLAG);
WRITE_PPS(sps->pcm_sample_bit_depth_luma_minus1 + 1,
PCM_SAMPLE_BIT_DEPTH_LUMA);
WRITE_PPS(sps->pcm_sample_bit_depth_chroma_minus1 + 1,
PCM_SAMPLE_BIT_DEPTH_CHROMA);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED),
PCM_LOOP_FILTER_DISABLED_FLAG);
WRITE_PPS(sps->log2_diff_max_min_pcm_luma_coding_block_size,
LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE);
WRITE_PPS(sps->log2_min_pcm_luma_coding_block_size_minus3 + 3,
LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE);
}
WRITE_PPS(sps->num_short_term_ref_pic_sets, NUM_SHORT_TERM_REF_PIC_SETS);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT),
LONG_TERM_REF_PICS_PRESENT_FLAG);
WRITE_PPS(sps->num_long_term_ref_pics_sps, NUM_LONG_TERM_REF_PICS_SPS);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED),
SPS_TEMPORAL_MVP_ENABLED_FLAG);
WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED),
STRONG_INTRA_SMOOTHING_ENABLED_FLAG);
/* write pps */
WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID);
WRITE_PPS(sps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED),
DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT),
OUTPUT_FLAG_PRESENT_FLAG);
WRITE_PPS(pps->num_extra_slice_header_bits, NUM_EXTRA_SLICE_HEADER_BITS);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED),
SIGN_DATA_HIDING_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT),
CABAC_INIT_PRESENT_FLAG);
WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1 + 1,
NUM_REF_IDX_L0_DEFAULT_ACTIVE);
WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1 + 1,
NUM_REF_IDX_L1_DEFAULT_ACTIVE);
WRITE_PPS(pps->init_qp_minus26, INIT_QP_MINUS26);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED),
CONSTRAINED_INTRA_PRED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED),
TRANSFORM_SKIP_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED),
CU_QP_DELTA_ENABLED_FLAG);
min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3;
ctb_log2_size_y = min_cb_log2_size_y +
sps->log2_diff_max_min_luma_coding_block_size;
ctb_size_y = 1 << ctb_log2_size_y;
log2_min_cu_qp_delta_size = ctb_log2_size_y - pps->diff_cu_qp_delta_depth;
WRITE_PPS(log2_min_cu_qp_delta_size, LOG2_MIN_CU_QP_DELTA_SIZE);
WRITE_PPS(pps->pps_cb_qp_offset, PPS_CB_QP_OFFSET);
WRITE_PPS(pps->pps_cr_qp_offset, PPS_CR_QP_OFFSET);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT),
PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED),
WEIGHTED_PRED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED),
WEIGHTED_BIPRED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED),
TRANSQUANT_BYPASS_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED),
TILES_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED),
ENTROPY_CODING_SYNC_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED),
PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED),
LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED),
DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER),
PPS_DEBLOCKING_FILTER_DISABLED_FLAG);
WRITE_PPS(pps->pps_beta_offset_div2, PPS_BETA_OFFSET_DIV2);
WRITE_PPS(pps->pps_tc_offset_div2, PPS_TC_OFFSET_DIV2);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT),
LISTS_MODIFICATION_PRESENT_FLAG);
WRITE_PPS(pps->log2_parallel_merge_level_minus2 + 2, LOG2_PARALLEL_MERGE_LEVEL);
WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT),
SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG);
WRITE_PPS(pps->num_tile_columns_minus1 + 1, NUM_TILE_COLUMNS);
WRITE_PPS(pps->num_tile_rows_minus1 + 1, NUM_TILE_ROWS);
if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
/* Userspace also provide column width and row height for uniform spacing */
for (i = 0; i <= pps->num_tile_columns_minus1; i++)
WRITE_PPS(pps->column_width_minus1[i], COLUMN_WIDTH(i));
for (i = 0; i <= pps->num_tile_rows_minus1; i++)
WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i));
} else {
WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
COLUMN_WIDTH(0));
WRITE_PPS(((sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
ROW_HEIGHT(0));
}
scaling_distance = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
scaling_list_address = hevc_ctx->priv_tbl.dma + scaling_distance;
WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS);
}
/*
* Creation of the Reference Picture Set memory blob for the hardware.
* The layout looks like this:
* [0] 32 bits for L0 (6 references + 2 bits of the 7th reference)
* [1] 32 bits for L0 (remaining 3 bits of the 7th reference + 5 references
* + 4 bits of the 13th reference)
* [2] 11 bits for L0 (remaining bit for 13 and 2 references) and
* 21 bits for L1 (4 references + first bit of 5)
* [3] 32 bits of padding with 0s
* [4] 32 bits for L1 (remaining 4 bits for 5 + 5 references + 3 bits of 11)
* [5] 22 bits for L1 (remaining 2 bits of 11 and 4 references)
* lowdelay flag (bit 23), rps bit offset long term (bit 24 - 32)
* [6] rps bit offset long term (bit 1 - 3), rps bit offset short term (bit 4 - 12)
* number of references (bit 13 - 16), remaining 16 bits of padding with 0s
* [7] 32 bits of padding with 0s
*
* Thus we have to set up padding in between reference 5 of the L1 list.
*/
static void assemble_sw_rps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
const struct v4l2_ctrl_hevc_slice_params *sl_params;
const struct v4l2_hevc_dpb_entry *dpb;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
struct rkvdec_rps_packet *hw_ps;
int i, j;
unsigned int lowdelay;
#define WRITE_RPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
#define REF_PIC_LONG_TERM_L0(n) BW_FIELD((n) * 5, 1)
#define REF_PIC_IDX_L0(n) BW_FIELD(1 + ((n) * 5), 4)
#define REF_PIC_LONG_TERM_L1(n) BW_FIELD(((n) < 5 ? 75 : 132) + ((n) * 5), 1)
#define REF_PIC_IDX_L1(n) BW_FIELD(((n) < 4 ? 76 : 128) + ((n) * 5), 4)
#define LOWDELAY BW_FIELD(182, 1)
#define LONG_TERM_RPS_BIT_OFFSET BW_FIELD(183, 10)
#define SHORT_TERM_RPS_BIT_OFFSET BW_FIELD(193, 9)
#define NUM_RPS_POC BW_FIELD(202, 4)
for (j = 0; j < run->num_slices; j++) {
uint st_bit_offset = 0;
uint num_l0_refs = 0;
uint num_l1_refs = 0;
sl_params = &run->slices_params[j];
dpb = decode_params->dpb;
if (sl_params->slice_type != V4L2_HEVC_SLICE_TYPE_I) {
num_l0_refs = sl_params->num_ref_idx_l0_active_minus1 + 1;
if (sl_params->slice_type == V4L2_HEVC_SLICE_TYPE_B)
num_l1_refs = sl_params->num_ref_idx_l1_active_minus1 + 1;
lowdelay = 1;
} else {
lowdelay = 0;
}
hw_ps = &priv_tbl->rps[j];
memset(hw_ps, 0, sizeof(*hw_ps));
for (i = 0; i < num_l0_refs; i++) {
const struct v4l2_hevc_dpb_entry dpb_l0 = dpb[sl_params->ref_idx_l0[i]];
WRITE_RPS(!!(dpb_l0.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE),
REF_PIC_LONG_TERM_L0(i));
WRITE_RPS(sl_params->ref_idx_l0[i], REF_PIC_IDX_L0(i));
if (dpb_l0.pic_order_cnt_val > sl_params->slice_pic_order_cnt)
lowdelay = 0;
}
for (i = 0; i < num_l1_refs; i++) {
const struct v4l2_hevc_dpb_entry dpb_l1 = dpb[sl_params->ref_idx_l1[i]];
int is_long_term =
!!(dpb_l1.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE);
WRITE_RPS(is_long_term, REF_PIC_LONG_TERM_L1(i));
WRITE_RPS(sl_params->ref_idx_l1[i], REF_PIC_IDX_L1(i));
if (dpb_l1.pic_order_cnt_val > sl_params->slice_pic_order_cnt)
lowdelay = 0;
}
WRITE_RPS(lowdelay, LOWDELAY);
if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) {
if (sl_params->short_term_ref_pic_set_size)
st_bit_offset = sl_params->short_term_ref_pic_set_size;
else if (sps->num_short_term_ref_pic_sets > 1)
st_bit_offset = fls(sps->num_short_term_ref_pic_sets - 1);
}
WRITE_RPS(st_bit_offset + sl_params->long_term_ref_pic_set_size,
LONG_TERM_RPS_BIT_OFFSET);
WRITE_RPS(sl_params->short_term_ref_pic_set_size,
SHORT_TERM_RPS_BIT_OFFSET);
WRITE_RPS(decode_params->num_poc_st_curr_before +
decode_params->num_poc_st_curr_after +
decode_params->num_poc_lt_curr,
NUM_RPS_POC);
}
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
struct rkvdec_dev *rkvdec = ctx->dev;
const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
const struct v4l2_ctrl_hevc_slice_params *sl_params = &run->slices_params[0];
const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_regs *regs = &hevc_ctx->regs;
dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
const struct v4l2_format *f;
dma_addr_t rlc_addr;
dma_addr_t refer_addr;
u32 rlc_len;
u32 hor_virstride;
u32 ver_virstride;
u32 y_virstride;
u32 yuv_virstride = 0;
u32 offset;
dma_addr_t dst_addr;
u32 reg, i;
memset(regs, 0, sizeof(*regs));
regs->common.reg02.dec_mode = RKVDEC_MODE_HEVC;
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
if (sps->chroma_format_idc == 0)
yuv_virstride = y_virstride;
else if (sps->chroma_format_idc == 1)
yuv_virstride = y_virstride + y_virstride / 2;
else if (sps->chroma_format_idc == 2)
yuv_virstride = 2 * y_virstride;
regs->common.reg03.slice_num_lowbits = run->num_slices;
regs->common.reg03.uv_hor_virstride = hor_virstride / 16;
regs->common.reg03.y_hor_virstride = hor_virstride / 16;
/* config rlc base address */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common.strm_rlc_base = rlc_addr;
rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
regs->common.stream_len = round_up(rlc_len, 16) + 64;
/* config cabac table */
offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
regs->common.cabactbl_base = priv_start_addr + offset;
/* config output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->common.decout_base = dst_addr;
regs->common.reg08.y_virstride = y_virstride / 16;
regs->common.reg09.yuv_virstride = yuv_virstride / 16;
/* config ref pic address */
for (i = 0; i < 15; i++) {
struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
if (i < 4 && decode_params->num_active_dpb_entries) {
reg = GENMASK(decode_params->num_active_dpb_entries - 1, 0);
reg = (reg >> (i * 4)) & 0xf;
} else {
reg = 0;
}
refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
regs->h26x.ref0_14_base[i].base_addr = refer_addr >> 4;
regs->h26x.ref0_14_base[i].field_ref = !!(reg & 1);
regs->h26x.ref0_14_base[i].topfield_used_ref = !!(reg & 2);
regs->h26x.ref0_14_base[i].botfield_used_ref = !!(reg & 4);
regs->h26x.ref0_14_base[i].colmv_use_flag_ref = !!(reg & 8);
regs->h26x.ref0_14_poc[i] = i < decode_params->num_active_dpb_entries
? dpb[i].pic_order_cnt_val
: 0;
}
regs->h26x.cur_poc = sl_params->slice_pic_order_cnt;
/* config hw pps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
regs->h26x.pps_base = priv_start_addr + offset;
/* config hw rps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
regs->h26x.rps_base = priv_start_addr + offset;
rkvdec_memcpy_toio(rkvdec->regs, regs,
MIN(sizeof(*regs), sizeof(u32) * rkvdec->variant->num_regs));
}
static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
const struct v4l2_ctrl_hevc_sps *sps)
{
if (sps->chroma_format_idc > 1)
/* Only 4:0:0 and 4:2:0 are supported */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
/* Only 8-bit and 10-bit are supported */
return -EINVAL;
if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
return -EINVAL;
return 0;
}
static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_priv_tbl *priv_tbl;
struct rkvdec_hevc_ctx *hevc_ctx;
hevc_ctx = kzalloc_obj(*hevc_ctx);
if (!hevc_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&hevc_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
kfree(hevc_ctx);
return -ENOMEM;
}
hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
hevc_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
sizeof(rkvdec_hevc_cabac_table));
ctx->priv = hevc_ctx;
return 0;
}
static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
kfree(hevc_ctx);
}
static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_run run;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
u32 reg;
rkvdec_hevc_run_preamble(ctx, &run);
rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
&hevc_ctx->scaling_matrix_cache);
assemble_hw_pps(ctx, &run);
assemble_sw_rps(ctx, &run);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
rkvdec_quirks_disable_qos(ctx);
/* Start decoding! */
reg = (run.pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) ?
0 : RKVDEC_WR_DDR_ALIGN_EN;
writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E | reg,
rkvdec->regs + RKVDEC_REG_INTERRUPT);
return 0;
}
static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops = {
.adjust_fmt = rkvdec_hevc_adjust_fmt,
.start = rkvdec_hevc_start,
.stop = rkvdec_hevc_stop,
.run = rkvdec_hevc_run,
.try_ctrl = rkvdec_hevc_try_ctrl,
.get_image_fmt = rkvdec_hevc_get_image_fmt,
};
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip video decoder Rows and Cols Buffers manager
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include "rkvdec.h"
#include "rkvdec-rcb.h"
#include <linux/iommu.h>
#include <linux/genalloc.h>
#include <linux/sizes.h>
#include <linux/types.h>
struct rkvdec_rcb_config {
struct rkvdec_aux_buf *rcb_bufs;
size_t rcb_count;
};
static size_t rkvdec_rcb_size(const struct rcb_size_info *size_info,
unsigned int width, unsigned int height)
{
return size_info->multiplier * (size_info->axis == PIC_HEIGHT ? height : width);
}
dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id)
{
return ctx->rcb_config->rcb_bufs[id].dma;
}
size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id)
{
return ctx->rcb_config->rcb_bufs[id].size;
}
int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx)
{
return ctx->rcb_config->rcb_count;
}
void rkvdec_free_rcb(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *dev = ctx->dev;
struct rkvdec_rcb_config *cfg = ctx->rcb_config;
unsigned long virt_addr;
int i;
if (!cfg)
return;
for (i = 0; i < cfg->rcb_count; i++) {
size_t rcb_size = cfg->rcb_bufs[i].size;
if (!cfg->rcb_bufs[i].cpu)
continue;
switch (cfg->rcb_bufs[i].type) {
case RKVDEC_ALLOC_SRAM:
virt_addr = (unsigned long)cfg->rcb_bufs[i].cpu;
if (dev->iommu_domain)
iommu_unmap(dev->iommu_domain, virt_addr, rcb_size);
gen_pool_free(dev->sram_pool, virt_addr, rcb_size);
break;
case RKVDEC_ALLOC_DMA:
dma_free_coherent(dev->dev,
rcb_size,
cfg->rcb_bufs[i].cpu,
cfg->rcb_bufs[i].dma);
break;
}
}
if (cfg->rcb_bufs)
devm_kfree(dev->dev, cfg->rcb_bufs);
devm_kfree(dev->dev, cfg);
}
int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
const struct rcb_size_info *size_info,
size_t rcb_count)
{
int ret, i;
u32 width, height;
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_rcb_config *cfg;
if (!size_info || !rcb_count) {
ctx->rcb_config = NULL;
return 0;
}
ctx->rcb_config = devm_kzalloc(rkvdec->dev, sizeof(*ctx->rcb_config), GFP_KERNEL);
if (!ctx->rcb_config)
return -ENOMEM;
cfg = ctx->rcb_config;
cfg->rcb_bufs = devm_kzalloc(rkvdec->dev, sizeof(*cfg->rcb_bufs) * rcb_count, GFP_KERNEL);
if (!cfg->rcb_bufs) {
ret = -ENOMEM;
goto err_alloc;
}
width = ctx->decoded_fmt.fmt.pix_mp.width;
height = ctx->decoded_fmt.fmt.pix_mp.height;
for (i = 0; i < rcb_count; i++) {
void *cpu = NULL;
dma_addr_t dma;
size_t rcb_size = rkvdec_rcb_size(&size_info[i], width, height);
enum rkvdec_alloc_type alloc_type = RKVDEC_ALLOC_SRAM;
/* Try allocating an SRAM buffer */
if (ctx->dev->sram_pool) {
if (rkvdec->iommu_domain)
rcb_size = ALIGN(rcb_size, SZ_4K);
cpu = gen_pool_dma_zalloc_align(ctx->dev->sram_pool,
rcb_size,
&dma,
SZ_4K);
}
/* If an IOMMU is used, map the SRAM address through it */
if (cpu && rkvdec->iommu_domain) {
unsigned long virt_addr = (unsigned long)cpu;
phys_addr_t phys_addr = dma;
ret = iommu_map(rkvdec->iommu_domain, virt_addr, phys_addr,
rcb_size, IOMMU_READ | IOMMU_WRITE, 0);
if (ret) {
gen_pool_free(ctx->dev->sram_pool,
(unsigned long)cpu,
rcb_size);
cpu = NULL;
goto ram_fallback;
}
/*
* The registers will be configured with the virtual
* address so that it goes through the IOMMU
*/
dma = virt_addr;
}
ram_fallback:
/* Fallback to RAM */
if (!cpu) {
cpu = dma_alloc_coherent(ctx->dev->dev,
rcb_size,
&dma,
GFP_KERNEL);
alloc_type = RKVDEC_ALLOC_DMA;
}
if (!cpu) {
ret = -ENOMEM;
goto err_alloc;
}
cfg->rcb_bufs[i].cpu = cpu;
cfg->rcb_bufs[i].dma = dma;
cfg->rcb_bufs[i].size = rcb_size;
cfg->rcb_bufs[i].type = alloc_type;
cfg->rcb_count += 1;
}
return 0;
err_alloc:
rkvdec_free_rcb(ctx);
return ret;
}
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip video decoder Rows and Cols Buffers manager
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <linux/types.h>
struct rkvdec_ctx;
enum rcb_axis {
PIC_WIDTH = 0,
PIC_HEIGHT = 1
};
struct rcb_size_info {
u8 multiplier;
enum rcb_axis axis;
};
int rkvdec_allocate_rcb(struct rkvdec_ctx *ctx,
const struct rcb_size_info *size_info,
size_t rcb_count);
dma_addr_t rkvdec_rcb_buf_dma_addr(struct rkvdec_ctx *ctx, int id);
size_t rkvdec_rcb_buf_size(struct rkvdec_ctx *ctx, int id);
int rkvdec_rcb_buf_count(struct rkvdec_ctx *ctx);
void rkvdec_free_rcb(struct rkvdec_ctx *ctx);
@@ -0,0 +1,433 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef RKVDEC_REGS_H_
#define RKVDEC_REGS_H_
#include <linux/types.h>
/*
* REG_INTERRUPT is accessed via writel to enable the decoder after
* configuring it and clear interrupt strmd_error_status
*/
#define RKVDEC_REG_INTERRUPT 0x004
#define RKVDEC_INTERRUPT_DEC_E BIT(0)
#define RKVDEC_CONFIG_DEC_CLK_GATE_E BIT(1)
#define RKVDEC_E_STRMD_CLKGATE_DIS BIT(2)
#define RKVDEC_TIMEOUT_MODE BIT(3)
#define RKVDEC_IRQ_DIS BIT(4)
#define RKVDEC_TIMEOUT_E BIT(5)
#define RKVDEC_BUF_EMPTY_E BIT(6)
#define RKVDEC_STRM_E_WAITDECFIFO_EMPTY BIT(7)
#define RKVDEC_IRQ BIT(8)
#define RKVDEC_IRQ_RAW BIT(9)
#define RKVDEC_E_REWRITE_VALID BIT(10)
#define RKVDEC_COMMONIRQ_MODE BIT(11)
#define RKVDEC_RDY_STA BIT(12)
#define RKVDEC_BUS_STA BIT(13)
#define RKVDEC_ERR_STA BIT(14)
#define RKVDEC_TIMEOUT_STA BIT(15)
#define RKVDEC_BUF_EMPTY_STA BIT(16)
#define RKVDEC_COLMV_REF_ERR_STA BIT(17)
#define RKVDEC_CABU_END_STA BIT(18)
#define RKVDEC_H264ORVP9_ERR_MODE BIT(19)
#define RKVDEC_SOFTRST_EN_P BIT(20)
#define RKVDEC_FORCE_SOFTRESET_VALID BIT(21)
#define RKVDEC_SOFTRESET_RDY BIT(22)
#define RKVDEC_WR_DDR_ALIGN_EN BIT(23)
#define RKVDEC_REG_QOS_CTRL 0x18C
/*
* Cache configuration is not covered in the range of the register struct
*/
#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410
#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450
/*
* Define the mode values
*/
#define RKVDEC_MODE_HEVC 0
#define RKVDEC_MODE_H264 1
#define RKVDEC_MODE_VP9 2
/* rkvcodec registers */
struct rkvdec_common_regs {
struct rkvdec_id {
u32 minor_ver : 8;
u32 level : 1;
u32 dec_support : 3;
u32 profile : 1;
u32 reserved0 : 1;
u32 codec_flag : 1;
u32 reserved1 : 1;
u32 prod_num : 16;
} reg00;
struct rkvdec_int {
u32 dec_e : 1;
u32 dec_clkgate_e : 1;
u32 dec_e_strmd_clkgate_dis : 1;
u32 timeout_mode : 1;
u32 dec_irq_dis : 1;
u32 dec_timeout_e : 1;
u32 buf_empty_en : 1;
u32 stmerror_waitdecfifo_empty : 1;
u32 dec_irq : 1;
u32 dec_irq_raw : 1;
u32 reserved2 : 2;
u32 dec_rdy_sta : 1;
u32 dec_bus_sta : 1;
u32 dec_error_sta : 1;
u32 dec_timeout_sta : 1;
u32 dec_empty_sta : 1;
u32 colmv_ref_error_sta : 1;
u32 cabu_end_sta : 1;
u32 h264orvp9_error_mode : 1;
u32 softrst_en_p : 1;
u32 force_softreset_valid : 1;
u32 softreset_rdy : 1;
u32 wr_ddr_align_en : 1;
u32 scl_down_en : 1;
u32 allow_not_wr_unref_bframe : 1;
u32 reserved1 : 6;
} reg01;
struct rkvdec_sysctrl {
u32 in_endian : 1;
u32 in_swap32_e : 1;
u32 in_swap64_e : 1;
u32 str_endian : 1;
u32 str_swap32_e : 1;
u32 str_swap64_e : 1;
u32 out_endian : 1;
u32 out_swap32_e : 1;
u32 out_cbcr_swap : 1;
u32 reserved0 : 1;
u32 rlc_mode_direct_write : 1;
u32 rlc_mode : 1;
u32 strm_start_bit : 7;
u32 reserved1 : 1;
u32 dec_mode : 2;
u32 reserved2 : 2;
u32 rps_mode : 1;
u32 stream_mode : 1;
u32 stream_lastpacket : 1;
u32 firstslice_flag : 1;
u32 frame_orslice : 1;
u32 buspr_slot_disable : 1;
u32 colmv_mode : 1;
u32 ycacherd_prior : 1;
} reg02;
struct rkvdec_picpar {
u32 y_hor_virstride : 9;
u32 reserved : 2;
u32 slice_num_highbit : 1;
u32 uv_hor_virstride : 9;
u32 slice_num_lowbits : 11;
} reg03;
u32 strm_rlc_base;
u32 stream_len;
u32 cabactbl_base;
u32 decout_base;
struct rkvdec_y_virstride {
u32 y_virstride : 20;
u32 reserved0 : 12;
} reg08;
struct rkvdec_yuv_virstride {
u32 yuv_virstride : 21;
u32 reserved0 : 11;
} reg09;
} __packed;
struct ref_base {
u32 field_ref : 1;
u32 topfield_used_ref : 1;
u32 botfield_used_ref : 1;
u32 colmv_use_flag_ref : 1;
u32 base_addr : 28;
};
struct rkvdec_h26x_regs {
struct ref_base ref0_14_base[15];
u32 ref0_14_poc[15];
u32 cur_poc;
u32 rlcwrite_base;
u32 pps_base;
u32 rps_base;
u32 strmd_error_e;
struct {
u32 strmd_error_status : 28;
u32 colmv_error_ref_picidx : 4;
} reg45;
struct {
u32 strmd_error_ctu_xoffset : 8;
u32 strmd_error_ctu_yoffset : 8;
u32 streamfifo_space2full : 7;
u32 reserved0 : 1;
u32 vp9_error_ctu0_en : 1;
u32 reserved1 : 7;
} reg46;
struct {
u32 saowr_xoffet : 9;
u32 reserved0 : 7;
u32 saowr_yoffset : 10;
u32 reserved1 : 6;
} reg47;
struct ref_base ref15_base;
u32 ref15_29_poc[15];
u32 performance_cycle;
u32 axi_ddr_rdata;
u32 axi_ddr_wdata;
struct {
u32 busifd_resetn : 1;
u32 cabac_resetn : 1;
u32 dec_ctrl_resetn : 1;
u32 transd_resetn : 1;
u32 intra_resetn : 1;
u32 inter_resetn : 1;
u32 recon_resetn : 1;
u32 filer_resetn : 1;
u32 reserved0 : 24;
} reg67;
struct {
u32 perf_cnt0_sel : 6;
u32 reserved0 : 2;
u32 perf_cnt1_sel : 6;
u32 reserved1 : 2;
u32 perf_cnt2_sel : 6;
u32 reserved2 : 10;
} reg68;
u32 perf_cnt0;
u32 perf_cnt1;
u32 perf_cnt2;
u32 ref30_poc;
u32 ref31_poc;
u32 cur_poc1;
u32 errorinfo_base;
struct {
u32 slicedec_num : 14;
u32 reserved0 : 1;
u32 strmd_detect_error_flag : 1;
u32 error_packet_num : 14;
u32 reserved1 : 2;
} reg76;
struct {
u32 error_en_highbits : 30;
u32 strmd_error_slice_en : 1;
u32 strmd_error_frame_en : 1;
} reg77;
u32 colmv_cur_base;
u32 colmv_ref_base[16];
u32 scanlist_addr;
u32 reg96_sd_decout_base;
u32 sd_y_virstride;
u32 sd_hor_stride;
u32 qos_ctrl;
u32 perf[8];
u32 qos1;
} __packed;
struct rkvdec_vp9_regs {
struct cprheader_offset {
u32 cprheader_offset : 16;
u32 reserved : 16;
} reg10;
u32 refer_bases[3];
u32 count_base;
u32 segidlast_base;
u32 segidcur_base;
struct frame_sizes {
u32 framewidth : 16;
u32 frameheight : 16;
} reg17_19[3];
struct segid_grp {
u32 segid_abs_delta : 1;
u32 segid_frame_qp_delta_en : 1;
u32 segid_frame_qp_delta : 9;
u32 segid_frame_loopfilter_value_en : 1;
u32 segid_frame_loopfilter_value : 7;
u32 segid_referinfo_en : 1;
u32 segid_referinfo : 2;
u32 segid_frame_skip_en : 1;
u32 reserved : 9;
} reg20_27[8];
struct cprheader_config {
u32 tx_mode : 3;
u32 frame_reference_mode : 2;
u32 reserved : 27;
} reg28;
struct ref_scale {
u32 ref_hor_scale : 16;
u32 ref_ver_scale : 16;
} reg29_31[3];
struct ref_deltas_lastframe {
u32 ref_deltas_lastframe0 : 7;
u32 ref_deltas_lastframe1 : 7;
u32 ref_deltas_lastframe2 : 7;
u32 ref_deltas_lastframe3 : 7;
u32 reserved : 4;
} reg32;
struct info_lastframe {
u32 mode_deltas_lastframe0 : 7;
u32 mode_deltas_lastframe1 : 7;
u32 reserved0 : 2;
u32 segmentation_enable_lstframe : 1;
u32 last_show_frame : 1;
u32 last_intra_only : 1;
u32 last_widthheight_eqcur : 1;
u32 color_space_lastkeyframe : 3;
u32 reserved1 : 9;
} reg33;
u32 intercmd_base;
struct intercmd_num {
u32 intercmd_num : 24;
u32 reserved : 8;
} reg35;
struct lasttile_size {
u32 lasttile_size : 24;
u32 reserved : 8;
} reg36;
struct hor_virstride {
u32 y_hor_virstride : 9;
u32 reserved0 : 7;
u32 uv_hor_virstride : 9;
u32 reserved1 : 7;
} reg37_39[3];
u32 cur_poc;
struct rlcwrite_base {
u32 reserved : 3;
u32 rlcwrite_base : 29;
} reg41;
struct pps_base {
u32 reserved : 4;
u32 pps_base : 28;
} reg42;
struct rps_base {
u32 reserved : 4;
u32 rps_base : 28;
} reg43;
struct strmd_error_en {
u32 strmd_error_e : 28;
u32 reserved : 4;
} reg44;
u32 vp9_error_info0;
struct strmd_error_ctu {
u32 strmd_error_ctu_xoffset : 8;
u32 strmd_error_ctu_yoffset : 8;
u32 streamfifo_space2full : 7;
u32 reserved0 : 1;
u32 error_ctu0_en : 1;
u32 reserved1 : 7;
} reg46;
struct sao_ctu_position {
u32 saowr_xoffet : 9;
u32 reserved0 : 7;
u32 saowr_yoffset : 10;
u32 reserved1 : 6;
} reg47;
struct ystride {
u32 virstride : 20;
u32 reserved : 12;
} reg48_50[3];
struct lastref_yuvstride {
u32 lastref_yuv_virstride : 21;
u32 reserved : 11;
} reg51;
u32 refcolmv_base;
u32 reserved0[11];
u32 performance_cycle;
u32 axi_ddr_rdata;
u32 axi_ddr_wdata;
struct fpgadebug_reset {
u32 busifd_resetn : 1;
u32 cabac_resetn : 1;
u32 dec_ctrl_resetn : 1;
u32 transd_resetn : 1;
u32 intra_resetn : 1;
u32 inter_resetn : 1;
u32 recon_resetn : 1;
u32 filer_resetn : 1;
u32 reserved : 24;
} reg67;
struct performance_sel {
u32 perf_cnt0_sel : 6;
u32 reserved0 : 2;
u32 perf_cnt1_sel : 6;
u32 reserved1 : 2;
u32 perf_cnt2_sel : 6;
u32 reserved : 10;
} reg68;
u32 perf_cnt0;
u32 perf_cnt1;
u32 perf_cnt2;
u32 reserved1[3];
u32 vp9_error_info1;
struct error_ctu1 {
u32 vp9_error_ctu1_x : 6;
u32 reserved0 : 2;
u32 vp9_error_ctu1_y : 6;
u32 reserved1 : 1;
u32 vp9_error_ctu1_en : 1;
u32 reserved2 : 16;
} reg76;
u32 reserved2;
} __packed;
struct rkvdec_regs {
struct rkvdec_common_regs common;
union {
struct rkvdec_h26x_regs h26x;
struct rkvdec_vp9_regs vp9;
};
} __packed;
#endif /* RKVDEC_REGS_H_ */
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VDPU381 Video Decoder H264 backend
*
* Copyright (C) 2024 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <media/v4l2-h264.h>
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-cabac.h"
#include "rkvdec-rcb.h"
#include "rkvdec-h264-common.h"
#include "rkvdec-vdpu381-regs.h"
struct rkvdec_sps {
u16 seq_parameter_set_id: 4;
u16 profile_idc: 8;
u16 constraint_set3_flag: 1;
u16 chroma_format_idc: 2;
u16 bit_depth_luma: 3;
u16 bit_depth_chroma: 3;
u16 qpprime_y_zero_transform_bypass_flag: 1;
u16 log2_max_frame_num_minus4: 4;
u16 max_num_ref_frames: 5;
u16 pic_order_cnt_type: 2;
u16 log2_max_pic_order_cnt_lsb_minus4: 4;
u16 delta_pic_order_always_zero_flag: 1;
u16 pic_width_in_mbs: 12;
u16 pic_height_in_mbs: 12;
u16 frame_mbs_only_flag: 1;
u16 mb_adaptive_frame_field_flag: 1;
u16 direct_8x8_inference_flag: 1;
u16 mvc_extension_enable: 1;
u16 num_views: 2;
u16 reserved_bits: 12;
u16 reserved[11];
} __packed;
struct rkvdec_pps {
u16 pic_parameter_set_id: 8;
u16 pps_seq_parameter_set_id: 5;
u16 entropy_coding_mode_flag: 1;
u16 bottom_field_pic_order_in_frame_present_flag: 1;
u16 num_ref_idx_l0_default_active_minus1: 5;
u16 num_ref_idx_l1_default_active_minus1: 5;
u16 weighted_pred_flag: 1;
u16 weighted_bipred_idc: 2;
u16 pic_init_qp_minus26: 7;
u16 pic_init_qs_minus26: 6;
u16 chroma_qp_index_offset: 5;
u16 deblocking_filter_control_present_flag: 1;
u16 constrained_intra_pred_flag: 1;
u16 redundant_pic_cnt_present: 1;
u16 transform_8x8_mode_flag: 1;
u16 second_chroma_qp_index_offset: 5;
u16 scaling_list_enable_flag: 1;
u32 scaling_list_address;
u16 is_longterm;
u8 reserved[3];
} __packed;
struct rkvdec_sps_pps {
struct rkvdec_sps sps;
struct rkvdec_pps pps;
} __packed;
/* Data structure describing auxiliary buffer format. */
struct rkvdec_h264_priv_tbl {
s8 cabac_table[4][464][2];
struct rkvdec_h264_scaling_list scaling_list;
struct rkvdec_sps_pps param_set[256];
struct rkvdec_rps rps;
};
struct rkvdec_h264_ctx {
struct rkvdec_aux_buf priv_tbl;
struct rkvdec_h264_reflists reflists;
struct rkvdec_vdpu381_regs_h264 regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
const struct v4l2_ctrl_h264_sps *sps = run->sps;
const struct v4l2_ctrl_h264_pps *pps = run->pps;
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps *hw_ps;
dma_addr_t scaling_list_address;
u32 scaling_distance;
u32 i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 32 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id;
hw_ps->sps.profile_idc = sps->profile_idc;
hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3));
hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8;
hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8;
hw_ps->sps.qpprime_y_zero_transform_bypass_flag =
!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames;
hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type;
hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 =
sps->log2_max_pic_order_cnt_lsb_minus4;
hw_ps->sps.delta_pic_order_always_zero_flag =
!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
hw_ps->sps.mvc_extension_enable = 1;
hw_ps->sps.num_views = 1;
/*
* Use the SPS values since they are already in macroblocks
* dimensions, height can be field height (halved) if
* V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
* decoding smaller images into larger allocation which can be used
* to implementing SVC spatial layer support.
*/
hw_ps->sps.pic_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1;
hw_ps->sps.pic_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1;
hw_ps->sps.frame_mbs_only_flag =
!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
hw_ps->sps.mb_adaptive_frame_field_flag =
!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
hw_ps->sps.direct_8x8_inference_flag =
!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
/* write pps */
hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id;
hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id;
hw_ps->pps.entropy_coding_mode_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
hw_ps->pps.bottom_field_pic_order_in_frame_present_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
hw_ps->pps.num_ref_idx_l0_default_active_minus1 =
pps->num_ref_idx_l0_default_active_minus1;
hw_ps->pps.num_ref_idx_l1_default_active_minus1 =
pps->num_ref_idx_l1_default_active_minus1;
hw_ps->pps.weighted_pred_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc;
hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26;
hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26;
hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset;
hw_ps->pps.deblocking_filter_control_present_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
hw_ps->pps.constrained_intra_pred_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
hw_ps->pps.redundant_pic_cnt_present =
!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
hw_ps->pps.transform_8x8_mode_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
hw_ps->pps.scaling_list_enable_flag =
!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT);
/*
* To be on the safe side, program the scaling matrix address
*/
scaling_distance = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
scaling_list_address = h264_ctx->priv_tbl.dma + scaling_distance;
hw_ps->pps.scaling_list_address = scaling_list_address;
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
hw_ps->pps.is_longterm |= (1 << i);
}
}
static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
&h264_ctx->regs.common,
sizeof(h264_ctx->regs.common));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
&h264_ctx->regs.h264_param,
sizeof(h264_ctx->regs.h264_param));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
&h264_ctx->regs.common_addr,
sizeof(h264_ctx->regs.common_addr));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
&h264_ctx->regs.h264_addr,
sizeof(h264_ctx->regs.h264_addr));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
&h264_ctx->regs.h264_highpoc,
sizeof(h264_ctx->regs.h264_highpoc));
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
struct rkvdec_vdpu381_regs_h264 *regs = &h264_ctx->regs;
const struct v4l2_format *f;
dma_addr_t rlc_addr;
dma_addr_t dst_addr;
u32 hor_virstride;
u32 ver_virstride;
u32 y_virstride;
u32 offset;
u32 pixels;
u32 i;
memset(regs, 0, sizeof(*regs));
/* Set H264 mode */
regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_H264;
/* Set config */
regs->common.reg011_important_en.buf_empty_en = 1;
regs->common.reg011_important_en.dec_clkgate_e = 1;
regs->common.reg011_important_en.dec_timeout_e = 1;
regs->common.reg011_important_en.pix_range_det_e = 1;
/*
* Even though the scan list address can be set in RPS,
* with some frames, it will try to use the address set in the register.
*/
regs->common.reg012_secondary_en.scanlist_addr_valid_en = 1;
/* Set IDR flag */
regs->common.reg013_en_mode_set.cur_pic_is_idr =
!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC);
/* Set input stream length */
regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
/* Set max slice number */
regs->common.reg017_slice_number.slice_num = MAX_SLICE_NUMBER;
/* Set strides */
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16;
regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16;
regs->common.reg020_y_stride.y_virstride = y_virstride / 16;
/* Activate block gating */
regs->common.reg026_block_gating_en.inter_auto_gating_e = 1;
regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1;
regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0;
regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1;
regs->common.reg026_block_gating_en.intra_auto_gating_e = 1;
regs->common.reg026_block_gating_en.mc_auto_gating_e = 1;
regs->common.reg026_block_gating_en.transd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.sram_auto_gating_e = 1;
regs->common.reg026_block_gating_en.cru_auto_gating_e = 1;
regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1;
/* Set timeout threshold */
pixels = dst_fmt->height * dst_fmt->width;
if (pixels < RKVDEC_1080P_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p;
else if (pixels < RKVDEC_4K_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K;
else if (pixels < RKVDEC_8K_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K;
else
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX;
/* Set TOP and BOTTOM POCs */
regs->h264_param.reg065_cur_top_poc = dec_params->top_field_order_cnt;
regs->h264_param.reg066_cur_bot_poc = dec_params->bottom_field_order_cnt;
/* Set ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct vb2_buffer *vb_buf = run->ref_buf[i];
dma_addr_t buf_dma;
/*
* If a DPB entry is unused or invalid, address of current destination
* buffer is returned.
*/
if (!vb_buf)
vb_buf = &dst_buf->vb2_buf;
buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
/* Set reference addresses */
regs->h264_addr.reg164_180_ref_base[i] = buf_dma;
/* Set COLMV addresses */
regs->h264_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset;
struct rkvdec_vdpu381_h264_ref_info *ref_info =
&regs->h264_param.reg099_102_ref_info_regs[i / 4].ref_info[i % 4];
ref_info->ref_field =
!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD);
ref_info->ref_colmv_use_flag =
!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE);
ref_info->ref_topfield_used =
!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF);
ref_info->ref_botfield_used =
!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF);
regs->h264_param.reg067_098_ref_poc[i * 2] =
dpb[i].top_field_order_cnt;
regs->h264_param.reg067_098_ref_poc[i * 2 + 1] =
dpb[i].bottom_field_order_cnt;
}
/* Set rlc base address (input stream) */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common_addr.rlc_base = rlc_addr;
regs->common_addr.rlcwrite_base = rlc_addr;
/* Set output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->common_addr.decout_base = dst_addr;
regs->common_addr.error_ref_base = dst_addr;
/* Set colmv address */
regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset;
/* Set RCB addresses */
for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++)
regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i);
/* Set hw pps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
regs->h264_addr.reg161_pps_base = priv_start_addr + offset;
/* Set hw rps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
regs->h264_addr.reg163_rps_base = priv_start_addr + offset;
/* Set cabac table */
offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
regs->h264_addr.reg199_cabactbl_base = priv_start_addr + offset;
offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
regs->h264_addr.reg181_scanlist_addr = priv_start_addr + offset;
rkvdec_write_regs(ctx);
}
static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_priv_tbl *priv_tbl;
struct rkvdec_h264_ctx *h264_ctx;
struct v4l2_ctrl *ctrl;
int ret;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_SPS);
if (!ctrl)
return -EINVAL;
ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
if (ret)
return ret;
h264_ctx = kzalloc_obj(*h264_ctx);
if (!h264_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&h264_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
ret = -ENOMEM;
goto err_free_ctx;
}
h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
h264_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
sizeof(rkvdec_h264_cabac_table));
ctx->priv = h264_ctx;
return 0;
err_free_ctx:
kfree(h264_ctx);
return ret;
}
static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
kfree(h264_ctx);
}
static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
{
struct v4l2_h264_reflist_builder reflist_builder;
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_h264_run run;
rkvdec_h264_run_preamble(ctx, &run);
/* Build the P/B{0,1} ref lists. */
v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
run.sps, run.decode_params->dpb);
v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
h264_ctx->reflists.b1);
assemble_hw_scaling_list(&run, &tbl->scaling_list);
assemble_hw_pps(ctx, &run);
lookup_ref_buf_idx(ctx, &run);
assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
rkvdec_schedule_watchdog(rkvdec, h264_ctx->regs.common.reg032_timeout_threshold);
/* Start decoding! */
writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
return 0;
}
static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops = {
.adjust_fmt = rkvdec_h264_adjust_fmt,
.get_image_fmt = rkvdec_h264_get_image_fmt,
.start = rkvdec_h264_start,
.stop = rkvdec_h264_stop,
.run = rkvdec_h264_run,
.try_ctrl = rkvdec_h264_try_ctrl,
};
@@ -0,0 +1,639 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VDPU381 HEVC backend
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-cabac.h"
#include "rkvdec-rcb.h"
#include "rkvdec-hevc-common.h"
#include "rkvdec-vdpu381-regs.h"
// SPS
struct rkvdec_hevc_sps {
u16 video_parameters_set_id : 4;
u16 seq_parameters_set_id_sps : 4;
u16 chroma_format_idc : 2;
u16 width : 16;
u16 height : 16;
u16 bit_depth_luma : 4;
u16 bit_depth_chroma : 4;
u16 max_pic_order_count_lsb : 5;
u16 diff_max_min_luma_coding_block_size : 2;
u16 min_luma_coding_block_size : 3;
u16 min_transform_block_size : 3;
u16 diff_max_min_transform_block_size : 2;
u16 max_transform_hierarchy_depth_inter : 3;
u16 max_transform_hierarchy_depth_intra : 3;
u16 scaling_list_enabled_flag : 1;
u16 amp_enabled_flag : 1;
u16 sample_adaptive_offset_enabled_flag : 1;
u16 pcm_enabled_flag : 1;
u16 pcm_sample_bit_depth_luma : 4;
u16 pcm_sample_bit_depth_chroma : 4;
u16 pcm_loop_filter_disabled_flag : 1;
u16 diff_max_min_pcm_luma_coding_block_size : 3;
u16 min_pcm_luma_coding_block_size : 3;
u16 num_short_term_ref_pic_sets : 7;
u16 long_term_ref_pics_present_flag : 1;
u16 num_long_term_ref_pics_sps : 6;
u16 sps_temporal_mvp_enabled_flag : 1;
u16 strong_intra_smoothing_enabled_flag : 1;
u16 reserved_0 : 7;
u16 sps_max_dec_pic_buffering_minus1 : 4;
u16 reserved_0_2 : 3;
u16 reserved_f : 8;
} __packed;
//PPS
struct rkvdec_hevc_pps {
u16 picture_parameters_set_id : 6;
u16 seq_parameters_set_id_pps : 4;
u16 dependent_slice_segments_enabled_flag : 1;
u16 output_flag_present_flag : 1;
u16 num_extra_slice_header_bits : 13;
u16 sign_data_hiding_enabled_flag : 1;
u16 cabac_init_present_flag : 1;
u16 num_ref_idx_l0_default_active : 4;
u16 num_ref_idx_l1_default_active : 4;
u16 init_qp_minus26 : 7;
u16 constrained_intra_pred_flag : 1;
u16 transform_skip_enabled_flag : 1;
u16 cu_qp_delta_enabled_flag : 1;
u16 log2_min_cb_size : 3;
u16 pps_cb_qp_offset : 5;
u16 pps_cr_qp_offset : 5;
u16 pps_slice_chroma_qp_offsets_present_flag : 1;
u16 weighted_pred_flag : 1;
u16 weighted_bipred_flag : 1;
u16 transquant_bypass_enabled_flag : 1;
u16 tiles_enabled_flag : 1;
u16 entropy_coding_sync_enabled_flag : 1;
u16 pps_loop_filter_across_slices_enabled_flag : 1;
u16 loop_filter_across_tiles_enabled_flag : 1;
u16 deblocking_filter_override_enabled_flag : 1;
u16 pps_deblocking_filter_disabled_flag : 1;
u16 pps_beta_offset_div2 : 4;
u16 pps_tc_offset_div2 : 4;
u16 lists_modification_present_flag : 1;
u16 log2_parallel_merge_level : 3;
u16 slice_segment_header_extension_present_flag : 1;
u16 zeroes : 3;
u16 num_tile_columns : 5;
u16 num_tile_rows : 5;
u16 sps_pps_mode : 4;
u16 reserved_bits : 14;
u16 reserved;
} __packed;
struct rkvdec_hevc_tile {
u16 value0 : 12;
u16 value1 : 12;
} __packed;
struct rkvdec_sps_pps_packet {
struct rkvdec_hevc_sps sps;
struct rkvdec_hevc_pps pps;
struct rkvdec_hevc_tile column_width[10];
struct rkvdec_hevc_tile row_height[11];
u32 zeroes[3];
u32 zeroes_bits : 6;
u32 padding_bits : 2;
u32 padding;
} __packed;
struct rkvdec_hevc_priv_tbl {
struct rkvdec_sps_pps_packet param_set[64];
struct rkvdec_rps rps;
struct scaling_factor scaling_list;
u8 cabac_table[27456];
};
struct rkvdec_hevc_ctx {
struct rkvdec_aux_buf priv_tbl;
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache;
struct rkvdec_vdpu381_regs_hevc regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
const struct v4l2_ctrl_hevc_pps *pps = run->pps;
struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps_packet *hw_ps;
bool tiles_enabled;
s32 max_cu_width;
s32 pic_in_cts_width;
s32 pic_in_cts_height;
u16 log2_min_cb_size, width, height;
u16 column_width[20];
u16 row_height[22];
u8 pcm_enabled;
u32 i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 32 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
hw_ps->sps.video_parameters_set_id = sps->video_parameter_set_id;
hw_ps->sps.seq_parameters_set_id_sps = sps->seq_parameter_set_id;
hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
width = sps->pic_width_in_luma_samples;
height = sps->pic_height_in_luma_samples;
hw_ps->sps.width = width;
hw_ps->sps.height = height;
hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
hw_ps->sps.max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
hw_ps->sps.diff_max_min_luma_coding_block_size =
sps->log2_diff_max_min_luma_coding_block_size;
hw_ps->sps.min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
hw_ps->sps.min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2;
hw_ps->sps.diff_max_min_transform_block_size =
sps->log2_diff_max_min_luma_transform_block_size;
hw_ps->sps.max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter;
hw_ps->sps.max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra;
hw_ps->sps.scaling_list_enabled_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED);
hw_ps->sps.amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED);
hw_ps->sps.sample_adaptive_offset_enabled_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET);
pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
hw_ps->sps.pcm_enabled_flag = pcm_enabled;
hw_ps->sps.pcm_sample_bit_depth_luma =
pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0;
hw_ps->sps.pcm_sample_bit_depth_chroma =
pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0;
hw_ps->sps.pcm_loop_filter_disabled_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED);
hw_ps->sps.diff_max_min_pcm_luma_coding_block_size =
sps->log2_diff_max_min_pcm_luma_coding_block_size;
hw_ps->sps.min_pcm_luma_coding_block_size =
pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0;
hw_ps->sps.num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
hw_ps->sps.long_term_ref_pics_present_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT);
hw_ps->sps.num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps;
hw_ps->sps.sps_temporal_mvp_enabled_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED);
hw_ps->sps.strong_intra_smoothing_enabled_flag =
!!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED);
hw_ps->sps.sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1;
hw_ps->sps.reserved_f = 0xff;
/* write pps */
hw_ps->pps.picture_parameters_set_id = pps->pic_parameter_set_id;
hw_ps->pps.seq_parameters_set_id_pps = sps->seq_parameter_set_id;
hw_ps->pps.dependent_slice_segments_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED);
hw_ps->pps.output_flag_present_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT);
hw_ps->pps.num_extra_slice_header_bits = pps->num_extra_slice_header_bits;
hw_ps->pps.sign_data_hiding_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED);
hw_ps->pps.cabac_init_present_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT);
hw_ps->pps.num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1;
hw_ps->pps.num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1;
hw_ps->pps.init_qp_minus26 = pps->init_qp_minus26;
hw_ps->pps.constrained_intra_pred_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED);
hw_ps->pps.transform_skip_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED);
hw_ps->pps.cu_qp_delta_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED);
hw_ps->pps.log2_min_cb_size = log2_min_cb_size +
sps->log2_diff_max_min_luma_coding_block_size -
pps->diff_cu_qp_delta_depth;
hw_ps->pps.pps_cb_qp_offset = pps->pps_cb_qp_offset;
hw_ps->pps.pps_cr_qp_offset = pps->pps_cr_qp_offset;
hw_ps->pps.pps_slice_chroma_qp_offsets_present_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT);
hw_ps->pps.weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED);
hw_ps->pps.weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
hw_ps->pps.transquant_bypass_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED);
tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
hw_ps->pps.tiles_enabled_flag = tiles_enabled;
hw_ps->pps.entropy_coding_sync_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED);
hw_ps->pps.pps_loop_filter_across_slices_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED);
hw_ps->pps.loop_filter_across_tiles_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED);
hw_ps->pps.deblocking_filter_override_enabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED);
hw_ps->pps.pps_deblocking_filter_disabled_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER);
hw_ps->pps.pps_beta_offset_div2 = pps->pps_beta_offset_div2;
hw_ps->pps.pps_tc_offset_div2 = pps->pps_tc_offset_div2;
hw_ps->pps.lists_modification_present_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT);
hw_ps->pps.log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2;
hw_ps->pps.slice_segment_header_extension_present_flag =
!!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT);
hw_ps->pps.num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 0;
hw_ps->pps.num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 0;
hw_ps->pps.sps_pps_mode = 0;
hw_ps->pps.reserved_bits = 0x3fff;
hw_ps->pps.reserved = 0xffff;
// Setup tiles information
memset(column_width, 0, sizeof(column_width));
memset(row_height, 0, sizeof(row_height));
max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
compute_tiles_uniform(run, log2_min_cb_size, width, height,
pic_in_cts_width, pic_in_cts_height,
column_width, row_height);
} else {
compute_tiles_non_uniform(run, log2_min_cb_size, width, height,
pic_in_cts_width, pic_in_cts_height,
column_width, row_height);
}
} else {
column_width[0] = (width + max_cu_width - 1) / max_cu_width;
row_height[0] = (height + max_cu_width - 1) / max_cu_width;
}
for (i = 0; i < 20; i++) {
if (column_width[i] > 0)
column_width[i]--;
if (i & 1)
hw_ps->column_width[i / 2].value1 = column_width[i];
else
hw_ps->column_width[i / 2].value0 = column_width[i];
}
for (i = 0; i < 22; i++) {
if (row_height[i] > 0)
row_height[i]--;
if (i & 1)
hw_ps->row_height[i / 2].value1 = row_height[i];
else
hw_ps->row_height[i / 2].value0 = row_height[i];
}
hw_ps->padding = 0xffffffff;
hw_ps->padding_bits = 0x3;
}
static void set_ref_valid(struct rkvdec_vdpu381_regs_hevc *regs, int id, u32 valid)
{
switch (id) {
case 0:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_0 = valid;
break;
case 1:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_1 = valid;
break;
case 2:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_2 = valid;
break;
case 3:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_3 = valid;
break;
case 4:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_4 = valid;
break;
case 5:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_5 = valid;
break;
case 6:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_6 = valid;
break;
case 7:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_7 = valid;
break;
case 8:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_8 = valid;
break;
case 9:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_9 = valid;
break;
case 10:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_10 = valid;
break;
case 11:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_11 = valid;
break;
case 12:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_12 = valid;
break;
case 13:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_13 = valid;
break;
case 14:
regs->hevc_param.reg099_hevc_ref_valid.hevc_ref_valid_14 = valid;
break;
}
}
static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_REGS,
&hevc_ctx->regs.common,
sizeof(hevc_ctx->regs.common));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_PARAMS_REGS,
&hevc_ctx->regs.hevc_param,
sizeof(hevc_ctx->regs.hevc_param));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_COMMON_ADDR_REGS,
&hevc_ctx->regs.common_addr,
sizeof(hevc_ctx->regs.common_addr));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_CODEC_ADDR_REGS,
&hevc_ctx->regs.hevc_addr,
sizeof(hevc_ctx->regs.hevc_addr));
rkvdec_memcpy_toio(rkvdec->regs + OFFSET_POC_HIGHBIT_REGS,
&hevc_ctx->regs.hevc_highpoc,
sizeof(hevc_ctx->regs.hevc_highpoc));
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
const struct v4l2_hevc_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_vdpu381_regs_hevc *regs = &hevc_ctx->regs;
dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
const struct v4l2_format *f;
dma_addr_t rlc_addr;
u32 hor_virstride = 0;
u32 ver_virstride = 0;
u32 y_virstride = 0;
u32 offset;
u32 pixels;
dma_addr_t dst_addr;
u32 i;
memset(regs, 0, sizeof(*regs));
/* Set HEVC mode */
regs->common.reg009_dec_mode.dec_mode = VDPU381_MODE_HEVC;
/* Set config */
regs->common.reg011_important_en.buf_empty_en = 1;
regs->common.reg011_important_en.dec_clkgate_e = 1;
regs->common.reg011_important_en.dec_timeout_e = 1;
regs->common.reg011_important_en.pix_range_det_e = 1;
/* Set IDR flag */
regs->common.reg013_en_mode_set.cur_pic_is_idr =
!!(dec_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC);
/* Set input stream length */
regs->common.reg016_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
/* Set max slice number */
regs->common.reg017_slice_number.slice_num = 1;
/* Set strides */
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
regs->common.reg018_y_hor_stride.y_hor_virstride = hor_virstride / 16;
regs->common.reg019_uv_hor_stride.uv_hor_virstride = hor_virstride / 16;
regs->common.reg020_y_stride.y_virstride = y_virstride / 16;
/* Activate block gating */
regs->common.reg026_block_gating_en.inter_auto_gating_e = 1;
regs->common.reg026_block_gating_en.filterd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.strmd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.mcp_auto_gating_e = 1;
regs->common.reg026_block_gating_en.busifd_auto_gating_e = 0;
regs->common.reg026_block_gating_en.dec_ctrl_auto_gating_e = 1;
regs->common.reg026_block_gating_en.intra_auto_gating_e = 1;
regs->common.reg026_block_gating_en.mc_auto_gating_e = 1;
regs->common.reg026_block_gating_en.transd_auto_gating_e = 1;
regs->common.reg026_block_gating_en.sram_auto_gating_e = 1;
regs->common.reg026_block_gating_en.cru_auto_gating_e = 1;
regs->common.reg026_block_gating_en.reg_cfg_gating_en = 1;
/* Set timeout threshold */
pixels = dst_fmt->height * dst_fmt->width;
if (pixels < RKVDEC_1080P_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_1080p;
else if (pixels < RKVDEC_4K_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_4K;
else if (pixels < RKVDEC_8K_PIXELS)
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_8K;
else
regs->common.reg032_timeout_threshold = RKVDEC_TIMEOUT_MAX;
/* Set POC val */
regs->hevc_param.reg065_cur_top_poc = dec_params->pic_order_cnt_val;
/* Set ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
dma_addr_t buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
u32 valid = !!(dec_params->num_active_dpb_entries > i);
/* Set reference addresses */
regs->hevc_addr.reg164_180_ref_base[i] = buf_dma;
/* Set COLMV addresses */
regs->hevc_addr.reg182_198_colmv_base[i] = buf_dma + ctx->colmv_offset;
regs->hevc_param.reg067_082_ref_poc[i] =
dpb[i].pic_order_cnt_val;
set_ref_valid(regs, i, valid);
regs->hevc_param.reg103_hevc_mvc0.ref_pic_layer_same_with_cur |= 1 << i;
}
/* Set rlc base address (input stream) */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common_addr.rlc_base = rlc_addr;
regs->common_addr.rlcwrite_base = rlc_addr;
/* Set output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->common_addr.decout_base = dst_addr;
regs->common_addr.error_ref_base = dst_addr;
/* Set colmv address */
regs->common_addr.colmv_cur_base = dst_addr + ctx->colmv_offset;
/* Set RCB addresses */
for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++)
regs->common_addr.rcb_base[i] = rkvdec_rcb_buf_dma_addr(ctx, i);
/* Set hw pps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
regs->hevc_addr.reg161_pps_base = priv_start_addr + offset;
/* Set hw rps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
regs->hevc_addr.reg163_rps_base = priv_start_addr + offset;
/* Set cabac table */
offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
regs->hevc_addr.reg199_cabactbl_base = priv_start_addr + offset;
/* Set scaling matrix */
offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
regs->hevc_addr.reg181_scanlist_addr = priv_start_addr + offset;
rkvdec_write_regs(ctx);
}
static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
const struct v4l2_ctrl_hevc_sps *sps)
{
if (sps->chroma_format_idc != 1)
/* Only 4:2:0 is supported */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
/* Only 8-bit and 10-bit are supported */
return -EINVAL;
if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
return -EINVAL;
return 0;
}
static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_priv_tbl *priv_tbl;
struct rkvdec_hevc_ctx *hevc_ctx;
struct v4l2_ctrl *ctrl;
int ret;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_SPS);
if (!ctrl)
return -EINVAL;
ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
if (ret)
return ret;
hevc_ctx = kzalloc_obj(*hevc_ctx);
if (!hevc_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&hevc_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
ret = -ENOMEM;
goto err_free_ctx;
}
hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
hevc_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
sizeof(rkvdec_hevc_cabac_table));
ctx->priv = hevc_ctx;
return 0;
err_free_ctx:
kfree(hevc_ctx);
return ret;
}
static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
kfree(hevc_ctx);
}
static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_run run;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
rkvdec_hevc_run_preamble(ctx, &run);
rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
&hevc_ctx->scaling_matrix_cache);
assemble_hw_pps(ctx, &run);
/*
* On vdpu381, not setting the long and short term ref sets will just output wrong frames.
* Let's just warn about it and let the decoder run anyway.
*/
if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
(!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
dev_warn_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
} else {
rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache);
}
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
rkvdec_schedule_watchdog(rkvdec, hevc_ctx->regs.common.reg032_timeout_threshold);
/* Start decoding! */
writel(VDPU381_DEC_E_BIT, rkvdec->regs + VDPU381_REG_DEC_E);
return 0;
}
static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops = {
.adjust_fmt = rkvdec_hevc_adjust_fmt,
.start = rkvdec_hevc_start,
.stop = rkvdec_hevc_stop,
.run = rkvdec_hevc_run,
.try_ctrl = rkvdec_hevc_try_ctrl,
.get_image_fmt = rkvdec_hevc_get_image_fmt,
};
@@ -0,0 +1,430 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip VDPU381 Video Decoder driver registers description
*
* Copyright (C) 2024 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <linux/types.h>
#ifndef _RKVDEC_REGS_H_
#define _RKVDEC_REGS_H_
#define OFFSET_COMMON_REGS (8 * sizeof(u32))
#define OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32))
#define OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32))
#define OFFSET_CODEC_ADDR_REGS (160 * sizeof(u32))
#define OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32))
#define VDPU381_MODE_HEVC 0
#define VDPU381_MODE_H264 1
#define VDPU381_MODE_VP9 2
#define VDPU381_MODE_AVS2 3
#define MAX_SLICE_NUMBER 0x3fff
#define RKVDEC_TIMEOUT_1080p (0xefffff)
#define RKVDEC_TIMEOUT_4K (0x2cfffff)
#define RKVDEC_TIMEOUT_8K (0x4ffffff)
#define RKVDEC_TIMEOUT_MAX (0xffffffff)
#define VDPU381_REG_DEC_E 0x028
#define VDPU381_DEC_E_BIT 1
#define VDPU381_REG_IMPORTANT_EN 0x02c
#define VDPU381_DEC_IRQ_DISABLE BIT(4)
#define VDPU381_REG_STA_INT 0x380
#define VDPU381_STA_INT_DEC_RDY_STA BIT(2)
#define VDPU381_STA_INT_ERROR BIT(4)
#define VDPU381_STA_INT_TIMEOUT BIT(5)
#define VDPU381_STA_INT_SOFTRESET_RDY BIT(9)
/* base: OFFSET_COMMON_REGS */
struct rkvdec_vdpu381_regs_common {
struct {
u32 in_endian : 1;
u32 in_swap32_e : 1;
u32 in_swap64_e : 1;
u32 str_endian : 1;
u32 str_swap32_e : 1;
u32 str_swap64_e : 1;
u32 out_endian : 1;
u32 out_swap32_e : 1;
u32 out_cbcr_swap : 1;
u32 out_swap64_e : 1;
u32 reserved : 22;
} reg008_in_out;
struct {
u32 dec_mode : 10;
u32 reserved : 22;
} reg009_dec_mode;
struct {
u32 dec_e : 1;
u32 reserved : 31;
} reg010_dec_e;
struct {
u32 reserved0 : 1;
u32 dec_clkgate_e : 1;
u32 dec_e_strmd_clkgate_dis : 1;
u32 reserved1 : 1;
u32 dec_irq_dis : 1;
u32 dec_timeout_e : 1;
u32 buf_empty_en : 1;
u32 reserved2 : 3;
u32 dec_e_rewrite_valid : 1;
u32 reserved3 : 9;
u32 softrst_en_p : 1;
u32 force_softreset_valid : 1;
u32 reserved4 : 2;
u32 pix_range_det_e : 1;
u32 reserved5 : 7;
} reg011_important_en;
struct {
u32 reserved0 : 1;
u32 colmv_compress_en : 1;
u32 fbc_e : 1;
u32 reserved1 : 1;
u32 buspr_slot_disable : 1;
u32 error_info_en : 1;
u32 collect_info_en : 1;
u32 error_auto_rst_disable : 1;
u32 scanlist_addr_valid_en : 1;
u32 scale_down_en : 1;
u32 error_cfg_wr_disable : 1;
u32 reserved2 : 21;
} reg012_secondary_en;
struct {
u32 reserved0 : 1;
u32 req_timeout_rst_sel : 1;
u32 reserved1 : 1;
u32 dec_commonirq_mode : 1;
u32 reserved2 : 2;
u32 stmerror_waitdecfifo_empty : 1;
u32 reserved3 : 5;
u32 allow_not_wr_unref_bframe : 1;
u32 fbc_output_wr_disable : 1;
u32 reserved4 : 4;
u32 error_mode : 1;
u32 reserved5 : 2;
u32 ycacherd_prior : 1;
u32 reserved6 : 2;
u32 cur_pic_is_idr : 1;
u32 reserved7 : 1;
u32 right_auto_rst_disable : 1;
u32 frame_end_err_rst_flag : 1;
u32 rd_prior_mode : 1;
u32 rd_ctrl_prior_mode : 1;
u32 reserved8 : 1;
u32 filter_outbuf_mode : 1;
} reg013_en_mode_set;
struct {
u32 fbc_force_uncompress : 1;
u32 reserved0 : 2;
u32 allow_16x8_cp_flag : 1;
u32 reserved1 : 2;
u32 fbc_h264_exten_4or8_flag : 1;
u32 reserved2 : 25;
} reg014_fbc_param_set;
struct {
u32 rlc_mode_direct_write : 1;
u32 rlc_mode : 1;
u32 reserved0 : 3;
u32 strm_start_bit : 7;
u32 reserved1 : 20;
} reg015_stream_param_set;
u32 reg016_stream_len;
struct {
u32 slice_num : 25;
u32 reserved : 7;
} reg017_slice_number;
struct {
u32 y_hor_virstride : 16;
u32 reserved : 16;
} reg018_y_hor_stride;
struct {
u32 uv_hor_virstride : 16;
u32 reserved : 16;
} reg019_uv_hor_stride;
struct {
u32 y_virstride : 28;
u32 reserved : 4;
} reg020_y_stride;
struct {
u32 inter_error_prc_mode : 1;
u32 error_intra_mode : 1;
u32 error_deb_en : 1;
u32 picidx_replace : 5;
u32 error_spread_e : 1;
u32 reserved0 : 3;
u32 error_inter_pred_cross_slice : 1;
u32 reserved1 : 11;
u32 roi_error_ctu_cal_en : 1;
u32 reserved2 : 7;
} reg021_error_ctrl_set;
struct {
u32 roi_x_ctu_offset_st : 12;
u32 reserved0 : 4;
u32 roi_y_ctu_offset_st : 12;
u32 reserved1 : 4;
} reg022_err_roi_ctu_offset_start;
struct {
u32 roi_x_ctu_offset_end : 12;
u32 reserved0 : 4;
u32 roi_y_ctu_offset_end : 12;
u32 reserved1 : 4;
} reg023_err_roi_ctu_offset_end;
struct {
u32 cabac_err_en_lowbits : 32;
} reg024_cabac_error_en_lowbits;
struct {
u32 cabac_err_en_highbits : 30;
u32 reserved : 2;
} reg025_cabac_error_en_highbits;
struct {
u32 inter_auto_gating_e : 1;
u32 filterd_auto_gating_e : 1;
u32 strmd_auto_gating_e : 1;
u32 mcp_auto_gating_e : 1;
u32 busifd_auto_gating_e : 1;
u32 reserved0 : 3;
u32 dec_ctrl_auto_gating_e : 1;
u32 intra_auto_gating_e : 1;
u32 mc_auto_gating_e : 1;
u32 transd_auto_gating_e : 1;
u32 reserved1 : 4;
u32 sram_auto_gating_e : 1;
u32 cru_auto_gating_e : 1;
u32 reserved2 : 13;
u32 reg_cfg_gating_en : 1;
} reg026_block_gating_en;
struct {
u32 core_safe_x_pixels : 16;
u32 core_safe_y_pixels : 16;
} reg027_core_safe_pixels;
struct {
u32 vp9_wr_prob_idx : 3;
u32 reserved0 : 1;
u32 vp9_rd_prob_idx : 3;
u32 reserved1 : 1;
u32 ref_req_advance_flag : 1;
u32 colmv_req_advance_flag : 1;
u32 poc_only_highbit_flag : 1;
u32 poc_arb_flag : 1;
u32 reserved2 : 4;
u32 film_idx : 10;
u32 reserved3 : 2;
u32 pu_req_mismatch_dis : 1;
u32 colmv_req_mismatch_dis : 1;
u32 reserved4 : 2;
} reg028_multiply_core_ctrl;
struct {
u32 scale_down_hor_ratio : 2;
u32 reserved0 : 6;
u32 scale_down_vrz_ratio : 2;
u32 reserved1 : 22;
} reg029_scale_down_ctrl;
struct {
u32 y_scale_down_tile8x8_hor_stride : 20;
u32 reserved0 : 12;
} reg030_y_scale_down_tile8x8_hor_stride;
struct {
u32 uv_scale_down8x8_tile_hor_stride : 20;
u32 reserved0 : 12;
} reg031_uv_scale_down_tile8x8_hor_stride;
u32 reg032_timeout_threshold;
} __packed;
/* base: OFFSET_COMMON_ADDR_REGS */
struct rkvdec_vdpu381_regs_common_addr {
u32 rlc_base;
u32 rlcwrite_base;
u32 decout_base;
u32 colmv_cur_base;
u32 error_ref_base;
u32 rcb_base[10];
} __packed;
struct rkvdec_vdpu381_h26x_set {
u32 h26x_frame_orslice : 1;
u32 h26x_rps_mode : 1;
u32 h26x_stream_mode : 1;
u32 h26x_stream_lastpacket : 1;
u32 h264_firstslice_flag : 1;
u32 reserved : 27;
} __packed;
/* base: OFFSET_CODEC_PARAMS_REGS */
struct rkvdec_vdpu381_regs_h264_params {
struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
u32 reg065_cur_top_poc;
u32 reg066_cur_bot_poc;
u32 reg067_098_ref_poc[32];
struct rkvdec_vdpu381_h264_info {
struct rkvdec_vdpu381_h264_ref_info {
u32 ref_field : 1;
u32 ref_topfield_used : 1;
u32 ref_botfield_used : 1;
u32 ref_colmv_use_flag : 1;
u32 reserved : 4;
} __packed ref_info[4];
} __packed reg099_102_ref_info_regs[4];
u32 reserved_103_111[9];
struct {
u32 avs2_ref_error_field : 1;
u32 avs2_ref_error_topfield : 1;
u32 ref_error_topfield_used : 1;
u32 ref_error_botfield_used : 1;
u32 reserved : 28;
} reg112_error_ref_info;
} __packed;
struct rkvdec_vdpu381_regs_hevc_params {
struct rkvdec_vdpu381_h26x_set reg064_h26x_set;
u32 reg065_cur_top_poc;
u32 reg066_cur_bot_poc;
u32 reg067_082_ref_poc[16];
u32 reserved_083_098[16];
struct {
u32 hevc_ref_valid_0 : 1;
u32 hevc_ref_valid_1 : 1;
u32 hevc_ref_valid_2 : 1;
u32 hevc_ref_valid_3 : 1;
u32 reserve0 : 4;
u32 hevc_ref_valid_4 : 1;
u32 hevc_ref_valid_5 : 1;
u32 hevc_ref_valid_6 : 1;
u32 hevc_ref_valid_7 : 1;
u32 reserve1 : 4;
u32 hevc_ref_valid_8 : 1;
u32 hevc_ref_valid_9 : 1;
u32 hevc_ref_valid_10 : 1;
u32 hevc_ref_valid_11 : 1;
u32 reserve2 : 4;
u32 hevc_ref_valid_12 : 1;
u32 hevc_ref_valid_13 : 1;
u32 hevc_ref_valid_14 : 1;
u32 reserve3 : 5;
} reg099_hevc_ref_valid;
u32 reserved_100_102[3];
struct {
u32 ref_pic_layer_same_with_cur : 16;
u32 reserve : 16;
} reg103_hevc_mvc0;
struct {
u32 poc_lsb_not_present_flag : 1;
u32 num_direct_ref_layers : 6;
u32 reserve0 : 1;
u32 num_reflayer_pics : 6;
u32 default_ref_layers_active_flag : 1;
u32 max_one_active_ref_layer_flag : 1;
u32 poc_reset_info_present_flag : 1;
u32 vps_poc_lsb_aligned_flag : 1;
u32 mvc_poc15_valid_flag : 1;
u32 reserve1 : 13;
} reg104_hevc_mvc1;
u32 reserved_105_111[7];
struct {
u32 avs2_ref_error_field : 1;
u32 avs2_ref_error_topfield : 1;
u32 ref_error_topfield_used : 1;
u32 ref_error_botfield_used : 1;
u32 reserve : 28;
} reg112_hevc_ref_info;
} __packed;
/* base: OFFSET_CODEC_ADDR_REGS */
struct rkvdec_vdpu381_regs_h26x_addr {
u32 reserved_160;
u32 reg161_pps_base;
u32 reserved_162;
u32 reg163_rps_base;
u32 reg164_180_ref_base[16];
u32 reg181_scanlist_addr;
u32 reg182_198_colmv_base[16];
u32 reg199_cabactbl_base;
} __packed;
struct rkvdec_vdpu381_regs_h26x_highpoc {
struct {
u32 ref0_poc_highbit : 4;
u32 ref1_poc_highbit : 4;
u32 ref2_poc_highbit : 4;
u32 ref3_poc_highbit : 4;
u32 ref4_poc_highbit : 4;
u32 ref5_poc_highbit : 4;
u32 ref6_poc_highbit : 4;
u32 ref7_poc_highbit : 4;
} reg200_203_ref_poc_highbit[4];
struct {
u32 cur_poc_highbit : 4;
u32 reserved : 28;
} reg204_cur_poc_highbit;
} __packed;
struct rkvdec_vdpu381_regs_h264 {
struct rkvdec_vdpu381_regs_common common;
struct rkvdec_vdpu381_regs_h264_params h264_param;
struct rkvdec_vdpu381_regs_common_addr common_addr;
struct rkvdec_vdpu381_regs_h26x_addr h264_addr;
struct rkvdec_vdpu381_regs_h26x_highpoc h264_highpoc;
} __packed;
struct rkvdec_vdpu381_regs_hevc {
struct rkvdec_vdpu381_regs_common common;
struct rkvdec_vdpu381_regs_hevc_params hevc_param;
struct rkvdec_vdpu381_regs_common_addr common_addr;
struct rkvdec_vdpu381_regs_h26x_addr hevc_addr;
struct rkvdec_vdpu381_regs_h26x_highpoc hevc_highpoc;
} __packed;
#endif /* __RKVDEC_REGS_H__ */
@@ -0,0 +1,467 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip Video Decoder VDPU383 H264 backend
*
* Copyright (C) 2024 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <media/v4l2-h264.h>
#include <media/v4l2-mem2mem.h>
#include <linux/iopoll.h>
#include "rkvdec-rcb.h"
#include "rkvdec-cabac.h"
#include "rkvdec-vdpu383-regs.h"
#include "rkvdec-h264-common.h"
#include "rkvdec-bitwriter.h"
#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4)
#define PROFILE_IDC BW_FIELD(4, 8)
#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1)
#define CHROMA_FORMAT_IDC BW_FIELD(13, 2)
#define BIT_DEPTH_LUMA BW_FIELD(15, 3)
#define BIT_DEPTH_CHROMA BW_FIELD(18, 3)
#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1)
#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4)
#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5)
#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2)
#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4)
#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1)
#define PIC_WIDTH_IN_MBS BW_FIELD(38, 16)
#define PIC_HEIGHT_IN_MBS BW_FIELD(54, 16)
#define FRAME_MBS_ONLY_FLAG BW_FIELD(70, 1)
#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(71, 1)
#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(72, 1)
#define MVC_EXTENSION_ENABLE BW_FIELD(73, 1)
#define NUM_VIEWS BW_FIELD(74, 2)
#define VIEW_ID(i) BW_FIELD(76 + ((i) * 10), 10) // i: 0-1
#define PIC_PARAMETER_SET_ID BW_FIELD(96, 8)
#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(104, 5)
#define ENTROPY_CODING_MODE_FLAG BW_FIELD(109, 1)
#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(110, 1)
#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(111 + ((i) * 5), 5) // i: 0-1
#define WEIGHTED_PRED_FLAG BW_FIELD(121, 1)
#define WEIGHTED_BIPRED_IDC BW_FIELD(122, 2)
#define PIC_INIT_QP_MINUS26 BW_FIELD(124, 7)
#define PIC_INIT_QS_MINUS26 BW_FIELD(131, 6)
#define CHROMA_QP_INDEX_OFFSET BW_FIELD(137, 5)
#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(142, 1)
#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(143, 1)
#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(144, 1)
#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(145, 1)
#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(146, 5)
#define SCALING_LIST_ENABLE_FLAG BW_FIELD(151, 1)
#define IS_LONG_TERM(i) BW_FIELD(152 + (i), 1) // i: 0-15
#define PIC_FIELD_FLAG BW_FIELD(184, 1)
#define PIC_ASSOCIATED_FLAG BW_FIELD(185, 1)
#define CUR_TOP_FIELD BW_FIELD(186, 32)
#define CUR_BOT_FIELD BW_FIELD(218, 32)
#define TOP_FIELD_ORDER_CNT(i) BW_FIELD(250 + (i) * 64, 32) // i: 0-15
#define BOT_FIELD_ORDER_CNT(i) BW_FIELD(282 + (i) * 64, 32) // i: 0-15
#define REF_FIELD_FLAGS(i) BW_FIELD(1274 + (i), 1) // i: 0-15
#define REF_TOPFIELD_USED(i) BW_FIELD(1290 + (i), 1) // i: 0-15
#define REF_BOTFIELD_USED(i) BW_FIELD(1306 + (i), 1) // i: 0-15
#define REF_COLMV_USE_FLAG(i) BW_FIELD(1322 + (i), 1) // i: 0-15
#define SPS_SIZE ALIGN(1322 + 16, 128)
struct rkvdec_sps_pps {
u32 info[SPS_SIZE / 8 / 4];
} __packed;
/* Data structure describing auxiliary buffer format. */
struct rkvdec_h264_priv_tbl {
s8 cabac_table[4][464][2];
struct rkvdec_h264_scaling_list scaling_list;
struct rkvdec_sps_pps param_set[256];
struct rkvdec_rps rps;
} __packed;
struct rkvdec_h264_ctx {
struct rkvdec_aux_buf priv_tbl;
struct rkvdec_h264_reflists reflists;
struct vdpu383_regs_h26x regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
const struct v4l2_ctrl_h264_sps *sps = run->sps;
const struct v4l2_ctrl_h264_pps *pps = run->pps;
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps *hw_ps;
u32 pic_width, pic_height;
int i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 32 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, PROFILE_IDC, sps->profile_idc);
rkvdec_set_bw_field(hw_ps->info, CONSTRAINT_SET3_FLAG,
!!(sps->constraint_set_flags & (1 << 3)));
rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc);
rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8);
rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8);
rkvdec_set_bw_field(hw_ps->info, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG,
!!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS));
rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_FRAME_NUM_MINUS4,
sps->log2_max_frame_num_minus4);
rkvdec_set_bw_field(hw_ps->info, MAX_NUM_REF_FRAMES, sps->max_num_ref_frames);
rkvdec_set_bw_field(hw_ps->info, PIC_ORDER_CNT_TYPE, sps->pic_order_cnt_type);
rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4,
sps->log2_max_pic_order_cnt_lsb_minus4);
rkvdec_set_bw_field(hw_ps->info, DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG,
!!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO));
rkvdec_set_bw_field(hw_ps->info, MVC_EXTENSION_ENABLE, 0);
rkvdec_set_bw_field(hw_ps->info, NUM_VIEWS, 0);
/*
* Use the SPS values since they are already in macroblocks
* dimensions, height can be field height (halved) if
* V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY is not set and also it allows
* decoding smaller images into larger allocation which can be used
* to implementing SVC spatial layer support.
*/
pic_width = 16 * (sps->pic_width_in_mbs_minus1 + 1);
pic_height = 16 * (sps->pic_height_in_map_units_minus1 + 1);
if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY))
pic_height *= 2;
if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC))
pic_height /= 2;
rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_MBS, pic_width);
rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_MBS, pic_height);
rkvdec_set_bw_field(hw_ps->info, FRAME_MBS_ONLY_FLAG,
!!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY));
rkvdec_set_bw_field(hw_ps->info, MB_ADAPTIVE_FRAME_FIELD_FLAG,
!!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD));
rkvdec_set_bw_field(hw_ps->info, DIRECT_8X8_INFERENCE_FLAG,
!!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE));
/* write pps */
rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, PPS_SEQ_PARAMETER_SET_ID, pps->seq_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_MODE_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE));
rkvdec_set_bw_field(hw_ps->info, BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG,
!!(pps->flags &
V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT));
rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0),
pps->num_ref_idx_l0_default_active_minus1);
rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1),
pps->num_ref_idx_l1_default_active_minus1);
rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED));
rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_IDC, pps->weighted_bipred_idc);
rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QP_MINUS26, pps->pic_init_qp_minus26);
rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QS_MINUS26, pps->pic_init_qs_minus26);
rkvdec_set_bw_field(hw_ps->info, CHROMA_QP_INDEX_OFFSET, pps->chroma_qp_index_offset);
rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT));
rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED));
rkvdec_set_bw_field(hw_ps->info, REDUNDANT_PIC_CNT_PRESENT,
!!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT));
rkvdec_set_bw_field(hw_ps->info, TRANSFORM_8X8_MODE_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE));
rkvdec_set_bw_field(hw_ps->info, SECOND_CHROMA_QP_INDEX_OFFSET,
pps->second_chroma_qp_index_offset);
rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLE_FLAG,
!!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT));
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
rkvdec_set_bw_field(hw_ps->info, TOP_FIELD_ORDER_CNT(i),
dpb[i].top_field_order_cnt);
rkvdec_set_bw_field(hw_ps->info, BOT_FIELD_ORDER_CNT(i),
dpb[i].bottom_field_order_cnt);
rkvdec_set_bw_field(hw_ps->info, IS_LONG_TERM(i),
!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM));
rkvdec_set_bw_field(hw_ps->info, REF_FIELD_FLAGS(i),
!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD));
rkvdec_set_bw_field(hw_ps->info, REF_COLMV_USE_FLAG(i),
!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE));
rkvdec_set_bw_field(hw_ps->info, REF_TOPFIELD_USED(i),
!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF));
rkvdec_set_bw_field(hw_ps->info, REF_BOTFIELD_USED(i),
!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF));
}
rkvdec_set_bw_field(hw_ps->info, PIC_FIELD_FLAG,
!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC));
rkvdec_set_bw_field(hw_ps->info, PIC_ASSOCIATED_FLAG,
!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD));
rkvdec_set_bw_field(hw_ps->info, CUR_TOP_FIELD, dec_params->top_field_order_cnt);
rkvdec_set_bw_field(hw_ps->info, CUR_BOT_FIELD, dec_params->bottom_field_order_cnt);
}
static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
&h264_ctx->regs.common,
sizeof(h264_ctx->regs.common));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
&h264_ctx->regs.common_addr,
sizeof(h264_ctx->regs.common_addr));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
&h264_ctx->regs.h26x_params,
sizeof(h264_ctx->regs.h26x_params));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
&h264_ctx->regs.h26x_addr,
sizeof(h264_ctx->regs.h26x_addr));
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
dma_addr_t priv_start_addr = h264_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
struct vdpu383_regs_h26x *regs = &h264_ctx->regs;
const struct v4l2_format *f;
dma_addr_t rlc_addr;
dma_addr_t dst_addr;
u32 hor_virstride;
u32 ver_virstride;
u32 y_virstride;
u32 offset;
u32 pixels;
u32 i;
memset(regs, 0, sizeof(*regs));
/* Set H264 mode */
regs->common.reg008_dec_mode = VDPU383_MODE_H264;
/* Set input stream length */
regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
/* Set strides */
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
pixels = dst_fmt->height * dst_fmt->width;
regs->h26x_params.reg068_hor_virstride = hor_virstride / 16;
regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16;
regs->h26x_params.reg070_y_virstride = y_virstride / 16;
/* Activate block gating */
regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.inter_auto_gating_e = 1;
regs->common.reg010_block_gating_en.intra_auto_gating_e = 1;
regs->common.reg010_block_gating_en.transd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.recon_auto_gating_e = 1;
regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.bus_auto_gating_e = 1;
regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1;
regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1;
regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1;
/* Set timeout threshold */
if (pixels < RKVDEC_1080P_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p;
else if (pixels < RKVDEC_4K_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K;
else if (pixels < RKVDEC_8K_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K;
else
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX;
regs->common.reg016_error_ctrl_set.error_proc_disable = 1;
/* Set ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
struct vb2_buffer *vb_buf = run->ref_buf[i];
dma_addr_t buf_dma;
/*
* If a DPB entry is unused or invalid, address of current destination
* buffer is returned.
*/
if (!vb_buf)
vb_buf = &dst_buf->vb2_buf;
buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
/* Set reference addresses */
regs->h26x_addr.reg170_185_ref_base[i] = buf_dma;
regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma;
/* Set COLMV addresses */
regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset;
}
/* Set rlc base address (input stream) */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common_addr.reg128_strm_base = rlc_addr;
/* Set output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->h26x_addr.reg168_decout_base = dst_addr;
regs->h26x_addr.reg169_error_ref_base = dst_addr;
regs->h26x_addr.reg192_payload_st_cur_base = dst_addr;
/* Set colmv address */
regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset;
/* Set RCB addresses */
for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) {
regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i);
regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i);
}
/* Set hw pps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, param_set);
regs->common_addr.reg131_gbl_base = priv_start_addr + offset;
regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_sps_pps) / 16;
/* Set hw rps address */
offset = offsetof(struct rkvdec_h264_priv_tbl, rps);
regs->common_addr.reg129_rps_base = priv_start_addr + offset;
/* Set cabac table */
offset = offsetof(struct rkvdec_h264_priv_tbl, cabac_table);
regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset;
/* Set scaling list address */
offset = offsetof(struct rkvdec_h264_priv_tbl, scaling_list);
regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset;
rkvdec_write_regs(ctx);
}
static int rkvdec_h264_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_priv_tbl *priv_tbl;
struct rkvdec_h264_ctx *h264_ctx;
struct v4l2_ctrl *ctrl;
int ret;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_H264_SPS);
if (!ctrl)
return -EINVAL;
ret = rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
if (ret)
return ret;
h264_ctx = kzalloc_obj(*h264_ctx);
if (!h264_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&h264_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
ret = -ENOMEM;
goto err_free_ctx;
}
h264_ctx->priv_tbl.size = sizeof(*priv_tbl);
h264_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_h264_cabac_table,
sizeof(rkvdec_h264_cabac_table));
ctx->priv = h264_ctx;
return 0;
err_free_ctx:
kfree(h264_ctx);
return ret;
}
static void rkvdec_h264_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, h264_ctx->priv_tbl.size,
h264_ctx->priv_tbl.cpu, h264_ctx->priv_tbl.dma);
kfree(h264_ctx);
}
static int rkvdec_h264_run(struct rkvdec_ctx *ctx)
{
struct v4l2_h264_reflist_builder reflist_builder;
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_h264_ctx *h264_ctx = ctx->priv;
struct rkvdec_h264_run run;
struct rkvdec_h264_priv_tbl *tbl = h264_ctx->priv_tbl.cpu;
u32 timeout_threshold;
rkvdec_h264_run_preamble(ctx, &run);
/* Build the P/B{0,1} ref lists. */
v4l2_h264_init_reflist_builder(&reflist_builder, run.decode_params,
run.sps, run.decode_params->dpb);
v4l2_h264_build_p_ref_list(&reflist_builder, h264_ctx->reflists.p);
v4l2_h264_build_b_ref_lists(&reflist_builder, h264_ctx->reflists.b0,
h264_ctx->reflists.b1);
assemble_hw_scaling_list(&run, &tbl->scaling_list);
assemble_hw_pps(ctx, &run);
lookup_ref_buf_idx(ctx, &run);
assemble_hw_rps(&reflist_builder, &run, &h264_ctx->reflists, &tbl->rps);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
timeout_threshold = h264_ctx->regs.common.reg013_core_timeout_threshold;
rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
/* Start decoding! */
writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
writel(0, rkvdec->link + VDPU383_LINK_IP_ENABLE);
writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
return 0;
}
static int rkvdec_h264_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_H264_SPS)
return rkvdec_h264_validate_sps(ctx, ctrl->p_new.p_h264_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops = {
.adjust_fmt = rkvdec_h264_adjust_fmt,
.get_image_fmt = rkvdec_h264_get_image_fmt,
.start = rkvdec_h264_start,
.stop = rkvdec_h264_stop,
.run = rkvdec_h264_run,
.try_ctrl = rkvdec_h264_try_ctrl,
};
@@ -0,0 +1,594 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Rockchip VDPU383 HEVC backend
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
#include "rkvdec-cabac.h"
#include "rkvdec-rcb.h"
#include "rkvdec-hevc-common.h"
#include "rkvdec-vdpu383-regs.h"
#include "rkvdec-bitwriter.h"
#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4)
#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4)
#define CHROMA_FORMAT_IDC BW_FIELD(8, 2)
#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 16)
#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(26, 16)
#define BIT_DEPTH_LUMA BW_FIELD(42, 3)
#define BIT_DEPTH_CHROMA BW_FIELD(45, 3)
#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(48, 5)
#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(53, 2)
#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(55, 3)
#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(58, 3)
#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(61, 2)
#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(63, 3)
#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(66, 3)
#define SCALING_LIST_ENABLED_FLAG BW_FIELD(69, 1)
#define AMP_ENABLED_FLAG BW_FIELD(70, 1)
#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(71, 1)
#define PCM_ENABLED_FLAG BW_FIELD(72, 1)
#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(73, 4)
#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(77, 4)
#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(81, 1)
#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(82, 3)
#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(85, 3)
#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(88, 7)
#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(95, 1)
#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(96, 6)
#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(102, 1)
#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(103, 1)
#define SPS_MAX_DEC_PIC_BUFFERING_MINUS1 BW_FIELD(111, 4)
#define SEPARATE_COLOUR_PLANE_FLAG BW_FIELD(115, 1)
#define HIGH_PRECISION_OFFSETS_ENABLED_FLAG BW_FIELD(116, 1)
#define PERSISTENT_RICE_ADAPTATION_ENABLED_FLAG BW_FIELD(117, 1)
/* PPS */
#define PIC_PARAMETER_SET_ID BW_FIELD(118, 6)
#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(124, 4)
#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(128, 1)
#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(129, 1)
#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(130, 13)
#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(143, 1)
#define CABAC_INIT_PRESENT_FLAG BW_FIELD(144, 1)
#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(145, 4)
#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(149, 4)
#define INIT_QP_MINUS26 BW_FIELD(153, 7)
#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(160, 1)
#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(161, 1)
#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(162, 1)
#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(163, 3)
#define PPS_CB_QP_OFFSET BW_FIELD(166, 5)
#define PPS_CR_QP_OFFSET BW_FIELD(171, 5)
#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(176, 1)
#define WEIGHTED_PRED_FLAG BW_FIELD(177, 1)
#define WEIGHTED_BIPRED_FLAG BW_FIELD(178, 1)
#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(179, 1)
#define TILES_ENABLED_FLAG BW_FIELD(180, 1)
#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(181, 1)
#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(182, 1)
#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(183, 1)
#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(184, 1)
#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(185, 1)
#define PPS_BETA_OFFSET_DIV2 BW_FIELD(186, 4)
#define PPS_TC_OFFSET_DIV2 BW_FIELD(190, 4)
#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(194, 1)
#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(195, 3)
#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(198, 1)
/* pps extensions */
#define LOG2_MAX_TRANSFORM_SKIP_BLOCK_SIZE BW_FIELD(202, 2)
#define CROSS_COMPONENT_PREDICTION_ENABLED_FLAG BW_FIELD(204, 1)
#define CHROMA_QP_OFFSET_LIST_ENABLED_FLAG BW_FIELD(205, 1)
#define LOG2_MIN_CU_CHROMA_QP_DELTA_SIZE BW_FIELD(206, 3)
#define CB_QP_OFFSET_LIST(i) BW_FIELD(209 + (i) * 5, 5) // i: 0-5
#define CB_CR_OFFSET_LIST(i) BW_FIELD(239 + (i) * 5, 5) // i: 0-5
#define CHROMA_QP_OFFSET_LIST_LEN_MINUS1 BW_FIELD(269, 3)
/* mvc0 && mvc1 */
#define MVC_FF BW_FIELD(272, 16)
#define MVC_00 BW_FIELD(288, 9)
/* poc info */
#define RESERVED2 BW_FIELD(297, 3)
#define CURRENT_POC BW_FIELD(300, 32)
#define REF_PIC_POC(i) BW_FIELD(332 + (i) * 32, 32) // i: 0-14
#define RESERVED3 BW_FIELD(812, 32)
#define REF_IS_VALID(i) BW_FIELD(844 + (i), 1) // i: 0-14
#define RESERVED4 BW_FIELD(859, 1)
/* tile info*/
#define NUM_TILE_COLUMNS BW_FIELD(860, 5)
#define NUM_TILE_ROWS BW_FIELD(865, 5)
#define COLUMN_WIDTH(i) BW_FIELD(870 + (i) * 12, 12) // i: 0-19
#define ROW_HEIGHT(i) BW_FIELD(1110 + (i) * 12, 12) // i: 0-21
#define HEVC_SPS_SIZE ALIGN(1110 + 22 * 12, 256)
struct rkvdec_hevc_sps_pps {
u32 info[HEVC_SPS_SIZE / 8 / 4];
};
struct rkvdec_hevc_priv_tbl {
struct rkvdec_hevc_sps_pps param_set;
struct rkvdec_rps rps;
struct scaling_factor scaling_list;
u8 cabac_table[27456];
} __packed;
struct rkvdec_hevc_ctx {
struct rkvdec_aux_buf priv_tbl;
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
struct v4l2_ctrl_hevc_ext_sps_st_rps st_cache;
struct vdpu383_regs_h26x regs;
};
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
struct rkvdec_hevc_ctx *h264_ctx = ctx->priv;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
const struct v4l2_ctrl_hevc_pps *pps = run->pps;
const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
struct rkvdec_hevc_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_hevc_sps_pps *hw_ps;
bool tiles_enabled;
s32 max_cu_width;
s32 pic_in_cts_width;
s32 pic_in_cts_height;
u16 log2_min_cb_size, width, height;
u16 column_width[22];
u16 row_height[22];
u8 pcm_enabled;
u32 i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
* offset from the base can be calculated by PPS_id * 32 (size per PPS
* packet unit). so the driver copy SPS/PPS information to the exact PPS
* packet unit for HW accessing.
*/
hw_ps = &priv_tbl->param_set;
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
rkvdec_set_bw_field(hw_ps->info, VIDEO_PARAMETER_SET_ID, sps->video_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc);
log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
width = sps->pic_width_in_luma_samples;
height = sps->pic_height_in_luma_samples;
rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_LUMA_SAMPLES, width);
rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_LUMA_SAMPLES, height);
rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8 + 8);
rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8 + 8);
rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB,
sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE,
sps->log2_diff_max_min_luma_coding_block_size);
rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_LUMA_CODING_BLOCK_SIZE,
sps->log2_min_luma_coding_block_size_minus3 + 3);
rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_TRANSFORM_BLOCK_SIZE,
sps->log2_min_luma_transform_block_size_minus2 + 2);
rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE,
sps->log2_diff_max_min_luma_transform_block_size);
rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTER,
sps->max_transform_hierarchy_depth_inter);
rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA,
sps->max_transform_hierarchy_depth_intra);
rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED));
rkvdec_set_bw_field(hw_ps->info, AMP_ENABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED));
rkvdec_set_bw_field(hw_ps->info, SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET));
pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
rkvdec_set_bw_field(hw_ps->info, PCM_ENABLED_FLAG, pcm_enabled);
rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_LUMA,
pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0);
rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_CHROMA,
pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0);
rkvdec_set_bw_field(hw_ps->info, PCM_LOOP_FILTER_DISABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED));
rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE,
sps->log2_diff_max_min_pcm_luma_coding_block_size);
rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE,
pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0);
rkvdec_set_bw_field(hw_ps->info, NUM_SHORT_TERM_REF_PIC_SETS,
sps->num_short_term_ref_pic_sets);
rkvdec_set_bw_field(hw_ps->info, LONG_TERM_REF_PICS_PRESENT_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT));
rkvdec_set_bw_field(hw_ps->info, NUM_LONG_TERM_REF_PICS_SPS,
sps->num_long_term_ref_pics_sps);
rkvdec_set_bw_field(hw_ps->info, SPS_TEMPORAL_MVP_ENABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED));
rkvdec_set_bw_field(hw_ps->info, STRONG_INTRA_SMOOTHING_ENABLED_FLAG,
!!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED));
rkvdec_set_bw_field(hw_ps->info, SPS_MAX_DEC_PIC_BUFFERING_MINUS1,
sps->sps_max_dec_pic_buffering_minus1);
/* write pps */
rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
rkvdec_set_bw_field(hw_ps->info, DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED));
rkvdec_set_bw_field(hw_ps->info, OUTPUT_FLAG_PRESENT_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT));
rkvdec_set_bw_field(hw_ps->info, NUM_EXTRA_SLICE_HEADER_BITS,
pps->num_extra_slice_header_bits);
rkvdec_set_bw_field(hw_ps->info, SIGN_DATA_HIDING_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED));
rkvdec_set_bw_field(hw_ps->info, CABAC_INIT_PRESENT_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT));
rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L0_DEFAULT_ACTIVE,
pps->num_ref_idx_l0_default_active_minus1 + 1);
rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L1_DEFAULT_ACTIVE,
pps->num_ref_idx_l1_default_active_minus1 + 1);
rkvdec_set_bw_field(hw_ps->info, INIT_QP_MINUS26, pps->init_qp_minus26);
rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED));
rkvdec_set_bw_field(hw_ps->info, TRANSFORM_SKIP_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED));
rkvdec_set_bw_field(hw_ps->info, CU_QP_DELTA_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED));
rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_CU_QP_DELTA_SIZE, log2_min_cb_size +
sps->log2_diff_max_min_luma_coding_block_size -
pps->diff_cu_qp_delta_depth);
rkvdec_set_bw_field(hw_ps->info, PPS_CB_QP_OFFSET, pps->pps_cb_qp_offset);
rkvdec_set_bw_field(hw_ps->info, PPS_CR_QP_OFFSET, pps->pps_cr_qp_offset);
rkvdec_set_bw_field(hw_ps->info, PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG,
!!(pps->flags &
V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT));
rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED));
rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED));
rkvdec_set_bw_field(hw_ps->info, TRANSQUANT_BYPASS_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED));
tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
rkvdec_set_bw_field(hw_ps->info, TILES_ENABLED_FLAG, tiles_enabled);
rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_SYNC_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED));
rkvdec_set_bw_field(hw_ps->info, PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG,
!!(pps->flags &
V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED));
rkvdec_set_bw_field(hw_ps->info, LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED));
rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG,
!!(pps->flags &
V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED));
rkvdec_set_bw_field(hw_ps->info, PPS_DEBLOCKING_FILTER_DISABLED_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER));
rkvdec_set_bw_field(hw_ps->info, PPS_BETA_OFFSET_DIV2, pps->pps_beta_offset_div2);
rkvdec_set_bw_field(hw_ps->info, PPS_TC_OFFSET_DIV2, pps->pps_tc_offset_div2);
rkvdec_set_bw_field(hw_ps->info, LISTS_MODIFICATION_PRESENT_FLAG,
!!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT));
rkvdec_set_bw_field(hw_ps->info, LOG2_PARALLEL_MERGE_LEVEL,
pps->log2_parallel_merge_level_minus2 + 2);
rkvdec_set_bw_field(hw_ps->info, SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG,
!!(pps->flags &
V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT));
rkvdec_set_bw_field(hw_ps->info, NUM_TILE_COLUMNS,
tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1);
rkvdec_set_bw_field(hw_ps->info, NUM_TILE_ROWS,
tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1);
rkvdec_set_bw_field(hw_ps->info, MVC_FF, 0xffff);
// Setup tiles information
memset(column_width, 0, sizeof(column_width));
memset(row_height, 0, sizeof(row_height));
max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size + log2_min_cb_size);
pic_in_cts_width = (width + max_cu_width - 1) / max_cu_width;
pic_in_cts_height = (height + max_cu_width - 1) / max_cu_width;
if (tiles_enabled) {
if (pps->flags & V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING) {
compute_tiles_uniform(run, log2_min_cb_size, width, height,
pic_in_cts_width, pic_in_cts_height,
column_width, row_height);
} else {
compute_tiles_non_uniform(run, log2_min_cb_size, width, height,
pic_in_cts_width, pic_in_cts_height,
column_width, row_height);
}
} else {
column_width[0] = (width + max_cu_width - 1) / max_cu_width;
row_height[0] = (height + max_cu_width - 1) / max_cu_width;
}
for (i = 0; i < 20; i++)
rkvdec_set_bw_field(hw_ps->info, COLUMN_WIDTH(i), column_width[i]);
for (i = 0; i < 22; i++)
rkvdec_set_bw_field(hw_ps->info, ROW_HEIGHT(i), row_height[i]);
// Setup POC information
rkvdec_set_bw_field(hw_ps->info, CURRENT_POC, dec_params->pic_order_cnt_val);
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
rkvdec_set_bw_field(hw_ps->info, REF_IS_VALID(i),
!!(dec_params->num_active_dpb_entries > i));
rkvdec_set_bw_field(hw_ps->info, REF_PIC_POC(i),
dec_params->dpb[i].pic_order_cnt_val);
}
}
static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_ctx *h265_ctx = ctx->priv;
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_REGS,
&h265_ctx->regs.common,
sizeof(h265_ctx->regs.common));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_COMMON_ADDR_REGS,
&h265_ctx->regs.common_addr,
sizeof(h265_ctx->regs.common_addr));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_PARAMS_REGS,
&h265_ctx->regs.h26x_params,
sizeof(h265_ctx->regs.h26x_params));
rkvdec_memcpy_toio(rkvdec->regs + VDPU383_OFFSET_CODEC_ADDR_REGS,
&h265_ctx->regs.h26x_addr,
sizeof(h265_ctx->regs.h26x_addr));
}
static void config_registers(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
const struct v4l2_ctrl_hevc_decode_params *dec_params = run->decode_params;
struct rkvdec_hevc_ctx *h265_ctx = ctx->priv;
const struct v4l2_ctrl_hevc_sps *sps = run->sps;
dma_addr_t priv_start_addr = h265_ctx->priv_tbl.dma;
const struct v4l2_pix_format_mplane *dst_fmt;
struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
struct vdpu383_regs_h26x *regs = &h265_ctx->regs;
const struct v4l2_format *f;
dma_addr_t rlc_addr;
dma_addr_t dst_addr;
u32 hor_virstride;
u32 ver_virstride;
u32 y_virstride;
u32 offset;
u32 pixels;
u32 i;
memset(regs, 0, sizeof(*regs));
/* Set HEVC mode */
regs->common.reg008_dec_mode = VDPU383_MODE_HEVC;
/* Set input stream length */
regs->h26x_params.reg066_stream_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
/* Set strides */
f = &ctx->decoded_fmt;
dst_fmt = &f->fmt.pix_mp;
hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
ver_virstride = dst_fmt->height;
y_virstride = hor_virstride * ver_virstride;
pixels = dst_fmt->height * dst_fmt->width;
regs->h26x_params.reg068_hor_virstride = hor_virstride / 16;
regs->h26x_params.reg069_raster_uv_hor_virstride = hor_virstride / 16;
regs->h26x_params.reg070_y_virstride = y_virstride / 16;
/* Activate block gating */
regs->common.reg010_block_gating_en.strmd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.inter_auto_gating_e = 1;
regs->common.reg010_block_gating_en.intra_auto_gating_e = 1;
regs->common.reg010_block_gating_en.transd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.recon_auto_gating_e = 1;
regs->common.reg010_block_gating_en.filterd_auto_gating_e = 1;
regs->common.reg010_block_gating_en.bus_auto_gating_e = 1;
regs->common.reg010_block_gating_en.ctrl_auto_gating_e = 1;
regs->common.reg010_block_gating_en.rcb_auto_gating_e = 1;
regs->common.reg010_block_gating_en.err_prc_auto_gating_e = 1;
/* Set timeout threshold */
if (pixels < RKVDEC_1080P_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_1080p;
else if (pixels < RKVDEC_4K_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_4K;
else if (pixels < RKVDEC_8K_PIXELS)
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_8K;
else
regs->common.reg013_core_timeout_threshold = VDPU383_TIMEOUT_MAX;
regs->common.reg016_error_ctrl_set.error_proc_disable = 1;
/* Set ref pic address & poc */
for (i = 0; i < ARRAY_SIZE(dec_params->dpb) - 1; i++) {
struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
dma_addr_t buf_dma;
buf_dma = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
/* Set reference addresses */
regs->h26x_addr.reg170_185_ref_base[i] = buf_dma;
regs->h26x_addr.reg195_210_payload_st_ref_base[i] = buf_dma;
/* Set COLMV addresses */
regs->h26x_addr.reg217_232_colmv_ref_base[i] = buf_dma + ctx->colmv_offset;
}
/* Set rlc base address (input stream) */
rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
regs->common_addr.reg128_strm_base = rlc_addr;
/* Set output base address */
dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
regs->h26x_addr.reg168_decout_base = dst_addr;
regs->h26x_addr.reg169_error_ref_base = dst_addr;
regs->h26x_addr.reg192_payload_st_cur_base = dst_addr;
/* Set colmv address */
regs->h26x_addr.reg216_colmv_cur_base = dst_addr + ctx->colmv_offset;
/* Set RCB addresses */
for (i = 0; i < rkvdec_rcb_buf_count(ctx); i++) {
regs->common_addr.reg140_162_rcb_info[i].offset = rkvdec_rcb_buf_dma_addr(ctx, i);
regs->common_addr.reg140_162_rcb_info[i].size = rkvdec_rcb_buf_size(ctx, i);
}
if (sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED) {
/* Set scaling matrix */
offset = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
regs->common_addr.reg132_scanlist_addr = priv_start_addr + offset;
}
/* Set hw pps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
regs->common_addr.reg131_gbl_base = priv_start_addr + offset;
regs->h26x_params.reg067_global_len = sizeof(struct rkvdec_hevc_sps_pps) / 16;
/* Set hw rps address */
offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
regs->common_addr.reg129_rps_base = priv_start_addr + offset;
/* Set cabac table */
offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
regs->common_addr.reg130_cabactbl_base = priv_start_addr + offset;
rkvdec_write_regs(ctx);
}
static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
const struct v4l2_ctrl_hevc_sps *sps)
{
if (sps->chroma_format_idc != 1)
/* Only 4:2:0 is supported */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
/* Luma and chroma bit depth mismatch */
return -EINVAL;
if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
/* Only 8-bit and 10-bit are supported */
return -EINVAL;
if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
return -EINVAL;
return 0;
}
static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_priv_tbl *priv_tbl;
struct rkvdec_hevc_ctx *hevc_ctx;
struct v4l2_ctrl *ctrl;
int ret;
ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
V4L2_CID_STATELESS_HEVC_SPS);
if (!ctrl)
return -EINVAL;
ret = rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
if (ret)
return ret;
hevc_ctx = kzalloc_obj(*hevc_ctx);
if (!hevc_ctx)
return -ENOMEM;
priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
&hevc_ctx->priv_tbl.dma, GFP_KERNEL);
if (!priv_tbl) {
ret = -ENOMEM;
goto err_free_ctx;
}
hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
hevc_ctx->priv_tbl.cpu = priv_tbl;
memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
sizeof(rkvdec_hevc_cabac_table));
ctx->priv = hevc_ctx;
return 0;
err_free_ctx:
kfree(hevc_ctx);
return ret;
}
static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
{
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_dev *rkvdec = ctx->dev;
dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
kfree(hevc_ctx);
}
static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
{
struct rkvdec_dev *rkvdec = ctx->dev;
struct rkvdec_hevc_run run;
struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
u32 timeout_threshold;
rkvdec_hevc_run_preamble(ctx, &run);
/*
* On vdpu383, not setting the long and short term ref sets leads to IOMMU page faults.
* To be on the safe side for this new v4l2 control, write an error in the log and mark
* the buffer as failed by returning an error here.
*/
if ((!ctx->has_sps_lt_rps && run.sps->num_long_term_ref_pics_sps) ||
(!ctx->has_sps_st_rps && run.sps->num_short_term_ref_pic_sets)) {
dev_err_ratelimited(rkvdec->dev, "Long and short term RPS not set\n");
return -EINVAL;
}
rkvdec_hevc_assemble_hw_scaling_list(ctx, &run, &tbl->scaling_list,
&hevc_ctx->scaling_matrix_cache);
assemble_hw_pps(ctx, &run);
rkvdec_hevc_assemble_hw_rps(&run, &tbl->rps, &hevc_ctx->st_cache);
config_registers(ctx, &run);
rkvdec_run_postamble(ctx, &run.base);
timeout_threshold = hevc_ctx->regs.common.reg013_core_timeout_threshold;
rkvdec_schedule_watchdog(rkvdec, timeout_threshold);
/* Start decoding! */
writel(timeout_threshold, rkvdec->link + VDPU383_LINK_TIMEOUT_THRESHOLD);
writel(VDPU383_IP_CRU_MODE, rkvdec->link + VDPU383_LINK_IP_ENABLE);
writel(VDPU383_DEC_E_BIT, rkvdec->link + VDPU383_LINK_DEC_ENABLE);
return 0;
}
static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
{
if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
return 0;
}
const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops = {
.adjust_fmt = rkvdec_hevc_adjust_fmt,
.start = rkvdec_hevc_start,
.stop = rkvdec_hevc_stop,
.run = rkvdec_hevc_run,
.try_ctrl = rkvdec_hevc_try_ctrl,
.get_image_fmt = rkvdec_hevc_get_image_fmt,
};
@@ -0,0 +1,281 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Video Decoder VDPU383 driver registers description
*
* Copyright (C) 2025 Collabora, Ltd.
* Detlev Casanova <detlev.casanova@collabora.com>
*/
#ifndef _RKVDEC_VDPU838_REGS_H_
#define _RKVDEC_VDPU838_REGS_H_
#include <linux/types.h>
#define VDPU383_OFFSET_COMMON_REGS (8 * sizeof(u32))
#define VDPU383_OFFSET_CODEC_PARAMS_REGS (64 * sizeof(u32))
#define VDPU383_OFFSET_COMMON_ADDR_REGS (128 * sizeof(u32))
#define VDPU383_OFFSET_CODEC_ADDR_REGS (168 * sizeof(u32))
#define VDPU383_OFFSET_POC_HIGHBIT_REGS (200 * sizeof(u32))
#define VDPU383_MODE_HEVC 0
#define VDPU383_MODE_H264 1
#define VDPU383_TIMEOUT_1080p (0xffffff)
#define VDPU383_TIMEOUT_4K (0x2cfffff)
#define VDPU383_TIMEOUT_8K (0x4ffffff)
#define VDPU383_TIMEOUT_MAX (0xffffffff)
#define VDPU383_LINK_TIMEOUT_THRESHOLD 0x54
#define VDPU383_LINK_IP_ENABLE 0x58
#define VDPU383_IP_CRU_MODE BIT(24)
#define VDPU383_LINK_DEC_ENABLE 0x40
#define VDPU383_DEC_E_BIT BIT(0)
#define VDPU383_LINK_INT_EN 0x048
#define VDPU383_INT_EN_IRQ BIT(0)
#define VDPU383_INT_EN_LINE_IRQ BIT(1)
#define VDPU383_LINK_STA_INT 0x04c
#define VDPU383_STA_INT_DEC_RDY_STA BIT(0)
#define VDPU383_STA_INT_SOFTRESET_RDY (BIT(10) | BIT(11))
#define VDPU383_STA_INT_ALL 0x3ff
struct vdpu383_regs_common {
u32 reg008_dec_mode;
struct {
u32 fbc_e : 1;
u32 tile_e : 1;
u32 reserve0 : 2;
u32 buf_empty_en : 1;
u32 scale_down_en : 1;
u32 reserve1 : 1;
u32 pix_range_det_e : 1;
u32 av1_fgs_en : 1;
u32 reserve2 : 7;
u32 line_irq_en : 1;
u32 out_cbcr_swap : 1;
u32 fbc_force_uncompress : 1;
u32 fbc_sparse_mode : 1;
u32 reserve3 : 12;
} reg009_important_en;
struct {
u32 strmd_auto_gating_e : 1;
u32 inter_auto_gating_e : 1;
u32 intra_auto_gating_e : 1;
u32 transd_auto_gating_e : 1;
u32 recon_auto_gating_e : 1;
u32 filterd_auto_gating_e : 1;
u32 bus_auto_gating_e : 1;
u32 ctrl_auto_gating_e : 1;
u32 rcb_auto_gating_e : 1;
u32 err_prc_auto_gating_e : 1;
u32 reserve0 : 22;
} reg010_block_gating_en;
struct {
u32 reserve0 : 9;
u32 dec_timeout_dis : 1;
u32 reserve1 : 22;
} reg011_cfg_para;
struct {
u32 reserve0 : 7;
u32 cache_hash_mask : 25;
} reg012_cache_hash_mask;
u32 reg013_core_timeout_threshold;
struct {
u32 dec_line_irq_step : 16;
u32 dec_line_offset_y_st : 16;
} reg014_line_irq_ctrl;
struct {
u32 rkvdec_frame_rdy_sta : 1;
u32 rkvdec_strm_error_sta : 1;
u32 rkvdec_core_timeout_sta : 1;
u32 rkvdec_ip_timeout_sta : 1;
u32 rkvdec_bus_error_sta : 1;
u32 rkvdec_buffer_empty_sta : 1;
u32 rkvdec_colmv_ref_error_sta : 1;
u32 rkvdec_error_spread_sta : 1;
u32 create_core_timeout_sta : 1;
u32 wlast_miss_match_sta : 1;
u32 rkvdec_core_rst_rdy_sta : 1;
u32 rkvdec_ip_rst_rdy_sta : 1;
u32 force_busidle_rdy_sta : 1;
u32 ltb_pause_rdy_sta : 1;
u32 ltb_end_flag : 1;
u32 unsupport_decmode_error_sta : 1;
u32 wmask_bits : 15;
u32 reserve0 : 1;
} reg015_irq_sta;
struct {
u32 error_proc_disable : 1;
u32 reserve0 : 7;
u32 error_spread_disable : 1;
u32 reserve1 : 15;
u32 roi_error_ctu_cal_en : 1;
u32 reserve2 : 7;
} reg016_error_ctrl_set;
struct {
u32 roi_x_ctu_offset_st : 12;
u32 reserve0 : 4;
u32 roi_y_ctu_offset_st : 12;
u32 reserve1 : 4;
} reg017_err_roi_ctu_offset_start;
struct {
u32 roi_x_ctu_offset_end : 12;
u32 reserve0 : 4;
u32 roi_y_ctu_offset_end : 12;
u32 reserve1 : 4;
} reg018_err_roi_ctu_offset_end;
struct {
u32 avs2_ref_error_field : 1;
u32 avs2_ref_error_topfield : 1;
u32 ref_error_topfield_used : 1;
u32 ref_error_botfield_used : 1;
u32 reserve0 : 28;
} reg019_error_ref_info;
u32 reg020_cabac_error_en_lowbits;
u32 reg021_cabac_error_en_highbits;
u32 reg022_reserved;
struct {
u32 fill_y : 10;
u32 fill_u : 10;
u32 fill_v : 10;
u32 reserve0 : 2;
} reg023_invalid_pixel_fill;
u32 reg024_026_reserved[3];
struct {
u32 reserve0 : 4;
u32 ctu_align_wr_en : 1;
u32 reserve1 : 27;
} reg027_align_en;
struct {
u32 axi_perf_work_e : 1;
u32 reserve0 : 2;
u32 axi_cnt_type : 1;
u32 rd_latency_id : 8;
u32 reserve1 : 4;
u32 rd_latency_thr : 12;
u32 reserve2 : 4;
} reg028_debug_perf_latency_ctrl0;
struct {
u32 addr_align_type : 2;
u32 ar_cnt_id_type : 1;
u32 aw_cnt_id_type : 1;
u32 ar_count_id : 8;
u32 reserve0 : 4;
u32 aw_count_id : 8;
u32 rd_band_width_mode : 1;
u32 reserve1 : 7;
} reg029_debug_perf_latency_ctrl1;
struct {
u32 axi_wr_qos_level : 4;
u32 reserve0 : 4;
u32 axi_wr_qos : 4;
u32 reserve1 : 4;
u32 axi_rd_qos_level : 4;
u32 reserve2 : 4;
u32 axi_rd_qos : 4;
u32 reserve3 : 4;
} reg030_qos_ctrl;
};
struct vdpu383_regs_common_addr {
u32 reg128_strm_base;
u32 reg129_rps_base;
u32 reg130_cabactbl_base;
u32 reg131_gbl_base;
u32 reg132_scanlist_addr;
u32 reg133_scale_down_base;
u32 reg134_fgs_base;
u32 reg135_139_reserved[5];
struct rcb_info {
u32 offset;
u32 size;
} reg140_162_rcb_info[11];
};
struct vdpu383_regs_h26x_addr {
u32 reg168_decout_base;
u32 reg169_error_ref_base;
u32 reg170_185_ref_base[16];
u32 reg186_191_reserved[6];
u32 reg192_payload_st_cur_base;
u32 reg193_fbc_payload_offset;
u32 reg194_payload_st_error_ref_base;
u32 reg195_210_payload_st_ref_base[16];
u32 reg211_215_reserved[5];
u32 reg216_colmv_cur_base;
u32 reg217_232_colmv_ref_base[16];
};
struct vdpu383_regs_h26x_params {
u32 reg064_start_decoder;
u32 reg065_strm_start_bit;
u32 reg066_stream_len;
u32 reg067_global_len;
u32 reg068_hor_virstride;
u32 reg069_raster_uv_hor_virstride;
u32 reg070_y_virstride;
u32 reg071_scl_ref_hor_virstride;
u32 reg072_scl_ref_raster_uv_hor_virstride;
u32 reg073_scl_ref_virstride;
u32 reg074_fgs_ref_hor_virstride;
u32 reg075_079_reserved[5];
u32 reg080_error_ref_hor_virstride;
u32 reg081_error_ref_raster_uv_hor_virstride;
u32 reg082_error_ref_virstride;
u32 reg083_ref0_hor_virstride;
u32 reg084_ref0_raster_uv_hor_virstride;
u32 reg085_ref0_virstride;
u32 reg086_ref1_hor_virstride;
u32 reg087_ref1_raster_uv_hor_virstride;
u32 reg088_ref1_virstride;
u32 reg089_ref2_hor_virstride;
u32 reg090_ref2_raster_uv_hor_virstride;
u32 reg091_ref2_virstride;
u32 reg092_ref3_hor_virstride;
u32 reg093_ref3_raster_uv_hor_virstride;
u32 reg094_ref3_virstride;
u32 reg095_ref4_hor_virstride;
u32 reg096_ref4_raster_uv_hor_virstride;
u32 reg097_ref4_virstride;
u32 reg098_ref5_hor_virstride;
u32 reg099_ref5_raster_uv_hor_virstride;
u32 reg100_ref5_virstride;
u32 reg101_ref6_hor_virstride;
u32 reg102_ref6_raster_uv_hor_virstride;
u32 reg103_ref6_virstride;
u32 reg104_ref7_hor_virstride;
u32 reg105_ref7_raster_uv_hor_virstride;
u32 reg106_ref7_virstride;
};
struct vdpu383_regs_h26x {
struct vdpu383_regs_common common; /* 8-30 */
struct vdpu383_regs_h26x_params h26x_params; /* 64-74, 80-106 */
struct vdpu383_regs_common_addr common_addr; /* 128-134, 140-161 */
struct vdpu383_regs_h26x_addr h26x_addr; /* 168-185, 192-210, 216-232 */
} __packed;
#endif /* __RKVDEC_VDPU838_REGS_H__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,199 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rockchip Video Decoder driver
*
* Copyright (C) 2019 Collabora, Ltd.
*
* Based on rkvdec driver by Google LLC. (Tomasz Figa <tfiga@chromium.org>)
* Based on s5p-mfc driver by Samsung Electronics Co., Ltd.
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*/
#ifndef RKVDEC_H_
#define RKVDEC_H_
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <linux/wait.h>
#include <linux/clk.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
#define RKVDEC_QUIRK_DISABLE_QOS BIT(0)
#define RKVDEC_1080P_PIXELS (1920 * 1088)
#define RKVDEC_4K_PIXELS (4096 * 2304)
#define RKVDEC_8K_PIXELS (7680 * 4320)
struct rkvdec_ctx;
struct rkvdec_rcb_config;
struct rkvdec_ctrl_desc {
struct v4l2_ctrl_config cfg;
};
struct rkvdec_ctrls {
const struct rkvdec_ctrl_desc *ctrls;
unsigned int num_ctrls;
};
struct rkvdec_run {
struct {
struct vb2_v4l2_buffer *src;
struct vb2_v4l2_buffer *dst;
} bufs;
};
struct rkvdec_vp9_decoded_buffer_info {
/* Info needed when the decoded frame serves as a reference frame. */
unsigned short width;
unsigned short height;
unsigned int bit_depth : 4;
};
struct rkvdec_decoded_buffer {
/* Must be the first field in this struct. */
struct v4l2_m2m_buffer base;
union {
struct rkvdec_vp9_decoded_buffer_info vp9;
};
};
static inline struct rkvdec_decoded_buffer *
vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf)
{
return container_of(buf, struct rkvdec_decoded_buffer,
base.vb.vb2_buf);
}
struct rkvdec_variant_ops {
irqreturn_t (*irq_handler)(struct rkvdec_ctx *ctx);
u32 (*colmv_size)(u16 width, u16 height);
void (*flatten_matrices)(u8 *output, const u8 *input, int matrices, int row_length);
};
struct rkvdec_variant {
unsigned int num_regs;
const struct rkvdec_coded_fmt_desc *coded_fmts;
size_t num_coded_fmts;
const struct rcb_size_info *rcb_sizes;
size_t num_rcb_sizes;
const struct rkvdec_variant_ops *ops;
bool has_single_reg_region;
unsigned int quirks;
};
struct rkvdec_coded_fmt_ops {
int (*adjust_fmt)(struct rkvdec_ctx *ctx,
struct v4l2_format *f);
int (*start)(struct rkvdec_ctx *ctx);
void (*stop)(struct rkvdec_ctx *ctx);
int (*run)(struct rkvdec_ctx *ctx);
void (*done)(struct rkvdec_ctx *ctx, struct vb2_v4l2_buffer *src_buf,
struct vb2_v4l2_buffer *dst_buf,
enum vb2_buffer_state result);
int (*try_ctrl)(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl);
enum rkvdec_image_fmt (*get_image_fmt)(struct rkvdec_ctx *ctx,
struct v4l2_ctrl *ctrl);
};
enum rkvdec_image_fmt {
RKVDEC_IMG_FMT_ANY = 0,
RKVDEC_IMG_FMT_420_8BIT,
RKVDEC_IMG_FMT_420_10BIT,
RKVDEC_IMG_FMT_422_8BIT,
RKVDEC_IMG_FMT_422_10BIT,
};
struct rkvdec_decoded_fmt_desc {
u32 fourcc;
enum rkvdec_image_fmt image_fmt;
};
struct rkvdec_coded_fmt_desc {
u32 fourcc;
struct v4l2_frmsize_stepwise frmsize;
const struct rkvdec_ctrls *ctrls;
const struct rkvdec_coded_fmt_ops *ops;
unsigned int num_decoded_fmts;
const struct rkvdec_decoded_fmt_desc *decoded_fmts;
u32 subsystem_flags;
};
struct rkvdec_dev {
struct v4l2_device v4l2_dev;
struct media_device mdev;
struct video_device vdev;
struct v4l2_m2m_dev *m2m_dev;
struct device *dev;
struct clk_bulk_data *clocks;
unsigned int num_clocks;
struct clk *axi_clk;
void __iomem *regs;
void __iomem *link;
struct mutex vdev_lock; /* serializes ioctls */
struct delayed_work watchdog_work;
struct gen_pool *sram_pool;
struct iommu_domain *iommu_domain;
struct iommu_domain *empty_domain;
const struct rkvdec_variant *variant;
};
struct rkvdec_ctx {
struct v4l2_fh fh;
struct v4l2_format coded_fmt;
struct v4l2_format decoded_fmt;
const struct rkvdec_coded_fmt_desc *coded_fmt_desc;
struct v4l2_ctrl_handler ctrl_hdl;
struct rkvdec_dev *dev;
enum rkvdec_image_fmt image_fmt;
struct rkvdec_rcb_config *rcb_config;
u32 colmv_offset;
void *priv;
u8 has_sps_st_rps: 1;
u8 has_sps_lt_rps: 1;
};
static inline struct rkvdec_ctx *file_to_rkvdec_ctx(struct file *filp)
{
return container_of(file_to_v4l2_fh(filp), struct rkvdec_ctx, fh);
}
enum rkvdec_alloc_type {
RKVDEC_ALLOC_DMA = 0,
RKVDEC_ALLOC_SRAM = 1,
};
struct rkvdec_aux_buf {
void *cpu;
dma_addr_t dma;
size_t size;
enum rkvdec_alloc_type type;
};
void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
void rkvdec_memcpy_toio(void __iomem *dst, void *src, size_t len);
void rkvdec_schedule_watchdog(struct rkvdec_dev *rkvdec, u32 timeout_threshold);
void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx);
/* RKVDEC ops */
extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops;
/* VDPU381 ops */
extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_h264_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu381_hevc_fmt_ops;
/* VDPU383 ops */
extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_h264_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_vdpu383_hevc_fmt_ops;
#endif /* RKVDEC_H_ */