|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | +/* |
| 3 | + * Rockchip video decoder h264 common functions |
| 4 | + * |
| 5 | + * Copyright (C) 2025 Collabora, Ltd. |
| 6 | + * Detlev Casanova <detlev.casanova@collabora.com> |
| 7 | + * |
| 8 | + * Copyright (C) 2019 Collabora, Ltd. |
| 9 | + * Boris Brezillon <boris.brezillon@collabora.com> |
| 10 | + * |
| 11 | + * Copyright (C) 2016 Rockchip Electronics Co., Ltd. |
| 12 | + * Jeffy Chen <jeffy.chen@rock-chips.com> |
| 13 | + */ |
| 14 | + |
| 15 | +#include <linux/v4l2-common.h> |
| 16 | +#include <media/v4l2-h264.h> |
| 17 | +#include <media/v4l2-mem2mem.h> |
| 18 | + |
| 19 | +#include "rkvdec.h" |
| 20 | +#include "rkvdec-h264-common.h" |
| 21 | + |
| 22 | +#define RKVDEC_NUM_REFLIST 3 |
| 23 | + |
| 24 | +static void set_dpb_info(struct rkvdec_rps_entry *entries, |
| 25 | + u8 reflist, |
| 26 | + u8 refnum, |
| 27 | + u8 info, |
| 28 | + bool bottom) |
| 29 | +{ |
| 30 | + struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8]; |
| 31 | + u8 idx = refnum % 8; |
| 32 | + |
| 33 | + switch (idx) { |
| 34 | + case 0: |
| 35 | + entry->dpb_info0 = info; |
| 36 | + entry->bottom_flag0 = bottom; |
| 37 | + break; |
| 38 | + case 1: |
| 39 | + entry->dpb_info1 = info; |
| 40 | + entry->bottom_flag1 = bottom; |
| 41 | + break; |
| 42 | + case 2: |
| 43 | + entry->dpb_info2 = info; |
| 44 | + entry->bottom_flag2 = bottom; |
| 45 | + break; |
| 46 | + case 3: |
| 47 | + entry->dpb_info3 = info; |
| 48 | + entry->bottom_flag3 = bottom; |
| 49 | + break; |
| 50 | + case 4: |
| 51 | + entry->dpb_info4 = info; |
| 52 | + entry->bottom_flag4 = bottom; |
| 53 | + break; |
| 54 | + case 5: |
| 55 | + entry->dpb_info5 = info; |
| 56 | + entry->bottom_flag5 = bottom; |
| 57 | + break; |
| 58 | + case 6: |
| 59 | + entry->dpb_info6 = info; |
| 60 | + entry->bottom_flag6 = bottom; |
| 61 | + break; |
| 62 | + case 7: |
| 63 | + entry->dpb_info7 = info; |
| 64 | + entry->bottom_flag7 = bottom; |
| 65 | + break; |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, |
| 70 | + struct rkvdec_h264_run *run) |
| 71 | +{ |
| 72 | + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; |
| 73 | + u32 i; |
| 74 | + |
| 75 | + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { |
| 76 | + struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx; |
| 77 | + const struct v4l2_h264_dpb_entry *dpb = run->decode_params->dpb; |
| 78 | + struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q; |
| 79 | + struct vb2_buffer *buf = NULL; |
| 80 | + |
| 81 | + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) { |
| 82 | + buf = vb2_find_buffer(cap_q, dpb[i].reference_ts); |
| 83 | + if (!buf) |
| 84 | + pr_debug("No buffer for reference_ts %llu", |
| 85 | + dpb[i].reference_ts); |
| 86 | + } |
| 87 | + |
| 88 | + run->ref_buf[i] = buf; |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder, |
| 93 | + struct rkvdec_h264_run *run, |
| 94 | + struct rkvdec_h264_reflists *reflists, |
| 95 | + struct rkvdec_rps *hw_rps) |
| 96 | +{ |
| 97 | + const struct v4l2_ctrl_h264_decode_params *dec_params = run->decode_params; |
| 98 | + const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb; |
| 99 | + |
| 100 | + u32 i, j; |
| 101 | + |
| 102 | + memset(hw_rps, 0, sizeof(*hw_rps)); |
| 103 | + |
| 104 | + /* |
| 105 | + * Assign an invalid pic_num if DPB entry at that position is inactive. |
| 106 | + * If we assign 0 in that position hardware will treat that as a real |
| 107 | + * reference picture with pic_num 0, triggering output picture |
| 108 | + * corruption. |
| 109 | + */ |
| 110 | + for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) { |
| 111 | + if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) |
| 112 | + continue; |
| 113 | + |
| 114 | + hw_rps->frame_num[i] = builder->refs[i].frame_num; |
| 115 | + } |
| 116 | + |
| 117 | + for (j = 0; j < RKVDEC_NUM_REFLIST; j++) { |
| 118 | + for (i = 0; i < builder->num_valid; i++) { |
| 119 | + struct v4l2_h264_reference *ref; |
| 120 | + bool dpb_valid; |
| 121 | + bool bottom; |
| 122 | + |
| 123 | + switch (j) { |
| 124 | + case 0: |
| 125 | + ref = &reflists->p[i]; |
| 126 | + break; |
| 127 | + case 1: |
| 128 | + ref = &reflists->b0[i]; |
| 129 | + break; |
| 130 | + case 2: |
| 131 | + ref = &reflists->b1[i]; |
| 132 | + break; |
| 133 | + } |
| 134 | + |
| 135 | + if (WARN_ON(ref->index >= ARRAY_SIZE(dec_params->dpb))) |
| 136 | + continue; |
| 137 | + |
| 138 | + dpb_valid = !!(run->ref_buf[ref->index]); |
| 139 | + bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF; |
| 140 | + |
| 141 | + set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom); |
| 142 | + } |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +void assemble_hw_scaling_list(struct rkvdec_h264_run *run, |
| 147 | + struct rkvdec_h264_scaling_list *scaling_list) |
| 148 | +{ |
| 149 | + const struct v4l2_ctrl_h264_scaling_matrix *scaling = run->scaling_matrix; |
| 150 | + const struct v4l2_ctrl_h264_pps *pps = run->pps; |
| 151 | + |
| 152 | + if (!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT)) |
| 153 | + return; |
| 154 | + |
| 155 | + BUILD_BUG_ON(sizeof(scaling_list->scaling_list_4x4) != |
| 156 | + sizeof(scaling->scaling_list_4x4)); |
| 157 | + BUILD_BUG_ON(sizeof(scaling_list->scaling_list_8x8) != |
| 158 | + sizeof(scaling->scaling_list_8x8)); |
| 159 | + |
| 160 | + memcpy(scaling_list->scaling_list_4x4, |
| 161 | + scaling->scaling_list_4x4, |
| 162 | + sizeof(scaling->scaling_list_4x4)); |
| 163 | + |
| 164 | + memcpy(scaling_list->scaling_list_8x8, |
| 165 | + scaling->scaling_list_8x8, |
| 166 | + sizeof(scaling->scaling_list_8x8)); |
| 167 | +} |
| 168 | + |
| 169 | +#define RKVDEC_H264_MAX_DEPTH_IN_BYTES 2 |
| 170 | + |
| 171 | +int rkvdec_h264_adjust_fmt(struct rkvdec_ctx *ctx, |
| 172 | + struct v4l2_format *f) |
| 173 | +{ |
| 174 | + struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp; |
| 175 | + |
| 176 | + fmt->num_planes = 1; |
| 177 | + if (!fmt->plane_fmt[0].sizeimage) |
| 178 | + fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height * |
| 179 | + RKVDEC_H264_MAX_DEPTH_IN_BYTES; |
| 180 | + return 0; |
| 181 | +} |
| 182 | + |
| 183 | +enum rkvdec_image_fmt rkvdec_h264_get_image_fmt(struct rkvdec_ctx *ctx, |
| 184 | + struct v4l2_ctrl *ctrl) |
| 185 | +{ |
| 186 | + const struct v4l2_ctrl_h264_sps *sps = ctrl->p_new.p_h264_sps; |
| 187 | + |
| 188 | + if (ctrl->id != V4L2_CID_STATELESS_H264_SPS) |
| 189 | + return RKVDEC_IMG_FMT_ANY; |
| 190 | + |
| 191 | + if (sps->bit_depth_luma_minus8 == 0) { |
| 192 | + if (sps->chroma_format_idc == 2) |
| 193 | + return RKVDEC_IMG_FMT_422_8BIT; |
| 194 | + else |
| 195 | + return RKVDEC_IMG_FMT_420_8BIT; |
| 196 | + } else if (sps->bit_depth_luma_minus8 == 2) { |
| 197 | + if (sps->chroma_format_idc == 2) |
| 198 | + return RKVDEC_IMG_FMT_422_10BIT; |
| 199 | + else |
| 200 | + return RKVDEC_IMG_FMT_420_10BIT; |
| 201 | + } |
| 202 | + |
| 203 | + return RKVDEC_IMG_FMT_ANY; |
| 204 | +} |
| 205 | + |
| 206 | +int rkvdec_h264_validate_sps(struct rkvdec_ctx *ctx, |
| 207 | + const struct v4l2_ctrl_h264_sps *sps) |
| 208 | +{ |
| 209 | + unsigned int width, height; |
| 210 | + |
| 211 | + if (sps->chroma_format_idc > 2) |
| 212 | + /* Only 4:0:0, 4:2:0 and 4:2:2 are supported */ |
| 213 | + return -EINVAL; |
| 214 | + if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) |
| 215 | + /* Luma and chroma bit depth mismatch */ |
| 216 | + return -EINVAL; |
| 217 | + if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2) |
| 218 | + /* Only 8-bit and 10-bit is supported */ |
| 219 | + return -EINVAL; |
| 220 | + |
| 221 | + width = (sps->pic_width_in_mbs_minus1 + 1) * 16; |
| 222 | + height = (sps->pic_height_in_map_units_minus1 + 1) * 16; |
| 223 | + |
| 224 | + /* |
| 225 | + * When frame_mbs_only_flag is not set, this is field height, |
| 226 | + * which is half the final height (see (7-18) in the |
| 227 | + * specification) |
| 228 | + */ |
| 229 | + if (!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)) |
| 230 | + height *= 2; |
| 231 | + |
| 232 | + if (width > ctx->coded_fmt.fmt.pix_mp.width || |
| 233 | + height > ctx->coded_fmt.fmt.pix_mp.height) |
| 234 | + return -EINVAL; |
| 235 | + |
| 236 | + return 0; |
| 237 | +} |
| 238 | + |
| 239 | +void rkvdec_h264_run_preamble(struct rkvdec_ctx *ctx, |
| 240 | + struct rkvdec_h264_run *run) |
| 241 | +{ |
| 242 | + struct v4l2_ctrl *ctrl; |
| 243 | + |
| 244 | + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, |
| 245 | + V4L2_CID_STATELESS_H264_DECODE_PARAMS); |
| 246 | + run->decode_params = ctrl ? ctrl->p_cur.p : NULL; |
| 247 | + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, |
| 248 | + V4L2_CID_STATELESS_H264_SPS); |
| 249 | + run->sps = ctrl ? ctrl->p_cur.p : NULL; |
| 250 | + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, |
| 251 | + V4L2_CID_STATELESS_H264_PPS); |
| 252 | + run->pps = ctrl ? ctrl->p_cur.p : NULL; |
| 253 | + ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, |
| 254 | + V4L2_CID_STATELESS_H264_SCALING_MATRIX); |
| 255 | + run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL; |
| 256 | + |
| 257 | + rkvdec_run_preamble(ctx, &run->base); |
| 258 | +} |
0 commit comments