tegra_bo_alloc currently uses drm_gem_get_pages only when tegra->domain is set; otherwise memory is allocated using dma_alloc_wc on the drm device. The latter is correct when clients are not backed by an IOMMU and they need physically contiguous buffers, but the path also gets taken with IOMMU-backed DMA API, as tegra->domain is only set when using the explicit IOMMU api.
Record whether clients are attached to a DMA IOMMU domain during initialization, and use the drm_gem_get_pages path in that case. Initialization for all clients has to finish before GEM allocation is available so this is safe. Typically all devices should be IOMMU-backed, or none of them. In a mixed configuration, fall back to the physical memory path since we don't know which client accesses the buffer at allocation time. The drm device's DMA masks are also changed to DMA_BIT_MASK(64) when the DMA IOMMU path is enabled to avoid bounce buffer related issues when mapping the allocated memory to the drm device for cache maintenance purposes. Signed-off-by: Mikko Perttunen <[email protected]> --- drivers/gpu/drm/tegra/drm.c | 23 ++++++++++++++++------- drivers/gpu/drm/tegra/drm.h | 2 ++ drivers/gpu/drm/tegra/gem.c | 16 +++++++--------- drivers/gpu/drm/tegra/gem.h | 4 +++- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 324480c87fb1..1013bbd662f8 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -958,14 +958,10 @@ int host1x_client_iommu_attach(struct host1x_client *client) } #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 - * domain. This allows using the IOMMU-backed DMA API. - */ - if (domain && domain->type != IOMMU_DOMAIN_IDENTITY && - domain != tegra->domain) + if (domain && iommu_is_dma_domain(domain)) { + tegra->use_dma_iommu = true; return 0; + } if (tegra->domain) { group = iommu_group_get(client->dev); @@ -981,6 +977,8 @@ int host1x_client_iommu_attach(struct host1x_client *client) } tegra->use_explicit_iommu = true; + } else { + tegra->use_direct_dma = true; } client->group = group; @@ -1232,6 +1230,17 @@ static int host1x_drm_probe(struct host1x_device *dev) iova_cache_put(); } + if (tegra->use_dma_iommu && tegra->use_direct_dma) { + dev_warn(&dev->dev, "Mixed IOMMU and direct DMA clients, using contiguous buffers\n"); + tegra->use_dma_iommu = false; + } + + if (tegra->use_dma_iommu) { + err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)); + if (err) + goto device; + } + if (tegra->hub) { err = tegra_display_hub_prepare(tegra->hub); if (err < 0) diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index bc720ae8d95e..63d99d3ff422 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -36,6 +36,8 @@ struct tegra_drm { struct iommu_domain *domain; bool use_explicit_iommu; + bool use_dma_iommu; + bool use_direct_dma; struct mutex mm_lock; struct drm_mm mm; diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index f76af733ea79..bb8d9f9e6453 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -111,10 +111,6 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_ } if (obj->pages) { - /* - * If the buffer object was allocated from the explicit IOMMU - * API code paths, construct an SG table from the pages. - */ err = sg_alloc_table_from_pages(map->sgt, obj->pages, obj->num_pages, 0, gem->size, GFP_KERNEL); if (err < 0) @@ -378,15 +374,17 @@ static int tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo) struct tegra_drm *tegra = drm->dev_private; int err; - if (tegra->domain) { + if (tegra->domain || tegra->use_dma_iommu) { err = tegra_bo_get_pages(drm, bo); if (err < 0) return err; - err = tegra_bo_iommu_map(tegra, bo); - if (err < 0) { - tegra_bo_free(drm, bo); - return err; + if (tegra->domain) { + err = tegra_bo_iommu_map(tegra, bo); + if (err < 0) { + tegra_bo_free(drm, bo); + return err; + } } } else { size_t size = bo->gem.size; diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index bf2cbd48eb3f..da43bef546ec 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h @@ -37,7 +37,9 @@ struct tegra_bo_tiling { * * Buffer source | Mapping API(*) | Fields * ---------------+-----------------+--------------- - * Allocated here | DMA API | iova (IOVA mapped to drm->dev), vaddr (CPU VA) + * Allocated here | DMA API, IOMMU | pages/num_pages (Phys. memory), sgt (Mapped to drm->dev) + * + * Allocated here | DMA API, direct | iova (DMA address on drm->dev), vaddr (CPU VA) * * Allocated here | IOMMU API | pages/num_pages (Phys. memory), sgt (Mapped to drm->dev), * | iova/size (Mapped to domain) --- base-commit: 20a33c6b6cbe7e4805dfb6cd68816937443aa148 change-id: 20261026-b4-tegradrm-allocate-shmem-a0090b948f5c
