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

Đang mở
#7,743 2 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
55/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
c, linux
Lĩnh vực
embedded-iot

Hướng nghiên cứu

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.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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;
 		}
 	}
Ngôn ngữ chính
C
Star
13.3k
Fork
5.5k
Merge trung bình
1 ngày 4 giờ
Pull request đã merge (30 ngày)
21

Chuẩn bị môi trường

Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của raspberrypi/linux

Tất cả issue của raspberrypi/linux

Issue tương tự

Thêm issue về C

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.