All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR
@ 2026-08-14 15:29 Thierry Reding
  2026-08-14 15:29 ` [PATCH v5 01/10] dt-bindings: reserved-memory: Document " Thierry Reding
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

This series adds support for the video protection region (VPR) used on
Tegra SoC devices. It's a special region of memory that is protected
from accesses by the CPU and used to store DRM protected content (both
decrypted stream data as well as decoded video frames).

Patches 1 through 3 add DT binding documentation for the VPR and add the
VPR to the list of memory-region items for display, host1x and NVDEC.

Patch 4 adds bitmap_allocate(), which is like bitmap_allocate_region()
but works on sizes that are not a power of two.

Patch 5 introduces new APIs needed by the Tegra VPR implementation that
allow memory to be allocated at a fixed offset within a CMA area. Tegra
VPR needs this in order to implement its own allocator on top of CMA to
meet the strict hardware requirements. This replaces the dynamic CMA
area creation patch from earlier versions.

Patch 6 adds some infrastructure for DMA heap implementations to provide
information through debugfs.

The Tegra VPR implementation is added in patch 7. See its commit message
for more details about the specifics of this implementation.

Finally, patches 8-10 add the VPR placeholder node on Tegra234 and
Tegra264 and hook it up to the host1x node so that it can make use of
this region.

Changes in v5:
- use a single CMA area in combination with the new cma_alloc_at() API
- drop dynamic CMA area allocation patch
- various cleanups
- Link to v4: https://patch.msgid.link/20260807-tegra-vpr-v4-0-5510d16af89e@nvidia.com

Changes in v4:
- Link to v3: https://patch.msgid.link/20260701-tegra-vpr-v3-0-d80f7b871bb4@nvidia.com
- fully remove from linear map while chunks are active
- address checkpatch.pl and Sashiko comments
- improve error handling
- remove freezer support

Changes in v3:
- Link to v2: https://patch.msgid.link/20260122161009.3865888-1-thierry.reding@kernel.org
- introduce set_memory_device() and set_memory_normal()
- rename VPR nodes to "protected"
- add Tegra264 placeholder nodes

Changes in v2:
- Link to v1: https://patch.msgid.link/20250902154630.4032984-1-thierry.reding@gmail.com
- Tegra VPR implementation is now more optimized to reduce the number of
  (very slow) resize operations, and allows cross-chunk allocations
- dynamic CMA areas are now trackd separately from static ones, but the
  global number of CMA pages accounts for all areas

Thierry

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Thierry Reding (10):
      dt-bindings: reserved-memory: Document Tegra VPR
      dt-bindings: display: tegra: Document memory regions
      dt-bindings: gpu: host1x: Document memory-regions for NVDEC
      bitmap: Add bitmap_allocate() function
      mm/cma: Introduce cma_alloc_at() API
      dma-buf: heaps: Add debugfs support
      dma-buf: heaps: Add support for Tegra VPR
      arm64: tegra: Add VPR placeholder node on Tegra234
      arm64: tegra: Hook up VPR to host1x
      arm64: tegra: Add VPR placeholder node on Tegra264

 .../display/tegra/nvidia,tegra124-vic.yaml         |    8 +
 .../bindings/display/tegra/nvidia,tegra186-dc.yaml |   10 +
 .../bindings/display/tegra/nvidia,tegra20-dc.yaml  |   10 +-
 .../display/tegra/nvidia,tegra20-host1x.yaml       |    7 +
 .../bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml |    8 +
 .../nvidia,tegra-video-protection-region.yaml      |   75 ++
 arch/arm64/boot/dts/nvidia/tegra234.dtsi           |   45 +
 arch/arm64/boot/dts/nvidia/tegra264.dtsi           |   33 +
 drivers/dma-buf/dma-heap.c                         |   52 +
 drivers/dma-buf/heaps/Kconfig                      |   12 +
 drivers/dma-buf/heaps/Makefile                     |    1 +
 drivers/dma-buf/heaps/tegra-vpr.c                  | 1410 ++++++++++++++++++++
 include/linux/bitmap.h                             |   25 +-
 include/linux/cma.h                                |    4 +
 include/linux/dma-heap.h                           |    2 +
 include/trace/events/cma.h                         |   63 +
 include/trace/events/tegra_vpr.h                   |   57 +
 mm/cma.c                                           |  148 ++
 18 files changed, 1964 insertions(+), 6 deletions(-)
---
base-commit: 48ae42ae65bf76d723943c717044a09ba6666e56
change-id: 20260507-tegra-vpr-cd4bc2509c4c

Best regards,
--  
Thierry Reding <treding@nvidia.com>


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v5 01/10] dt-bindings: reserved-memory: Document Tegra VPR
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:39   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions Thierry Reding
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

The Video Protection Region (VPR) found on NVIDIA Tegra chips is a
region of memory that is protected from CPU accesses. It is used to
decode and play back DRM protected content.

It is a standard reserved memory region that can exist in two forms:
static VPR where the base address and size are fixed (uses the "reg"
property to describe the memory) and a resizable VPR where only the
size is known upfront and the OS can allocate it wherever it can be
accomodated.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v4:
- move oneOf into allOf for a bit more tidiness
- fix example "reg" property

Changes in v3:
- add examples for fixed and resizable VPR
---
 .../nvidia,tegra-video-protection-region.yaml      | 75 ++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml
new file mode 100644
index 000000000000..862bfd391378
--- /dev/null
+++ b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/reserved-memory/nvidia,tegra-video-protection-region.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NVIDIA Tegra Video Protection Region (VPR)
+
+maintainers:
+  - Thierry Reding <thierry.reding@gmail.com>
+  - Jon Hunter <jonathanh@nvidia.com>
+
+description: |
+  NVIDIA Tegra chips have long supported a mechanism to protect a single,
+  contiguous memory region from non-secure memory accesses. Typically this
+  region is used for decoding and playback of DRM protected content. Various
+  devices, such as the display controller and multimedia engines (video
+  decoder) can access this region in a secure way. Access from the CPU is
+  generally forbidden.
+
+  Two variants exist for VPR: one is fixed in both the base address and size,
+  while the other is resizable. Fixed VPR can be described by just a "reg"
+  property specifying the base address and size, whereas the resizable VPR
+  is defined by a size/alignment pair of properties. For resizable VPR the
+  memory is reusable by the rest of the system when it's unused for VPR and
+  therefore the "reusable" property must be specified along with it. For a
+  fixed VPR, the memory is permanently protected, and therefore it's not
+  reusable and must also be marked as "no-map" to prevent any (including
+  speculative) accesses to it.
+
+allOf:
+  - $ref: reserved-memory.yaml
+  - oneOf:
+      - required:
+          - compatible
+          - reg
+
+      - required:
+          - compatible
+          - size
+
+properties:
+  compatible:
+    const: nvidia,tegra-video-protection-region
+
+dependencies:
+  size: [alignment, reusable]
+  alignment: [size, reusable]
+  reusable: [alignment, size]
+
+  reg: [no-map]
+  no-map: [reg]
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    /* resizable VPR */
+    protected {
+      compatible = "nvidia,tegra-video-protection-region";
+
+      size = <0x0 0x70000000>;
+      alignment = <0x0 0x100000>;
+      reusable;
+    };
+
+  - |
+    /* fixed VPR */
+    protected@a8000000 {
+      compatible = "nvidia,tegra-video-protection-region";
+
+      /* fixed VPR */
+      reg = <0xa8000000 0x70000000>;
+      no-map;
+    };

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
  2026-08-14 15:29 ` [PATCH v5 01/10] dt-bindings: reserved-memory: Document " Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:36   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC Thierry Reding
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

Add the memory-region and memory-region-names properties to the bindings
for the display controllers and the host1x engine found on various Tegra
generations. These memory regions are used to access firmware-provided
framebuffer memory as well as the video protection region.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v4:
- typofix

Changes in v3:
- document properties for VIC
---
 .../devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml |  8 ++++++++
 .../devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml  | 10 ++++++++++
 .../devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml   | 10 +++++++++-
 .../bindings/display/tegra/nvidia,tegra20-host1x.yaml          |  7 +++++++
 4 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
index bdf981781bd5..fdd3fe9e2547 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra124-vic.yaml
@@ -68,6 +68,14 @@ properties:
       - const: dma-mem # read
       - const: write
 
+  memory-region:
+    items:
+      - description: reference to the video protection memory region
+
+  memory-region-names:
+    items:
+      - const: protected
+
   dma-coherent: true
 
 additionalProperties: false
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
index ce4589466a18..881bfbf4764d 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra186-dc.yaml
@@ -57,6 +57,16 @@ properties:
       - const: dma-mem # read-0
       - const: read-1
 
+  memory-region:
+    minItems: 1
+    maxItems: 2
+
+  memory-region-names:
+    items:
+      enum: [ framebuffer, protected ]
+    minItems: 1
+    maxItems: 2
+
   nvidia,outputs:
     description: A list of phandles of outputs that this display
       controller can drive.
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
index 69be95afd562..3bf06dcd6198 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-dc.yaml
@@ -65,7 +65,15 @@ properties:
     items:
       - description: phandle to the core power domain
 
-  memory-region: true
+  memory-region:
+    minItems: 1
+    maxItems: 2
+
+  memory-region-names:
+    items:
+      enum: [ framebuffer, protected ]
+    minItems: 1
+    maxItems: 2
 
   nvidia,head:
     $ref: /schemas/types.yaml#/definitions/uint32
diff --git a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
index 8312b7699cbe..420a1fe5ee80 100644
--- a/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
+++ b/Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
@@ -98,6 +98,13 @@ properties:
     items:
       - description: phandle to the HEG or core power domain
 
+  memory-region:
+    maxItems: 1
+
+  memory-region-names:
+    items:
+      - const: protected
+
 required:
   - compatible
   - interrupts

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
  2026-08-14 15:29 ` [PATCH v5 01/10] dt-bindings: reserved-memory: Document " Thierry Reding
  2026-08-14 15:29 ` [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:39   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 04/10] bitmap: Add bitmap_allocate() function Thierry Reding
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

The video protection region is a reserved memory region that can be used
for secure video playback. NVDEC can access this region to decode images
into securely.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 .../devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml     | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
index 4eb325cfd296..bcaaabca945d 100644
--- a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
+++ b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
@@ -60,6 +60,14 @@ properties:
       - const: dma-mem
       - const: write
 
+  memory-region:
+    items:
+      - description: reference to the video protection memory region
+
+  memory-region-names:
+    items:
+      - const: protected
+
   nvidia,memory-controller:
     $ref: /schemas/types.yaml#/definitions/phandle
     description:

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 04/10] bitmap: Add bitmap_allocate() function
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (2 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:36   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API Thierry Reding
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

This is similar to bitmap_allocate_region() but allows allocation of
non-power of two pages/bits.

While at it, reimplement bitmap_allocate_region() in terms of this new
helper to remove a sliver of code duplication.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 include/linux/bitmap.h | 25 ++++++++++++++++++++-----
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 7df1573a409c..c2ab02985b5f 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -716,10 +716,10 @@ void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order)
 }
 
 /**
- * bitmap_allocate_region - allocate bitmap region
+ * bitmap_allocate - allocate bitmap region
  *	@bitmap: array of unsigned longs corresponding to the bitmap
  *	@pos: beginning of bit region to allocate
- *	@order: region size (log base 2 of number of bits) to allocate
+ *	@len: number of bits to allocate
  *
  * Allocate (set bits in) a specified region of a bitmap.
  *
@@ -727,16 +727,31 @@ void bitmap_release_region(unsigned long *bitmap, unsigned int pos, int order)
  * free (not all bits were zero).
  */
 static __always_inline
-int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order)
+int bitmap_allocate(unsigned long *bitmap, unsigned int pos, unsigned int len)
 {
-	unsigned int len = BIT(order);
-
 	if (find_next_bit(bitmap, pos + len, pos) < pos + len)
 		return -EBUSY;
 	bitmap_set(bitmap, pos, len);
 	return 0;
 }
 
+/**
+ * bitmap_allocate_region - allocate bitmap region
+ *	@bitmap: array of unsigned longs corresponding to the bitmap
+ *	@pos: beginning of bit region to allocate
+ *	@order: region size (log base 2 of number of bits) to allocate
+ *
+ * Allocate (set bits in) a specified region of a bitmap.
+ *
+ * Returns: 0 on success, or %-EBUSY if specified region wasn't
+ * free (not all bits were zero).
+ */
+static __always_inline
+int bitmap_allocate_region(unsigned long *bitmap, unsigned int pos, int order)
+{
+	return bitmap_allocate(bitmap, pos, BIT(order));
+}
+
 /**
  * bitmap_find_free_region - find a contiguous aligned mem region
  *	@bitmap: array of unsigned longs corresponding to the bitmap

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (3 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 04/10] bitmap: Add bitmap_allocate() function Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:38   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 06/10] dma-buf: heaps: Add debugfs support Thierry Reding
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

This API can be used to allocate a number of CMA pages starting at a
fixed offset. This is useful, for example, if the CMA area is used as
backing storage for a nested allocator that has stricter requirements
than CMA itself.

Suggested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 include/linux/cma.h        |   4 ++
 include/trace/events/cma.h |  63 +++++++++++++++++++
 mm/cma.c                   | 148 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 215 insertions(+)

diff --git a/include/linux/cma.h b/include/linux/cma.h
index 8555d38a97b1..844404459a42 100644
--- a/include/linux/cma.h
+++ b/include/linux/cma.h
@@ -49,11 +49,15 @@ extern int cma_init_reserved_mem(phys_addr_t base, phys_addr_t size,
 					struct cma **res_cma);
 extern struct page *cma_alloc(struct cma *cma, unsigned long count, unsigned int align,
 			      bool no_warn);
+extern struct page *cma_alloc_at(struct cma *cma, unsigned long offset,
+				 unsigned long count, bool no_warn);
 extern bool cma_release(struct cma *cma, const struct page *pages, unsigned long count);
 
 struct page *cma_alloc_frozen(struct cma *cma, unsigned long count,
 		unsigned int align, bool no_warn);
 struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order);
+struct page *cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
+				 unsigned long count, bool no_warn);
 bool cma_release_frozen(struct cma *cma, const struct page *pages,
 		unsigned long count);
 
diff --git a/include/trace/events/cma.h b/include/trace/events/cma.h
index 37195edf2498..00b622a9da97 100644
--- a/include/trace/events/cma.h
+++ b/include/trace/events/cma.h
@@ -132,6 +132,69 @@ TRACE_EVENT(cma_alloc_busy_retry,
 		  __entry->align)
 );
 
+TRACE_EVENT(cma_alloc_at_start,
+
+	TP_PROTO(const char *name, unsigned long pfn,
+		 unsigned long request_count, unsigned long available_count,
+		 unsigned long total_count),
+
+	TP_ARGS(name, pfn, request_count, available_count, total_count),
+
+	TP_STRUCT__entry(
+		__string(name, name)
+		__field(unsigned long, pfn)
+		__field(unsigned long, request_count)
+		__field(unsigned long, available_count)
+		__field(unsigned long, total_count)
+	),
+
+	TP_fast_assign(
+		__assign_str(name);
+		__entry->pfn = pfn;
+		__entry->request_count = request_count;
+		__entry->available_count = available_count;
+		__entry->total_count = total_count;
+	),
+
+	TP_printk("name=%s pfn=%lx, request_count=%lu available_count=%lu total_count=%lu",
+		  __get_str(name),
+		  __entry->pfn,
+		  __entry->request_count,
+		  __entry->available_count,
+		  __entry->total_count)
+);
+
+TRACE_EVENT(cma_alloc_at_finish,
+
+	TP_PROTO(const char *name, unsigned long pfn, const struct page *page,
+		 unsigned long count, int errorno),
+
+	TP_ARGS(name, pfn, page, count, errorno),
+
+	TP_STRUCT__entry(
+		__string(name, name)
+		__field(unsigned long, pfn)
+		__field(const struct page *, page)
+		__field(unsigned long, count)
+		__field(int, errorno)
+	),
+
+	TP_fast_assign(
+		__assign_str(name);
+		__entry->pfn = pfn;
+		__entry->page = page;
+		__entry->count = count;
+		__entry->errorno = errorno;
+	),
+
+	TP_printk("name=%s pfn=0x%lx page=%p count=%lu errorno=%d",
+		  __get_str(name),
+		  __entry->pfn,
+		  __entry->page,
+		  __entry->count,
+		  __entry->errorno)
+);
+
 #endif /* _TRACE_CMA_H */
 
 /* This part must be outside protection */
diff --git a/mm/cma.c b/mm/cma.c
index a10ea37a261d..1e1ebae79090 100644
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -936,6 +936,141 @@ struct page *cma_alloc_frozen_compound(struct cma *cma, unsigned int order)
 	return __cma_alloc_frozen(cma, 1 << order, order, gfp);
 }
 
+static int cma_range_alloc_at(struct cma *cma, struct cma_memrange *cmr,
+			      unsigned long offset, unsigned long count,
+			      struct page **pagep, gfp_t gfp)
+{
+	struct page *page = NULL;
+	unsigned long pfn;
+	int ret = -EBUSY;
+
+	spin_lock_irq(&cma->lock);
+
+	/*
+	 * If the request is larger than the available number of pages, stop
+	 * right away.
+	 */
+	if (count > cma->available_count)
+		goto unlock;
+
+	ret = bitmap_allocate(cmr->bitmap, offset, count);
+	if (ret < 0)
+		goto unlock;
+
+	pfn = cmr->base_pfn + offset;
+	page = pfn_to_page(pfn);
+
+	/*
+	 * Do not hand out page ranges that are not contiguous, so
+	 * callers can just iterate the pages without having to worry
+	 * about these corner cases.
+	 */
+	if (!page_range_contiguous(page, count)) {
+		pr_warn_ratelimited("%s: %s: skipping non-contiguous area [0x%lx-0x%lx]",
+				    __func__, cma->name, pfn, pfn + count - 1);
+		ret = -EBUSY;
+		goto clear;
+	}
+
+	cma->available_count -= count;
+
+	/*
+	 * It's safe to drop the lock here. We've marked this region for
+	 * our exclusive use. If the migration fails we will take the
+	 * lock again and unmark it.
+	 */
+	spin_unlock_irq(&cma->lock);
+
+	mutex_lock(&cma->alloc_mutex);
+	ret = alloc_contig_frozen_range(pfn, pfn + count, ACR_FLAGS_CMA, gfp);
+	mutex_unlock(&cma->alloc_mutex);
+
+	if (ret < 0)
+		goto free;
+
+	*pagep = page;
+
+	return 0;
+
+free:
+	/* we need to reacquire the lock to clean up the internal state */
+	spin_lock_irq(&cma->lock);
+	cma->available_count += count;
+clear:
+	bitmap_clear(cmr->bitmap, offset, count);
+unlock:
+	spin_unlock_irq(&cma->lock);
+	return ret;
+}
+
+static struct page *__cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
+					  unsigned long count, gfp_t gfp)
+{
+	const char *name = cma ? cma->name : NULL;
+	struct page *page = NULL;
+	int ret = -ENOMEM, r;
+	unsigned long i;
+
+	if (!cma || !cma->count)
+		return page;
+
+	pr_debug("%s(cma %p, name: %s, offset %lu, count %lu)\n", __func__,
+		 (void *)cma, cma->name, offset, count);
+
+	if (!count)
+		return page;
+
+	trace_cma_alloc_at_start(name, offset, count, cma->available_count,
+				 cma->count);
+
+	for (r = 0; r < cma->nranges; r++) {
+		page = NULL;
+
+		ret = cma_range_alloc_at(cma, &cma->ranges[r], offset, count,
+					 &page, gfp);
+		if (ret != -EBUSY || page)
+			break;
+	}
+
+	/*
+	 * CMA can allocate multiple page blocks, which results in different
+	 * blocks being marked with different tags. Reset the tags to ignore
+	 * those page blocks.
+	 */
+	if (page) {
+		for (i = 0; i < count; i++)
+			page_kasan_tag_reset(page + i);
+	}
+
+	if (ret && !(gfp & __GFP_NOWARN)) {
+		pr_err_ratelimited("%s: %s: alloc failed, request: %lu, %lu pages, ret: %d\n",
+				   __func__, cma->name, offset, count, ret);
+		cma_debug_show_areas(cma);
+	}
+
+	pr_debug("%s(): returned %p\n", __func__, page);
+	trace_cma_alloc_at_finish(name, page ? page_to_pfn(page) : 0, page,
+				  count, ret);
+
+	if (page) {
+		count_vm_event(CMA_ALLOC_SUCCESS);
+		cma_sysfs_account_success_pages(cma, count);
+	} else {
+		count_vm_event(CMA_ALLOC_FAIL);
+		cma_sysfs_account_fail_pages(cma, count);
+	}
+
+	return page;
+}
+
+struct page *cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
+				 unsigned long count, bool no_warn)
+{
+	gfp_t gfp = GFP_KERNEL | (no_warn ? __GFP_NOWARN : 0);
+
+	return __cma_alloc_at_frozen(cma, offset, count, gfp);
+}
+
 /**
  * cma_alloc() - allocate pages from contiguous area
  * @cma:   Contiguous memory region for which the allocation is performed.
@@ -959,6 +1094,19 @@ struct page *cma_alloc(struct cma *cma, unsigned long count,
 }
 EXPORT_SYMBOL_GPL(cma_alloc);
 
+struct page *cma_alloc_at(struct cma *cma, unsigned long pfn,
+			  unsigned long count, bool no_warn)
+{
+	struct page *page;
+
+	page = cma_alloc_at_frozen(cma, pfn, count, no_warn);
+	if (page)
+		set_pages_refcounted(page, count);
+
+	return page;
+}
+EXPORT_SYMBOL_GPL(cma_alloc_at);
+
 static struct cma_memrange *find_cma_memrange(struct cma *cma,
 		const struct page *pages, unsigned long count)
 {

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 06/10] dma-buf: heaps: Add debugfs support
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (4 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:43   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

Add a callback to struct dma_heap_ops that heap providers can implement
to show information about the state of the heap in debugfs. A top-level
directory named "dma_heap" is created in debugfs and individual files
will be named after the heaps.

Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v5:
- fix failure handling during debugfs root directory creation
- add more cleanup to the newly introduced dma_heap_exit()
---
 drivers/dma-buf/dma-heap.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/dma-heap.h   |  2 ++
 2 files changed, 54 insertions(+)

diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index 3937dd41bb0f..f279b8fdd3ce 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/cdev.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/dma-buf.h>
 #include <linux/dma-heap.h>
@@ -225,6 +226,40 @@ const char *dma_heap_get_name(struct dma_heap *heap)
 }
 EXPORT_SYMBOL_NS_GPL(dma_heap_get_name, "DMA_BUF_HEAP");
 
+#ifdef CONFIG_DEBUG_FS
+static int dma_heap_debug_show(struct seq_file *s, void *unused)
+{
+	struct dma_heap *heap = s->private;
+	int err = 0;
+
+	if (heap->ops && heap->ops->show)
+		err = heap->ops->show(s, heap);
+
+	return err;
+}
+DEFINE_SHOW_ATTRIBUTE(dma_heap_debug);
+
+static struct dentry *dma_heap_debugfs_dir;
+
+static void dma_heap_init_debugfs(void)
+{
+	dma_heap_debugfs_dir = debugfs_create_dir("dma_heap", NULL);
+}
+
+static void dma_heap_exit_debugfs(void)
+{
+	debugfs_remove_recursive(dma_heap_debugfs_dir);
+}
+#else
+static void dma_heap_init_debugfs(void)
+{
+}
+
+static void dma_heap_exit_debugfs(void)
+{
+}
+#endif
+
 /**
  * dma_heap_add - adds a heap to dmabuf heaps
  * @exp_info: information needed to register this heap
@@ -299,6 +334,13 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
 
 	/* Add heap to the list */
 	list_add(&heap->list, &heap_list);
+
+#ifdef CONFIG_DEBUG_FS
+	if (heap->ops && heap->ops->show)
+		debugfs_create_file(heap->name, 0444, dma_heap_debugfs_dir,
+				    heap, &dma_heap_debug_fops);
+#endif
+
 	mutex_unlock(&heap_list_lock);
 
 	return heap;
@@ -335,6 +377,16 @@ static int dma_heap_init(void)
 	}
 	dma_heap_class->devnode = dma_heap_devnode;
 
+	dma_heap_init_debugfs();
+
 	return 0;
 }
 subsys_initcall(dma_heap_init);
+
+static void __exit dma_heap_exit(void)
+{
+	dma_heap_exit_debugfs();
+	class_destroy(dma_heap_class);
+	unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
+}
+__exitcall(dma_heap_exit);
diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h
index 648328a64b27..1c9bed1f4dde 100644
--- a/include/linux/dma-heap.h
+++ b/include/linux/dma-heap.h
@@ -12,6 +12,7 @@
 #include <linux/types.h>
 
 struct dma_heap;
+struct seq_file;
 
 /**
  * struct dma_heap_ops - ops to operate on a given heap
@@ -24,6 +25,7 @@ struct dma_heap_ops {
 				    unsigned long len,
 				    u32 fd_flags,
 				    u64 heap_flags);
+	int (*show)(struct seq_file *s, struct dma_heap *heap);
 };
 
 /**

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (5 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 06/10] dma-buf: heaps: Add debugfs support Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:43   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234 Thierry Reding
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

NVIDIA Tegra SoCs commonly define a Video-Protection-Region, which is a
region of memory dedicated to content-protected video decode and
playback. This memory cannot be accessed by the CPU and only certain
hardware devices have access to it.

Expose the VPR as a DMA heap so that applications and drivers can
allocate buffers from this region for use-cases that require this kind
of protected memory.

VPR has a few very critical peculiarities. First, it must be a single
contiguous region of memory (there is a single pair of registers that
set the base address and size of the region), which is configured by
calling back into the secure monitor. The memory region also needs to
quite large for some use-cases because it needs to fit multiple video
frames (8K video should be supported), so VPR sizes of ~2 GiB are
expected. However, some devices cannot afford to reserve this amount
of memory for a particular use-case, and therefore the VPR must be
resizable.

Unfortunately, resizing the VPR is slightly tricky because the GPU found
on Tegra SoCs must be in reset during the VPR resize operation. This is
currently implemented by freezing all userspace processes and calling
invoking the GPU's freeze() implementation, resizing and the thawing the
GPU and userspace processes. This is quite heavy-handed, so eventually
it might be better to implement thawing/freezing in the GPU driver in
such a way that they block accesses to the GPU so that the VPR resize
operation can happen without suspending all userspace.

In order to balance the memory usage versus the amount of resizing that
needs to happen, the VPR is divided into multiple chunks. Each chunk is
implemented as a CMA area that is completely allocated on first use to
guarantee the contiguity of the VPR. Once all buffers from a chunk have
been freed, the CMA area is deallocated and the memory returned to the
system.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v5:
- use newly introduced cma_alloc_at() and work with a single CMA area
- setup CMA early and initialize VPR later during boot
- remove some unused variables
- use kalloc_objs()

Changes in v4:
- address Sashiko and checkpatch comments
- fully remove from linear map while chunks are allocated
- improve error handling
- remove freezer support

Changes in v3:
- use set_memory_device() and set_memory_normal() helpers
- use kzalloc_obj() instead of kzalloc() with sizeof()

Changes in v2:
- cluster allocations to reduce the number of resize operations
- support cross-chunk allocation
---
 drivers/dma-buf/heaps/Kconfig     |   12 +
 drivers/dma-buf/heaps/Makefile    |    1 +
 drivers/dma-buf/heaps/tegra-vpr.c | 1410 +++++++++++++++++++++++++++++++++++++
 include/trace/events/tegra_vpr.h  |   57 ++
 4 files changed, 1480 insertions(+)

diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
index bb729e91545c..8909330bfaa2 100644
--- a/drivers/dma-buf/heaps/Kconfig
+++ b/drivers/dma-buf/heaps/Kconfig
@@ -20,3 +20,15 @@ config DMABUF_HEAPS_CMA
 	  Choose this option to enable dma-buf CMA heap. This heap is backed
 	  by the Contiguous Memory Allocator (CMA). If your system has these
 	  regions, you should say Y here.
+
+config DMABUF_HEAPS_TEGRA_VPR
+	bool "NVIDIA Tegra Video-Protected-Region DMA-BUF Heap"
+	depends on DMABUF_HEAPS && DMA_CMA
+	help
+	  Choose this option to enable Video-Protected-Region (VPR) support on
+	  a range of NVIDIA Tegra devices. Access to VPR memory is limited to
+	  a subset of hardware engines and specifically disallowed from the
+	  CPU. The region can be fixed, in which case no linear mapping exists
+	  for the memory, or it can be resizable on systems that want to reuse
+	  the memory for other uses when content-protected video is not played
+	  back.
diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile
index 974467791032..265b77a7b889 100644
--- a/drivers/dma-buf/heaps/Makefile
+++ b/drivers/dma-buf/heaps/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_DMABUF_HEAPS_SYSTEM)	+= system_heap.o
 obj-$(CONFIG_DMABUF_HEAPS_CMA)		+= cma_heap.o
+obj-$(CONFIG_DMABUF_HEAPS_TEGRA_VPR)	+= tegra-vpr.o
diff --git a/drivers/dma-buf/heaps/tegra-vpr.c b/drivers/dma-buf/heaps/tegra-vpr.c
new file mode 100644
index 000000000000..c8d9f9c66dde
--- /dev/null
+++ b/drivers/dma-buf/heaps/tegra-vpr.c
@@ -0,0 +1,1410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DMA-BUF restricted heap exporter for NVIDIA Video-Protection-Region (VPR)
+ *
+ * Copyright (C) 2024-2026 NVIDIA Corporation
+ */
+
+#define pr_fmt(fmt) "tegra-vpr: " fmt
+
+#include <linux/arm-smccc.h>
+#include <linux/cma.h>
+#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-heap.h>
+#include <linux/find.h>
+#include <linux/memory.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/set_memory.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/tegra_vpr.h>
+
+#define TEGRA_VPR_MAX_CHUNKS 64
+
+struct tegra_vpr;
+
+struct tegra_vpr_device {
+	struct list_head node;
+	struct device *dev;
+};
+
+struct tegra_vpr_chunk {
+	phys_addr_t start;
+	phys_addr_t limit;
+	size_t size;
+
+	struct tegra_vpr *vpr;
+	bool invalid;
+	bool active;
+
+	struct page *start_page;
+	unsigned int offset;
+	unsigned long virt;
+	pgoff_t num_pages;
+
+	unsigned int num_buffers;
+};
+
+struct tegra_vpr {
+	struct list_head list;
+
+	struct device_node *dev_node;
+	unsigned long align;
+	phys_addr_t base;
+	phys_addr_t size;
+	int nid;
+
+	struct list_head buffers;
+	unsigned long *bitmap;
+	pgoff_t num_pages;
+
+	/* resizable VPR */
+	struct cma *cma;
+	unsigned long *active;
+	struct tegra_vpr_chunk *chunks;
+	unsigned int num_chunks;
+	struct page *start_page;
+	bool resizable;
+
+	unsigned int first;
+	unsigned int last;
+
+	struct list_head devices;
+
+	/**
+	 * @lock: Protects concurrent access to the allocation bitmap, as well
+	 * as the buffers and devices lists.
+	 */
+	struct mutex lock;
+};
+
+static DEFINE_MUTEX(vpr_lock);
+static LIST_HEAD(vpr_list);
+
+struct tegra_vpr_buffer {
+	struct list_head attachments;
+	struct tegra_vpr *vpr;
+	struct list_head list;
+
+	/**
+	 * @lock: Protects concurrent access to the list of attachments.
+	 */
+	struct mutex lock;
+
+	struct page **pages;
+	pgoff_t num_pages;
+	phys_addr_t start;
+	phys_addr_t limit;
+	size_t size;
+	int pageno;
+	int order;
+
+	DECLARE_BITMAP(chunks, TEGRA_VPR_MAX_CHUNKS);
+};
+
+struct tegra_vpr_attachment {
+	struct device *dev;
+	struct sg_table sgt;
+	struct list_head list;
+};
+
+#define ARM_SMCCC_TE_FUNC_PROGRAM_VPR 0x3
+
+#define ARM_SMCCC_VENDOR_SIP_TE_PROGRAM_VPR_FUNC_ID		\
+	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
+			   ARM_SMCCC_SMC_32,			\
+			   ARM_SMCCC_OWNER_SIP,			\
+			   ARM_SMCCC_TE_FUNC_PROGRAM_VPR)
+
+static int tegra_vpr_set(phys_addr_t base, phys_addr_t size)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(ARM_SMCCC_VENDOR_SIP_TE_PROGRAM_VPR_FUNC_ID, base, size,
+		      0, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+static int tegra_vpr_get_extents(struct tegra_vpr *vpr, phys_addr_t *base,
+				 phys_addr_t *size)
+{
+	phys_addr_t start = ~0, limit = 0;
+	unsigned int i;
+
+	for (i = 0; i < vpr->num_chunks; i++) {
+		struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+		if (chunk->active) {
+			if (chunk->start < start)
+				start = chunk->start;
+
+			if (chunk->limit > limit)
+				limit = chunk->limit;
+		}
+	}
+
+	if (limit > start) {
+		*size = limit - start;
+		*base = start;
+	} else {
+		*base = *size = 0;
+	}
+
+	return 0;
+}
+
+static int tegra_vpr_resize(struct tegra_vpr *vpr)
+{
+	struct tegra_vpr_device *node;
+	phys_addr_t base, size;
+	int err, status = 0;
+
+	err = tegra_vpr_get_extents(vpr, &base, &size);
+	if (err < 0) {
+		pr_err("%s(): failed to get VPR extents: %d\n", __func__, err);
+		return err;
+	}
+
+	list_for_each_entry(node, &vpr->devices, node) {
+		err = pm_generic_freeze(node->dev);
+		if (err < 0) {
+			pr_err("failed to freeze %s: %d\n",
+			       dev_name(node->dev), err);
+			status = err;
+			goto thaw;
+		}
+	}
+
+	trace_tegra_vpr_set(base, size);
+
+	err = tegra_vpr_set(base, size);
+	if (err < 0) {
+		pr_err("failed to secure VPR: %d\n", err);
+		status = err;
+	}
+
+thaw:
+	list_for_each_entry_continue_reverse(node, &vpr->devices, node) {
+		err = pm_generic_thaw(node->dev);
+		if (err < 0) {
+			pr_err("failed to thaw %s: %d\n",
+			       dev_name(node->dev), err);
+			continue;
+		}
+	}
+
+	return status;
+}
+
+static int tegra_vpr_chunk_init(struct tegra_vpr *vpr,
+				struct tegra_vpr_chunk *chunk,
+				phys_addr_t start, size_t size,
+				unsigned int order, const char *name)
+{
+	chunk->start = start;
+	chunk->limit = start + size;
+	chunk->size = size;
+	chunk->vpr = vpr;
+
+	chunk->offset = (start - vpr->base) >> PAGE_SHIFT;
+	chunk->num_pages = size >> PAGE_SHIFT;
+	chunk->num_buffers = 0;
+
+	/* CMA area is not reserved yet */
+	chunk->start_page = NULL;
+	chunk->virt = 0;
+
+	return 0;
+}
+
+static void tegra_vpr_chunk_free(struct tegra_vpr_chunk *chunk)
+{
+}
+
+static inline bool tegra_vpr_chunk_is_last(const struct tegra_vpr_chunk *chunk)
+{
+	phys_addr_t limit = chunk->vpr->base + chunk->vpr->size;
+
+	return chunk->limit == limit;
+}
+
+static inline bool tegra_vpr_chunk_is_leaf(const struct tegra_vpr_chunk *chunk)
+{
+	const struct tegra_vpr_chunk *next = chunk + 1;
+
+	if (tegra_vpr_chunk_is_last(chunk))
+		return true;
+
+	return !next->active;
+}
+
+static int tegra_vpr_chunk_alloc(struct tegra_vpr_chunk *chunk)
+{
+	chunk->start_page = cma_alloc_at(chunk->vpr->cma, chunk->offset,
+					 chunk->num_pages, false);
+	if (!chunk->start_page)
+		return -ENOMEM;
+
+	chunk->virt = (unsigned long)page_to_virt(chunk->start_page);
+
+	return 0;
+}
+
+static int tegra_vpr_chunk_activate(struct tegra_vpr_chunk *chunk)
+{
+	unsigned int num_errors = 0;
+	int err, status = 0;
+	pgoff_t i;
+
+	trace_tegra_vpr_chunk_activate(chunk->start, chunk->limit);
+
+	for (i = 0; i < chunk->num_pages; i++) {
+		err = set_direct_map_invalid_noflush(chunk->start_page + i);
+		if (err)
+			goto restore;
+	}
+
+	flush_tlb_kernel_range(chunk->virt, chunk->virt + chunk->size);
+	chunk->invalid = false;
+	chunk->active = true;
+
+	return 0;
+
+restore:
+	status = err;
+
+	while (i--) {
+		err = set_direct_map_default_noflush(chunk->start_page + i);
+		if (err)
+			num_errors++;
+	}
+
+	flush_tlb_kernel_range(chunk->virt, chunk->virt + chunk->size);
+	chunk->invalid = num_errors > 0;
+
+	return status;
+}
+
+static int tegra_vpr_chunk_deactivate(struct tegra_vpr_chunk *chunk)
+{
+	unsigned int num_errors = 0;
+	int err, status = 0;
+	pgoff_t i;
+
+	if (!chunk->active)
+		return 0;
+
+	/* do not deactivate if there are buffers left in this chunk */
+	if (WARN_ON(chunk->num_buffers > 0))
+		return -EBUSY;
+
+	trace_tegra_vpr_chunk_deactivate(chunk->start, chunk->limit);
+
+	for (i = 0; i < chunk->num_pages; i++) {
+		err = set_direct_map_default_noflush(chunk->start_page + i);
+		if (err)
+			goto restore;
+	}
+
+	flush_tlb_kernel_range(chunk->virt, chunk->virt + chunk->size);
+	chunk->invalid = false;
+	chunk->active = false;
+
+	return 0;
+
+restore:
+	status = err;
+
+	while (i--) {
+		err = set_direct_map_invalid_noflush(chunk->start_page + i);
+		if (err)
+			num_errors++;
+	}
+
+	flush_tlb_kernel_range(chunk->virt, chunk->virt + chunk->size);
+	chunk->invalid = num_errors > 0;
+
+	return status;
+}
+
+static void tegra_vpr_chunk_release(struct tegra_vpr_chunk *chunk)
+{
+	if (!WARN_ON(chunk->active || chunk->invalid)) {
+		cma_release(chunk->vpr->cma, chunk->start_page,
+			    chunk->num_pages);
+		chunk->start_page = NULL;
+		chunk->virt = 0;
+	}
+}
+
+static bool tegra_vpr_chunk_overlaps(struct tegra_vpr_chunk *chunk,
+				     unsigned int start, unsigned int limit)
+{
+	unsigned int first = chunk->offset;
+	unsigned int last = chunk->offset + chunk->num_pages - 1;
+
+	if (last < start || first >= limit)
+		return false;
+
+	return true;
+}
+
+static int tegra_vpr_activate_chunks(struct tegra_vpr *vpr,
+				     struct tegra_vpr_buffer *buffer)
+{
+	DECLARE_BITMAP(dirty, vpr->num_chunks);
+	unsigned int i, bottom, top;
+	int err = 0, ret;
+
+	bitmap_zero(dirty, vpr->num_chunks);
+
+	/* activate any inactive chunks that overlap this buffer */
+	for_each_set_bit(i, buffer->chunks, vpr->num_chunks) {
+		struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+		if (chunk->active)
+			continue;
+
+		err = tegra_vpr_chunk_alloc(chunk);
+		if (err < 0)
+			goto deactivate;
+
+		err = tegra_vpr_chunk_activate(chunk);
+		if (err < 0) {
+			tegra_vpr_chunk_release(chunk);
+			goto deactivate;
+		}
+
+		set_bit(i, vpr->active);
+		set_bit(i, dirty);
+	}
+
+	/*
+	 * Activating chunks above may have created holes, but since the VPR
+	 * can only ever be a single contiguous region, make sure to activate
+	 * any missing chunks.
+	 */
+	for_each_clear_bitrange(bottom, top, vpr->active, vpr->num_chunks) {
+		/* inactive chunks at the bottom or the top are harmless */
+		if (bottom == 0 || top == vpr->num_chunks)
+			continue;
+
+		for (i = bottom; i < top; i++) {
+			struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+			err = tegra_vpr_chunk_alloc(chunk);
+			if (err < 0)
+				goto deactivate;
+
+			err = tegra_vpr_chunk_activate(chunk);
+			if (err < 0) {
+				tegra_vpr_chunk_release(chunk);
+				goto deactivate;
+			}
+
+			set_bit(i, vpr->active);
+			set_bit(i, dirty);
+		}
+	}
+
+	/* if any chunks have been activated, VPR needs to be resized */
+	if (!bitmap_empty(dirty, vpr->num_chunks)) {
+		err = tegra_vpr_resize(vpr);
+		if (err < 0) {
+			pr_err("failed to grow VPR: %d\n", err);
+			goto deactivate;
+		}
+	}
+
+	/* increment buffer count for each chunk */
+	for_each_set_bit(i, buffer->chunks, vpr->num_chunks)
+		vpr->chunks[i].num_buffers++;
+
+	return 0;
+
+deactivate:
+	/* deactivate any of the previously inactive chunks on failure */
+	for_each_set_bit(i, dirty, vpr->num_chunks) {
+		struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+		ret = tegra_vpr_chunk_deactivate(chunk);
+		if (WARN_ON(ret < 0)) {
+			pr_err("failed to deactivate chunk #%u: %d\n", i, ret);
+		} else {
+			tegra_vpr_chunk_release(chunk);
+			clear_bit(i, vpr->active);
+		}
+	}
+
+	return err;
+}
+
+/*
+ * Retrieve the range of pages within the activate region of the VPR.
+ */
+static bool tegra_vpr_get_active_range(struct tegra_vpr *vpr,
+				       unsigned int *first,
+				       unsigned int *last)
+{
+	unsigned long i, j;
+
+	i = find_first_bit(vpr->active, vpr->num_chunks);
+	if (i >= vpr->num_chunks)
+		return false;
+
+	j = find_last_bit(vpr->active, vpr->num_chunks);
+	if (j >= vpr->num_chunks)
+		return false;
+
+	*first = vpr->chunks[i].offset;
+	*last = vpr->chunks[j].offset + vpr->chunks[j].num_pages;
+
+	return true;
+}
+
+/*
+ * Try to find and allocate a free region within a specific page range.
+ * Returns the page number if successful, -ENOSPC otherwise.
+ *
+ * This function mimics bitmap_find_free_region() but restricts the search
+ * to a specific range to enable allocation within individual chunks.
+ */
+static int tegra_vpr_find_free_region_in_range(struct tegra_vpr *vpr,
+					       unsigned int start_page,
+					       unsigned int end_page,
+					       unsigned int num_pages,
+					       unsigned int align)
+{
+	unsigned int pos, next = ALIGN(start_page, align);
+
+	/* Scan through aligned positions, trying to allocate at each one */
+	for (pos = next; pos + num_pages <= end_page; pos = next) {
+		next = find_next_bit(vpr->bitmap, pos + num_pages, pos);
+
+		if (next >= pos + num_pages) {
+			bitmap_set(vpr->bitmap, pos, num_pages);
+			return pos;
+		}
+
+		next = find_next_zero_bit(vpr->bitmap, vpr->num_pages, next);
+		next = ALIGN(next, align);
+	}
+
+	return -ENOSPC;
+}
+
+static int tegra_vpr_find_free_region(struct tegra_vpr *vpr,
+				      unsigned int num_pages,
+				      unsigned long align)
+{
+	return tegra_vpr_find_free_region_in_range(vpr, 0, vpr->num_pages - 1,
+						   num_pages, align);
+}
+
+static int tegra_vpr_find_free_region_clustered(struct tegra_vpr *vpr,
+						unsigned int num_pages,
+						unsigned int align)
+{
+	unsigned int target, first, last;
+	int pageno;
+
+	/*
+	 * If there are no allocations, abort the clustered allocation scheme
+	 * and use the generic allocation scheme instead.
+	 */
+	if (vpr->first > vpr->last)
+		return -ENOSPC;
+
+	/*
+	 * First, try to allocate within the currently allocated region. This
+	 * keeps allocations tightly packed and minimizes the VPR size needed.
+	 */
+	pageno = tegra_vpr_find_free_region_in_range(vpr, vpr->first,
+						     vpr->last + 1, num_pages,
+						     align);
+	if (pageno >= 0)
+		return pageno;
+
+	/*
+	 * If not enough free space exists within the currently allocated
+	 * region, check to see if the allocation fits anywhere within the
+	 * active region, avoiding the need to resize the VPR.
+	 */
+	if (tegra_vpr_get_active_range(vpr, &first, &last)) {
+		pageno = tegra_vpr_find_free_region_in_range(vpr, first, last,
+							     num_pages, align);
+		if (pageno >= 0)
+			return pageno;
+	}
+
+	/*
+	 * If not enough free space exists within the currently active region,
+	 * try to allocate adjacent to it to grow it contiguously and ensure
+	 * optimal packing.
+	 */
+
+	/*
+	 * Calculate where the allocation should start to end right at the
+	 * first allocated page, with proper alignment.
+	 */
+	if (vpr->first >= num_pages) {
+		target = ALIGN_DOWN(vpr->first - num_pages, align);
+
+		if (!bitmap_allocate(vpr->bitmap, target, num_pages))
+			return target;
+	}
+
+	/* Try after the last allocation */
+	target = ALIGN(vpr->last + 1, align);
+
+	if (target + num_pages <= vpr->num_pages &&
+	    !bitmap_allocate(vpr->bitmap, target, num_pages))
+		return target;
+
+	/*
+	 * Couldn't allocate at the ideal adjacent position, search for any
+	 * available space before the first allocated page.
+	 */
+	pageno = tegra_vpr_find_free_region_in_range(vpr, 0, vpr->first,
+						     num_pages, align);
+	if (pageno >= 0)
+		return pageno;
+
+	/*
+	 * Couldn't allocate at the ideal adjacent position, search
+	 * for any available space after the last allocated page.
+	 */
+	pageno = tegra_vpr_find_free_region_in_range(vpr, vpr->last + 1,
+						     vpr->num_pages, num_pages,
+						     align);
+	if (pageno >= 0)
+		return pageno;
+
+	return -ENOSPC;
+}
+
+/*
+ * Find a free region, preferring locations near existing allocations to
+ * minimize VPR fragmentation. The allocation strategy is to first allocate
+ * within or adjacent to the existing region to keep allocations clustered.
+ * Otherwise fall back to a generic allocation using the first available
+ * space.
+ *
+ * This approach focuses on page-level allocation first, then the chunk
+ * system determines which chunks need to be activated based on where the
+ * pages ended up.
+ */
+static int tegra_vpr_allocate_region(struct tegra_vpr *vpr,
+				     unsigned int num_pages,
+				     unsigned int align)
+{
+	int pageno;
+
+	/*
+	 * For non-resizable VPR (no chunks), use simple first-fit allocation.
+	 * Clustering optimization is only beneficial for resizable VPR where
+	 * keeping allocations together minimizes the active VPR size.
+	 */
+	if (!vpr->resizable)
+		return tegra_vpr_find_free_region(vpr, num_pages, align);
+
+	/*
+	 * Check if there are any existing allocations in the bitmap. If so,
+	 * try to allocate near them to minimize fragmentation.
+	 */
+	pageno = tegra_vpr_find_free_region_clustered(vpr, num_pages, align);
+	if (pageno >= 0)
+		return pageno;
+
+	/*
+	 * If there are no existing allocations, or no space adjacent to them,
+	 * fall back to the first available space anywhere in the VPR.
+	 */
+	pageno = tegra_vpr_find_free_region(vpr, num_pages, align);
+	if (pageno >= 0)
+		return pageno;
+
+	return -ENOSPC;
+}
+
+static struct tegra_vpr_buffer *
+tegra_vpr_buffer_allocate(struct tegra_vpr *vpr, size_t size)
+{
+	unsigned int num_pages = size >> PAGE_SHIFT;
+	unsigned int order = get_order(size);
+	struct tegra_vpr_buffer *buffer;
+	unsigned long first, last;
+	int pageno, err;
+
+	/*
+	 * Quick sanity check that we're not trying to allocate a buffer that
+	 * has no chance of fitting into the VPR.
+	 */
+	if (size > vpr->size)
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * "order" defines the alignment and size, so this may result in
+	 * fragmented memory depending on the allocation patterns. However,
+	 * since this is used primarily for video frames, it is expected that
+	 * a number of buffers of the same size will be allocated, so
+	 * fragmentation should be negligible.
+	 */
+	pageno = tegra_vpr_allocate_region(vpr, num_pages, 1);
+	if (pageno < 0)
+		return ERR_PTR(pageno);
+
+	first = find_first_bit(vpr->bitmap, vpr->num_pages);
+	last = find_last_bit(vpr->bitmap, vpr->num_pages);
+
+	buffer = kzalloc_obj(*buffer, GFP_KERNEL);
+	if (!buffer) {
+		err = -ENOMEM;
+		goto release;
+	}
+
+	INIT_LIST_HEAD(&buffer->attachments);
+	INIT_LIST_HEAD(&buffer->list);
+	mutex_init(&buffer->lock);
+	buffer->start = vpr->base + (pageno << PAGE_SHIFT);
+	buffer->limit = buffer->start + size;
+	buffer->size = size;
+	buffer->num_pages = num_pages;
+	buffer->pageno = pageno;
+	buffer->order = order;
+
+	/* track which chunks this buffer overlaps */
+	if (vpr->resizable) {
+		unsigned int limit = buffer->pageno + buffer->num_pages;
+		pgoff_t i;
+
+		/*
+		 * Memory is backed by struct page, so track which ones we
+		 * use.
+		 */
+		buffer->pages = kvmalloc_array(buffer->num_pages,
+					       sizeof(*buffer->pages),
+					       GFP_KERNEL);
+		if (!buffer->pages) {
+			err = -ENOMEM;
+			goto free;
+		}
+
+		for (i = 0; i < buffer->num_pages; i++)
+			buffer->pages[i] = &vpr->start_page[pageno + i];
+
+		for (i = 0; i < vpr->num_chunks; i++) {
+			struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+			if (tegra_vpr_chunk_overlaps(chunk, pageno, limit))
+				set_bit(i, buffer->chunks);
+		}
+
+		/* activate chunks if necessary */
+		err = tegra_vpr_activate_chunks(vpr, buffer);
+		if (err < 0) {
+			kfree(buffer->pages);
+			goto free;
+		}
+
+		/* track first and last allocated pages */
+		if (buffer->pageno < vpr->first)
+			vpr->first = buffer->pageno;
+
+		if (limit - 1 > vpr->last)
+			vpr->last = limit - 1;
+	}
+
+	return buffer;
+
+free:
+	kfree(buffer);
+release:
+	bitmap_clear(vpr->bitmap, pageno, num_pages);
+	return ERR_PTR(err);
+}
+
+static void tegra_vpr_buffer_release(struct tegra_vpr_buffer *buffer)
+{
+	struct tegra_vpr *vpr = buffer->vpr;
+	struct tegra_vpr_buffer *entry;
+	unsigned int i;
+
+	/*
+	 * Decrement buffer count for each overlapping chunk. Note that chunks
+	 * are not deactivated here yet, that's done in tegra_vpr_recycle()
+	 * instead.
+	 */
+	for_each_set_bit(i, buffer->chunks, vpr->num_chunks) {
+		if (!WARN_ON(vpr->chunks[i].num_buffers == 0))
+			vpr->chunks[i].num_buffers--;
+	}
+
+	/* track first and last allocated pages */
+	if (list_is_first(&buffer->list, &vpr->buffers) &&
+	    list_is_last(&buffer->list, &vpr->buffers)) {
+		/* if there are no remaining buffers after this, reset */
+		vpr->first = ~0U;
+		vpr->last = 0U;
+	} else if (list_is_first(&buffer->list, &vpr->buffers)) {
+		entry = list_next_entry(buffer, list);
+		vpr->first = entry->pageno;
+	} else if (list_is_last(&buffer->list, &vpr->buffers)) {
+		entry = list_prev_entry(buffer, list);
+		vpr->last = entry->pageno + entry->num_pages - 1;
+	}
+
+	bitmap_clear(vpr->bitmap, buffer->pageno, buffer->num_pages);
+	list_del(&buffer->list);
+	kfree(buffer->pages);
+	kfree(buffer);
+}
+
+static int tegra_vpr_attach(struct dma_buf *buf,
+			    struct dma_buf_attachment *attachment)
+{
+	struct tegra_vpr_buffer *buffer = buf->priv;
+	struct tegra_vpr_attachment *attach;
+	int err;
+
+	attach = kzalloc_obj(*attach, GFP_KERNEL);
+	if (!attach)
+		return -ENOMEM;
+
+	/*
+	 * For resizable VPR, the memory is backed by struct page, so we can
+	 * use the convenient helper to create the SG table.
+	 */
+	if (buffer->pages) {
+		err = sg_alloc_table_from_pages(&attach->sgt, buffer->pages,
+						buffer->num_pages, 0,
+						buffer->size, GFP_KERNEL);
+		if (err < 0)
+			goto free;
+	} else {
+		if (sg_alloc_table(&attach->sgt, 1, GFP_KERNEL)) {
+			err = -ENOMEM;
+			goto free;
+		}
+
+		sg_set_page(attach->sgt.sgl, NULL, buffer->size, 0);
+		sg_dma_address(attach->sgt.sgl) = buffer->start;
+		sg_dma_len(attach->sgt.sgl) = buffer->size;
+	}
+
+	attach->dev = attachment->dev;
+	INIT_LIST_HEAD(&attach->list);
+	attachment->priv = attach;
+
+	mutex_lock(&buffer->lock);
+	list_add(&attach->list, &buffer->attachments);
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+
+free:
+	kfree(attach);
+	return err;
+}
+
+static void tegra_vpr_detach(struct dma_buf *buf,
+			     struct dma_buf_attachment *attachment)
+{
+	struct tegra_vpr_buffer *buffer = buf->priv;
+	struct tegra_vpr_attachment *attach = attachment->priv;
+
+	mutex_lock(&buffer->lock);
+	list_del(&attach->list);
+	mutex_unlock(&buffer->lock);
+
+	sg_free_table(&attach->sgt);
+	kfree(attach);
+}
+
+static struct sg_table *
+tegra_vpr_map_dma_buf(struct dma_buf_attachment *attachment,
+		      enum dma_data_direction direction)
+{
+	struct tegra_vpr_attachment *attach = attachment->priv;
+	struct sg_table *sgt = &attach->sgt;
+	int err;
+
+	err = dma_map_sgtable(attachment->dev, sgt, direction,
+			      DMA_ATTR_SKIP_CPU_SYNC);
+	if (err < 0)
+		return ERR_PTR(err);
+
+	return sgt;
+}
+
+static void tegra_vpr_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				    struct sg_table *sgt,
+				    enum dma_data_direction direction)
+{
+	dma_unmap_sgtable(attachment->dev, sgt, direction,
+			  DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void tegra_vpr_recycle(struct tegra_vpr *vpr)
+{
+	DECLARE_BITMAP(dirty, vpr->num_chunks);
+	unsigned int i;
+	int err;
+
+	if (!vpr->resizable)
+		return;
+
+	bitmap_zero(dirty, vpr->num_chunks);
+
+	/*
+	 * Deactivate any unused chunks from the bottom...
+	 */
+	for (i = 0; i < vpr->num_chunks; i++) {
+		struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+		if (!chunk->active)
+			continue;
+
+		if (chunk->num_buffers > 0)
+			break;
+
+		err = tegra_vpr_chunk_deactivate(chunk);
+		if (err < 0) {
+			pr_err("failed to deactivate chunk #%u: %d\n", i, err);
+			goto activate;
+		} else {
+			clear_bit(i, vpr->active);
+			set_bit(i, dirty);
+		}
+	}
+
+	/*
+	 * ... and the top.
+	 */
+	for (i = 0; i < vpr->num_chunks; i++) {
+		unsigned int index = vpr->num_chunks - i - 1;
+		struct tegra_vpr_chunk *chunk = &vpr->chunks[index];
+
+		if (!chunk->active)
+			continue;
+
+		if (chunk->num_buffers > 0)
+			break;
+
+		err = tegra_vpr_chunk_deactivate(chunk);
+		if (err < 0) {
+			pr_err("failed to deactivate chunk #%u: %d\n", index,
+			       err);
+			goto activate;
+		} else {
+			clear_bit(index, vpr->active);
+			set_bit(index, dirty);
+		}
+	}
+
+	if (!bitmap_empty(dirty, vpr->num_chunks)) {
+		err = tegra_vpr_resize(vpr);
+		if (err < 0) {
+			pr_err("failed to shrink VPR: %d\n", err);
+			goto activate;
+		}
+	}
+
+	/* release the CMA memory associated with deactivated chunks */
+	for_each_set_bit(i, dirty, vpr->num_chunks)
+		tegra_vpr_chunk_release(&vpr->chunks[i]);
+
+	return;
+
+activate:
+	for_each_set_bit(i, dirty, vpr->num_chunks) {
+		err = tegra_vpr_chunk_activate(&vpr->chunks[i]);
+		if (WARN_ON(err < 0))
+			pr_err("failed to activate chunk #%u: %d\n", i, err);
+
+		/*
+		 * This may not be fully activated at this point, but we need
+		 * to keep track of it anyway to make sure the CMA region can
+		 * eventually be released. The WARN_ON above tells us when it
+		 * happens: here be dragons.
+		 */
+		set_bit(i, vpr->active);
+	}
+}
+
+static void tegra_vpr_release(struct dma_buf *buf)
+{
+	struct tegra_vpr_buffer *buffer = buf->priv;
+	struct tegra_vpr *vpr = buffer->vpr;
+
+	mutex_lock(&vpr->lock);
+
+	tegra_vpr_buffer_release(buffer);
+	tegra_vpr_recycle(vpr);
+
+	mutex_unlock(&vpr->lock);
+}
+
+/*
+ * Prohibit userspace mapping because the CPU cannot access this memory
+ * anyway.
+ */
+static int tegra_vpr_begin_cpu_access(struct dma_buf *buf,
+				      enum dma_data_direction direction)
+{
+	return -EPERM;
+}
+
+static int tegra_vpr_end_cpu_access(struct dma_buf *buf,
+				    enum dma_data_direction direction)
+{
+	return -EPERM;
+}
+
+static int tegra_vpr_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
+{
+	return -EPERM;
+}
+
+static const struct dma_buf_ops tegra_vpr_buf_ops = {
+	.attach = tegra_vpr_attach,
+	.detach = tegra_vpr_detach,
+	.map_dma_buf = tegra_vpr_map_dma_buf,
+	.unmap_dma_buf = tegra_vpr_unmap_dma_buf,
+	.release = tegra_vpr_release,
+	.begin_cpu_access = tegra_vpr_begin_cpu_access,
+	.end_cpu_access = tegra_vpr_end_cpu_access,
+	.mmap = tegra_vpr_mmap,
+};
+
+static struct dma_buf *tegra_vpr_allocate(struct dma_heap *heap,
+					  unsigned long len, u32 fd_flags,
+					  u64 heap_flags)
+{
+	struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+	struct tegra_vpr_buffer *buffer, *entry;
+	size_t size = ALIGN(len, vpr->align);
+	DEFINE_DMA_BUF_EXPORT_INFO(export);
+	struct dma_buf *buf;
+
+	mutex_lock(&vpr->lock);
+
+	buffer = tegra_vpr_buffer_allocate(vpr, size);
+	if (IS_ERR(buffer)) {
+		mutex_unlock(&vpr->lock);
+		return ERR_CAST(buffer);
+	}
+
+	/* insert in the correct order */
+	if (!list_empty(&vpr->buffers)) {
+		list_for_each_entry(entry, &vpr->buffers, list) {
+			if (buffer->pageno < entry->pageno) {
+				list_add_tail(&buffer->list, &entry->list);
+				break;
+			}
+		}
+	}
+
+	if (list_empty(&buffer->list))
+		list_add_tail(&buffer->list, &vpr->buffers);
+
+	buffer->vpr = vpr;
+
+	/*
+	 * If a valid buffer was allocated, wrap it in a dma_buf
+	 * and return it.
+	 */
+	export.exp_name = dma_heap_get_name(heap);
+	export.ops = &tegra_vpr_buf_ops;
+	export.size = buffer->size;
+	export.flags = fd_flags;
+	export.priv = buffer;
+
+	buf = dma_buf_export(&export);
+	if (IS_ERR(buf)) {
+		tegra_vpr_buffer_release(buffer);
+		tegra_vpr_recycle(vpr);
+	}
+
+	mutex_unlock(&vpr->lock);
+	return buf;
+}
+
+static void tegra_vpr_debugfs_show_buffers(struct tegra_vpr *vpr,
+					   struct seq_file *s)
+{
+	struct tegra_vpr_buffer *buffer;
+	char buf[16];
+
+	mutex_lock(&vpr->lock);
+
+	list_for_each_entry(buffer, &vpr->buffers, list) {
+		string_get_size(buffer->size, 1, STRING_UNITS_2, buf,
+				sizeof(buf));
+		seq_printf(s, "  %pap-%pap (%s)\n", &buffer->start,
+			   &buffer->limit, buf);
+	}
+
+	mutex_unlock(&vpr->lock);
+}
+
+static void tegra_vpr_debugfs_show_chunks(struct tegra_vpr *vpr,
+					  struct seq_file *s)
+{
+	struct tegra_vpr_buffer *buffer;
+	unsigned int i;
+	char buf[16];
+
+	for (i = 0; i < vpr->num_chunks; i++) {
+		const struct tegra_vpr_chunk *chunk = &vpr->chunks[i];
+
+		string_get_size(chunk->size, 1, STRING_UNITS_2, buf,
+				sizeof(buf));
+		seq_printf(s, "  %pap-%pap (%s) (%s, %u buffers)\n",
+			   &chunk->start, &chunk->limit, buf,
+			   chunk->active ? "active" : "inactive",
+			   chunk->num_buffers);
+	}
+
+	list_for_each_entry(buffer, &vpr->buffers, list) {
+		string_get_size(buffer->size, 1, STRING_UNITS_2, buf,
+				sizeof(buf));
+		seq_printf(s, "%pap-%pap (%s, chunks: %*pbl)\n",
+			   &buffer->start, &buffer->limit, buf,
+			   vpr->num_chunks, buffer->chunks);
+	}
+}
+
+static int tegra_vpr_debugfs_show(struct seq_file *s, struct dma_heap *heap)
+{
+	struct tegra_vpr *vpr = dma_heap_get_drvdata(heap);
+	phys_addr_t limit = vpr->base + vpr->size;
+	char buf[16];
+
+	string_get_size(vpr->size, 1, STRING_UNITS_2, buf, sizeof(buf));
+	seq_printf(s, "%pap-%pap (%s)\n", &vpr->base, &limit, buf);
+
+	if (!vpr->resizable)
+		tegra_vpr_debugfs_show_buffers(vpr, s);
+	else
+		tegra_vpr_debugfs_show_chunks(vpr, s);
+
+	return 0;
+}
+
+static const struct dma_heap_ops tegra_vpr_heap_ops = {
+	.allocate = tegra_vpr_allocate,
+	.show = tegra_vpr_debugfs_show,
+};
+
+static int tegra_vpr_setup_chunks(struct tegra_vpr *vpr, const char *name)
+{
+	phys_addr_t start, limit;
+	unsigned int order, i = 0;
+	size_t max_size;
+	int err;
+
+	/* Memory is backed by struct page, so track the first one. */
+	vpr->start_page = phys_to_page(vpr->base);
+
+	/* This seems a reasonable value, so hard-code it for now. */
+	vpr->num_chunks = 4;
+
+	vpr->chunks = kzalloc_objs(*vpr->chunks, vpr->num_chunks);
+	if (!vpr->chunks)
+		return -ENOMEM;
+
+	vpr->active = bitmap_zalloc(vpr->num_chunks, GFP_KERNEL);
+	if (!vpr->active) {
+		err = -ENOMEM;
+		goto free;
+	}
+
+	max_size = PAGE_SIZE << (get_order(vpr->size) - ilog2(vpr->num_chunks));
+	order = get_order(vpr->align);
+
+	/*
+	 * Allocate CMA areas for VPR. All areas will be roughtly the same
+	 * size, with the last area taking up the rest.
+	 */
+	start = vpr->base;
+	limit = vpr->base + vpr->size;
+
+	pr_debug("VPR: %pap-%pap (%lu pages, %u chunks, %lu MiB)\n", &start,
+		 &limit, vpr->num_pages, vpr->num_chunks,
+		 (unsigned long)vpr->size / 1024 / 1024);
+
+	for (i = 0; i < vpr->num_chunks; i++) {
+		size_t size = limit - start;
+		phys_addr_t end;
+
+		size = min_t(size_t, size, max_size);
+		end = start + size - 1;
+
+		err = tegra_vpr_chunk_init(vpr, &vpr->chunks[i], start, size,
+					   order, name);
+		if (err < 0) {
+			pr_err("failed to create VPR chunk: %d\n", err);
+			goto free;
+		}
+
+		pr_debug("  %2u: %pap-%pap (%lu MiB)\n", i, &start, &end,
+			 size / 1024 / 1024);
+		start += size;
+	}
+
+	vpr->first = ~0U;
+	vpr->last = 0U;
+
+	return 0;
+
+free:
+	while (i--)
+		tegra_vpr_chunk_free(&vpr->chunks[i]);
+
+	kfree(vpr->active);
+	kfree(vpr->chunks);
+	return err;
+}
+
+static void tegra_vpr_free_chunks(struct tegra_vpr *vpr)
+{
+	unsigned int i;
+
+	for (i = 0; i < vpr->num_chunks; i++)
+		tegra_vpr_chunk_free(&vpr->chunks[i]);
+
+	kfree(vpr->chunks);
+}
+
+static int tegra_vpr_setup_static(struct tegra_vpr *vpr)
+{
+	phys_addr_t start, limit;
+
+	start = vpr->base;
+	limit = vpr->base + vpr->size;
+
+	pr_debug("VPR: %pap-%pap (%lu pages, %lu MiB)\n", &start, &limit,
+		 vpr->num_pages, (unsigned long)vpr->size / 1024 / 1024);
+
+	return 0;
+}
+
+static int tegra_vpr_add_heap(struct reserved_mem *rmem,
+			      struct device_node *np)
+{
+	struct dma_heap_export_info info = {};
+	unsigned long first, last;
+	struct dma_heap *heap;
+	struct tegra_vpr *vpr;
+	int err;
+
+	vpr = kzalloc_obj(*vpr, GFP_KERNEL);
+	if (!vpr)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&vpr->list);
+	INIT_LIST_HEAD(&vpr->buffers);
+	INIT_LIST_HEAD(&vpr->devices);
+	mutex_init(&vpr->lock);
+
+	vpr->resizable = !of_property_read_bool(np, "no-map");
+	vpr->dev_node = of_node_get(np);
+	vpr->align = PAGE_SIZE;
+	vpr->base = rmem->base;
+	vpr->size = rmem->size;
+	vpr->num_pages = vpr->size >> PAGE_SHIFT;
+	vpr->nid = of_node_to_nid(np);
+	vpr->cma = rmem->priv;
+
+	vpr->bitmap = bitmap_zalloc(vpr->num_pages, GFP_KERNEL);
+	if (!vpr->bitmap) {
+		err = -ENOMEM;
+		goto free;
+	}
+
+	first = find_first_bit(vpr->bitmap, vpr->num_pages);
+	last = find_last_bit(vpr->bitmap, vpr->num_pages);
+
+	if (vpr->resizable)
+		err = tegra_vpr_setup_chunks(vpr, rmem->name);
+	else
+		err = tegra_vpr_setup_static(vpr);
+
+	if (err < 0)
+		goto free;
+
+	info.name = vpr->dev_node->name;
+	info.ops = &tegra_vpr_heap_ops;
+	info.priv = vpr;
+
+	heap = dma_heap_add(&info);
+	if (IS_ERR(heap)) {
+		err = PTR_ERR(heap);
+		goto cleanup;
+	}
+
+	mutex_lock(&vpr_lock);
+	list_add_tail(&vpr->list, &vpr_list);
+	mutex_unlock(&vpr_lock);
+
+	return 0;
+
+cleanup:
+	if (vpr->resizable)
+		tegra_vpr_free_chunks(vpr);
+free:
+	bitmap_free(vpr->bitmap);
+	kfree(vpr);
+	return err;
+}
+
+static int tegra_vpr_init(void)
+{
+	const char *compatible = "nvidia,tegra-video-protection-region";
+	struct device_node *parent;
+	struct reserved_mem *rmem;
+	int err;
+
+	parent = of_find_node_by_path("/reserved-memory");
+	if (!parent)
+		return 0;
+
+	for_each_child_of_node_scoped(parent, child) {
+		if (!of_device_is_compatible(child, compatible))
+			continue;
+
+		rmem = of_reserved_mem_lookup(child);
+		if (!rmem)
+			continue;
+
+		err = tegra_vpr_add_heap(rmem, child);
+		if (err < 0)
+			pr_err("failed to add VPR heap for %pOF: %d\n", child,
+			       err);
+
+		/* only a single VPR heap is supported */
+		break;
+	}
+
+	of_node_put(parent);
+	return 0;
+}
+module_init(tegra_vpr_init);
+
+static int __init tegra_vpr_node_init(unsigned long offset,
+				      struct reserved_mem *rmem)
+{
+	struct cma *cma;
+	int err;
+
+	if (!IS_ALIGNED(rmem->base, SZ_1M)) {
+		pr_err("%s: base is not aligned to 1 MiB\n", rmem->name);
+		return -EINVAL;
+	}
+
+	if (!IS_ALIGNED(rmem->size, SZ_1M)) {
+		pr_err("%s: size is not aligned to 1 MiB\n", rmem->name);
+		return -EINVAL;
+	}
+
+	err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name,
+				    &cma);
+	if (err < 0) {
+		pr_err("%s: failed to initialize CMA: %d\n", __func__, err);
+		return err;
+	}
+
+	rmem->priv = cma;
+
+	return 0;
+}
+
+static struct tegra_vpr *tegra_vpr_lookup(struct cma *cma)
+{
+	struct tegra_vpr *vpr;
+
+	mutex_lock(&vpr_lock);
+
+	list_for_each_entry(vpr, &vpr_list, list) {
+		if (vpr->cma == cma) {
+			mutex_unlock(&vpr_lock);
+			return vpr;
+		}
+	}
+
+	mutex_unlock(&vpr_lock);
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int tegra_vpr_device_init(struct reserved_mem *rmem, struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver->pm;
+	struct tegra_vpr_device *node;
+	struct cma *cma = rmem->priv;
+	struct tegra_vpr *vpr;
+
+	vpr = tegra_vpr_lookup(cma);
+	if (IS_ERR(vpr))
+		return PTR_ERR(vpr);
+
+	if (!pm || !pm->freeze || !pm->thaw)
+		return -EINVAL;
+
+	node = kzalloc_obj(*node, GFP_KERNEL);
+	if (!node)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&node->node);
+	node->dev = dev;
+
+	mutex_lock(&vpr->lock);
+	list_add_tail(&node->node, &vpr->devices);
+	mutex_unlock(&vpr->lock);
+
+	return 0;
+}
+
+static void tegra_vpr_device_release(struct reserved_mem *rmem,
+				     struct device *dev)
+{
+	struct tegra_vpr_device *node, *tmp;
+	struct cma *cma = rmem->priv;
+	struct tegra_vpr *vpr;
+
+	vpr = tegra_vpr_lookup(cma);
+	if (IS_ERR(vpr)) {
+		dev_WARN(dev, "failed to find VPR for CMA '%s'\n",
+			 cma_get_name(cma));
+		return;
+	}
+
+	mutex_lock(&vpr->lock);
+
+	list_for_each_entry_safe(node, tmp, &vpr->devices, node) {
+		if (node->dev == dev) {
+			list_del(&node->node);
+			kfree(node);
+		}
+	}
+
+	mutex_unlock(&vpr->lock);
+}
+
+static const struct reserved_mem_ops tegra_vpr_rmem_ops = {
+	.node_init = tegra_vpr_node_init,
+	.device_init = tegra_vpr_device_init,
+	.device_release = tegra_vpr_device_release,
+};
+
+RESERVEDMEM_OF_DECLARE(tegra_vpr, "nvidia,tegra-video-protection-region",
+		       &tegra_vpr_rmem_ops);
+
+MODULE_DESCRIPTION("NVIDIA Tegra Video-Protection-Region DMA-BUF heap driver");
+MODULE_LICENSE("GPL");
diff --git a/include/trace/events/tegra_vpr.h b/include/trace/events/tegra_vpr.h
new file mode 100644
index 000000000000..f8ceb17679fe
--- /dev/null
+++ b/include/trace/events/tegra_vpr.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if !defined(_TRACE_TEGRA_VPR_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_TEGRA_VPR_H
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tegra_vpr
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(tegra_vpr_chunk_activate,
+	TP_PROTO(phys_addr_t start, phys_addr_t limit),
+	TP_ARGS(start, limit),
+	TP_STRUCT__entry(
+		__field(phys_addr_t, start)
+		__field(phys_addr_t, limit)
+	),
+	TP_fast_assign(
+		__entry->start = start;
+		__entry->limit = limit;
+	),
+	TP_printk("%pap-%pap", &__entry->start,
+		  &__entry->limit)
+);
+
+TRACE_EVENT(tegra_vpr_chunk_deactivate,
+	TP_PROTO(phys_addr_t start, phys_addr_t limit),
+	TP_ARGS(start, limit),
+	TP_STRUCT__entry(
+		__field(phys_addr_t, start)
+		__field(phys_addr_t, limit)
+	),
+	TP_fast_assign(
+		__entry->start = start;
+		__entry->limit = limit;
+	),
+	TP_printk("%pap-%pap", &__entry->start,
+		  &__entry->limit)
+);
+
+TRACE_EVENT(tegra_vpr_set,
+	TP_PROTO(phys_addr_t base, phys_addr_t size),
+	TP_ARGS(base, size),
+	TP_STRUCT__entry(
+		__field(phys_addr_t, start)
+		__field(phys_addr_t, limit)
+	),
+	TP_fast_assign(
+		__entry->start = base;
+		__entry->limit = base + size;
+	),
+	TP_printk("%pap-%pap", &__entry->start, &__entry->limit)
+);
+
+#endif /* _TRACE_TEGRA_VPR_H */
+
+#include <trace/define_trace.h>

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (6 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:38   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x Thierry Reding
  2026-08-14 15:29 ` [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264 Thierry Reding
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

This node contains two sets of properties, one for the case where the
VPR is resizable (in which case the VPR region will be dynamically
allocated at boot time) and another case where the VPR is fixed in size
and initialized by early firmware.

The firmware running on the device is responsible for updating the node
with the real physical address for the fixed VPR case and remove the
properties needed only for resizable VPR. Similarly, if the VPR is
resizable, the firmware should remove the "reg" property since it is no
longer needed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- comment out fixed VPR properties, assume resizable by default
- rename node to "protected"
---
 arch/arm64/boot/dts/nvidia/tegra234.dtsi | 39 ++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 5e29316a4d75..6c4739efc8a5 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -29,6 +29,45 @@ aliases {
 		i2c8 = &dp_aux_ch3_i2c;
 	};
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		vpr: protected {
+			compatible = "nvidia,tegra-video-protection-region";
+			status = "disabled";
+
+			/*
+			 * Two variants exist for this. For fixed VPR, the
+			 * firmware is supposed to update the "reg" property
+			 * with the fixed memory region configured as VPR.
+			 *
+			 * For resizable VPR we don't care about the exact
+			 * address and instead want a reserved region to be
+			 * allocated with a certain size and alignment at
+			 * boot time.
+			 *
+			 * The below assumes resizable VPR by default. If the
+			 * firmwares sets up fixed VPR, it is responsible for
+			 * adding the missing "reg" property, removing any of
+			 * the unused properties, as well as adding a unit-
+			 * address matching the "reg" property.
+			 */
+
+			/* fixed VPR */
+			/*
+			reg = <0x0 0x0 0x0 0x0>;
+			no-map;
+			*/
+
+			/* resizable VPR */
+			size = <0x0 0x70000000>;
+			alignment = <0x0 0x100000>;
+			reusable;
+		};
+	};
+
 	bus@0 {
 		compatible = "simple-bus";
 

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (7 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234 Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:44   ` sashiko-bot
  2026-08-14 15:29 ` [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264 Thierry Reding
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

The host1x needs access to the VPR region, so make sure to reference it
via the memory-region property.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra234.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
index 6c4739efc8a5..c6a5ced069e7 100644
--- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
@@ -4479,6 +4479,9 @@ vic@15340000 {
 				interconnect-names = "dma-mem", "write";
 				iommus = <&smmu_niso1 TEGRA234_SID_VIC>;
 				dma-coherent;
+
+				memory-region = <&vpr>;
+				memory-region-names = "protected";
 			};
 
 			nvdec@15480000 {
@@ -4497,6 +4500,9 @@ nvdec@15480000 {
 				iommus = <&smmu_niso1 TEGRA234_SID_NVDEC>;
 				dma-coherent;
 
+				memory-region = <&vpr>;
+				memory-region-names = "protected";
+
 				nvidia,memory-controller = <&mc>;
 
 				/*

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264
  2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
                   ` (8 preceding siblings ...)
  2026-08-14 15:29 ` [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x Thierry Reding
@ 2026-08-14 15:29 ` Thierry Reding
  2026-08-14 15:41   ` sashiko-bot
  9 siblings, 1 reply; 21+ messages in thread
From: Thierry Reding @ 2026-08-14 15:29 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, David Airlie, Simona Vetter, Maarten Lankhorst,
	Maxime Ripard, Thomas Zimmermann, Sowjanya Komatineni,
	Luca Ceresoli, Mikko Perttunen, Yury Norov, Rasmus Villemoes,
	Russell King, Alexander Gordeev, Gerald Schaefer, Heiko Carstens,
	Vasily Gorbik, Christian Borntraeger, Sven Schnelle,
	Andrew Morton, David Hildenbrand, Lorenzo Stoakes,
	Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
	Suren Baghdasaryan, Michal Hocko, Marek Szyprowski, Robin Murphy,
	Sumit Semwal, Benjamin Gaignard, Brian Starkey, John Stultz,
	T.J. Mercier, Christian König, Steven Rostedt,
	Masami Hiramatsu, Mathieu Desnoyers, Catalin Marinas, Will Deacon,
	Chun Ng
  Cc: Thierry Reding, devicetree, linux-tegra, linux-kernel, dri-devel,
	linux-media, linux-arm-kernel, linux-s390, linux-mm, iommu,
	linaro-mm-sig, linux-trace-kernel, Thierry Reding

From: Thierry Reding <treding@nvidia.com>

This node contains two sets of properties, one for the case where the
VPR is resizable (in which case the VPR region will be dynamically
allocated at boot time) and another case where the VPR is fixed in size
and initialized by early firmware.

The firmware running on the device is responsible for updating the node
with the real physical address for the fixed VPR case and remove the
properties needed only for resizable VPR. Similarly, if the VPR is
resizable, the firmware should remove the "reg" property since it is no
longer needed.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- comment out fixed VPR properties, assume resizable by default
- rename node to "protected"
---
 arch/arm64/boot/dts/nvidia/tegra264.dtsi | 33 ++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
index 31bd29df8e46..b3a8ea64aa37 100644
--- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
@@ -24,6 +24,39 @@ shmem_bpmp: shmem@86070000 {
 			reg = <0x0 0x86070000 0x0 0x2000>;
 			no-map;
 		};
+
+		vpr: protected {
+			compatible = "nvidia,tegra-video-protection-region";
+			status = "disabled";
+
+			/*
+			 * Two variants exist for this. For fixed VPR, the
+			 * firmware is supposed to update the "reg" property
+			 * with the fixed memory region configured as VPR.
+			 *
+			 * For resizable VPR we don't care about the exact
+			 * address and instead want a reserved region to be
+			 * allocated with a certain size and alignment at
+			 * boot time.
+			 *
+			 * The below assumes resizable VPR by default. If the
+			 * firmwares sets up fixed VPR, it is responsible for
+			 * adding the missing "reg" property, removing any of
+			 * the unused properties, as well as adding a unit-
+			 * address matching the "reg" property.
+			 */
+
+			/* fixed VPR */
+			/*
+			reg = <0x0 0x0 0x0 0x0>;
+			no-map;
+			*/
+
+			/* resizable VPR */
+			size = <0x0 0x70000000>;
+			alignment = <0x0 0x100000>;
+			reusable;
+		};
 	};
 
 	/* SYSTEM MMIO */

-- 
2.55.0


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions
  2026-08-14 15:29 ` [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions Thierry Reding
@ 2026-08-14 15:36   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:36 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel, linux-trace-kernel, conor+dt, Heiko Carstens,
	Alexander Gordeev, devicetree, linux-s390, media-ci, robh,
	Christian Borntraeger, Vasily Gorbik

> From: Thierry Reding <treding@nvidia.com>
> 
> Add the memory-region and memory-region-names properties to the bindings
> for the display controllers and the host1x engine found on various Tegra
> generations. These memory regions are used to access firmware-provided
> framebuffer memory as well as the video protection region.
> 
> Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=2


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 04/10] bitmap: Add bitmap_allocate() function
  2026-08-14 15:29 ` [PATCH v5 04/10] bitmap: Add bitmap_allocate() function Thierry Reding
@ 2026-08-14 15:36   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:36 UTC (permalink / raw)
  To: Thierry Reding
  Cc: media-ci, linux-trace-kernel, Heiko Carstens, Vasily Gorbik,
	devicetree, Alexander Gordeev, dri-devel, linux-s390, conor+dt,
	robh, Christian Borntraeger

> From: Thierry Reding <treding@nvidia.com>
> 
> This is similar to bitmap_allocate_region() but allows allocation of
> non-power of two pages/bits.
> 
> While at it, reimplement bitmap_allocate_region() in terms of this new
> helper to remove a sliver of code duplication.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=4


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234
  2026-08-14 15:29 ` [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234 Thierry Reding
@ 2026-08-14 15:38   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:38 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Christian Borntraeger, media-ci, linux-s390, Alexander Gordeev,
	conor+dt, robh, linux-trace-kernel, devicetree, Heiko Carstens,
	dri-devel, Vasily Gorbik

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The DT template comments out the `no-map` property to satisfy schema validation for resizable VPRs, but fails to instruct the firmware to add it back for fixed VPRs. Without `no-map`, the kernel will map the hardware-protected Video Protection Region into the CPU linear map, leading to fatal SError panics from speculative execution.
- [Medium] Mismatch between the commit message instructions and the DT template regarding the `reg` property. The commit message instructs firmware to remove `reg` for resizable VPRs, but it has already been commented out in the code.
--

--- Patch [8]: [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234 ---
commit 19202d4b4a42362e9866fdb06d4d5ebfaf462002
Author: Thierry Reding <treding@nvidia.com>

arm64: tegra: Add VPR placeholder node on Tegra234

This commit adds a placeholder device tree node for the Video Protection Region
(VPR) on Tegra234. It provides properties for both fixed and resizable VPR
configurations, expecting early firmware to finalize the node.

> Similarly, if the VPR is
> resizable, the firmware should remove the "reg" property since it is no
> longer needed.

[Severity: Medium]
Is this commit message instruction accurate for the final version of the patch?

The DT template in arch/arm64/boot/dts/nvidia/tegra234.dtsi comments out the
`reg` property by default. If early firmware strictly follows this commit
message and attempts to delete the `reg` property (e.g., using fdt_delprop()),
could it fail and potentially abort boot if it doesn't handle the missing
property gracefully?

> diff --git a/arch/arm64/boot/dts/nvidia/tegra234.dtsi b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
> index 8e0c51e496e20..52ff11873580d 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra234.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra234.dtsi
> @@ -29,6 +29,45 @@ aliases {

[ ... ]

> +			/*
> +			 * The below assumes resizable VPR by default. If the
> +			 * firmwares sets up fixed VPR, it is responsible for
> +			 * adding the missing "reg" property, removing any of
> +			 * the unused properties, as well as adding a unit-
> +			 * address matching the "reg" property.
> +			 */
> +
> +			/* fixed VPR */
> +			/*
> +			reg = <0x0 0x0 0x0 0x0>;
> +			no-map;
> +			*/

[Severity: High]
Does the firmware also need to be instructed to add the `no-map` property back
for fixed VPR configurations?

Since `no-map` is completely commented out and omitted from the instructional
comment in the vpr node, firmware might not add it. Without `no-map`, the
kernel's of_reserved_mem_init() will use memblock_reserve() instead of
memblock_mark_nomap().

Could this cause a regression where the kernel maps the hardware-protected Video
Protection Region into the CPU linear map, leading to a fatal SError panic from
speculative execution?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=8

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API
  2026-08-14 15:29 ` [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API Thierry Reding
@ 2026-08-14 15:38   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:38 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Christian Borntraeger, robh, dri-devel, devicetree, Vasily Gorbik,
	Heiko Carstens, conor+dt, media-ci, linux-trace-kernel,
	Alexander Gordeev, linux-s390

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Missing bounds check on `offset` and `count` in `cma_range_alloc_at()` allows out-of-bounds heap read/write.
- [High] Failure to scale offsets and counts by `cma->order_per_bit` causes state corruption and memory leaks.
- [High] `__cma_alloc_at_frozen()` illogically applies the exact same relative `offset` to every disjoint range.
- [Medium] Misleading parameter naming (`pfn` vs `offset`) in `cma_alloc_at()` leads to dangerous misinterpretations.
--

--- Patch [5]: [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API ---
commit 86953b3fddeca8ceb93fb736c755a0bfda094530
Author: Thierry Reding <treding@nvidia.com>

mm/cma: Introduce cma_alloc_at() API
    
This API can be used to allocate a number of CMA pages starting at a
fixed offset. This is useful, for example, if the CMA area is used as
backing storage for a nested allocator that has stricter requirements
than CMA itself.

> diff --git a/include/trace/events/cma.h b/include/trace/events/cma.h
> index 37195edf24986..00b622a9da97b 100644
> --- a/include/trace/events/cma.h
> +++ b/include/trace/events/cma.h
[ ... ]
> @@ -132,6 +132,69 @@ TRACE_EVENT(cma_alloc_busy_retry,
>  		  __entry->align)
>  );
>  
> +TRACE_EVENT(cma_alloc_at_start,
> +
> +	TP_PROTO(const char *name, unsigned long pfn,

[Severity: Medium]
Could this parameter name cause confusion? The function signature in cma.h uses
offset, but this tracepoint logs it as pfn. If a caller mistakenly passes an
absolute physical frame number instead of a relative offset, could this lead
to an incorrect allocation address computation later in cma_range_alloc_at()?

> +		 unsigned long request_count, unsigned long available_count,
> +		 unsigned long total_count),
[ ... ]
> diff --git a/mm/cma.c b/mm/cma.c
> index a13ce4999b39f..4bc6eef8b6060 100644
> --- a/mm/cma.c
> +++ b/mm/cma.c
[ ... ]
> +static int cma_range_alloc_at(struct cma *cma, struct cma_memrange *cmr,
> +			      unsigned long offset, unsigned long count,
> +			      struct page **pagep, gfp_t gfp)
> +{
> +	struct page *page = NULL;
> +	unsigned long pfn;
> +	int ret = -EBUSY;
> +
> +	spin_lock_irq(&cma->lock);
> +
> +	/*
> +	 * If the request is larger than the available number of pages, stop
> +	 * right away.
> +	 */
> +	if (count > cma->available_count)
> +		goto unlock;
> +
> +	ret = bitmap_allocate(cmr->bitmap, offset, count);

[Severity: Critical]
Does this code allow an out-of-bounds read or write on the bitmap? It does not
appear to check if offset + count is within cma_bitmap_maxno(cma, cmr).
Because bitmap_allocate() scans and sets bits without knowing the allocation
bounds, a large offset + count could overrun the bitmap.

[Severity: High]
Does this code treat page counts as raw bit indices? The offset and count
parameters represent pages, but are passed directly to bitmap_allocate()
without scaling by cma->order_per_bit.

> +	if (ret < 0)
> +		goto unlock;
> +
> +	pfn = cmr->base_pfn + offset;

[Severity: High]
If offset represents raw pages, shouldn't it be consistent with how the
bitmap was updated above? If cma->order_per_bit is non-zero, this treats
offset as pages while the bitmap logic treated it as bits.

> +	page = pfn_to_page(pfn);
[ ... ]
> +free:
> +	/* we need to reacquire the lock to clean up the internal state */
> +	spin_lock_irq(&cma->lock);
> +	cma->available_count += count;
> +clear:
> +	bitmap_clear(cmr->bitmap, offset, count);

[Severity: High]
Does this similarly fail to convert the page offset and count to bit indices?
If cma_release() later frees these pages, it will convert the PFNs to bits,
potentially clearing different bits than were set and permanently leaking the
original allocation.

> +unlock:
> +	spin_unlock_irq(&cma->lock);
> +	return ret;
> +}
> +
> +static struct page *__cma_alloc_at_frozen(struct cma *cma, unsigned long offset,
> +					  unsigned long count, gfp_t gfp)
> +{
[ ... ]
> +	for (r = 0; r < cma->nranges; r++) {
> +		page = NULL;
> +
> +		ret = cma_range_alloc_at(cma, &cma->ranges[r], offset, count,
> +					 &page, gfp);

[Severity: High]
Does this apply the same unadjusted offset to every physical memrange? If the
allocation falls back to a subsequent range, it will attempt the allocation at
ranges[r].base_pfn + offset. If offset is meant to be a logical index across
the contiguous CMA area, it seems it should be decremented by the size of
preceding ranges as the loop iterates.

> +		if (ret != -EBUSY || page)
> +			break;
> +	}
[ ... ]
> +struct page *cma_alloc_at(struct cma *cma, unsigned long pfn,
> +			  unsigned long count, bool no_warn)

[Severity: Medium]
Is pfn the correct name for this parameter? It appears to be used as a
relative offset in cma_range_alloc_at(). Passing an absolute physical frame
number could result in computing cmr->base_pfn + pfn, producing an incorrect
physical address.

> +{
> +	struct page *page;
> +
> +	page = cma_alloc_at_frozen(cma, pfn, count, no_warn);
> +	if (page)
> +		set_pages_refcounted(page, count);
> +
> +	return page;
> +}
> +EXPORT_SYMBOL_GPL(cma_alloc_at);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=5

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC
  2026-08-14 15:29 ` [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC Thierry Reding
@ 2026-08-14 15:39   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel, Alexander Gordeev, conor+dt, media-ci, linux-s390,
	devicetree, robh, linux-trace-kernel, Heiko Carstens,
	Christian Borntraeger, Vasily Gorbik

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Missing `memory-region` and `memory-region-names` properties in `nvidia,tegra210-nvdec.yaml`
- [Low] Missing word in the commit message sentence describing NVDEC behavior
--

--- Patch [3]: [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC ---
commit d7576d345a96bd859ab402d685153853ecaa3898
Author: Thierry Reding <treding@nvidia.com>

dt-bindings: gpu: host1x: Document memory-regions for NVDEC

This commit documents the memory-region and memory-region-names properties
for NVDEC in the nvidia,tegra234-nvdec.yaml schema. It specifies a reference
to the video protection memory region used for secure video playback.

> The video protection region is a reserved memory region that can be used
> for secure video playback. NVDEC can access this region to decode images
> into securely.

[Severity: Low]
This isn't a bug, but there seems to be a missing word in the commit message.
Should this be "decode images into it securely"?

> diff --git a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
> index 0b7561c8b9bb2..72313f776dbd8 100644
> --- a/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
> +++ b/Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra234-nvdec.yaml
> @@ -60,6 +60,14 @@ properties:
>        - const: dma-mem
>        - const: write
>  
> +  memory-region:
> +    items:
> +      - description: reference to the video protection memory region
> +
> +  memory-region-names:
> +    items:
> +      - const: protected
> +

[Severity: Medium]
Will this same feature be used on older hardware variants?

The Video Protection Region (VPR) appears to be available on older Tegra
devices as well. Since the schema located at
Documentation/devicetree/bindings/gpu/host1x/nvidia,tegra210-nvdec.yaml
uses additionalProperties: false, attempting to configure the VPR on older
NVDEC nodes will fail schema validation.

Should memory-region and memory-region-names be added to
nvidia,tegra210-nvdec.yaml too?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=3

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 01/10] dt-bindings: reserved-memory: Document Tegra VPR
  2026-08-14 15:29 ` [PATCH v5 01/10] dt-bindings: reserved-memory: Document " Thierry Reding
@ 2026-08-14 15:39   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: conor+dt, dri-devel, linux-s390, robh, linux-trace-kernel,
	Alexander Gordeev, devicetree, Heiko Carstens, Vasily Gorbik,
	Christian Borntraeger, media-ci

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The examples use 64-bit values for `size` and `alignment` but are missing a parent `reserved-memory` node to define `#size-cells = <2>`.
--

--- Patch [1]: [PATCH v5 01/10] dt-bindings: reserved-memory: Document Tegra VPR ---
commit f3aef7e4bf1dca49011b483a566145d2ddfdd64a
Author: Thierry Reding <treding@nvidia.com>

dt-bindings: reserved-memory: Document Tegra VPR

This commit documents the device tree bindings for the NVIDIA Tegra Video
Protection Region (VPR). It defines a fixed VPR using the "reg" property
and a resizable VPR using "size" and "alignment".

> diff --git a/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml
> new file mode 100644
> index 0000000000000..862bfd3913786
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/reserved-memory/nvidia,tegra-video-protection-region.yaml

[ ... ]

> +examples:
> +  - |
> +    /* resizable VPR */
> +    protected {
> +      compatible = "nvidia,tegra-video-protection-region";
> +
> +      size = <0x0 0x70000000>;
> +      alignment = <0x0 0x100000>;

[Severity: Low]
Will this cause dt_binding_check to fail?

The 64-bit values for size and alignment contain 2 cells each. This violates
the core reserved-memory.yaml schema when parsed with the default root
node context, which sets #size-cells = <1>.

Could the examples be wrapped in a reserved-memory parent node that
explicitly sets #address-cells = <2> and #size-cells = <2>?

> +      reusable;
> +    };

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=1

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264
  2026-08-14 15:29 ` [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264 Thierry Reding
@ 2026-08-14 15:41   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:41 UTC (permalink / raw)
  To: Thierry Reding
  Cc: conor+dt, devicetree, linux-s390, Christian Borntraeger,
	linux-trace-kernel, Heiko Carstens, robh, Alexander Gordeev,
	media-ci, dri-devel, Vasily Gorbik

> From: Thierry Reding <treding@nvidia.com>
> 
> This node contains two sets of properties, one for the case where the
> VPR is resizable (in which case the VPR region will be dynamically
> allocated at boot time) and another case where the VPR is fixed in size
> and initialized by early firmware.
> 
> The firmware running on the device is responsible for updating the node
> with the real physical address for the fixed VPR case and remove the
> properties needed only for resizable VPR. Similarly, if the VPR is
> resizable, the firmware should remove the "reg" property since it is no
> longer needed.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=10


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 06/10] dma-buf: heaps: Add debugfs support
  2026-08-14 15:29 ` [PATCH v5 06/10] dma-buf: heaps: Add debugfs support Thierry Reding
@ 2026-08-14 15:43   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:43 UTC (permalink / raw)
  To: Thierry Reding
  Cc: devicetree, dri-devel, Vasily Gorbik, robh, linux-s390,
	Alexander Gordeev, linux-trace-kernel, media-ci,
	Christian Borntraeger, conor+dt, Heiko Carstens

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] A race condition in `dma_heap_add()` allows userspace to trigger an exploitable use-after-free by opening the character device before the initialization completes.
--

--- Patch [6]: [PATCH v5 06/10] dma-buf: heaps: Add debugfs support ---
commit f236972ccf6e5f7088156d7adcf0aed10121c6c6
Author: Thierry Reding <treding@nvidia.com>

dma-buf: heaps: Add debugfs support
    
Add a callback to struct dma_heap_ops that heap providers can implement
to show information about the state of the heap in debugfs. A top-level
directory named "dma_heap" is created in debugfs and individual files
will be named after the heaps.

> diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
> index 3937dd41bb0f6..f279b8fdd3ce0 100644
> --- a/drivers/dma-buf/dma-heap.c
> +++ b/drivers/dma-buf/dma-heap.c

[ ... ]

> @@ -299,6 +334,13 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
>  
>  	/* Add heap to the list */
>  	list_add(&heap->list, &heap_list);
> +
> +#ifdef CONFIG_DEBUG_FS
> +	if (heap->ops && heap->ops->show)
> +		debugfs_create_file(heap->name, 0444, dma_heap_debugfs_dir,
> +				    heap, &dma_heap_debug_fops);
> +#endif
> +

[Severity: High]
This is a pre-existing issue, but is there a race condition in this function
that could lead to a use-after-free?

In dma_heap_add(), cdev_add() is called earlier, making the device accessible.
If the duplicate name check fails, it goes to the error path, destroying the
device and freeing the heap structure:

drivers/dma-buf/dma-heap.c:dma_heap_add() {
    ...
    ret = cdev_add(&heap->heap_cdev, heap->heap_devt, 1);
    ...
    list_for_each_entry(h, &heap_list, list) {
        if (!strcmp(h->name, exp_info->name)) {
            ...
            goto err3;
        }
    }
    ...
err3:
    device_destroy(dma_heap_class, heap->heap_devt);
    ...
err0:
    kfree(heap);
    return err_ret;
}

If a user concurrently opens the device between cdev_add() and the kfree()
on the error path, would the resulting file descriptor retain a dangling
pointer to the freed heap structure in file->private_data?

Could a subsequent ioctl() on this file descriptor then dereference the freed
heap, triggering a use-after-free?

>  	mutex_unlock(&heap_list_lock);
>  
>  	return heap;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=6

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR
  2026-08-14 15:29 ` [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
@ 2026-08-14 15:43   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:43 UTC (permalink / raw)
  To: Thierry Reding
  Cc: dri-devel, Alexander Gordeev, conor+dt, media-ci, Vasily Gorbik,
	linux-trace-kernel, robh, linux-s390, Heiko Carstens,
	Christian Borntraeger, devicetree

Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider:
- [High] Calling `dma_map_sgtable()` on a scatterlist with a `NULL` page pointer will overwrite the manual DMA address and cause a kernel panic for static VPRs.
- [High] Memory allocated with `kvmalloc_array()` is incorrectly freed with `kfree()`, which will cause a SLUB BUG or kernel panic if it falls back to `vmalloc()`.
- [High] Signed integer overflow in physical address calculation for VPR sizes exceeding ~2 GiB.
- [High] Unconditional CMA initialization on `no-map` reserved memory will cause a kernel panic.
- [High] SMCCC calling convention truncates 64-bit physical addresses to 32 bits.
- [High] Race condition and framework abuse by calling `pm_generic_freeze()` at runtime without acquiring the device lock.
- [High] Dereferencing `dev->driver` when calling `tegra_vpr_device_init()` will cause a boot-time kernel panic.
- [Medium] Missing locking in `tegra_vpr_debugfs_show_chunks` leads to use-after-free and list corruption.
- [Medium] Memory and OF node leaks on the error path of `tegra_vpr_add_heap()`.
- [Medium] Off-by-one error prevents allocation of the last VPR page.
- [Low] Missing Kconfig dependencies for `ARM_SMCCC` will cause compilation and link errors on unsupported architectures.
- [Low] The use of `DECLARE_BITMAP(dirty, vpr->num_chunks)` creates a Variable Length Array (VLA), which is forbidden in the kernel.
--

--- Patch [7]: [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR ---
commit 4503ba247ad23e5d232fb07fec29334ba71260ab
Author: Thierry Reding <treding@nvidia.com>
Subject: dma-buf: heaps: Add support for Tegra VPR

This commit introduces a new DMA-BUF heap for the NVIDIA Tegra
Video-Protection-Region (VPR). It exposes VPR memory, which is restricted
from CPU access, to drivers and applications that handle content-protected
video. The heap supports both static and resizable VPR configurations.

> diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
> index bb729e91545c4..8909330bfaa2a 100644
> --- a/drivers/dma-buf/heaps/Kconfig
> +++ b/drivers/dma-buf/heaps/Kconfig
> @@ -20,3 +20,15 @@ config DMABUF_HEAPS_CMA
> +config DMABUF_HEAPS_TEGRA_VPR
> +	bool "NVIDIA Tegra Video-Protected-Region DMA-BUF Heap"
> +	depends on DMABUF_HEAPS && DMA_CMA

[Severity: Low]
Is it possible for this driver to be compiled on unsupported architectures?

The driver uses arm_smccc_smc(), which relies on __arm_smccc_smc. This is
only implemented on ARM/ARM64. 

Without an ARM_SMCCC or ARCH_TEGRA dependency, this could lead to unresolved
symbol linker failures on other architectures like x86.

> diff --git a/drivers/dma-buf/heaps/tegra-vpr.c b/drivers/dma-buf/heaps/tegra-vpr.c
> new file mode 100644
> index 0000000000000..c8d9f9c66dded
> --- /dev/null
> +++ b/drivers/dma-buf/heaps/tegra-vpr.c
[ ... ]
> @@ -112,6 +112,6 @@ struct tegra_vpr_attachment {
>  
> +#define ARM_SMCCC_VENDOR_SIP_TE_PROGRAM_VPR_FUNC_ID		\
> +	ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL,			\
> +			   ARM_SMCCC_SMC_32,			\
> +			   ARM_SMCCC_OWNER_SIP,			\
> +			   ARM_SMCCC_TE_FUNC_PROGRAM_VPR)

[Severity: High]
Does the use of ARM_SMCCC_SMC_32 truncate the physical addresses passed to
the secure monitor? 

When passing 64-bit phys_addr_t arguments for base and size in
tegra_vpr_set(), the SMC32 convention truncates them to 32 bits.

This might cause the secure monitor to program the VPR to an incorrect
memory location.

[ ... ]
> @@ -166,5 +166,5 @@ static int tegra_vpr_resize(struct tegra_vpr *vpr)
>  
> +	list_for_each_entry(node, &vpr->devices, node) {
> +		err = pm_generic_freeze(node->dev);

[Severity: High]
Is it safe to call pm_generic_freeze() here without acquiring the device
lock? 

Calling it directly without holding device_lock() can race with concurrent
runtime PM transitions, device removal, or unbind operations. 

Could this lead to crashes if dev->driver is cleared concurrently?

[ ... ]
> @@ -365,3 +365,3 @@ static int tegra_vpr_activate_chunks(struct tegra_vpr *vpr,
> +{
> +	DECLARE_BITMAP(dirty, vpr->num_chunks);

[Severity: Low]
Does this declaration create a Variable Length Array (VLA)? 

Because vpr->num_chunks is a struct member evaluated at runtime, this
creates a VLA on the stack. Since the kernel is compiled with -Werror=vla,
this will result in a build failure.

[ ... ]
> @@ -498,4 +498,4 @@ static int tegra_vpr_find_free_region(struct tegra_vpr *vpr,
> +{
> +	return tegra_vpr_find_free_region_in_range(vpr, 0, vpr->num_pages - 1,
> +						   num_pages, align);

[Severity: Medium]
Will passing vpr->num_pages - 1 prevent the allocation of the last VPR page?

Looking at tegra_vpr_find_free_region_in_range(), the loop condition is
pos + num_pages <= end_page. 

If end_page is decremented by 1, it seems it will incorrectly exclude the
final page from ever being allocated.

[ ... ]
> @@ -642,3 +642,3 @@ tegra_vpr_buffer_allocate(struct tegra_vpr *vpr, size_t size)
> +	INIT_LIST_HEAD(&buffer->list);
> +	mutex_init(&buffer->lock);
> +	buffer->start = vpr->base + (pageno << PAGE_SHIFT);

[Severity: High]
Can this calculation overflow for VPR sizes exceeding 2 GiB? 

Because pageno is a signed 32-bit int, pageno << PAGE_SHIFT can overflow
and become a negative value before being promoted and added to the 64-bit
phys_addr_t vpr->base. 

This could yield a completely incorrect DMA address.

[ ... ]
> @@ -654,4 +654,4 @@ tegra_vpr_buffer_allocate(struct tegra_vpr *vpr, size_t size)
> +		buffer->pages = kvmalloc_array(buffer->num_pages,
> +					       sizeof(*buffer->pages),
> +					       GFP_KERNEL);
> +		if (!buffer->pages) {
[ ... ]
> @@ -672,3 +672,3 @@ free:
> +	kfree(buffer->pages);
> +release:

[Severity: High]
Should this be kvfree() instead of kfree()? 

Memory allocated with kvmalloc_array() might fall back to vmalloc(). 
If that happens, calling kfree() on a vmalloc address will cause a SLUB BUG
or kernel panic during virt_to_page() translation.

[ ... ]
> @@ -728,4 +728,4 @@ static void tegra_vpr_buffer_release(struct tegra_vpr_buffer *buffer)
> +	bitmap_clear(vpr->bitmap, buffer->pageno, buffer->num_pages);
> +	list_del(&buffer->list);
> +	kfree(buffer->pages);

[Severity: High]
Similarly, should this teardown path also use kvfree() to release
buffer->pages since it was allocated using kvmalloc_array()?

[ ... ]
> @@ -785,4 +785,4 @@ tegra_vpr_map_dma_buf(struct dma_buf_attachment *attachment,
> +
> +	err = dma_map_sgtable(attachment->dev, sgt, direction,
> +			      DMA_ATTR_SKIP_CPU_SYNC);

[Severity: High]
Can this crash for static VPR configurations? 

In the static VPR case, the scatterlist is populated with a NULL page
pointer. 

Unconditionally calling dma_map_sgtable() here will cause the DMA framework
to iterate over the scatterlist and dereference the NULL page pointer. This
could corrupt the DMA address or cause a kernel panic.

[ ... ]
> @@ -796,4 +796,4 @@ static void tegra_vpr_unmap_dma_buf(struct dma_buf_attachment *attachment,
> +{
> +	dma_unmap_sgtable(attachment->dev, sgt, direction,
> +			  DMA_ATTR_SKIP_CPU_SYNC);

[Severity: High]
This appears to have the same issue as the mapping path. Calling
dma_unmap_sgtable() unconditionally attempts to unmap a NULL-paged
scatterlist, which could lead to a panic.

[ ... ]
> @@ -800,3 +800,3 @@ static void tegra_vpr_recycle(struct tegra_vpr *vpr)
> +{
> +	DECLARE_BITMAP(dirty, vpr->num_chunks);

[Severity: Low]
Does this declaration also create a Variable Length Array (VLA) on the
stack? This will similarly cause a build failure under -Werror=vla.

[ ... ]
> @@ -1048,4 +1048,4 @@ static void tegra_vpr_debugfs_show_chunks(struct tegra_vpr *vpr,
> +
> +	list_for_each_entry(buffer, &vpr->buffers, list) {
> +		string_get_size(buffer->size, 1, STRING_UNITS_2, buf,

[Severity: Medium]
Should this list iteration be protected by vpr->lock? 

Iterating over vpr->buffers without the lock can race with concurrent
dma-buf allocations or frees, potentially leading to a use-after-free or a
panic from list corruption.

[ ... ]
> @@ -1199,6 +1199,6 @@ static int tegra_vpr_add_heap(struct reserved_mem *rmem,
> +
> +cleanup:
> +	if (vpr->resizable)
> +		tegra_vpr_free_chunks(vpr);
> +free:
> +	bitmap_free(vpr->bitmap);
> +	kfree(vpr);
> +	return err;

[Severity: Medium]
Are there a couple of resource leaks on this error path? 

If dma_heap_add() fails, tegra_vpr_free_chunks() is called, but it looks
like the vpr->active bitmap is never freed. 

Additionally, it seems the device node reference acquired via of_node_get()
is not released with of_node_put(vpr->dev_node).

[ ... ]
> @@ -1305,4 +1305,4 @@ static int __init tegra_vpr_node_init(unsigned long offset,
> +
> +	err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name,
> +				    &cma);

[Severity: High]
Will this cause a panic for static VPRs? 

Static VPRs use the "no-map" property, which removes the memory from the
linear map and prevents struct page creation. 

However, this calls cma_init_reserved_mem() unconditionally, and CMA
expects struct page to be present when initializing pageblocks.

[ ... ]
> @@ -1359,3 +1359,3 @@ static int tegra_vpr_device_init(struct reserved_mem *rmem, struct device *dev)
> +{
> +	const struct dev_pm_ops *pm = dev->driver->pm;

[Severity: High]
Can this unconditionally dereference a NULL pointer? 

This function acts as the device_init callback for reserved memory
operations. When devices are instantiated and memory regions are attached
via of_dma_configure_id(), this callback is invoked before a driver is
matched or bound to the device.

This means dev->driver would be NULL here.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=7

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x
  2026-08-14 15:29 ` [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x Thierry Reding
@ 2026-08-14 15:44   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-08-14 15:44 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Christian Borntraeger, robh, linux-s390, Heiko Carstens,
	Vasily Gorbik, Alexander Gordeev, media-ci, conor+dt, devicetree,
	dri-devel, linux-trace-kernel

> From: Thierry Reding <treding@nvidia.com>
> 
> The host1x needs access to the VPR region, so make sure to reference it
> via the memory-region property.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260814-tegra-vpr-v5-0-71832b5d0246@nvidia.com?part=9


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2026-08-14 15:44 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-14 15:29 [PATCH v5 00/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
2026-08-14 15:29 ` [PATCH v5 01/10] dt-bindings: reserved-memory: Document " Thierry Reding
2026-08-14 15:39   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 02/10] dt-bindings: display: tegra: Document memory regions Thierry Reding
2026-08-14 15:36   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 03/10] dt-bindings: gpu: host1x: Document memory-regions for NVDEC Thierry Reding
2026-08-14 15:39   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 04/10] bitmap: Add bitmap_allocate() function Thierry Reding
2026-08-14 15:36   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 05/10] mm/cma: Introduce cma_alloc_at() API Thierry Reding
2026-08-14 15:38   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 06/10] dma-buf: heaps: Add debugfs support Thierry Reding
2026-08-14 15:43   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 07/10] dma-buf: heaps: Add support for Tegra VPR Thierry Reding
2026-08-14 15:43   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 08/10] arm64: tegra: Add VPR placeholder node on Tegra234 Thierry Reding
2026-08-14 15:38   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 09/10] arm64: tegra: Hook up VPR to host1x Thierry Reding
2026-08-14 15:44   ` sashiko-bot
2026-08-14 15:29 ` [PATCH v5 10/10] arm64: tegra: Add VPR placeholder node on Tegra264 Thierry Reding
2026-08-14 15:41   ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.