* [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC)
@ 2026-09-15 2:14 ` Mikko Perttunen
2026-09-15 2:14 ` [PATCH 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
` (11 more replies)
0 siblings, 12 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:14 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Depending on the probe ordering, TegraDRM does not currently probe on
kernels with CONFIG_ARM_DMA_USE_IOMMU enabled, such as with
multi_v7_defconfig. The Tegra SMMU puts several display and 2D/3D
clients into one IOMMU group, and the legacy 32-bit ARM DMA API path
creates one UNMANAGED domain per device and attaches it with
iommu_attach_device(). That only works for one device per group, so
every client after the first fails to attach,
iommu_device_use_default_domain() rejects the group with -EBUSY, and the
driver goes down with it. tegra_defconfig is currently working by virtue
of not enabling CONFIG_ARM_DMA_USE_IOMMU.
Robin suggested [1] that instead of teaching the legacy path about
groups, we should explore modernizing the 32-bit ARM DMA code a bit by
making it use domain cookies and have the core code own the domains, as
is the case on other architectures. Still, ARM's own DMA code is kept to
support existing drivers and APIs that expect it, and because dma-iommu
is considered too big and unnecessary for 32-bit ARM.
The series is in four parts:
- Patches 1-2 are preparation in the IOMMU core:
1. Add iommu_teardown_dma_ops, counterpart to iommu_setup_dma_ops.
2. Add cookie type for ARM's DMA domains.
- Patches 3-6 implement the DMA API side on 32-bit ARM:
3. Split up domain and address space management code in arch/arm/.
4. Implement parallel dma_ops / cookie APIs on ARM side.
5. Avoid domain allocation when IOMMU core does it.
6. Support foreign-allocated sg_tables.
- Patch 7 enables 32-bit ARM IOMMU drivers to provide a DMA default
domain.
- Patches 8-11 convert Tegra:
8. Allows deferring translation enable until driver handshake for
selected devices.
9-10. Removes code detaching ARM UNMANAGED DMA domains in
Host1x/TegraDRM.
11. Enables DMA default domain for selected Tegra devices.
None of this should have any functional effect on any device other than
the Tegra devices enabled in patches 8-11. Other IOMMU drivers can opt
in later. The new DMA default domains are enabled only when the IOMMU
driver explicitly specifies a DMA domain type, just returning 0 for the
default is not enough (unlike on arm64), keeping the current behavior.
Patches 8-11 are provided for demonstration but we can deal with them
separately if the rest of the series is accepted.
Patches 1,2,3 are applicable independently.
Patches 4,5 depend on the previous patches.
Patch 6 can be applied independently but is only required once
the whole series lands (by TegraDRM).
Patch 7 depends on patch 4.
Patches 1-7 only touch drivers/iommu/ and arch/arm/mm/ and associated
headers.
Best regards,
Mikko
[1] https://lore.kernel.org/linux-tegra/910d1492-ad3b-4201-93c6-208be1dcde7d@arm.com/
---
Mikko Perttunen (11):
iommu: Add iommu_teardown_dma_ops
iommu: Add cookie type for ARM_DMA_USE_IOMMU
ARM: dma-mapping: Split mapping allocation from domain allocation
ARM: dma-mapping: Implement and expose DMA setup functions
ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
iommu: Let 32-bit ARM drivers opt into a DMA API default domain
iommu/tegra-smmu: Defer display client translation to driver handover
gpu: host1x: Drop release of legacy 32-bit ARM DMA domain
drm/tegra: Drop release of legacy 32-bit ARM DMA domain
iommu/tegra-smmu: Use a DMA API default domain per swgroup
arch/arm/include/asm/dma-iommu.h | 17 ++++
arch/arm/mm/dma-mapping.c | 209 ++++++++++++++++++++++++++++++++-------
drivers/gpu/drm/tegra/dc.c | 8 ++
drivers/gpu/drm/tegra/drm.c | 15 ---
drivers/gpu/host1x/dev.c | 15 ---
drivers/iommu/Kconfig | 1 +
drivers/iommu/dma-iommu.c | 5 +
drivers/iommu/dma-iommu.h | 52 +++++++---
drivers/iommu/iommu.c | 24 ++---
drivers/iommu/tegra-smmu.c | 109 +++++++++++++++++++-
drivers/memory/tegra/tegra114.c | 15 ++-
drivers/memory/tegra/tegra124.c | 12 ++-
drivers/memory/tegra/tegra210.c | 6 +-
drivers/memory/tegra/tegra30.c | 18 ++--
include/linux/iommu.h | 3 +
include/soc/tegra/mc.h | 20 ++++
16 files changed, 410 insertions(+), 119 deletions(-)
---
base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
change-id: 20260728-b4-arm32-iommu-dma-cookie-3170e07f9125
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 01/11] iommu: Add iommu_teardown_dma_ops
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
@ 2026-09-15 2:14 ` Mikko Perttunen
2026-09-15 2:14 ` [PATCH 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU Mikko Perttunen
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:14 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Add a counterpart to iommu_setup_dma_ops, tearing down state set by it.
Currently this teardown is open-coded in iommu_deinit_device; however
when 32-bit ARM gains a parallel implementation of iommu_setup_dma_ops
it becomes nicer to have a matching parallel iommu_teardown_dma_ops as
well.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/dma-iommu.c | 5 +++++
drivers/iommu/dma-iommu.h | 5 +++++
drivers/iommu/iommu.c | 3 +--
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9abaec0703ef..26674eec5996 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2166,6 +2166,11 @@ void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain)
dev_clear_dma_iommu(dev);
}
+void iommu_teardown_dma_ops(struct device *dev)
+{
+ dev_clear_dma_iommu(dev);
+}
+
static bool has_msi_cookie(const struct iommu_domain *domain)
{
return domain && (domain->cookie_type == IOMMU_COOKIE_DMA_IOVA ||
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 040d00252563..349d6a57e5a0 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -10,6 +10,7 @@
#ifdef CONFIG_IOMMU_DMA
void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
+void iommu_teardown_dma_ops(struct device *dev);
int iommu_get_dma_cookie(struct iommu_domain *domain);
void iommu_put_dma_cookie(struct iommu_domain *domain);
@@ -31,6 +32,10 @@ static inline void iommu_setup_dma_ops(struct device *dev,
{
}
+static inline void iommu_teardown_dma_ops(struct device *dev)
+{
+}
+
static inline int iommu_dma_init_fq(struct iommu_domain *domain)
{
return -EINVAL;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e8f13dcebbde..75687d1aafe9 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -611,8 +611,7 @@ static void iommu_deinit_device(struct device *dev)
dev->iommu_group = NULL;
module_put(ops->owner);
dev_iommu_free(dev);
- if (IS_ENABLED(CONFIG_IOMMU_DMA))
- dev_clear_dma_iommu(dev);
+ iommu_teardown_dma_ops(dev);
}
static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
2026-09-15 2:14 ` [PATCH 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
@ 2026-09-15 2:14 ` Mikko Perttunen
2026-09-15 2:14 ` [PATCH 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation Mikko Perttunen
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:14 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
The ARM_DMA_USE_IOMMU DMA API implementation keeps its state in a struct
dma_iommu_mapping. Currently that struct owns an IOMMU domain -- to
allow inverting that relation, add a cookie type pointing to a struct
dma_iommu_mapping, more in line with how other architectures work.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/iommu.c | 1 +
include/linux/iommu.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 75687d1aafe9..43dd69662fac 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -2127,6 +2127,7 @@ void iommu_domain_free(struct iommu_domain *domain)
{
switch (domain->cookie_type) {
case IOMMU_COOKIE_DMA_IOVA:
+ case IOMMU_COOKIE_ARM_DMA:
iommu_put_dma_cookie(domain);
break;
case IOMMU_COOKIE_DMA_MSI:
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index d20aa6f6863a..e2f8513d2067 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -43,6 +43,7 @@ struct notifier_block;
struct iommu_sva;
struct iommu_dma_cookie;
struct iommu_dma_msi_cookie;
+struct dma_iommu_mapping;
struct iommu_fault_param;
struct iommufd_ctx;
struct iommufd_viommu;
@@ -174,6 +175,7 @@ enum iommu_domain_cookie_type {
IOMMU_COOKIE_FAULT_HANDLER,
IOMMU_COOKIE_SVA,
IOMMU_COOKIE_IOMMUFD,
+ IOMMU_COOKIE_ARM_DMA,
};
/* Domain feature flags */
@@ -234,6 +236,7 @@ struct iommu_domain {
union { /* cookie */
struct iommu_dma_cookie *iova_cookie;
struct iommu_dma_msi_cookie *msi_cookie;
+ struct dma_iommu_mapping *arm_cookie;
struct iommufd_hw_pagetable *iommufd_hwpt;
struct {
iommu_fault_handler_t handler;
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
2026-09-15 2:14 ` [PATCH 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
2026-09-15 2:14 ` [PATCH 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU Mikko Perttunen
@ 2026-09-15 2:14 ` Mikko Perttunen
2026-09-15 2:14 ` [PATCH 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:14 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
arm_iommu_create_mapping() allocates two separate things: address space
bookkeeping tracking allocated IOVA ranges; and the corresponding IOMMU
domain.
Split out the former into __arm_iommu_alloc_mapping(), with a matching
__arm_iommu_free_mapping(), so that the bookkeeping can be created
independently when a domain is allocated outside the ARM dma-mapping
code. A later patch uses this to turn struct dma_iommu_mapping into an
IOMMU domain cookie.
No functional change.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 76 +++++++++++++++++++++++++++++------------------
1 file changed, 47 insertions(+), 29 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f9bc53b60f99..a7ad194960ce 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1470,21 +1470,8 @@ static const struct dma_map_ops iommu_ops = {
.sync_sg_for_device = arm_iommu_sync_sg_for_device,
};
-/**
- * arm_iommu_create_mapping
- * @dev: pointer to the client device (for IOMMU calls)
- * @base: start address of the valid IO address space
- * @size: maximum size of the valid IO address space
- *
- * Creates a mapping structure which holds information about used/unused
- * IO address ranges, which is required to perform memory allocation and
- * mapping with IOMMU aware functions.
- *
- * The client device need to be attached to the mapping with
- * arm_iommu_attach_device function.
- */
-struct dma_iommu_mapping *
-arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
+static struct dma_iommu_mapping *
+__arm_iommu_alloc_mapping(dma_addr_t base, u64 size)
{
unsigned int bits = size >> PAGE_SHIFT;
unsigned int bitmap_size = BITS_TO_LONGS(bits) * sizeof(long);
@@ -1525,16 +1512,7 @@ arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
spin_lock_init(&mapping->lock);
- mapping->domain = iommu_paging_domain_alloc(dev);
- if (IS_ERR(mapping->domain)) {
- err = PTR_ERR(mapping->domain);
- goto err4;
- }
-
- kref_init(&mapping->kref);
return mapping;
-err4:
- kfree(mapping->bitmaps[0]);
err3:
kfree(mapping->bitmaps);
err2:
@@ -1542,21 +1520,61 @@ arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
err:
return ERR_PTR(err);
}
-EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
-static void release_iommu_mapping(struct kref *kref)
+static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
{
int i;
- struct dma_iommu_mapping *mapping =
- container_of(kref, struct dma_iommu_mapping, kref);
- iommu_domain_free(mapping->domain);
for (i = 0; i < mapping->nr_bitmaps; i++)
kfree(mapping->bitmaps[i]);
kfree(mapping->bitmaps);
kfree(mapping);
}
+/**
+ * arm_iommu_create_mapping
+ * @dev: pointer to the client device (for IOMMU calls)
+ * @base: start address of the valid IO address space
+ * @size: maximum size of the valid IO address space
+ *
+ * Creates a mapping structure which holds information about used/unused
+ * IO address ranges, which is required to perform memory allocation and
+ * mapping with IOMMU aware functions.
+ *
+ * The client device need to be attached to the mapping with
+ * arm_iommu_attach_device function.
+ */
+struct dma_iommu_mapping *
+arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
+{
+ struct dma_iommu_mapping *mapping;
+
+ mapping = __arm_iommu_alloc_mapping(base, size);
+ if (IS_ERR(mapping))
+ return mapping;
+
+ mapping->domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(mapping->domain)) {
+ int err = PTR_ERR(mapping->domain);
+
+ __arm_iommu_free_mapping(mapping);
+ return ERR_PTR(err);
+ }
+
+ kref_init(&mapping->kref);
+ return mapping;
+}
+EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
+
+static void release_iommu_mapping(struct kref *kref)
+{
+ struct dma_iommu_mapping *mapping =
+ container_of(kref, struct dma_iommu_mapping, kref);
+
+ iommu_domain_free(mapping->domain);
+ __arm_iommu_free_mapping(mapping);
+}
+
static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
{
int next_bitmap;
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 04/11] ARM: dma-mapping: Implement and expose DMA setup functions
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (2 preceding siblings ...)
2026-09-15 2:14 ` [PATCH 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation Mikko Perttunen
@ 2026-09-15 2:14 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:14 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
ARM DMA domains are currently allocated by the ARM DMA code, with struct
dma_iommu_mapping owning the domain and the IOVA bookkeeping. To allow
the IOMMU core to own DMA domains as is the case with other
architectures, we have to allow that code to allocate the domain and get
the cookie from the ARM DMA code.
Implement ARM versions of iommu_get/put_dma_cookie that create a struct
dma_iommu_mapping that doesn't have its own domain, as well as
iommu_setup/teardown_dma_ops that set the device's archdata.mapping,
allowing ARM DMA ops to seamlessly work on these devices.
The code is protected by CONFIG_ARM_DMA_USE_IOMMU and mutually exclusive
with the same-named functions in the dma-iommu code; and not reachable
yet.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/include/asm/dma-iommu.h | 17 ++++++
arch/arm/mm/dma-mapping.c | 118 ++++++++++++++++++++++++++++++++++-----
drivers/iommu/dma-iommu.h | 50 +++++++++++------
3 files changed, 154 insertions(+), 31 deletions(-)
diff --git a/arch/arm/include/asm/dma-iommu.h b/arch/arm/include/asm/dma-iommu.h
index 2ce4c5683e6d..85cf9b05faab 100644
--- a/arch/arm/include/asm/dma-iommu.h
+++ b/arch/arm/include/asm/dma-iommu.h
@@ -19,6 +19,14 @@ struct dma_iommu_mapping {
size_t bits; /* per bitmap */
dma_addr_t base;
+ /*
+ * Set for mappings created by arm_iommu_create_mapping(), where
+ * @domain is owned by the mapping.
+ * Clear for mappings that are a cookie of a domain owned by the IOMMU
+ * core.
+ */
+ bool owns_domain;
+
spinlock_t lock;
struct kref kref;
};
@@ -32,5 +40,14 @@ int arm_iommu_attach_device(struct device *dev,
struct dma_iommu_mapping *mapping);
void arm_iommu_detach_device(struct device *dev);
+#ifdef CONFIG_ARM_DMA_USE_IOMMU
+struct iommu_domain;
+
+void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
+void iommu_teardown_dma_ops(struct device *dev);
+int iommu_get_dma_cookie(struct iommu_domain *domain);
+void iommu_put_dma_cookie(struct iommu_domain *domain);
+#endif
+
#endif /* __KERNEL__ */
#endif
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index a7ad194960ce..e0d58778c5f5 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1531,6 +1531,79 @@ static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
kfree(mapping);
}
+static int __arm_iommu_init_cookie(struct iommu_domain *domain,
+ dma_addr_t base, u64 size)
+{
+ struct dma_iommu_mapping *mapping;
+
+ if (domain->cookie_type != IOMMU_COOKIE_NONE)
+ return -EEXIST;
+
+ mapping = __arm_iommu_alloc_mapping(base, size);
+ if (IS_ERR(mapping))
+ return PTR_ERR(mapping);
+
+ mapping->domain = domain;
+ domain->cookie_type = IOMMU_COOKIE_ARM_DMA;
+ domain->arm_cookie = mapping;
+
+ return 0;
+}
+
+/**
+ * iommu_get_dma_cookie - Set up a domain cookie for ARM IOMMU-based DMA API
+ * @domain: IOMMU_DOMAIN_DMA domain being configured
+ *
+ * Allocates IOVA bookkeeping for the domain based on the domain's specified
+ * geometry. The aperture can currently be at most 4GB in size, otherwise
+ * fails with -ERANGE.
+ */
+int iommu_get_dma_cookie(struct iommu_domain *domain)
+{
+ dma_addr_t base = domain->geometry.aperture_start;
+ u64 size;
+
+ if (base > domain->geometry.aperture_end)
+ return -EINVAL;
+
+ size = (u64)domain->geometry.aperture_end - base + 1;
+
+ return __arm_iommu_init_cookie(domain, base, size);
+}
+
+/**
+ * iommu_put_dma_cookie - Free domain cookie for ARM IOMMU-based DMA API
+ * @domain: the domain being freed
+ */
+void iommu_put_dma_cookie(struct iommu_domain *domain)
+{
+ __arm_iommu_free_mapping(domain->arm_cookie);
+}
+
+/**
+ * iommu_setup_dma_ops - Record a device's DMA API domain cookie
+ * @dev: the device
+ * @domain: the group's new default domain
+ *
+ * Records the cookie of @domain on @dev, or clears it if @domain is not a DMA
+ * API domain. The DMA ops themselves are per driver binding and are installed
+ * by arch_setup_dma_ops().
+ */
+void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain)
+{
+ to_dma_iommu_mapping(dev) = iommu_is_dma_domain(domain) ?
+ domain->arm_cookie : NULL;
+}
+
+/**
+ * iommu_teardown_dma_ops - Clear a device's DMA API domain cookie
+ * @dev: the device
+ */
+void iommu_teardown_dma_ops(struct device *dev)
+{
+ to_dma_iommu_mapping(dev) = NULL;
+}
+
/**
* arm_iommu_create_mapping
* @dev: pointer to the client device (for IOMMU calls)
@@ -1541,28 +1614,32 @@ static void __arm_iommu_free_mapping(struct dma_iommu_mapping *mapping)
* IO address ranges, which is required to perform memory allocation and
* mapping with IOMMU aware functions.
*
+ * Unlike a default domain set up by the IOMMU core, the returned mapping owns
+ * the domain it describes and releases it along with the last reference.
+ *
* The client device need to be attached to the mapping with
* arm_iommu_attach_device function.
*/
struct dma_iommu_mapping *
arm_iommu_create_mapping(struct device *dev, dma_addr_t base, u64 size)
{
- struct dma_iommu_mapping *mapping;
-
- mapping = __arm_iommu_alloc_mapping(base, size);
- if (IS_ERR(mapping))
- return mapping;
+ struct iommu_domain *domain;
+ int err;
- mapping->domain = iommu_paging_domain_alloc(dev);
- if (IS_ERR(mapping->domain)) {
- int err = PTR_ERR(mapping->domain);
+ domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(domain))
+ return ERR_CAST(domain);
- __arm_iommu_free_mapping(mapping);
+ err = __arm_iommu_init_cookie(domain, base, size);
+ if (err) {
+ iommu_domain_free(domain);
return ERR_PTR(err);
}
- kref_init(&mapping->kref);
- return mapping;
+ domain->arm_cookie->owns_domain = true;
+ kref_init(&domain->arm_cookie->kref);
+
+ return domain->arm_cookie;
}
EXPORT_SYMBOL_GPL(arm_iommu_create_mapping);
@@ -1571,8 +1648,8 @@ static void release_iommu_mapping(struct kref *kref)
struct dma_iommu_mapping *mapping =
container_of(kref, struct dma_iommu_mapping, kref);
+ /* Frees mapping too, via iommu_put_dma_cookie(). */
iommu_domain_free(mapping->domain);
- __arm_iommu_free_mapping(mapping);
}
static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
@@ -1595,8 +1672,13 @@ static int extend_iommu_mapping(struct dma_iommu_mapping *mapping)
void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping)
{
- if (mapping)
- kref_put(&mapping->kref, release_iommu_mapping);
+ if (!mapping)
+ return;
+
+ if (WARN_ON(!mapping->owns_domain))
+ return;
+
+ kref_put(&mapping->kref, release_iommu_mapping);
}
EXPORT_SYMBOL_GPL(arm_iommu_release_mapping);
@@ -1660,6 +1742,14 @@ void arm_iommu_detach_device(struct device *dev)
return;
}
+ /*
+ * This pairs with arm_iommu_attach_device() on a driver-owned mapping.
+ * A mapping which is merely the cookie of a core-owned default domain
+ * must not be torn down from here.
+ */
+ if (WARN_ON(!mapping->owns_domain))
+ return;
+
iommu_detach_device(mapping->domain, dev);
kref_put(&mapping->kref, release_iommu_mapping);
to_dma_iommu_mapping(dev) = NULL;
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 349d6a57e5a0..71b23cb698c0 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -7,25 +7,24 @@
#include <linux/iommu.h>
-#ifdef CONFIG_IOMMU_DMA
+/*
+ * Both dma-iommu.c and arch/arm/mm/dma-mapping.c implement functions to set up
+ * IOMMU_DOMAIN_DMA default domains. Only one provider can be compiled in at a
+ * time.
+ */
+#if defined(CONFIG_ARM_DMA_USE_IOMMU)
+
+#include <asm/dma-iommu.h>
+
+#elif defined(CONFIG_IOMMU_DMA)
void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain);
void iommu_teardown_dma_ops(struct device *dev);
int iommu_get_dma_cookie(struct iommu_domain *domain);
void iommu_put_dma_cookie(struct iommu_domain *domain);
-void iommu_put_msi_cookie(struct iommu_domain *domain);
-int iommu_dma_init_fq(struct iommu_domain *domain);
-
-void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
-
-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr);
-
-extern bool iommu_dma_forcedac;
-
-#else /* CONFIG_IOMMU_DMA */
+#else
static inline void iommu_setup_dma_ops(struct device *dev,
struct iommu_domain *domain)
@@ -36,11 +35,6 @@ static inline void iommu_teardown_dma_ops(struct device *dev)
{
}
-static inline int iommu_dma_init_fq(struct iommu_domain *domain)
-{
- return -EINVAL;
-}
-
static inline int iommu_get_dma_cookie(struct iommu_domain *domain)
{
return -ENODEV;
@@ -50,6 +44,28 @@ static inline void iommu_put_dma_cookie(struct iommu_domain *domain)
{
}
+#endif
+
+#ifdef CONFIG_IOMMU_DMA
+
+void iommu_put_msi_cookie(struct iommu_domain *domain);
+
+int iommu_dma_init_fq(struct iommu_domain *domain);
+
+void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
+
+int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr);
+
+extern bool iommu_dma_forcedac;
+
+#else /* CONFIG_IOMMU_DMA */
+
+static inline int iommu_dma_init_fq(struct iommu_domain *domain)
+{
+ return -EINVAL;
+}
+
static inline void iommu_put_msi_cookie(struct iommu_domain *domain)
{
}
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (3 preceding siblings ...)
2026-09-15 2:14 ` [PATCH 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
ARM's arch_setup_dma_ops / arm_setup_iommu_dma_ops creates a custom
IOMMU domain for DMA API usage, and continues to do so when the IOMMU
driver doesn't explicitly request a DMA default domain type.
When, however, that DMA default domain is requested and the core owns
the domain, we need to skip our own domain management. Add checks to
arm_setup/teardown_iommu_dma_ops for these cases.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index e0d58778c5f5..91f12fb91509 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1761,9 +1761,18 @@ EXPORT_SYMBOL_GPL(arm_iommu_detach_device);
static void arm_setup_iommu_dma_ops(struct device *dev)
{
- struct dma_iommu_mapping *mapping;
+ struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
u64 dma_base = 0, size = 1ULL << 32;
+ /*
+ * An existing cookie means the core owns the domain. Only set the
+ * per-binding DMA ops.
+ */
+ if (mapping) {
+ set_dma_ops(dev, &iommu_ops);
+ return;
+ }
+
if (dev->dma_range_map) {
dma_base = dma_range_map_min(dev->dma_range_map);
size = dma_range_map_max(dev->dma_range_map) - dma_base;
@@ -1792,6 +1801,9 @@ static void arm_teardown_iommu_dma_ops(struct device *dev)
if (!mapping)
return;
+ if (!mapping->owns_domain)
+ return;
+
arm_iommu_detach_device(dev);
arm_iommu_release_mapping(mapping);
}
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (4 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain Mikko Perttunen
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
arm_iommu_get_sgtable can only describe buffers which came from
arm_iommu_alloc_attrs, and returns -ENXIO for anything else. This is
stricter than iommu_dma_get_sgtable(), which falls back to describing
the buffer as a single chunk.
The difference shows up when a buffer is allocated for one device and
mapped for another, as TegraDRM does. Buffers are allocated on the
host1x logical device, which has no dma_configure and therefore uses
direct DMA ops, and are then mapped for a display controller behind the
SMMU.
Add the same fallback as iommu_dma_get_sgtable().
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
arch/arm/mm/dma-mapping.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 91f12fb91509..22bde6acd1d3 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1169,12 +1169,29 @@ static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
{
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
struct page **pages = __iommu_get_pages(cpu_addr, attrs);
+ struct page *page;
+ int ret;
- if (!pages)
- return -ENXIO;
+ if (pages)
+ return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
+ GFP_KERNEL);
- return sg_alloc_table_from_pages(sgt, pages, count, 0, size,
- GFP_KERNEL);
+ /*
+ * The buffer was not allocated through these DMA ops. It may belong to
+ * a device which is not behind an IOMMU at all, which can happen when a
+ * buffer is allocated on one device and mapped for another. Describe it
+ * as a single chunk, as iommu_dma_get_sgtable() does.
+ */
+ if (is_vmalloc_addr(cpu_addr))
+ page = vmalloc_to_page(cpu_addr);
+ else
+ page = virt_to_page(cpu_addr);
+
+ ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+ if (!ret)
+ sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+
+ return ret;
}
/*
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (5 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
iommu_get_default_domain_type() unconditionally forces IDENTITY on
32-bit ARM as ARM_DMA_USE_IOMMU installs its own UNMANAGED domain on top
of whatever the group already had. Now that the ARM DMA API state is an
ordinary domain cookie, IOMMU drivers can ask for a real
IOMMU_DOMAIN_DMA default domain instead. Drop the override so that
->def_domain_type is honoured, and let a driver-requested DMA domain
through the !CONFIG_IOMMU_DMA guard when ARM_DMA_USE_IOMMU can back it.
IOMMU_DOMAIN_DMA stays strictly opt-in on 32-bit ARM and is never the
fallback to avoid unexpected changes, as drivers today return 0 from
->def_domain_type while expecting the legacy behaviour.
Behaviour is therefore unchanged for every existing 32-bit ARM IOMMU
driver:
- exynos-iommu, omap-iommu, rockchip-iommu, msm_iommu, ipmmu-vmsa,
mtk_iommu*, sun50i-iommu and qcom_iommu have no ->def_domain_type,
so iommu_get_def_domain_type() returns the unchanged cur_type of 0,
which is forced to IDENTITY as before.
- arm-smmu returns IDENTITY for legacy bindings and 0 otherwise, which
ends up in the same place.
- tegra-smmu returns IDENTITY explicitly, and keeps doing so until a
later patch.
Also move the static_assert on the two DMA API implementations being
mutually exclusive to drivers/iommu/dma-iommu.h, next to the
declarations that depend on it.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/dma-iommu.h | 3 +++
drivers/iommu/iommu.c | 20 +++++---------------
2 files changed, 8 insertions(+), 15 deletions(-)
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 71b23cb698c0..24fa4165450f 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -12,6 +12,9 @@
* IOMMU_DOMAIN_DMA default domains. Only one provider can be compiled in at a
* time.
*/
+static_assert(!(IS_ENABLED(CONFIG_IOMMU_DMA) &&
+ IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)));
+
#if defined(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 43dd69662fac..6b4c328852db 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1884,18 +1884,6 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
lockdep_assert_held(&group->mutex);
- /*
- * ARM32 drivers supporting CONFIG_ARM_DMA_USE_IOMMU can declare an
- * identity_domain and it will automatically become their default
- * domain. Later on ARM_DMA_USE_IOMMU will install its UNMANAGED domain.
- * Override the selection to IDENTITY.
- */
- if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) {
- static_assert(!(IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) &&
- IS_ENABLED(CONFIG_IOMMU_DMA)));
- driver_type = IOMMU_DOMAIN_IDENTITY;
- }
-
for_each_group_device(group, gdev) {
driver_type = iommu_get_def_domain_type(group, gdev->dev,
driver_type);
@@ -1913,11 +1901,13 @@ static int iommu_get_default_domain_type(struct iommu_group *group,
/*
* If the common dma ops are not selected in kconfig then we cannot use
- * IOMMU_DOMAIN_DMA at all. Force IDENTITY if nothing else has been
- * selected.
+ * IOMMU_DOMAIN_DMA at all, unless this is ARM32 and the driver asked
+ * for it explicitly, where CONFIG_ARM_DMA_USE_IOMMU provides the
+ * implementation. Force IDENTITY if nothing else has been selected.
*/
if (!IS_ENABLED(CONFIG_IOMMU_DMA)) {
- if (WARN_ON(driver_type == IOMMU_DOMAIN_DMA))
+ if (WARN_ON(driver_type == IOMMU_DOMAIN_DMA &&
+ !IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)))
return -1;
if (!driver_type)
driver_type = IOMMU_DOMAIN_IDENTITY;
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 08/11] iommu/tegra-smmu: Defer display client translation to driver handover
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (6 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain Mikko Perttunen
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Bootloaders may leave display controllers scanning out a boot splash
while the kernel boots. Enabling translation for them without special
care during that time would cause translation faults.
Because of this, tegra_smmu_def_domain_type has kept every device on an
identity domain at boot, and TegraDRM has parallel custom IOMMU domain
management code paths for these clients.
Mark the dc and dcb swgroups on Tegra30, Tegra114, Tegra124 and Tegra210
with a new defer_enable flag. tegra_smmu_enable() sets those swgroups up
as usual, ASID and per-client enables and all, but leaves
SMMU_ASID_ENABLE clear, so the clients stay in bypass. tegra_dc_probe()
then calls tegra_smmu_enable_translation() once it has asserted the
display controller's reset (and set up its final IOMMU domain), at which
point the hardware is guaranteed not to be fetching and translation can
be turned on.
If the display driver never probes, the clients simply stay in bypass,
which is what they do today.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/drm/tegra/dc.c | 8 +++++++
drivers/iommu/tegra-smmu.c | 51 ++++++++++++++++++++++++++++++++++++++++-
drivers/memory/tegra/tegra114.c | 6 +++--
drivers/memory/tegra/tegra124.c | 6 +++--
drivers/memory/tegra/tegra210.c | 6 +++--
drivers/memory/tegra/tegra30.c | 6 +++--
include/soc/tegra/mc.h | 13 +++++++++++
7 files changed, 87 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 0b3fcc7011b3..2ae4da07bbc9 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -19,6 +19,7 @@
#include <linux/reset.h>
#include <soc/tegra/common.h>
+#include <soc/tegra/mc.h>
#include <soc/tegra/pmc.h>
#include <drm/drm_atomic.h>
@@ -2751,6 +2752,13 @@ static int tegra_dc_init(struct host1x_client *client)
return err;
}
+ /*
+ * Enable translation now that the final domain is attached.
+ * The hardware was reset at probe and has not been programmed
+ * since.
+ */
+ tegra_smmu_enable_translation(client->dev);
+
if (dc->soc->wgrps)
primary = tegra_dc_add_shared_planes(drm, dc);
else
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 67e7a7b925f0..6636a044905f 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -368,7 +368,13 @@ static void tegra_smmu_enable(struct tegra_smmu *smmu, unsigned int swgroup,
value = smmu_readl(smmu, group->reg);
value &= ~SMMU_ASID_MASK;
value |= SMMU_ASID_VALUE(asid);
- value |= SMMU_ASID_ENABLE;
+ /*
+ * A deferred client is set up completely, but with translation
+ * gated off, so it stays in bypass until its driver calls
+ * tegra_smmu_enable_translation().
+ */
+ if (!group->defer_enable)
+ value |= SMMU_ASID_ENABLE;
smmu_writel(smmu, value, group->reg);
} else {
pr_warn("%s group from swgroup %u not found\n", __func__,
@@ -417,6 +423,49 @@ static void tegra_smmu_disable(struct tegra_smmu *smmu, unsigned int swgroup,
}
}
+/**
+ * tegra_smmu_enable_translation - Release a client's swgroups to translation
+ * @dev: memory client device, already quiesced by its driver
+ *
+ * Enables translation for swgroups which were left in bypass at attach time
+ * because the client could still have been running from a pre-kernel
+ * configuration. Must be called after @dev has been attached to the domain it
+ * will use. Safe to call for any device; clients which were not deferred are
+ * unaffected.
+ */
+void tegra_smmu_enable_translation(struct device *dev)
+{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+ struct iommu_domain *domain;
+ unsigned int i;
+
+ if (!fwspec || !smmu)
+ return;
+
+ domain = iommu_get_domain_for_dev(dev);
+ if (!domain || !(domain->type & __IOMMU_DOMAIN_PAGING))
+ return;
+
+ mutex_lock(&smmu->lock);
+
+ for (i = 0; i < fwspec->num_ids; i++) {
+ const struct tegra_smmu_swgroup *group;
+ u32 value;
+
+ group = tegra_smmu_find_swgroup(smmu, fwspec->ids[i]);
+ if (!group || !group->defer_enable)
+ continue;
+
+ value = smmu_readl(smmu, group->reg);
+ value |= SMMU_ASID_ENABLE;
+ smmu_writel(smmu, value, group->reg);
+ }
+
+ mutex_unlock(&smmu->lock);
+}
+EXPORT_SYMBOL_GPL(tegra_smmu_enable_translation);
+
static int tegra_smmu_as_prepare(struct tegra_smmu *smmu,
struct tegra_smmu_as *as)
{
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 2cb153091228..2883c4f91144 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1028,8 +1028,10 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
{ .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index df87c5038625..a38fbf2baf6c 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1070,8 +1070,10 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index f58f3ef6f681..92e809cd7c26 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -1166,8 +1166,10 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra210_swgroups[] = {
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
{ .name = "isp2", .swgroup = TEGRA_SWGROUP_ISP2, .reg = 0x258 },
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 8389e3af0121..0ee4715413ba 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1139,8 +1139,10 @@ static const struct tegra_mc_client tegra30_mc_clients[] = {
};
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
- { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240 },
- { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244 },
+ { .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
+ .defer_enable = true },
+ { .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
+ .defer_enable = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
{ .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
{ .name = "mpe", .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index 25d465d70493..ea67972fd585 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -70,6 +70,14 @@ struct tegra_smmu_swgroup {
const char *name;
unsigned int swgroup;
unsigned int reg;
+
+ /*
+ * Set for clients which may already be running when the kernel takes
+ * over, e.g. display controllers scanning out a boot splash. They are
+ * left in bypass until their driver calls
+ * tegra_smmu_enable_translation().
+ */
+ bool defer_enable;
};
struct tegra_smmu_group_soc {
@@ -103,6 +111,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc);
void tegra_smmu_remove(struct tegra_smmu *smmu);
+void tegra_smmu_enable_translation(struct device *dev);
#else
static inline struct tegra_smmu *
tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
@@ -114,6 +123,10 @@ tegra_smmu_probe(struct device *dev, const struct tegra_smmu_soc *soc,
static inline void tegra_smmu_remove(struct tegra_smmu *smmu)
{
}
+
+static inline void tegra_smmu_enable_translation(struct device *dev)
+{
+}
#endif
struct tegra_mc_reset {
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (7 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 10/11] drm/tegra: " Mikko Perttunen
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
host1x_iommu_attach currently detaches a possible ARM DMA mapping on the
device, since it would get in the way of the shared domain host1x wants.
We are about to enable default domain based DMA domains for Host1x and
TegraDRM devices. These will take the same path as on 64-bit ARM, but
we need to avoid trying to detach the now perfectly functional mapping
on the device, so get rid of that code.
Strictly speaking, the code removed here would not be harmful but would
cause WARNs to happen.
nouveau and tegra-vde have the same construct, but their clients stay on
identity domains for now, so those copies are left alone.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/host1x/dev.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 3f475f0e6545..27ca523a6ada 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -23,10 +23,6 @@
#include <trace/events/host1x.h>
#undef CREATE_TRACE_POINTS
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-#include <asm/dma-iommu.h>
-#endif
-
#include "bus.h"
#include "channel.h"
#include "context.h"
@@ -379,17 +375,6 @@ static struct iommu_domain *host1x_iommu_attach(struct host1x *host)
struct iommu_domain *domain = iommu_get_domain_for_dev(host->dev);
int err;
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
- if (host->dev->archdata.mapping) {
- struct dma_iommu_mapping *mapping =
- to_dma_iommu_mapping(host->dev);
- arm_iommu_detach_device(host->dev);
- arm_iommu_release_mapping(mapping);
-
- domain = iommu_get_domain_for_dev(host->dev);
- }
-#endif
-
/*
* We may not always want to enable IOMMU support (for example if the
* host1x firewall is already enabled and we don't support addressing
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 10/11] drm/tegra: Drop release of legacy 32-bit ARM DMA domain
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (8 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-15 2:15 ` [PATCH 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup Mikko Perttunen
2026-09-25 15:20 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Marek Szyprowski
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
host1x_client_iommu_attach currently detaches a possible ARM DMA mapping
on the device, since it would get in the way of the shared domain
TegraDRM wants.
We are about to enable default domain based DMA domains for Host1x and
TegraDRM devices. These will take the same path as on 64-bit ARM, but
we need to avoid trying to detach the now perfectly functional mapping
on the device, so get rid of that code.
Strictly speaking, the code removed here would not be harmful but would
cause WARNs to happen.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/gpu/drm/tegra/drm.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index dd6c564ff408..39a271241cf0 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -25,10 +25,6 @@
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
-#include <asm/dma-iommu.h>
-#endif
-
#include "dc.h"
#include "drm.h"
#include "gem.h"
@@ -947,17 +943,6 @@ int host1x_client_iommu_attach(struct host1x_client *client)
struct iommu_group *group = NULL;
int err;
-#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
- if (client->dev->archdata.mapping) {
- struct dma_iommu_mapping *mapping =
- to_dma_iommu_mapping(client->dev);
- arm_iommu_detach_device(client->dev);
- arm_iommu_release_mapping(mapping);
-
- domain = iommu_get_domain_for_dev(client->dev);
- }
-#endif
-
/*
* If the host1x client is already attached to an IOMMU domain that is
* not the shared IOMMU domain, don't try to attach it to a different
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (9 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 10/11] drm/tegra: " Mikko Perttunen
@ 2026-09-15 2:15 ` Mikko Perttunen
2026-09-25 15:20 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Marek Szyprowski
11 siblings, 0 replies; 13+ messages in thread
From: Mikko Perttunen @ 2026-09-15 2:15 UTC (permalink / raw)
To: Robin Murphy, Marek Szyprowski, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
Mikko Perttunen, David Airlie, Simona Vetter, Jonathan Hunter,
Krishna Reddy, Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
Everything is now in place for the Tegra SMMU to give devices a real
IOMMU_DOMAIN_DMA default domain.
Some device drivers still rely on explicit domains (and being able to
detach a legacy ARM DMA domain if any), so DMA default domains are
enabled on a per-swgroup basis via a use_dma_api flag. As drivers are
converted to DMA API usage we can eventually get rid of this. Initially
the flag is enabled for TegraDRM and Host1x devices.
On multi_v7_defconfig (where CONFIG_ARM_DMA_USE_IOMMU was always
enabled), this will fix TegraDRM probing with certain probe orderings
where the legacy implementation's lack of support for multiple devices
in one IOMMU group would cause breakage.
On tegra_defconfig, as this now enables CONFIG_ARM_DMA_USE_IOMMU, all
devices move to either the legacy ARM DMA path or the new one. This
should in principle not cause issues as the drivers would already have
been verified on multi_v7_defconfig.
Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
---
drivers/iommu/Kconfig | 1 +
drivers/iommu/tegra-smmu.c | 58 ++++++++++++++++++++++++++++++++++++++---
drivers/memory/tegra/tegra114.c | 13 +++++----
drivers/memory/tegra/tegra124.c | 10 ++++---
drivers/memory/tegra/tegra30.c | 16 +++++++-----
include/soc/tegra/mc.h | 7 +++++
6 files changed, 86 insertions(+), 19 deletions(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6e07bd69467a..817e84e0e6ab 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -252,6 +252,7 @@ config TEGRA_IOMMU_SMMU
depends on TEGRA_AHB
depends on TEGRA_MC
select IOMMU_API
+ select ARM_DMA_USE_IOMMU if ARM
help
This driver supports the IOMMU hardware (SMMU) found on NVIDIA Tegra
SoCs (Tegra30 up to Tegra210).
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 6636a044905f..8a5c3f80fcff 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -1037,12 +1037,26 @@ static int tegra_smmu_of_xlate(struct device *dev,
static int tegra_smmu_def_domain_type(struct device *dev)
{
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
+ unsigned int i;
+
+ if (!fwspec || !smmu)
+ return IOMMU_DOMAIN_IDENTITY;
+
/*
- * FIXME: For now we want to run all translation in IDENTITY mode, due
- * to some device quirks. Better would be to just quirk the troubled
- * devices.
+ * Only clients known to work with the DMA API get a DMA default domain.
+ * The rest keep running in identity mode.
*/
- return IOMMU_DOMAIN_IDENTITY;
+ for (i = 0; i < fwspec->num_ids; i++) {
+ const struct tegra_smmu_swgroup *group;
+
+ group = tegra_smmu_find_swgroup(smmu, fwspec->ids[i]);
+ if (!group || !group->use_dma_api)
+ return IOMMU_DOMAIN_IDENTITY;
+ }
+
+ return IOMMU_DOMAIN_DMA;
}
static const struct iommu_ops tegra_smmu_ops = {
@@ -1151,6 +1165,40 @@ static void tegra_smmu_debugfs_exit(struct tegra_smmu *smmu)
debugfs_remove_recursive(smmu->debugfs);
}
+/*
+ * The default domain type is a property of the IOMMU group, so all swgroups in
+ * one group must agree on use_dma_api. Otherwise the core sees conflicting
+ * def_domain_type and falls back to identity for the whole group.
+ */
+static void tegra_smmu_check_groups(struct tegra_smmu *smmu)
+{
+ const struct tegra_smmu_soc *soc = smmu->soc;
+ unsigned int i, j;
+
+ for (i = 0; i < soc->num_groups; i++) {
+ const struct tegra_smmu_group_soc *group = &soc->groups[i];
+ int dma_api = -1;
+
+ for (j = 0; j < group->num_swgroups; j++) {
+ const struct tegra_smmu_swgroup *swgrp;
+
+ swgrp = tegra_smmu_find_swgroup(smmu,
+ group->swgroups[j]);
+ if (!swgrp)
+ continue;
+
+ if (dma_api < 0)
+ dma_api = swgrp->use_dma_api;
+ else if (swgrp->use_dma_api != !!dma_api)
+ break;
+ }
+
+ WARN(j < group->num_swgroups,
+ "SMMU group %s mixes DMA API and identity swgroups\n",
+ group->name);
+ }
+}
+
struct tegra_smmu *tegra_smmu_probe(struct device *dev,
const struct tegra_smmu_soc *soc,
struct tegra_mc *mc)
@@ -1185,6 +1233,8 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
smmu->dev = dev;
smmu->mc = mc;
+ tegra_smmu_check_groups(smmu);
+
smmu->pfn_mask =
BIT_MASK(mc->soc->num_address_bits - SMMU_PTE_SHIFT) - 1;
dev_dbg(dev, "address bits: %u, PFN mask: %#lx\n",
diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c
index 2883c4f91144..024c63bdd480 100644
--- a/drivers/memory/tegra/tegra114.c
+++ b/drivers/memory/tegra/tegra114.c
@@ -1029,15 +1029,18 @@ static const struct tegra_mc_client tegra114_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index a38fbf2baf6c..17068aaf163f 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -1071,13 +1071,14 @@ static const struct tegra_mc_client tegra124_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "msenc", .swgroup = TEGRA_SWGROUP_MSENC, .reg = 0x264 },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x274 },
@@ -1093,7 +1094,8 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
{ .name = "sdmmc2a", .swgroup = TEGRA_SWGROUP_SDMMC2A, .reg = 0xa98 },
{ .name = "sdmmc3a", .swgroup = TEGRA_SWGROUP_SDMMC3A, .reg = 0xa9c },
{ .name = "sdmmc4a", .swgroup = TEGRA_SWGROUP_SDMMC4A, .reg = 0xaa0 },
- { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284 },
+ { .name = "vic", .swgroup = TEGRA_SWGROUP_VIC, .reg = 0x284,
+ .use_dma_api = true },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
};
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index 0ee4715413ba..df6b5a499ccc 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -1140,19 +1140,23 @@ static const struct tegra_mc_client tegra30_mc_clients[] = {
static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
{ .name = "dc", .swgroup = TEGRA_SWGROUP_DC, .reg = 0x240,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "dcb", .swgroup = TEGRA_SWGROUP_DCB, .reg = 0x244,
- .defer_enable = true },
+ .defer_enable = true, .use_dma_api = true },
{ .name = "epp", .swgroup = TEGRA_SWGROUP_EPP, .reg = 0x248 },
- { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c },
+ { .name = "g2", .swgroup = TEGRA_SWGROUP_G2, .reg = 0x24c,
+ .use_dma_api = true },
{ .name = "mpe", .swgroup = TEGRA_SWGROUP_MPE, .reg = 0x264 },
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
{ .name = "afi", .swgroup = TEGRA_SWGROUP_AFI, .reg = 0x238 },
{ .name = "avpc", .swgroup = TEGRA_SWGROUP_AVPC, .reg = 0x23c },
- { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268 },
- { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c },
+ { .name = "nv", .swgroup = TEGRA_SWGROUP_NV, .reg = 0x268,
+ .use_dma_api = true },
+ { .name = "nv2", .swgroup = TEGRA_SWGROUP_NV2, .reg = 0x26c,
+ .use_dma_api = true },
{ .name = "hda", .swgroup = TEGRA_SWGROUP_HDA, .reg = 0x254 },
- { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250 },
+ { .name = "hc", .swgroup = TEGRA_SWGROUP_HC, .reg = 0x250,
+ .use_dma_api = true },
{ .name = "ppcs", .swgroup = TEGRA_SWGROUP_PPCS, .reg = 0x270 },
{ .name = "sata", .swgroup = TEGRA_SWGROUP_SATA, .reg = 0x278 },
{ .name = "vde", .swgroup = TEGRA_SWGROUP_VDE, .reg = 0x27c },
diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h
index ea67972fd585..1cbd990fc0f4 100644
--- a/include/soc/tegra/mc.h
+++ b/include/soc/tegra/mc.h
@@ -78,6 +78,13 @@ struct tegra_smmu_swgroup {
* tegra_smmu_enable_translation().
*/
bool defer_enable;
+
+ /*
+ * Set once the clients behind this swgroup are known to work with an
+ * IOMMU_DOMAIN_DMA default domain. All swgroups of a
+ * tegra_smmu_group_soc must agree.
+ */
+ bool use_dma_api;
};
struct tegra_smmu_group_soc {
--
2.55.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC)
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
` (10 preceding siblings ...)
2026-09-15 2:15 ` [PATCH 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup Mikko Perttunen
@ 2026-09-25 15:20 ` Marek Szyprowski
11 siblings, 0 replies; 13+ messages in thread
From: Marek Szyprowski @ 2026-09-25 15:20 UTC (permalink / raw)
To: Mikko Perttunen, Robin Murphy, Joerg Roedel (AMD),
Will Deacon, Russell King, Heiko Stuebner, Thierry Reding,
David Airlie, Simona Vetter, Jonathan Hunter, Krishna Reddy,
Krzysztof Kozlowski, Jason Gunthorpe
Cc: Peter Griffin, Alim Akhtar, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Yong Wu, Matthias Brugger,
AngeloGioacchino Del Regno, Rob Clark, Geert Uytterhoeven, iommu,
linux-kernel, linux-arm-kernel, dri-devel, linux-tegra
On 15.09.2026 04:14, Mikko Perttunen wrote:
> Depending on the probe ordering, TegraDRM does not currently probe on
> kernels with CONFIG_ARM_DMA_USE_IOMMU enabled, such as with
> multi_v7_defconfig. The Tegra SMMU puts several display and 2D/3D
> clients into one IOMMU group, and the legacy 32-bit ARM DMA API path
> creates one UNMANAGED domain per device and attaches it with
> iommu_attach_device(). That only works for one device per group, so
> every client after the first fails to attach,
> iommu_device_use_default_domain() rejects the group with -EBUSY, and the
> driver goes down with it. tegra_defconfig is currently working by virtue
> of not enabling CONFIG_ARM_DMA_USE_IOMMU.
>
> Robin suggested [1] that instead of teaching the legacy path about
> groups, we should explore modernizing the 32-bit ARM DMA code a bit by
> making it use domain cookies and have the core code own the domains, as
> is the case on other architectures. Still, ARM's own DMA code is kept to
> support existing drivers and APIs that expect it, and because dma-iommu
> is considered too big and unnecessary for 32-bit ARM.
I agree that such little modernization is the right step to unify a bit
ARM 32bit with modern archs and get rid of some workaround in the drivers.
It was me who raised the concern about the drivers depending on the first-fit
allocation algorithm used in the32-bit ARM DMA code. This is an issue for some
legacy Samsung Exynos drivers. I not aware of any other driver used on ARM 32bit
SoCs that depends on this behavior.
If one is still interested in switching ARM 32bit to generic IOMMU-DMA code I
can prepare a workaround for those Exynos drivers. I still have it on my todo
list, but in meantime there was no interest in updating ARM 32bit DMA code.
> The series is in four parts:
>
> - Patches 1-2 are preparation in the IOMMU core:
> 1. Add iommu_teardown_dma_ops, counterpart to iommu_setup_dma_ops.
> 2. Add cookie type for ARM's DMA domains.
> - Patches 3-6 implement the DMA API side on 32-bit ARM:
> 3. Split up domain and address space management code in arch/arm/.
> 4. Implement parallel dma_ops / cookie APIs on ARM side.
> 5. Avoid domain allocation when IOMMU core does it.
> 6. Support foreign-allocated sg_tables.
> - Patch 7 enables 32-bit ARM IOMMU drivers to provide a DMA default
> domain.
> - Patches 8-11 convert Tegra:
> 8. Allows deferring translation enable until driver handshake for
> selected devices.
> 9-10. Removes code detaching ARM UNMANAGED DMA domains in
> Host1x/TegraDRM.
> 11. Enables DMA default domain for selected Tegra devices.
>
> None of this should have any functional effect on any device other than
> the Tegra devices enabled in patches 8-11. Other IOMMU drivers can opt
> in later. The new DMA default domains are enabled only when the IOMMU
> driver explicitly specifies a DMA domain type, just returning 0 for the
> default is not enough (unlike on arm64), keeping the current behavior.
>
> Patches 8-11 are provided for demonstration but we can deal with them
> separately if the rest of the series is accepted.
>
> Patches 1,2,3 are applicable independently.
> Patches 4,5 depend on the previous patches.
> Patch 6 can be applied independently but is only required once
> the whole series lands (by TegraDRM).
> Patch 7 depends on patch 4.
>
> Patches 1-7 only touch drivers/iommu/ and arch/arm/mm/ and associated
> headers.
>
> Best regards,
> Mikko
>
> [1] https://lore.kernel.org/linux-tegra/910d1492-ad3b-4201-93c6-208be1dcde7d@arm.com/
>
> ---
> Mikko Perttunen (11):
> iommu: Add iommu_teardown_dma_ops
> iommu: Add cookie type for ARM_DMA_USE_IOMMU
> ARM: dma-mapping: Split mapping allocation from domain allocation
> ARM: dma-mapping: Implement and expose DMA setup functions
> ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains
> ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable
> iommu: Let 32-bit ARM drivers opt into a DMA API default domain
> iommu/tegra-smmu: Defer display client translation to driver handover
> gpu: host1x: Drop release of legacy 32-bit ARM DMA domain
> drm/tegra: Drop release of legacy 32-bit ARM DMA domain
> iommu/tegra-smmu: Use a DMA API default domain per swgroup
>
> arch/arm/include/asm/dma-iommu.h | 17 ++++
> arch/arm/mm/dma-mapping.c | 209 ++++++++++++++++++++++++++++++++-------
> drivers/gpu/drm/tegra/dc.c | 8 ++
> drivers/gpu/drm/tegra/drm.c | 15 ---
> drivers/gpu/host1x/dev.c | 15 ---
> drivers/iommu/Kconfig | 1 +
> drivers/iommu/dma-iommu.c | 5 +
> drivers/iommu/dma-iommu.h | 52 +++++++---
> drivers/iommu/iommu.c | 24 ++---
> drivers/iommu/tegra-smmu.c | 109 +++++++++++++++++++-
> drivers/memory/tegra/tegra114.c | 15 ++-
> drivers/memory/tegra/tegra124.c | 12 ++-
> drivers/memory/tegra/tegra210.c | 6 +-
> drivers/memory/tegra/tegra30.c | 18 ++--
> include/linux/iommu.h | 3 +
> include/soc/tegra/mc.h | 20 ++++
> 16 files changed, 410 insertions(+), 119 deletions(-)
> ---
> base-commit: f5098b6bae761e346ebcd9da7f95622c04733cff
> change-id: 20260728-b4-arm32-iommu-dma-cookie-3170e07f9125
>
>
Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-09-25 15:21 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <CGME20260915021542eucas1p254addab18c10cb117f42ccf3d3522883@eucas1p2.samsung.com>
2026-09-15 2:14 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Mikko Perttunen
2026-09-15 2:14 ` [PATCH 01/11] iommu: Add iommu_teardown_dma_ops Mikko Perttunen
2026-09-15 2:14 ` [PATCH 02/11] iommu: Add cookie type for ARM_DMA_USE_IOMMU Mikko Perttunen
2026-09-15 2:14 ` [PATCH 03/11] ARM: dma-mapping: Split mapping allocation from domain allocation Mikko Perttunen
2026-09-15 2:14 ` [PATCH 04/11] ARM: dma-mapping: Implement and expose DMA setup functions Mikko Perttunen
2026-09-15 2:15 ` [PATCH 05/11] ARM: dma-mapping: Bypass the legacy path for core-owned DMA API domains Mikko Perttunen
2026-09-15 2:15 ` [PATCH 06/11] ARM: dma-mapping: Handle foreign buffers in arm_iommu_get_sgtable Mikko Perttunen
2026-09-15 2:15 ` [PATCH 07/11] iommu: Let 32-bit ARM drivers opt into a DMA API default domain Mikko Perttunen
2026-09-15 2:15 ` [PATCH 08/11] iommu/tegra-smmu: Defer display client translation to driver handover Mikko Perttunen
2026-09-15 2:15 ` [PATCH 09/11] gpu: host1x: Drop release of legacy 32-bit ARM DMA domain Mikko Perttunen
2026-09-15 2:15 ` [PATCH 10/11] drm/tegra: " Mikko Perttunen
2026-09-15 2:15 ` [PATCH 11/11] iommu/tegra-smmu: Use a DMA API default domain per swgroup Mikko Perttunen
2026-09-25 15:20 ` [PATCH 00/11] Make the 32-bit ARM DMA API work with normal IOMMU DMA default domains (+ Tegra PoC) Marek Szyprowski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®