From a637d597b37853a90e27d4dab980d3d1e8513fce Mon Sep 17 00:00:00 2001 From: Janghyuck Kim Date: Tue, 8 Aug 2017 15:15:08 +0900 Subject: [PATCH] iommu/exynos: support 1:1 mapping between DVA and PA This patch supports interface of 1:1 mapping between DVA and PA, which is required for frame buffer handover Change-Id: Ib7b3a2382621900e05016e289fbadccb51c5f663 Signed-off-by: Janghyuck Kim Signed-off-by: ChiHun Won --- drivers/iommu/exynos-iovmm.c | 63 ++++++++++++++++++++++++++++++++++++ include/linux/exynos_iovmm.h | 2 ++ 2 files changed, 65 insertions(+) diff --git a/drivers/iommu/exynos-iovmm.c b/drivers/iommu/exynos-iovmm.c index df4857049094..07c449f4fe63 100644 --- a/drivers/iommu/exynos-iovmm.c +++ b/drivers/iommu/exynos-iovmm.c @@ -613,6 +613,69 @@ void exynos_iovmm_unmap_userptr(struct device *dev, dma_addr_t iova) } } +int iovmm_map_oto(struct device *dev, phys_addr_t phys, size_t size) +{ + struct exynos_iovmm *vmm = exynos_get_iovmm(dev); + int ret; + + BUG_ON(!IS_ALIGNED(phys, PAGE_SIZE)); + BUG_ON(!IS_ALIGNED(size, PAGE_SIZE)); + + if (vmm == NULL) { + dev_err(dev, "%s: IOVMM not found\n", __func__); + return -EINVAL; + } + + if (WARN_ON((phys < vmm->iova_start) || + (phys + size) >= (vmm->iova_start + vmm->iovm_size))) { + dev_err(dev, + "Unable to create one to one mapping for %#zx @ %pa\n", + size, &phys); + return -EINVAL; + } + + if (!add_iovm_region(vmm, (dma_addr_t)phys, size)) + return -EADDRINUSE; + + ret = iommu_map(vmm->domain, (dma_addr_t)phys, phys, size, 0); + if (ret < 0) + free_iovm_region(vmm, + remove_iovm_region(vmm, (dma_addr_t)phys)); + + return ret; +} + +void iovmm_unmap_oto(struct device *dev, phys_addr_t phys) +{ + struct exynos_iovmm *vmm = exynos_get_iovmm(dev); + struct exynos_vm_region *region; + size_t unmap_size; + + /* This function must not be called in IRQ handlers */ + BUG_ON(in_irq()); + BUG_ON(!IS_ALIGNED(phys, PAGE_SIZE)); + + if (vmm == NULL) { + dev_err(dev, "%s: IOVMM not found\n", __func__); + return; + } + + region = remove_iovm_region(vmm, (dma_addr_t)phys); + if (region) { + unmap_size = iommu_unmap(vmm->domain, (dma_addr_t)phys, + region->size); + WARN_ON(unmap_size != region->size); + + exynos_sysmmu_tlb_invalidate(vmm->domain, (dma_addr_t)phys, + region->size); + + free_iovm_region(vmm, region); + + dev_dbg(dev, "IOVMM: Unmapped %#zx bytes from %pa.\n", + unmap_size, &phys); + } +} + static struct dentry *exynos_iovmm_debugfs_root; static struct dentry *exynos_iommu_debugfs_root; diff --git a/include/linux/exynos_iovmm.h b/include/linux/exynos_iovmm.h index d219a9c37bc3..6c69c939c8d7 100644 --- a/include/linux/exynos_iovmm.h +++ b/include/linux/exynos_iovmm.h @@ -240,6 +240,8 @@ dma_addr_t exynos_iovmm_map_userptr(struct device *dev, unsigned long vaddr, */ void exynos_iovmm_unmap_userptr(struct device *dev, dma_addr_t iova); +int iovmm_map_oto(struct device *dev, phys_addr_t phys, size_t size); +void iovmm_unmap_oto(struct device *dev, phys_addr_t phys); /* * The handle_pte_fault() is called by exynos_sysmmu_map_user_pages(). * Driver cannot include include/linux/huge_mm.h because -- 2.20.1