From ad2f32c0f002564fced70ee7a8320792695ec241 Mon Sep 17 00:00:00 2001 From: John Cox Date: Mon, 31 Aug 2026 13:22:38 +0100 Subject: [PATCH] media/hevc_d: Improve checking and cleanup of decoder A number of fixes and cleanup - many Claude inspired. Optimise guessing of new PU/Coeff buffers is we have already had to reallocate in this context. Fix buffer size roundup code so it rounds up as intended rather than to twice that amount. Reset slot tracking on streamoff so it doesn't become fatally confused if a second run is started before closing the context. Fix accounting for size of scaling factor list when alloocating command buffers. Previously would over allocate a bit. A number of fixes to decoder cleanup on release or probe error where structures could be leaked or were freed in the wrong order. Fix typo and pointless && clause in Kconfig Collocated ref index was bounds checked against the wrong value, potentially ending up with feeding random addresses to the h/w. Many more slice header and PPS parameters checked that could potentially misprogram the hardware. Generaly tidy and remove dead code and structures. Signed-off-by: John Cox --- .../platform/raspberrypi/hevc_dec/Kconfig | 4 +- .../platform/raspberrypi/hevc_dec/hevc_d.c | 24 +- .../platform/raspberrypi/hevc_dec/hevc_d.h | 4 +- .../raspberrypi/hevc_dec/hevc_d_h265.c | 382 +++++++++++------- .../raspberrypi/hevc_dec/hevc_d_h265.h | 1 + .../raspberrypi/hevc_dec/hevc_d_video.c | 75 +--- .../raspberrypi/hevc_dec/hevc_d_video.h | 6 - 7 files changed, 272 insertions(+), 224 deletions(-) diff --git a/drivers/media/platform/raspberrypi/hevc_dec/Kconfig b/drivers/media/platform/raspberrypi/hevc_dec/Kconfig index ae1fd079e5c91d..fc4d0baf5c0f83 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/Kconfig +++ b/drivers/media/platform/raspberrypi/hevc_dec/Kconfig @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 config VIDEO_RPI_HEVC_DEC - tristate "Rasperry Pi HEVC decoder" - depends on VIDEO_DEV && VIDEO_DEV + tristate "Raspberry Pi HEVC decoder" + depends on VIDEO_DEV depends on OF select MEDIA_CONTROLLER select MEDIA_CONTROLLER_REQUEST_API diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c index 0d06bab61af23c..392fd40119fcfb 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c @@ -52,6 +52,7 @@ static const struct v4l2_ctrl_config hevc_d_ctrls[] = { }, { .name = "Slice param array", .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, + .ops = &hevc_d_hevc_slice_params_ctrl_ops, .type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS, .flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY, .dims = { 600 }, @@ -78,7 +79,6 @@ void *hevc_d_find_control_data(struct hevc_d_ctx *ctx, u32 id) static int hevc_d_init_ctrls(struct hevc_d_dev *dev, struct hevc_d_ctx *ctx) { struct v4l2_ctrl_handler *hdl = &ctx->hdl; - struct v4l2_ctrl *ctrl; unsigned int i; v4l2_ctrl_handler_init(hdl, ARRAY_SIZE(hevc_d_ctrls)); @@ -89,7 +89,7 @@ static int hevc_d_init_ctrls(struct hevc_d_dev *dev, struct hevc_d_ctx *ctx) } for (i = 0; i < ARRAY_SIZE(hevc_d_ctrls); i++) { - ctrl = v4l2_ctrl_new_custom(hdl, &hevc_d_ctrls[i], ctx); + v4l2_ctrl_new_custom(hdl, &hevc_d_ctrls[i], ctx); if (hdl->error) { v4l2_err(&dev->v4l2_dev, "Failed to create new custom control id=%#x\n", @@ -154,16 +154,13 @@ static int hevc_d_release(struct file *file) struct hevc_d_ctx *ctx = container_of(file->private_data, struct hevc_d_ctx, fh); - v4l2_fh_del(&ctx->fh, file); - - v4l2_ctrl_handler_free(&ctx->hdl); - v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); - + v4l2_fh_del(&ctx->fh, file); v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->hdl); mutex_destroy(&ctx->ctx_mutex); - kfree(ctx); + return 0; } @@ -221,14 +218,14 @@ static int hevc_d_probe(struct platform_device *pdev) return ret; } - mutex_init(&dev->dev_mutex); - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { dev_err_probe(&pdev->dev, ret, "Failed to register V4L2 device\n"); - return ret; + goto err_hw; } + mutex_init(&dev->dev_mutex); + vfd = &dev->vfd; vfd->lock = &dev->dev_mutex; vfd->v4l2_dev = &dev->v4l2_dev; @@ -293,9 +290,13 @@ static int hevc_d_probe(struct platform_device *pdev) err_video: video_unregister_device(&dev->vfd); err_m2m: + media_device_cleanup(&dev->mdev); v4l2_m2m_release(dev->m2m_dev); err_v4l2: + mutex_destroy(&dev->dev_mutex); v4l2_device_unregister(&dev->v4l2_dev); +err_hw: + hevc_d_hw_remove(dev); return ret; } @@ -310,6 +311,7 @@ static void hevc_d_remove(struct platform_device *pdev) v4l2_m2m_release(dev->m2m_dev); video_unregister_device(&dev->vfd); + mutex_destroy(&dev->dev_mutex); v4l2_device_unregister(&dev->v4l2_dev); hevc_d_hw_remove(dev); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h index 367c9624c63d38..6d40d91229a6b5 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h @@ -133,6 +133,9 @@ struct hevc_d_ctx { unsigned int p2idx; struct hevc_d_hwbuf pu_bufs[HEVC_D_P2BUF_COUNT]; struct hevc_d_hwbuf coeff_bufs[HEVC_D_P2BUF_COUNT]; + /* Last sizes we expanded to */ + size_t pu_size_max; + size_t coeff_size_max; /* Aux structures only used in setup so no locking needed */ struct hevc_d_q_aux *aux_free; @@ -194,7 +197,6 @@ extern int hevc_d_v4l2_debug; #define hevc_d_dbg(level, dev, fmt, arg...)\ v4l2_dbg((level), hevc_d_v4l2_debug, (dev), fmt, ## arg) -struct v4l2_ctrl *hevc_d_find_ctrl(struct hevc_d_ctx *ctx, u32 id); void *hevc_d_find_control_data(struct hevc_d_ctx *ctx, u32 id); #endif diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c index 1d4a7fbd9d4aaa..83ce62afd158b7 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c @@ -11,7 +11,6 @@ * Copyright (C) 2018 Bootlin */ -//#include #include #include @@ -132,7 +131,7 @@ static size_t next_size(const size_t x) #define PROB_BACKUP ((20 << 12) + (20 << 6) + (0 << 0)) #define PROB_RELOAD ((20 << 12) + (20 << 0) + (0 << 6)) -#define HEVC_MAX_REFS V4L2_HEVC_DPB_ENTRIES_NUM_MAX +#define NUM_REF_IDX_ACTIVE_MAX 15 struct hevc_d_q_aux { unsigned int refcount; @@ -181,7 +180,7 @@ struct hevc_d_dec_env { unsigned int entry_ctb_y; unsigned int entry_tile_x; unsigned int entry_tile_y; - unsigned int entry_qp; + int entry_qp; u32 entry_slice; u32 rpi_config2; @@ -204,7 +203,7 @@ struct hevc_d_dec_env { u32 pu_stride; u32 coeff_stride; -#define SLICE_MSGS_MAX (2 * HEVC_MAX_REFS * 8 + 3) +#define SLICE_MSGS_MAX (2 * NUM_REF_IDX_ACTIVE_MAX * 8 + 3) u16 slice_msgs[SLICE_MSGS_MAX]; u8 scaling_factors[NUM_SCALING_FACTORS]; @@ -229,13 +228,13 @@ struct hevc_d_dec_state { unsigned int tile_width; /* Width in tiles */ unsigned int tile_height; /* Height in tiles */ - int *col_bd; - int *row_bd; + unsigned int *col_bd; + unsigned int *row_bd; int *ctb_addr_rs_to_ts; int *ctb_addr_ts_to_rs; /* Aux storage for DPB */ - struct hevc_d_q_aux *ref_aux[HEVC_MAX_REFS]; + struct hevc_d_q_aux *ref_aux[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; struct hevc_d_q_aux *frame_aux; /* Slice vars */ @@ -250,7 +249,7 @@ struct hevc_d_dec_state { const struct v4l2_ctrl_hevc_slice_params *sh; const struct v4l2_ctrl_hevc_decode_params *dec; unsigned int nb_refs[2]; - unsigned int slice_qp; + int slice_qp; unsigned int max_num_merge_cand; /* 0 if I-slice */ bool dependent_slice_segment_flag; u32 data_len; @@ -297,7 +296,7 @@ static void p1_apb_write(struct hevc_d_dec_env *const de, const u16 addr, de->cmd_len++; } -static int ctb_to_tile(unsigned int ctb, unsigned int *bd, int num) +static int ctb_to_tile(unsigned int ctb, unsigned int *bd) { int i; @@ -310,13 +309,13 @@ static int ctb_to_tile(unsigned int ctb, unsigned int *bd, int num) static unsigned int ctb_to_tile_x(const struct hevc_d_dec_state *const s, const unsigned int ctb_x) { - return ctb_to_tile(ctb_x, s->col_bd, s->tile_width); + return ctb_to_tile(ctb_x, s->col_bd); } static unsigned int ctb_to_tile_y(const struct hevc_d_dec_state *const s, const unsigned int ctb_y) { - return ctb_to_tile(ctb_y, s->row_bd, s->tile_height); + return ctb_to_tile(ctb_y, s->row_bd); } static void aux_q_free(struct hevc_d_ctx *const ctx, @@ -337,6 +336,15 @@ static struct hevc_d_q_aux *aux_q_alloc(struct hevc_d_ctx *const ctx, if (!aq) return NULL; + /* + * Calculate col mv size from capture size as that provides an + * upper bound on actual size. Size in SPS might vary but must + * be less than the capture format + */ + ctx->colmv_stride = ALIGN(ctx->dst_fmt.width, 64); + ctx->colmv_picsize = ctx->colmv_stride * + (ALIGN(ctx->dst_fmt.height, 64) >> 4); + if (hwbuf_alloc(dev, &aq->col, ctx->colmv_picsize, DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_KERNEL_MAPPING)) goto fail; @@ -493,11 +501,11 @@ static void write_prob(struct hevc_d_dec_env *const de, const struct hevc_d_dec_state *const s) { const unsigned int init_type = - ((s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) != 0 && - s->sh->slice_type != HEVC_SLICE_I) ? - s->sh->slice_type + 1 : - 2 - s->sh->slice_type; - const int q = clamp((int)s->slice_qp, 0, 51); + s->sh->slice_type == HEVC_SLICE_I ? 0 : + (s->sh->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) ? + (s->sh->slice_type == HEVC_SLICE_P ? 2 : 1) : + (s->sh->slice_type == HEVC_SLICE_P ? 1 : 2); + const int q = clamp(s->slice_qp, 0, 51); const u8 *p = prob_init[init_type]; u8 dst[RPI_PROB_ARRAY_SIZE]; unsigned int i; @@ -531,7 +539,7 @@ static void write_prob(struct hevc_d_dec_env *const de, p1_apb_write(de, RPI_TRANSFER, PROB_BACKUP); } -#define CMDS_WRITE_SCALING_FACTORS NUM_SCALING_FACTORS +#define CMDS_WRITE_SCALING_FACTORS (NUM_SCALING_FACTORS / 4) static void write_scaling_factors(struct hevc_d_dec_env *const de) { const u8 *p = (u8 *)de->scaling_factors; @@ -548,7 +556,7 @@ static inline __u32 dma_to_axi_addr(dma_addr_t a) } #define CMDS_WRITE_BITSTREAM 4 -static int write_bitstream(struct hevc_d_dec_env *const de, +static void write_bitstream(struct hevc_d_dec_env *const de, const struct hevc_d_dec_state *const s) { /* V4L2 always has emulation prevention bytes in the stream */ @@ -561,7 +569,6 @@ static int write_bitstream(struct hevc_d_dec_env *const de, p1_apb_write(de, RPI_BFNUM, len); p1_apb_write(de, RPI_BFCONTROL, offset + (1 << 7)); /* Stop */ p1_apb_write(de, RPI_BFCONTROL, offset + (rpi_use_emu << 6)); - return 0; } /* @@ -700,6 +707,32 @@ static int has_backward(const struct v4l2_hevc_dpb_entry *const dpb, return 1; } +static void pre_slice_weights(struct hevc_d_dec_env *const de, + const struct v4l2_hevc_pred_weight_table *const w, + const __s8 delta_luma_weight[V4L2_HEVC_DPB_ENTRIES_NUM_MAX], + const __s8 luma_offset[V4L2_HEVC_DPB_ENTRIES_NUM_MAX], + const __s8 delta_chroma_weight[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2], + const __s8 chroma_offset[V4L2_HEVC_DPB_ENTRIES_NUM_MAX][2], + const unsigned int idx) +{ + const int luma_log2_weight_denom = min(w->luma_log2_weight_denom, 7); + const int chroma_log2_weight_denom = clamp(luma_log2_weight_denom + + w->delta_chroma_log2_weight_denom, + 0, 7); + const int luma_weight_denom = (1 << luma_log2_weight_denom); + const int chroma_weight_denom = (1 << chroma_log2_weight_denom); + + msg_slice(de, luma_log2_weight_denom | + (((delta_luma_weight[idx] + luma_weight_denom) & 0x1ff) << 3)); + msg_slice(de, luma_offset[idx] & 0xff); + msg_slice(de, chroma_log2_weight_denom | + (((delta_chroma_weight[idx][0] + chroma_weight_denom) & 0x1ff) << 3)); + msg_slice(de, chroma_offset[idx][0] & 0xff); + msg_slice(de, chroma_log2_weight_denom | + (((delta_chroma_weight[idx][1] + chroma_weight_denom) & 0x1ff) << 3)); + msg_slice(de, chroma_offset[idx][1] & 0xff); +} + static void pre_slice_decode(struct hevc_d_dec_env *const de, const struct hevc_d_dec_state *const s) { @@ -738,14 +771,14 @@ static void pre_slice_decode(struct hevc_d_dec_env *const de, cmd_slice |= no_backward_pred_flag << 10; msg_slice(de, cmd_slice); - if (s->slice_temporal_mvp) { - const u8 *const rpl = collocated_from_l0_flag ? - sh->ref_idx_l0 : sh->ref_idx_l1; - if (sh->collocated_ref_idx >= dec->num_active_dpb_entries) - de->dpbno_col = rpl[0]; - else - de->dpbno_col = rpl[sh->collocated_ref_idx]; - } + if (s->slice_temporal_mvp) + de->dpbno_col = collocated_from_l0_flag ? + (sh->collocated_ref_idx < s->nb_refs[L0] ? + sh->ref_idx_l0[sh->collocated_ref_idx] : + sh->ref_idx_l0[0]) : + (sh->collocated_ref_idx < s->nb_refs[L1] ? + sh->ref_idx_l1[sh->collocated_ref_idx] : + sh->ref_idx_l1[0]); /* Write reference picture descriptions */ weighted_pred_flag = @@ -764,38 +797,14 @@ static void pre_slice_decode(struct hevc_d_dec_env *const de, (weighted_pred_flag ? (3 << 5) : 0)); msg_slice(de, dec->dpb[dpb_no].pic_order_cnt_val & 0xffff); - if (weighted_pred_flag) { - const struct v4l2_hevc_pred_weight_table - *const w = &sh->pred_weight_table; - const int luma_weight_denom = - (1 << w->luma_log2_weight_denom); - const unsigned int chroma_log2_weight_denom = - (w->luma_log2_weight_denom + - w->delta_chroma_log2_weight_denom); - const int chroma_weight_denom = - (1 << chroma_log2_weight_denom); - - msg_slice(de, - w->luma_log2_weight_denom | - (((w->delta_luma_weight_l0[idx] + - luma_weight_denom) & 0x1ff) - << 3)); - msg_slice(de, w->luma_offset_l0[idx] & 0xff); - msg_slice(de, - chroma_log2_weight_denom | - (((w->delta_chroma_weight_l0[idx][0] + - chroma_weight_denom) & 0x1ff) - << 3)); - msg_slice(de, - w->chroma_offset_l0[idx][0] & 0xff); - msg_slice(de, - chroma_log2_weight_denom | - (((w->delta_chroma_weight_l0[idx][1] + - chroma_weight_denom) & 0x1ff) - << 3)); - msg_slice(de, - w->chroma_offset_l0[idx][1] & 0xff); - } + if (weighted_pred_flag) + pre_slice_weights(de, + &sh->pred_weight_table, + sh->pred_weight_table.delta_luma_weight_l0, + sh->pred_weight_table.luma_offset_l0, + sh->pred_weight_table.delta_chroma_weight_l0, + sh->pred_weight_table.chroma_offset_l0, + idx); } for (idx = 0; idx < s->nb_refs[L1]; ++idx) { @@ -808,37 +817,15 @@ static void pre_slice_decode(struct hevc_d_dec_env *const de, (1 << 4) : 0) | (weighted_pred_flag ? (3 << 5) : 0)); msg_slice(de, dec->dpb[dpb_no].pic_order_cnt_val & 0xffff); - if (weighted_pred_flag) { - const struct v4l2_hevc_pred_weight_table - *const w = &sh->pred_weight_table; - const int luma_weight_denom = - (1 << w->luma_log2_weight_denom); - const unsigned int chroma_log2_weight_denom = - (w->luma_log2_weight_denom + - w->delta_chroma_log2_weight_denom); - const int chroma_weight_denom = - (1 << chroma_log2_weight_denom); - - msg_slice(de, - w->luma_log2_weight_denom | - (((w->delta_luma_weight_l1[idx] + - luma_weight_denom) & 0x1ff) << 3)); - msg_slice(de, w->luma_offset_l1[idx] & 0xff); - msg_slice(de, - chroma_log2_weight_denom | - (((w->delta_chroma_weight_l1[idx][0] + - chroma_weight_denom) & 0x1ff) - << 3)); - msg_slice(de, - w->chroma_offset_l1[idx][0] & 0xff); - msg_slice(de, - chroma_log2_weight_denom | - (((w->delta_chroma_weight_l1[idx][1] + - chroma_weight_denom) & 0x1ff) - << 3)); - msg_slice(de, - w->chroma_offset_l1[idx][1] & 0xff); - } + + if (weighted_pred_flag) + pre_slice_weights(de, + &sh->pred_weight_table, + sh->pred_weight_table.delta_luma_weight_l1, + sh->pred_weight_table.luma_offset_l1, + sh->pred_weight_table.delta_chroma_weight_l1, + sh->pred_weight_table.chroma_offset_l1, + idx); } } else { msg_slice(de, cmd_slice); @@ -898,7 +885,7 @@ static void new_entry_point(struct hevc_d_dec_env *const de, const unsigned int tile_y, const unsigned int ctb_col, const unsigned int ctb_row, - const unsigned int slice_qp, + const int slice_qp, const u32 slice_const) { const unsigned int endx = s->col_bd[tile_x + 1] - 1; @@ -915,10 +902,11 @@ static void new_entry_point(struct hevc_d_dec_env *const de, write_slice(de, s, slice_const, endx, endy); if (reset_qp_y) { - unsigned int sps_qp_bd_offset = + int sps_qp_bd_offset = 6 * s->sps.bit_depth_luma_minus8; - p1_apb_write(de, RPI_QP, sps_qp_bd_offset + slice_qp); + p1_apb_write(de, RPI_QP, clamp(sps_qp_bd_offset + slice_qp, + 0, sps_qp_bd_offset + 51)); } p1_apb_write(de, RPI_MODE, @@ -1035,9 +1023,7 @@ static int wpp_decode_slice(struct hevc_d_dec_env *const de, if (rv) return rv; - rv = write_bitstream(de, s); - if (rv) - return rv; + write_bitstream(de, s); if (!s->start_ts || indep || s->ctb_width == 1) write_prob(de, s); @@ -1152,9 +1138,7 @@ static int decode_slice(struct hevc_d_dec_env *const de, return rv; pre_slice_decode(de, s); - rv = write_bitstream(de, s); - if (rv) - return rv; + write_bitstream(de, s); reset_qp_y = !s->start_ts || !s->dependent_slice_segment_flag || @@ -1337,12 +1321,15 @@ static void fill_rs_to_ts(struct hevc_d_dec_state *const s) static int updated_ps(struct hevc_d_dec_state *const s) { unsigned int i; + int rv = -ENOMEM; free_ps_info(s); /* Inferred parameters */ s->log2_ctb_size = s->sps.log2_min_luma_coding_block_size_minus3 + 3 + s->sps.log2_diff_max_min_luma_coding_block_size; + if (s->log2_ctb_size < s->pps.diff_cu_qp_delta_depth) + goto fail_inval; s->ctb_width = (s->sps.pic_width_in_luma_samples + (1 << s->log2_ctb_size) - 1) >> @@ -1380,34 +1367,35 @@ static int updated_ps(struct hevc_d_dec_state *const s) if (!s->row_bd) goto fail; + /* Can't check col widths/row heights in PPS validation so do here */ s->col_bd[0] = 0; - for (i = 1; i < s->tile_width; i++) + for (i = 1; i < s->tile_width; i++) { s->col_bd[i] = s->col_bd[i - 1] + s->pps.column_width_minus1[i - 1] + 1; + if (s->col_bd[i] >= s->ctb_width) + goto fail_inval; + } s->col_bd[s->tile_width] = s->ctb_width; s->row_bd[0] = 0; - for (i = 1; i < s->tile_height; i++) + for (i = 1; i < s->tile_height; i++) { s->row_bd[i] = s->row_bd[i - 1] + s->pps.row_height_minus1[i - 1] + 1; + if (s->row_bd[i] >= s->ctb_height) + goto fail_inval; + } s->row_bd[s->tile_height] = s->ctb_height; fill_rs_to_ts(s); return 0; +fail_inval: + rv = -EINVAL; fail: free_ps_info(s); /* Set invalid to force reload */ s->sps.pic_width_in_luma_samples = 0; - return -ENOMEM; -} - -static void setup_colmv(struct hevc_d_ctx *const ctx, struct hevc_d_run *run, - struct hevc_d_dec_state *const s) -{ - ctx->colmv_stride = ALIGN(s->sps.pic_width_in_luma_samples, 64); - ctx->colmv_picsize = ctx->colmv_stride * - (ALIGN(s->sps.pic_height_in_luma_samples, 64) >> 4); + return rv; } static struct hevc_d_dec_env *dec_env_new(struct hevc_d_ctx *const ctx) @@ -1462,7 +1450,7 @@ static int dec_env_init(struct hevc_d_ctx *const ctx) { unsigned int i; - ctx->dec_pool = kzalloc(sizeof(*ctx->dec_pool) * HEVC_D_DEC_ENV_COUNT, + ctx->dec_pool = kcalloc(HEVC_D_DEC_ENV_COUNT, sizeof(*ctx->dec_pool), GFP_KERNEL); if (!ctx->dec_pool) return -ENOMEM; @@ -1518,7 +1506,7 @@ static u32 mk_config2(const struct hevc_d_dec_state *const s) c |= BIT(14); if (s->mk_aux) c |= BIT(15); /* Write motion vectors to external memory */ - c |= (pps->log2_parallel_merge_level_minus2 + 2) << 16; + c |= min(s->log2_ctb_size, pps->log2_parallel_merge_level_minus2 + 2) << 16; if (s->slice_temporal_mvp) c |= BIT(19); if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED) @@ -1549,7 +1537,7 @@ static int hevc_d_h265_setup(struct hevc_d_ctx *ctx, struct hevc_d_run *run) struct hevc_d_q_aux *dpb_q_aux[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; struct hevc_d_dec_state *const s = ctx->state; struct vb2_queue *vq; - struct hevc_d_dec_env *de = ctx->dec0; + struct hevc_d_dec_env *de; unsigned int prev_rs; unsigned int i; int rv; @@ -1715,20 +1703,8 @@ static int hevc_d_h265_setup(struct hevc_d_ctx *ctx, struct hevc_d_run *run) s->sps.pic_width_in_luma_samples; de->rpi_currpoc = sh0->slice_pic_order_cnt; - if (s->sps.flags & - V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED) { - setup_colmv(ctx, run, s); - } - s->slice_idx = 0; - if (sh0->slice_segment_addr != 0) { - v4l2_warn(&dev->v4l2_dev, - "New frame but segment_addr=%d\n", - sh0->slice_segment_addr); - goto fail; - } - /* Either map src buffer or use directly */ s->src_addr = 0; @@ -1746,21 +1722,32 @@ static int hevc_d_h265_setup(struct hevc_d_ctx *ctx, struct hevc_d_run *run) const bool last_slice = i + 1 == run->h265.slice_ents; unsigned int bit_size = old_bits ? sh->bit_size - 8 * sh->data_byte_offset : sh->bit_size; - const u32 byte_size = DIV_ROUND_UP(bit_size, 8); + const u32 byte_size = DIV_ROUND_UP_POW2(bit_size, 8); unsigned int j; s->sh = sh; - if (old_bits && sh->bit_size <= 8 * sh->data_byte_offset) { + /* + * slice_segment_addr indexes ctb_addr_rs_to_ts so must be + * checked before use. + */ + if (sh->slice_segment_addr >= s->ctb_size) { + v4l2_warn(&dev->v4l2_dev, + "Slice %u: segment addr %u >= pic size in CTBs %u\n", + i, sh->slice_segment_addr, s->ctb_size); + goto fail; + } + + if (old_bits && sh->bit_size <= 8 * (u64)sh->data_byte_offset) { v4l2_warn(&dev->v4l2_dev, - "data_byte_offset %d * 8 >= bits %d\n", + "data_byte_offset %u * 8 >= bits %u\n", sh->data_byte_offset, sh->bit_size); goto fail; } - if (sh->data_byte_offset + byte_size > run->src->planes[0].bytesused) { + if ((u64)sh->data_byte_offset + byte_size > run->src->planes[0].bytesused) { v4l2_warn(&dev->v4l2_dev, - "data_byte_offset %d + bits %d (= %d bytes) > bytesused %d\n", + "data_byte_offset %u + bits %u (= %u bytes) > bytesused %u\n", sh->data_byte_offset, bit_size, byte_size, run->src->planes[0].bytesused); goto fail; @@ -2097,24 +2084,25 @@ static void phase1_thread(struct hevc_d_dev *const dev, void *v) struct hevc_d_hwbuf *const coeff_hwbuf = ctx->coeff_bufs + ctx->p2idx; if (de->p1_status & STATUS_PU_EXHAUSTED) { - if (hwbuf_realloc_new(dev, pu_hwbuf, next_size(pu_hwbuf->size))) { - v4l2_err(&dev->v4l2_dev, - "%s: PU realloc (%zx) failed\n", + if (hwbuf_realloc_new(dev, pu_hwbuf, + max(next_size(pu_hwbuf->size), ctx->pu_size_max))) { + v4l2_err(&dev->v4l2_dev, "%s: PU realloc (%zx) failed\n", __func__, pu_hwbuf->size); goto fail; } + ctx->pu_size_max = pu_hwbuf->size; hevc_d_dbg(1, &dev->v4l2_dev, "%s: PU realloc (%zx) OK\n", __func__, pu_hwbuf->size); } if (de->p1_status & STATUS_COEFF_EXHAUSTED) { if (hwbuf_realloc_new(dev, coeff_hwbuf, - next_size(coeff_hwbuf->size))) { - v4l2_err(&dev->v4l2_dev, - "%s: Coeff realloc (%zx) failed\n", + max(next_size(coeff_hwbuf->size), ctx->coeff_size_max))) { + v4l2_err(&dev->v4l2_dev, "%s: Coeff realloc (%zx) failed\n", __func__, coeff_hwbuf->size); goto fail; } + ctx->coeff_size_max = coeff_hwbuf->size; hevc_d_dbg(1, &dev->v4l2_dev, "%s: Coeff realloc (%zx) OK\n", __func__, coeff_hwbuf->size); } @@ -2218,7 +2206,7 @@ static void dec_state_delete(struct hevc_d_ctx *const ctx) free_ps_info(s); - for (i = 0; i != HEVC_MAX_REFS; ++i) + for (i = 0; i != V4L2_HEVC_DPB_ENTRIES_NUM_MAX; ++i) aux_q_release(ctx, &s->ref_aux[i]); aux_q_release(ctx, &s->frame_aux); @@ -2283,6 +2271,8 @@ static void h265_ctx_uninit(struct hevc_d_dev *const dev, struct hevc_d_ctx *ctx hwbuf_free(dev, ctx->pu_bufs + i); for (i = 0; i != ARRAY_SIZE(ctx->coeff_bufs); ++i) hwbuf_free(dev, ctx->coeff_bufs + i); + ctx->pu_size_max = 0; + ctx->coeff_size_max = 0; } void hevc_d_h265_stop(struct hevc_d_ctx *ctx) @@ -2302,8 +2292,11 @@ int hevc_d_h265_start(struct hevc_d_ctx *ctx) size_t pu_size; size_t coeff_size; + /* Reset values that must be zeroed on a second run */ ctx->fatal_err = 0; ctx->dec0 = NULL; + memset(ctx->slots, 0, sizeof(ctx->slots)); + ctx->state = kzalloc(sizeof(*ctx->state), GFP_KERNEL); if (!ctx->state) { v4l2_err(&dev->v4l2_dev, "Failed to allocate decode state\n"); @@ -2375,6 +2368,13 @@ static int try_ctrl_sps(struct v4l2_ctrl *ctrl) const struct v4l2_ctrl_hevc_sps *const sps = ctrl->p_new.p_hevc_sps; struct hevc_d_ctx *const ctx = ctrl->priv; struct hevc_d_dev *const dev = ctx->dev; + const unsigned int ctb_log2_size_y = + sps->log2_min_luma_coding_block_size_minus3 + 3 + + sps->log2_diff_max_min_luma_coding_block_size; + const unsigned int min_tb_log2_size_y = + sps->log2_min_luma_transform_block_size_minus2 + 2; + const unsigned int max_tb_log2_size_y = min_tb_log2_size_y + + sps->log2_diff_max_min_luma_transform_block_size; if (sps->chroma_format_idc != 1) { v4l2_warn(&dev->v4l2_dev, @@ -2410,6 +2410,31 @@ static int try_ctrl_sps(struct v4l2_ctrl *ctrl) return -EINVAL; } + /* Limits from H.265 7.4.3.2.1 */ + if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12) + return -EINVAL; + if (sps->sps_max_dec_pic_buffering_minus1 > 15) + return -EINVAL; + if (sps->sps_max_num_reorder_pics > + sps->sps_max_dec_pic_buffering_minus1) + return -EINVAL; + if (ctb_log2_size_y > 6) + return -EINVAL; + if (max_tb_log2_size_y > 5) + return -EINVAL; + if (max_tb_log2_size_y > ctb_log2_size_y) + return -EINVAL; + if (sps->max_transform_hierarchy_depth_inter > + (ctb_log2_size_y - min_tb_log2_size_y)) + return -EINVAL; + if (sps->max_transform_hierarchy_depth_intra > + (ctb_log2_size_y - min_tb_log2_size_y)) + return -EINVAL; + if (sps->num_short_term_ref_pic_sets > 64) + return -EINVAL; + if (sps->num_long_term_ref_pics_sps > 32) + return -EINVAL; + return 0; } @@ -2425,14 +2450,25 @@ static int try_ctrl_pps(struct v4l2_ctrl *ctrl) if ((pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED) && - (pps->flags & - V4L2_HEVC_PPS_FLAG_TILES_ENABLED) && + (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) && (pps->num_tile_columns_minus1 || pps->num_tile_rows_minus1)) { v4l2_warn(&dev->v4l2_dev, "WPP + Tiles not supported\n"); return -EINVAL; } + if ((pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) && + (pps->num_tile_columns_minus1 > + ARRAY_SIZE(pps->column_width_minus1) || + pps->num_tile_rows_minus1 > + ARRAY_SIZE(pps->row_height_minus1))) { + v4l2_warn(&dev->v4l2_dev, "Tiles cols/rows too big\n"); + return -EINVAL; + } + + if (pps->log2_parallel_merge_level_minus2 > 4) + return -EINVAL; + return 0; } @@ -2440,6 +2476,66 @@ const struct v4l2_ctrl_ops hevc_d_hevc_pps_ctrl_ops = { .try_ctrl = try_ctrl_pps, }; +/* Check the DPB indices that decode will use are all in range */ +static bool ref_idx_valid(const __u8 *const ref_idx, const unsigned int n_minus_1) +{ + unsigned int i; + + if (n_minus_1 > NUM_REF_IDX_ACTIVE_MAX - 1) + return false; + for (i = 0; i <= n_minus_1; ++i) + if (ref_idx[i] >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX) + return false; + + return true; +} + +static int try_ctrl_slice_params(struct v4l2_ctrl *ctrl) +{ + struct hevc_d_ctx *const ctx = ctrl->priv; + struct hevc_d_dev *const dev = ctx->dev; + const struct v4l2_ctrl_hevc_slice_params *sh = ctrl->p_new.p_hevc_slice_params; + unsigned int i; + + /* + * Cannot know the size from the slice header alone so can only test + * slice_segment_address for slice 0 + */ + if (sh->slice_segment_addr != 0) { + v4l2_warn(&dev->v4l2_dev, "New frame but segment_addr=%d\n", + sh->slice_segment_addr); + return -EINVAL; + } + + for (i = 0; i != ctrl->new_elems; ++i, ++sh) { + if (sh->slice_type != HEVC_SLICE_B && + sh->slice_type != HEVC_SLICE_P && + sh->slice_type != HEVC_SLICE_I) { + v4l2_warn(&dev->v4l2_dev, + "Slice %u: bad slice type %u\n", + i, sh->slice_type); + return -EINVAL; + } + + if (sh->slice_type != HEVC_SLICE_I) { + if (!ref_idx_valid(sh->ref_idx_l0, sh->num_ref_idx_l0_active_minus1)) + return -EINVAL; + if (sh->five_minus_max_num_merge_cand > 4) + return -EINVAL; + } + if (sh->slice_type == HEVC_SLICE_B) { + if (!ref_idx_valid(sh->ref_idx_l1, sh->num_ref_idx_l1_active_minus1)) + return -EINVAL; + } + } + + return 0; +} + +const struct v4l2_ctrl_ops hevc_d_hevc_slice_params_ctrl_ops = { + .try_ctrl = try_ctrl_slice_params, +}; + void hevc_d_device_run(void *priv) { struct hevc_d_ctx *const ctx = priv; @@ -2468,12 +2564,7 @@ void hevc_d_device_run(void *priv) ctrl = v4l2_ctrl_find(ctx->fh.ctrl_handler, V4L2_CID_STATELESS_HEVC_SLICE_PARAMS); - if (!ctrl || !ctrl->elems) { - v4l2_err(&dev->v4l2_dev, "%s: Missing slice params\n", - __func__); - goto fail; - } - run.h265.slice_ents = ctrl->elems; + run.h265.slice_ents = ctrl->elems; /* Framework ensures >= 1 */ run.h265.slice_params = ctrl->p_cur.p; run.h265.scaling_matrix = @@ -2493,6 +2584,7 @@ void hevc_d_device_run(void *priv) fail: /* We really shouldn't get here but tidy up what we can */ + v4l2_ctrl_request_complete(src_req, &ctx->hdl); v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx, VB2_BUF_STATE_ERROR); media_request_manual_complete(src_req); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h index 48f08d37362b19..9961917bf20401 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.h @@ -12,6 +12,7 @@ extern const struct v4l2_ctrl_ops hevc_d_hevc_sps_ctrl_ops; extern const struct v4l2_ctrl_ops hevc_d_hevc_pps_ctrl_ops; +extern const struct v4l2_ctrl_ops hevc_d_hevc_slice_params_ctrl_ops; int hevc_d_h265_start(struct hevc_d_ctx *ctx); void hevc_d_h265_stop(struct hevc_d_ctx *ctx); diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c index 9758d7310ec74f..27e92ccad24dfa 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c @@ -29,9 +29,9 @@ static inline struct hevc_d_ctx *hevc_d_file2ctx(struct file *file) size_t hevc_d_round_up_size(const size_t x) { /* Admit no size < 256 */ - const unsigned int n = x < 256 ? 8 : ilog2(x); + const unsigned int n = x < 256 ? 7 : ilog2(x) - 1; - return x >= (3 << n) ? 4 << n : (3 << n); + return x >= ((size_t)3 << n) ? (size_t)4 << n : ((size_t)3 << n); } static u32 bit_buf_size(unsigned int w, unsigned int h, unsigned int bits_minus8) @@ -83,6 +83,11 @@ void hevc_d_prepare_src_format(struct v4l2_pix_format_mplane *pix_fmt) /* Zero bytes per line for encoded source. */ pix_fmt->plane_fmt[0].bytesperline = 0; pix_fmt->plane_fmt[0].sizeimage = sizeimage; + + /* Zero all unused planes */ + memset(pix_fmt->plane_fmt + pix_fmt->num_planes, 0, + (ARRAY_SIZE(pix_fmt->plane_fmt) - pix_fmt->num_planes) * + sizeof(*pix_fmt->plane_fmt)); } /* Take any pix_format and make it valid */ @@ -171,6 +176,11 @@ static void hevc_d_prepare_dst_format(struct v4l2_pix_format_mplane *pix_fmt) pix_fmt->num_planes = 1; break; } + + /* Zero all unused planes */ + memset(pix_fmt->plane_fmt + pix_fmt->num_planes, 0, + (ARRAY_SIZE(pix_fmt->plane_fmt) - pix_fmt->num_planes) * + sizeof(*pix_fmt->plane_fmt)); } static int hevc_d_querycap(struct file *file, void *priv, @@ -199,61 +209,8 @@ static int hevc_d_enum_fmt_vid_out(struct file *file, void *priv, return -EINVAL; } -static int hevc_d_hevc_validate_sps(const struct v4l2_ctrl_hevc_sps * const sps) -{ - const unsigned int ctb_log2_size_y = - sps->log2_min_luma_coding_block_size_minus3 + 3 + - sps->log2_diff_max_min_luma_coding_block_size; - const unsigned int min_tb_log2_size_y = - sps->log2_min_luma_transform_block_size_minus2 + 2; - const unsigned int max_tb_log2_size_y = min_tb_log2_size_y + - sps->log2_diff_max_min_luma_transform_block_size; - - /* Local limitations */ - if (sps->pic_width_in_luma_samples < 32 || - sps->pic_width_in_luma_samples > 4096) - return 0; - if (sps->pic_height_in_luma_samples < 32 || - sps->pic_height_in_luma_samples > 4096) - return 0; - if (!(sps->bit_depth_luma_minus8 == 0 || - sps->bit_depth_luma_minus8 == 2)) - return 0; - if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8) - return 0; - if (sps->chroma_format_idc != 1) - return 0; - - /* Limits from H.265 7.4.3.2.1 */ - if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12) - return 0; - if (sps->sps_max_dec_pic_buffering_minus1 > 15) - return 0; - if (sps->sps_max_num_reorder_pics > - sps->sps_max_dec_pic_buffering_minus1) - return 0; - if (ctb_log2_size_y > 6) - return 0; - if (max_tb_log2_size_y > 5) - return 0; - if (max_tb_log2_size_y > ctb_log2_size_y) - return 0; - if (sps->max_transform_hierarchy_depth_inter > - (ctb_log2_size_y - min_tb_log2_size_y)) - return 0; - if (sps->max_transform_hierarchy_depth_intra > - (ctb_log2_size_y - min_tb_log2_size_y)) - return 0; - /* Check pcm stuff */ - if (sps->num_short_term_ref_pic_sets > 64) - return 0; - if (sps->num_long_term_ref_pics_sps > 32) - return 0; - return 1; -} - static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps, - const int index) + const unsigned int index) { static const u32 all_formats[] = { V4L2_PIX_FMT_NV12MT_COL128, @@ -263,7 +220,7 @@ static u32 pixelformat_from_sps(const struct v4l2_ctrl_hevc_sps * const sps, }; u32 pf = 0; - if (!is_sps_set(sps) || !hevc_d_hevc_validate_sps(sps)) { + if (!is_sps_set(sps)) { /* Treat this as an error? For now return both */ if (index < ARRAY_SIZE(all_formats)) @@ -315,7 +272,7 @@ hevc_d_hevc_default_dst_fmt(struct hevc_d_ctx * const ctx) } static u32 hevc_d_hevc_get_dst_pixelformat(struct hevc_d_ctx * const ctx, - const int index) + const unsigned int index) { const struct v4l2_ctrl_hevc_sps * const sps = hevc_d_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SPS); @@ -373,7 +330,7 @@ static int hevc_d_try_fmt_vid_cap(struct file *file, void *priv, const struct v4l2_ctrl_hevc_sps * const sps = hevc_d_find_control_data(ctx, V4L2_CID_STATELESS_HEVC_SPS); u32 pixelformat; - int i; + unsigned int i; for (i = 0; (pixelformat = pixelformat_from_sps(sps, i)) != 0; i++) { if (f->fmt.pix_mp.pixelformat == pixelformat) diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.h b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.h index e7952937ed865e..5a1ca7e4f06d46 100644 --- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.h +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.h @@ -14,12 +14,6 @@ #ifndef _HEVC_D_VIDEO_H_ #define _HEVC_D_VIDEO_H_ -struct hevc_d_format { - u32 pixelformat; - u32 directions; - unsigned int capabilities; -}; - static inline int is_sps_set(const struct v4l2_ctrl_hevc_sps * const sps) { return sps && sps->pic_width_in_luma_samples;