rpi-hevc-dec: `memory-region` property is ignored; the decoder's internal buffers always come from the default CMA pool
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 55/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- c, linux
- Área
- embedded-iot
Línea de trabajo
Start with drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c, hevc_d.h, and hevc_d_h265.c, then read the proposed diff and compare it with the existing s5p_mfc_alloc_memdev() pattern. Check how internal buffers are allocated and freed, keeping the vb2 frame and bitstream buffers on the platform device. Done means the optional memory-region supplies internal buffers with per-buffer fallback and matching frees, the Coeff log reports the requested size, and the described decode and allocation checks pass.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Describe the bug
rpi-hevc-dec (the BCM2711 stateless HEVC decoder) ignores a memory-region property on its device-tree node: hevc_d_probe() never calls of_reserved_mem_device_init*(). The decoder's ~45 MiB of private working buffers (PU / coeff / cmd FIFO / colmv, allocated at STREAMON) therefore always come from the default CMA pool. On BCM2711 that pool must stay below 1 GiB (the HVS scans out from the first 1 GiB), and userspace may already have filled it by then.
Effect: a V4L2 stateless client (GStreamer's v4l2slh265dec) pre-sizes its capture pool from the stream's declared level before the decoder starts. For footage whose level undersells its real size, e.g. 3840x2880 10-bit HEVC declaring level 5.2 (action-camera footage), that pool alone reaches ~440 MiB of the default 512 MiB CMA pool, and STREAMON fails with -ENOMEM.
A second, small bug in the same file: the failure message Failed to alloc %#zx Coeff%d buffer prints pu_size (a quarter of the size actually requested) instead of coeff_size.
Both are still present on the current rpi-6.18.y head (31a348853888): the driver has no memory-region / of_reserved_mem handling and the message still prints pu_size.
Steps to reproduce the behaviour
- Raspberry Pi 4 (BCM2711), Raspberry Pi OS,
rpi-hevc-decas shipped, default CMA pool (512 MiB). - Decode a 3840x2880 10-bit HEVC clip (level 5.2) through the stateless decoder, for example
gst-launch-1.0 filesrc location=clip.mp4 ! qtdemux ! h265parse ! v4l2slh265dec ! fakesink
->VIDIOC_STREAMON(SINK) failed: Cannot allocate memory(dmesg under "Logs"). - For the
memory-regionpart: reserve a pool and point the decoder node at it with this overlay
(dtc -@ -I dts -O dtb), enable thecma:cma_alloc_*tracepoints and decode again. The region is
reserved and present in the device tree, but every allocation of the decoder still comes fromlinux,cma.
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2711";
fragment@0 {
target-path = "/reserved-memory";
__overlay__ {
hevc_cma: hevc_cma@0 {
compatible = "shared-dma-pool";
reusable;
size = <0x8000000>;
};
};
};
fragment@1 {
target-path = "/scb/codec@7eb10000";
__overlay__ {
memory-region = <&hevc_cma>;
};
};
};
Device (s)
Raspberry Pi 4 Mod. B
System
/etc/rpi-issue: Raspberry Pi reference 2026-09-15 (pi-gen 2c235fa703cacb65e0fe0b2ab2fcb23d44dfd268, stage2)
vcgencmd version: Sep 7 2026 10:23:43 / version 465206a286cc1a7b13c2e86bc199f74dde7a8309 (clean) (release) (start)
uname: Linux 6.18.50+rpt-rpi-v8 #1 SMP PREEMPT Debian 1:6.18.50-1+rpt1 (2026-09-11) aarch64
board: Raspberry Pi 4 Model B Rev 1.5
The stock rpi-hevc-dec of this kernel matches rpi-6.18.y commit f2bc178ed43d. The measurements below were taken with the stock module and with the patched one on the same kernel.
Logs
Stock module, default CMA pool, 3840x2880 10-bit HEVC (level 5.2):
cma: __cma_alloc: linux,cma: alloc failed, req-size: 3072 pages, ret: -16
rpi-hevc-dec feb00000.codec: Failed to alloc 0x300000 Coeff0 buffer
rpi-hevc-dec feb00000.codec: hevc_d_start_streaming: qtype=10: FAIL
ERROR: from element .../v4l2slh265dec0: Could not enable the decoder driver.
VIDIOC_STREAMON(SINK) failed: Cannot allocate memory
Wrong size in the message: with the CMA pool hogged until STREAMON fails, a failed 4096-page (0x1000000) request is reported by the stock module as Failed to alloc 0x400000 Coeff0 buffer (it prints pu_size); with the patch it prints Failed to alloc 0x1000000 Coeff2 buffer.
Where the decoder's allocations come from (128 MiB reserved region attached to the decoder node, cma:cma_alloc_* tracepoints):
| module | internal buffers (16-, 1024- and 4096-page allocations) | frame buffers |
|---|---|---|
| stock | linux,cma (the region is reserved and present in the DT, but not used) |
linux,cma |
| stock + patch below | the reserved region (hevc_cma@0) |
linux,cma |
Additional context
Proposed change: attach the memory-region (if present) to a child "memdev" device, the same pattern as s5p_mfc_alloc_memdev(), and use it in preference to the default pool only for the decoder's own internal buffers. vb2 frame/bitstream buffers stay on the platform device's default CMA, since those may need to be reachable by the display. Each internal buffer records the device it came from, so a too-small or missing region falls back per buffer to the default pool and frees go to the matching pool. It also fixes the Coeff%d log size. With no memory-region present nothing changes.
Measured with the patch: decode speed is the same (48.9 vs 48.8 fps on that clip, GStreamer v4l2slh265dec), output is bit-exact against software decode (avdec_h265) over repeated runs, and module load/unload is clean.
A config-only workaround exists (raising the default CMA pool), so this is not urgent; the dedicated pool is the cleaner fix.
The patch below applies cleanly to rpi-6.18.y at 31a348853888; it does not apply to rpi-6.19.y without rebasing (the context changed), which I have not looked into. It is offered as-is, not as a pull request and without a sign-off: feel free to take it, adapt it or write a different fix.
Patch (diff against rpi-6.18.y)
diff --git a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c
index 392fd40..023e3cc 100644
--- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c
+++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.c
@@ -14,6 +14,10 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -198,6 +202,81 @@ static const struct media_device_ops hevc_d_m2m_media_ops = {
.req_queue = hevc_d_media_req_queue,
};
+/*
+ * Child device used to allocate the decoder's internal working buffers from a
+ * dedicated reserved-memory region, if the DT node has a "memory-region".
+ *
+ * The region is deliberately not attached to the platform device itself:
+ * that would also move all vb2 (frame) buffers into it, and frame buffers
+ * may have to be scanned out by a display controller with tighter DMA
+ * addressing limits than the decoder (e.g. the BCM2711 HVS can only reach
+ * the first 1GB, whereas the decoder can address the whole of RAM).
+ *
+ * Same pattern as s5p_mfc_alloc_memdev().
+ */
+struct hevc_d_memdev {
+ struct device dev;
+ u64 dma_mask;
+};
+
+static void hevc_d_memdev_release(struct device *dev)
+{
+ struct hevc_d_memdev *md = container_of(dev, struct hevc_d_memdev, dev);
+
+ of_reserved_mem_device_release(dev);
+ kfree(md);
+}
+
+static struct device *hevc_d_memdev_create(struct device *parent)
+{
+ struct hevc_d_memdev *md;
+ struct device *child;
+ int ret;
+
+ if (!of_property_present(parent->of_node, "memory-region"))
+ return NULL;
+
+ md = kzalloc(sizeof(*md), GFP_KERNEL);
+ if (!md)
+ return ERR_PTR(-ENOMEM);
+
+ child = &md->dev;
+ device_initialize(child);
+ child->parent = parent;
+ child->release = hevc_d_memdev_release;
+ md->dma_mask = dma_get_mask(parent);
+ child->dma_mask = &md->dma_mask;
+ child->coherent_dma_mask = parent->coherent_dma_mask;
+
+ ret = dev_set_name(child, "%s:mem", dev_name(parent));
+ if (ret)
+ goto err_put;
+
+ /* Inherit the decoder node's DMA configuration (ranges, coherency) */
+ ret = of_dma_configure(child, parent->of_node, true);
+ if (ret)
+ goto err_put;
+ ret = dma_set_mask_and_coherent(child, dma_get_mask(parent));
+ if (ret)
+ goto err_put;
+
+ ret = device_add(child);
+ if (ret)
+ goto err_put;
+
+ ret = of_reserved_mem_device_init_by_idx(child, parent->of_node, 0);
+ if (ret) {
+ device_del(child);
+ goto err_put;
+ }
+
+ return child;
+
+err_put:
+ put_device(child);
+ return ERR_PTR(ret);
+}
+
static int hevc_d_probe(struct platform_device *pdev)
{
struct hevc_d_dev *dev;
@@ -240,13 +319,21 @@ static int hevc_d_probe(struct platform_device *pdev)
goto err_v4l2;
}
+ dev->mem_dev = hevc_d_memdev_create(dev->dev);
+ if (IS_ERR(dev->mem_dev)) {
+ dev_warn(dev->dev,
+ "Failed to set up memory-region (%pe); using default CMA\n",
+ dev->mem_dev);
+ dev->mem_dev = NULL;
+ }
+
dev->m2m_dev = v4l2_m2m_init(&hevc_d_m2m_ops);
if (IS_ERR(dev->m2m_dev)) {
v4l2_err(&dev->v4l2_dev,
"Failed to initialize V4L2 M2M device\n");
ret = PTR_ERR(dev->m2m_dev);
- goto err_v4l2;
+ goto err_memdev;
}
dev->mdev.dev = &pdev->dev;
@@ -292,6 +379,9 @@ static int hevc_d_probe(struct platform_device *pdev)
err_m2m:
media_device_cleanup(&dev->mdev);
v4l2_m2m_release(dev->m2m_dev);
+err_memdev:
+ if (dev->mem_dev)
+ device_unregister(dev->mem_dev);
err_v4l2:
mutex_destroy(&dev->dev_mutex);
v4l2_device_unregister(&dev->v4l2_dev);
@@ -314,6 +404,9 @@ static void hevc_d_remove(struct platform_device *pdev)
mutex_destroy(&dev->dev_mutex);
v4l2_device_unregister(&dev->v4l2_dev);
+ if (dev->mem_dev)
+ device_unregister(dev->mem_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 6d40d91..e624a83 100644
--- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h
+++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d.h
@@ -88,6 +88,8 @@ struct hevc_d_hwbuf {
u8 *ptr;
dma_addr_t addr;
unsigned long attrs;
+ /* Device the buffer was allocated against (and must be freed via) */
+ struct device *alloc_dev;
};
struct hevc_d_dev;
@@ -178,6 +180,15 @@ struct hevc_d_dev {
struct media_device mdev;
struct platform_device *pdev;
struct device *dev;
+ /*
+ * Optional child device bound to a dedicated reserved-memory region
+ * (DT "memory-region"). If present, the decoder's internal working
+ * buffers (PU, coeff, cmd FIFOs, colmv) are allocated from it in
+ * preference to the default CMA pool. vb2 (frame/bitstream) buffers
+ * always use dev, as they may need to be accessible to other devices
+ * (e.g. the display) with tighter addressing limits.
+ */
+ struct device *mem_dev;
struct v4l2_m2m_dev *m2m_dev;
/* Video device file (vfd) mutex */
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 8be35a9..5325c49 100644
--- a/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c
+++ b/drivers/media/platform/raspberrypi/hevc_dec/hevc_d_h265.c
@@ -38,27 +38,59 @@ enum hevc_slice_type {
enum hevc_layer { L0 = 0, L1 = 1 };
+/*
+ * Allocate hwbuf->size bytes for decoder-internal use.
+ *
+ * These buffers are only ever accessed by the decoder itself, so if a
+ * dedicated memory-region has been provided (dev->mem_dev) try that first,
+ * quietly, and only fall back to the default CMA pool (shared with vb2 frame
+ * buffers, the display, etc.) if that fails.
+ * Records the device used so the buffer can be freed against it.
+ */
+static void *hwbuf_dma_alloc(struct hevc_d_dev *const dev,
+ struct hevc_d_hwbuf *const hwbuf)
+{
+ if (dev->mem_dev) {
+ hwbuf->alloc_dev = dev->mem_dev;
+ hwbuf->ptr = dma_alloc_attrs(dev->mem_dev, hwbuf->size,
+ &hwbuf->addr,
+ GFP_KERNEL | __GFP_NOWARN,
+ hwbuf->attrs);
+ if (hwbuf->ptr)
+ return hwbuf->ptr;
+ hevc_d_dbg(1, &dev->v4l2_dev,
+ "%s: %#zx not available from memory-region, using default pool\n",
+ __func__, hwbuf->size);
+ }
+
+ hwbuf->alloc_dev = dev->dev;
+ hwbuf->ptr = dma_alloc_attrs(dev->dev, hwbuf->size, &hwbuf->addr,
+ GFP_KERNEL, hwbuf->attrs);
+ if (!hwbuf->ptr)
+ hwbuf->alloc_dev = NULL;
+ return hwbuf->ptr;
+}
+
static int hwbuf_alloc(struct hevc_d_dev *const dev, struct hevc_d_hwbuf *hwbuf,
size_t size, unsigned long attrs)
{
hwbuf->size = size;
hwbuf->attrs = attrs;
hwbuf->addr = 0;
- hwbuf->ptr = dma_alloc_attrs(dev->dev, hwbuf->size, &hwbuf->addr,
- GFP_KERNEL, hwbuf->attrs);
- return !hwbuf->ptr ? -ENOMEM : 0;
+ return !hwbuf_dma_alloc(dev, hwbuf) ? -ENOMEM : 0;
}
static void hwbuf_free(struct hevc_d_dev *const dev,
struct hevc_d_hwbuf *const hwbuf)
{
if (hwbuf->ptr)
- dma_free_attrs(dev->dev, hwbuf->size, hwbuf->ptr, hwbuf->addr,
- hwbuf->attrs);
+ dma_free_attrs(hwbuf->alloc_dev, hwbuf->size, hwbuf->ptr,
+ hwbuf->addr, hwbuf->attrs);
hwbuf->size = 0;
hwbuf->ptr = NULL;
hwbuf->addr = 0;
hwbuf->attrs = 0;
+ hwbuf->alloc_dev = NULL;
}
/* Realloc but do not copy
@@ -77,20 +109,16 @@ static int hwbuf_realloc_new(struct hevc_d_dev * const dev,
return 0;
if (hwbuf->ptr)
- dma_free_attrs(dev->dev, hwbuf->size, hwbuf->ptr,
+ dma_free_attrs(hwbuf->alloc_dev, hwbuf->size, hwbuf->ptr,
hwbuf->addr, hwbuf->attrs);
hwbuf->addr = 0;
hwbuf->size = size;
- hwbuf->ptr = dma_alloc_attrs(dev->dev, hwbuf->size,
- &hwbuf->addr, GFP_KERNEL, hwbuf->attrs);
- if (!hwbuf->ptr) {
+ if (!hwbuf_dma_alloc(dev, hwbuf)) {
hwbuf->addr = 0;
hwbuf->size = old_size;
- hwbuf->ptr = dma_alloc_attrs(dev->dev, hwbuf->size,
- &hwbuf->addr, GFP_KERNEL, hwbuf->attrs);
- if (!hwbuf->ptr) {
+ if (!hwbuf_dma_alloc(dev, hwbuf)) {
hwbuf->size = 0;
hwbuf->addr = 0;
hwbuf->attrs = 0;
@@ -2366,7 +2394,7 @@ int hevc_d_h265_start(struct hevc_d_ctx *ctx)
DMA_ATTR_NO_KERNEL_MAPPING)) {
v4l2_err(&dev->v4l2_dev,
"Failed to alloc %#zx Coeff%d buffer\n",
- pu_size, i);
+ coeff_size, i);
goto fail;
}
}
- Lenguaje dominante
- C
- Estrellas
- 13.3k
- Forks
- 5.5k
- Merge medio
- 1 d 4 h
- PR fusionados (30 d)
- 21
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de raspberrypi/linux
-
drm/vc4: hdmi: vc4_hdmi_hpd_irq_thread() checks !force_hotplug globally instead of per-connector bitmaskPosiblemente ocupada @popcornmix la tomó hace 12 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
raspberrypi/linux#7724 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 63/100
raspberrypi/linux#7718 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
raspberrypi/linux#7415 · 2 comentarios · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
rp1-cfe doesn't forward V4L2_EVENT_SOURCE_CHANGE event from csi-2 sensor driver to userspace appAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
raspberrypi/linux#7399 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Documentation update requiredAbierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 82/100
raspberrypi/linux#7357 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de raspberrypi/linux
Issues similares
-
good first issue help wanted program
Dificultad 2/5 1-3 horas Aptitud para principiantes 83/100
hffgzktbcc-blip/ounce-core#297 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
mt7621 编译报错:expected 'struct sockaddr *' but argument is of type 'struct sockaddr_unsized *'Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
coolsnowwolf/lede#14283 · 1 comentario ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
OpenVisualCloud/SVT-JPEG-XS#130 ·
Los mantenedores suelen responder en 1 día