rpi-hevc-dec: `memory-region` property is ignored; the decoder's internal buffers always come from the default CMA pool

オープン
#7,743 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
55/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
c, linux
領域
embedded-iot

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

説明

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
  1. Raspberry Pi 4 (BCM2711), Raspberry Pi OS, rpi-hevc-dec as shipped, default CMA pool (512 MiB).
  2. 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").
  3. For the memory-region part: reserve a pool and point the decoder node at it with this overlay
    (dtc -@ -I dts -O dtb), enable the cma:cma_alloc_* tracepoints and decode again. The region is
    reserved and present in the device tree, but every allocation of the decoder still comes from linux,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;
 		}
 	}
主要言語
C
スター
13.3k
フォーク
5.5k
平均マージ
1日 4時間
マージ済み PR(30日)
21

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

raspberrypi/linux のほかの issue

raspberrypi/linux の issue をすべて見る

似ている issue

C の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。