[PATCH 05/10] staging: media: cedrus: mpeg2: derive the field order of field pictures
Nicolas Dufresne
nicolas.dufresne at collabora.com
Mon Nov 2 11:00:20 PST 2026
Le lundi 02 novembre 2026 à 18:02 +0100, Jernej Skrabec a écrit :
> The hardware uses the top field first bit of the picture header to tell
> the first field of a frame from the second one. However,
> top_field_first is always 0 in field pictures, so userspace passes
> V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST cleared, and the second field of
> every top field first pair is predicted from the wrong reference.
>
> Detect the second field in the driver instead: it has the opposite
> parity of the previous field and is decoded into the same capture
> buffer. This works with unmodified userspace.
>
> Fixes: 50e761516f2b ("media: platform: Add Cedrus VPU decoder driver")
> Signed-off-by: Jernej Skrabec <jernej.skrabec at gmail.com>
Its quite likely other MPEG-2 decoder are affected. Would have been nice to pass
the picture structure explicitly in the decode params in the first place. Not
your fault.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne at collabora.com>
> ---
> drivers/staging/media/sunxi/cedrus/cedrus.h | 5 +++
> .../staging/media/sunxi/cedrus/cedrus_mpeg2.c | 32 +++++++++++++++++--
> 2 files changed, 35 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.h b/drivers/staging/media/sunxi/cedrus/cedrus.h
> index 54f867949fba..a523b56a9b8c 100644
> --- a/drivers/staging/media/sunxi/cedrus/cedrus.h
> +++ b/drivers/staging/media/sunxi/cedrus/cedrus.h
> @@ -144,6 +144,11 @@ struct cedrus_ctx {
> void *entry_points_buf;
> dma_addr_t entry_points_buf_addr;
> } h265;
> + struct {
> + bool pair_pending;
> + unsigned int pair_dst;
> + u8 pair_structure;
> + } mpeg2;
> struct {
> unsigned int last_frame_p_type;
> unsigned int last_filter_type;
> diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
> index ca66a3c6b830..4c8accee5389 100644
> --- a/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
> +++ b/drivers/staging/media/sunxi/cedrus/cedrus_mpeg2.c
> @@ -58,7 +58,7 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
> struct vb2_queue *vq;
> const u8 *matrix;
> unsigned int i;
> - bool field;
> + bool field, tff;
> u32 reg;
>
> seq = run->mpeg2.sequence;
> @@ -87,6 +87,26 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
> cedrus_write(dev, VE_DEC_MPEG_IQMINPUT, reg);
> }
>
> + /*
> + * top_field_first is always 0 in field pictures, but the hardware
> + * uses this bit to tell the first field of a frame from the second.
> + * A field is the second one when it has the opposite parity of the
> + * previous field and is decoded into the same capture buffer.
> + */
> + if (pic->picture_structure == V4L2_MPEG2_PIC_FRAME) {
> + tff = !!(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST);
> + ctx->codec.mpeg2.pair_pending = false;
> + } else {
> + bool second = ctx->codec.mpeg2.pair_pending &&
> + ctx->codec.mpeg2.pair_structure != pic->picture_structure &&
> + ctx->codec.mpeg2.pair_dst == run->dst->vb2_buf.index;
> +
> + tff = (pic->picture_structure == V4L2_MPEG2_PIC_TOP_FIELD) != second;
> + ctx->codec.mpeg2.pair_pending = !second;
> + ctx->codec.mpeg2.pair_structure = pic->picture_structure;
> + ctx->codec.mpeg2.pair_dst = run->dst->vb2_buf.index;
> + }
> +
> /* Set MPEG picture header. */
>
> reg = VE_DEC_MPEG_MP12HDR_SLICE_TYPE(pic->picture_coding_type);
> @@ -96,7 +116,7 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
> reg |= VE_DEC_MPEG_MP12HDR_F_CODE(1, 1, pic->f_code[1][1]);
> reg |= VE_DEC_MPEG_MP12HDR_INTRA_DC_PRECISION(pic->intra_dc_precision);
> reg |= VE_DEC_MPEG_MP12HDR_INTRA_PICTURE_STRUCTURE(pic->picture_structure);
> - reg |= VE_DEC_MPEG_MP12HDR_TOP_FIELD_FIRST(pic->flags & V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST);
> + reg |= VE_DEC_MPEG_MP12HDR_TOP_FIELD_FIRST(tff);
> reg |= VE_DEC_MPEG_MP12HDR_FRAME_PRED_FRAME_DCT(pic->flags & V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT);
> reg |= VE_DEC_MPEG_MP12HDR_CONCEALMENT_MOTION_VECTORS(pic->flags & V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV);
> reg |= VE_DEC_MPEG_MP12HDR_Q_SCALE_TYPE(pic->flags & V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE);
> @@ -186,6 +206,13 @@ static int cedrus_mpeg2_setup(struct cedrus_ctx *ctx, struct cedrus_run *run)
> return 0;
> }
>
> +static int cedrus_mpeg2_start(struct cedrus_ctx *ctx)
> +{
> + memset(&ctx->codec.mpeg2, 0, sizeof(ctx->codec.mpeg2));
> +
> + return 0;
> +}
> +
> static void cedrus_mpeg2_trigger(struct cedrus_ctx *ctx)
> {
> struct cedrus_dev *dev = ctx->dev;
> @@ -203,5 +230,6 @@ struct cedrus_dec_ops cedrus_dec_ops_mpeg2 = {
> .irq_disable = cedrus_mpeg2_irq_disable,
> .irq_status = cedrus_mpeg2_irq_status,
> .setup = cedrus_mpeg2_setup,
> + .start = cedrus_mpeg2_start,
> .trigger = cedrus_mpeg2_trigger,
> };
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 228 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20261102/31418cea/attachment-0001.sig>
More information about the linux-arm-kernel
mailing list