diff mbox series

[v2,4/4] media: platform: s5p-mfc: add support for generic DMA-IOMMU glue code

Message ID 20200918144833.14618-5-m.szyprowski@samsung.com
State New
Headers show
Series None | expand

Commit Message

Marek Szyprowski Sept. 18, 2020, 2:48 p.m. UTC
S5P-MFC driver relies on the way the ARM DMA-IOMMU glue code works:
the IOVA allocator uses first-fit algorithm, so the first allocated
buffer is at 0x0 DMA/IOVA address. This is not true for the generic
IOMMU-DMA glue code that will be used for ARM architecture soon, so add
the needed code to support it too.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/media/platform/s5p-mfc/s5p_mfc.c      | 35 ++++++++++++++++++-
 .../media/platform/s5p-mfc/s5p_mfc_common.h   |  2 ++
 2 files changed, 36 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index 4e50c342b322..63cbf1d29c43 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -10,6 +10,7 @@ 
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iommu.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/sched.h>
@@ -1201,6 +1202,10 @@  static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
 	if (!mfc_dev->mem_bitmap)
 		return -ENOMEM;
 
+	/* MFC v5 can access memory only via the 128M window */
+	if (exynos_is_iommu_available(dev) && !IS_MFCV6_PLUS(mfc_dev))
+		dma_set_mask_and_coherent(dev, SZ_128M - 1);
+
 	mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size,
 					       &mfc_dev->mem_base, GFP_KERNEL);
 	if (!mfc_dev->mem_virt) {
@@ -1218,13 +1223,37 @@  static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
 	 * as used (to keep required base alignment) and adjust base address
 	 */
 	if (mfc_dev->mem_base == (dma_addr_t)0) {
-		unsigned int offset = 1 << MFC_BASE_ALIGN_ORDER;
+		unsigned int offset = MFC_MIN_VALID_BASE;
 
 		bitmap_set(mfc_dev->mem_bitmap, 0, offset >> PAGE_SHIFT);
 		mfc_dev->dma_base[BANK_L_CTX] += offset;
 		mfc_dev->dma_base[BANK_R_CTX] += offset;
 	}
 
+	/*
+	 * Generic DMA-IOMMU use last-fit memory allocation algorithm, so
+	 * remap the firmware to the lowest supported address for MFC v5 to
+	 * let HW properly address buffers as an offset from the firmware.
+	 */
+	if (IS_ENABLED(CONFIG_IOMMU_DMA) && exynos_is_iommu_available(dev) &&
+	    !IS_MFCV6_PLUS(mfc_dev)) {
+		struct sg_table sgt;
+		int size;
+
+		if (dma_get_sgtable(dev, &sgt, mfc_dev->mem_virt,
+				    mfc_dev->mem_base, mfc_dev->mem_size) != 0)
+			return -ENOMEM;
+		size = iommu_map_sgtable(iommu_get_domain_for_dev(dev),
+					 MFC_MIN_VALID_BASE, &sgt,
+					 IOMMU_READ | IOMMU_WRITE);
+		sg_free_table(&sgt);
+		if (size != mem_size)
+			return -ENOMEM;
+
+		mfc_dev->dma_base[BANK_L_CTX] = MFC_MIN_VALID_BASE;
+		mfc_dev->dma_base[BANK_R_CTX] = MFC_MIN_VALID_BASE;
+	}
+
 	/* Firmware allocation cannot fail in this case */
 	s5p_mfc_alloc_firmware(mfc_dev);
 
@@ -1241,6 +1270,10 @@  static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev)
 {
 	struct device *dev = &mfc_dev->plat_dev->dev;
 
+	if (IS_ENABLED(CONFIG_IOMMU_DMA) && exynos_is_iommu_available(dev) &&
+	    !IS_MFCV6_PLUS(mfc_dev))
+		iommu_unmap(iommu_get_domain_for_dev(dev), MFC_MIN_VALID_BASE,
+			    mfc_dev->mem_size);
 	dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt,
 			  mfc_dev->mem_base);
 	kfree(mfc_dev->mem_bitmap);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index 96d1ecd1521b..f28c943b8426 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -37,6 +37,8 @@ 
 #define MFC_BANK2_ALIGN_ORDER	13
 #define MFC_BASE_ALIGN_ORDER	17
 
+#define MFC_MIN_VALID_BASE	(1 << MFC_BASE_ALIGN_ORDER)
+
 #define MFC_FW_MAX_VERSIONS	2
 
 #include <media/videobuf2-dma-contig.h>