[PATCH v6 5/6] media: platform: Add Raspberry Pi HEVC decoder driver
Maíra Canal
mcanal at igalia.com
Mon Mar 9 05:59:06 PDT 2026
Hi Dave,
On 3/4/26 11:05, Dave Stevenson wrote:
> From: John Cox <john.cox at raspberrypi.com>
>
> The BCM2711 and BCM2712 SoCs used on Rapsberry Pi 4 and Raspberry
s/Rapsberry/Raspberry
> diff --git a/drivers/media/platform/raspberrypi/hevc_dec/Kconfig b/drivers/media/platform/raspberrypi/hevc_dec/Kconfig
> new file mode 100644
> index 000000000000..ae1fd079e5c9
> --- /dev/null
> +++ b/drivers/media/platform/raspberrypi/hevc_dec/Kconfig
> @@ -0,0 +1,17 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config VIDEO_RPI_HEVC_DEC
> + tristate "Rasperry Pi HEVC decoder"
s/Rapsberry/Raspberry
> + depends on VIDEO_DEV && VIDEO_DEV
VIDEO_DEV is listed twice.
[...]
> +
> +/*
> + * Stop the clock for this context
> + * clk_disable_unprepare does ref counting so this will not actually
> + * disable the clock if there are other running contexts
> + */
> +void hevc_d_hw_stop_clock(struct hevc_d_dev *dev)
I believe it would be more idiomatic if you use runtime PM to handle
this stop_clock()/start_clock() semantics.
> +{
> + clk_disable_unprepare(dev->clock);
In the case that the clock is actually disabled (no other running
contexts), I believe the IRQs should be also disabled before disabling
the clock.
> +}
> +
> +/* Always starts the clock if it isn't already on this ctx */
> +int hevc_d_hw_start_clock(struct hevc_d_dev *dev)
> +{
> + int rv;
> +
> + rv = clk_set_min_rate(dev->clock, dev->max_clock_rate);
> + if (rv) {
> + dev_err(dev->dev, "Failed to set clock rate\n");
> + return rv;
> + }
After I land [1], you will be able to drop this call and just add
`maximize = true` to the HEVC clock.
[1]
https://lore.kernel.org/dri-devel/20260218-v3d-power-management-v6-1-40683fd39865@igalia.com/
> +
> + rv = clk_prepare_enable(dev->clock);
> + if (rv) {
> + dev_err(dev->dev, "Failed to enable clock\n");
> + return rv;
> + }
Considering that the clock was disabled, I believe you should re-enable
IRQs and reset any pending interrupts here, just like you do in
hw_setup().
> + return 0;
> +}
> +
[...]
> diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c
> new file mode 100644
> index 000000000000..d39a2e228595
> --- /dev/null
> +++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_video.c
> @@ -0,0 +1,634 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Raspberry Pi HEVC driver
> + *
> + * Copyright (C) 2026 Raspberry Pi Ltd
> + *
> + * Based on the Cedrus VPU driver, that is:
> + *
> + * Copyright (C) 2016 Florent Revest <florent.revest at free-electrons.com>
> + * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski at bootlin.com>
> + * Copyright (C) 2018 Bootlin
> + */
> +
> +#include <media/videobuf2-dma-contig.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-mem2mem.h>
> +
> +#include "hevc_d.h"
> +#include "hevc_d_h265.h"
> +#include "hevc_d_hw.h"
> +#include "hevc_d_video.h"
> +
> +static inline struct hevc_d_ctx *hevc_d_file2ctx(struct file *file)
> +{
> + return container_of(file->private_data, struct hevc_d_ctx, fh);
> +}
> +
> +/* constrain x to y,y*2 */
> +static inline unsigned int constrain2x(unsigned int x, unsigned int y)
> +{
> + return (x < y) ?
> + y :
> + (x > y * 2) ? y : x;
> +}
constrain2x() doesn't seem to be used anywhere in the driver.
[...]
> +
> +void hevc_d_prepare_src_format(struct v4l2_pix_format_mplane *pix_fmt)
> +{
> + size_t size;
> + u32 w;
> + u32 h;
> +
> + w = pix_fmt->width;
> + h = pix_fmt->height;
> + if (!w || !h) {
> + w = HEVC_D_DEFAULT_WIDTH;
> + h = HEVC_D_DEFAULT_HEIGHT;
> + }
> + if (w > HEVC_D_MAX_WIDTH)
> + w = HEVC_D_MAX_WIDTH;
> + if (h > HEVC_D_MAX_HEIGHT)
> + h = HEVC_D_MAX_HEIGHT;
> +
> + if (!pix_fmt->plane_fmt[0].sizeimage ||
> + pix_fmt->plane_fmt[0].sizeimage > SZ_32M) {
> + /* Unspecified or way too big - pick max for size */
> + size = hevc_d_bit_buf_size(w, h, 2);
> + }
> + /* Set a minimum */
> + size = max_t(u32, SZ_4K, pix_fmt->plane_fmt[0].sizeimage);
The size computed by hevc_d_bit_buf_size() inside the if-block is
immediately overwritten here unconditionally.
Should the else case be explicit? Something like:
if (!pix_fmt->plane_fmt[0].sizeimage ||
pix_fmt->plane_fmt[0].sizeimage > SZ_32M) {
size = hevc_d_bit_buf_size(w, h, 2);
} else {
size = pix_fmt->plane_fmt[0].sizeimage;
}
size = max_t(u32, SZ_4K, size);
[...]
> +
> +static int hevc_d_start_streaming(struct vb2_queue *vq, unsigned int count)
> +{
> + struct hevc_d_ctx *ctx = vb2_get_drv_priv(vq);
> + struct hevc_d_dev *dev = ctx->dev;
> + int ret = 0;
> +
> + v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, vq);
> +
> + if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
> + ret = hevc_d_hw_start_clock(dev);
> + if (ret)
> + goto fail_cleanup;
> +
> + ret = hevc_d_h265_start(ctx);
> + if (ret)
> + goto fail_stop_clock;
> + }
> +
> + return 0;
> +
> +fail_stop_clock:
> + hevc_d_hw_stop_clock(dev);
> +fail_cleanup:
> + v4l2_err(&dev->v4l2_dev, "%s: qtype=%d: FAIL\n", __func__, vq->type);
> + hevc_d_queue_cleanup(vq, VB2_BUF_STATE_QUEUED);
> + return ret;
> +}
> +
> +static void hevc_d_stop_streaming(struct vb2_queue *vq)
> +{
> + struct hevc_d_ctx *ctx = vb2_get_drv_priv(vq);
> + struct hevc_d_dev *dev = ctx->dev;
> +
> + if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
> + hevc_d_h265_stop(ctx);
> + hevc_d_hw_stop_clock(dev);
> + }
> +
> + hevc_d_queue_cleanup(vq, VB2_BUF_STATE_ERROR);
> +
> + vb2_wait_for_all_buffers(vq);
> +
> + v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, vq);
The order here looks a bit odd to me. Shouldn't we stop the clock after
we stop the streaming state and wait for all buffers?
Best regards,
- Maíra
More information about the linux-arm-kernel
mailing list