diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index 45b8cdef94b0..1a87af65c06a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1184,6 +1184,18 @@ static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) struct device *dev = &mfc_dev->plat_dev->dev; unsigned long mem_size = SZ_8M; unsigned int bitmap_size; + /* + * When IOMMU is available, we cannot use the default configuration, + * because of MFC firmware requirements: address space limited to + * 256M and non-zero default start address. + * This is still simplified, not optimal configuration, but for now + * IOMMU core doesn't allow to configure device's IOMMUs channel + * separately. + */ + int ret = exynos_configure_iommu(dev, S5P_MFC_IOMMU_DMA_BASE, + S5P_MFC_IOMMU_DMA_SIZE); + if (ret) + return ret; if (mfc_mem_size) mem_size = memparse(mfc_mem_size, NULL); @@ -1191,8 +1203,10 @@ static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) bitmap_size = BITS_TO_LONGS(mem_size >> PAGE_SHIFT) * sizeof(long); mfc_dev->mem_bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!mfc_dev->mem_bitmap) + if (!mfc_dev->mem_bitmap) { + exynos_unconfigure_iommu(dev); return -ENOMEM; + } mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size, &mfc_dev->mem_base, GFP_KERNEL); @@ -1200,24 +1214,13 @@ static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) kfree(mfc_dev->mem_bitmap); dev_err(dev, "failed to preallocate %ld MiB for the firmware and context buffers\n", (mem_size / SZ_1M)); + exynos_unconfigure_iommu(dev); return -ENOMEM; } mfc_dev->mem_size = mem_size; mfc_dev->dma_base[BANK1_CTX] = mfc_dev->mem_base; mfc_dev->dma_base[BANK2_CTX] = mfc_dev->mem_base; - /* - * MFC hardware cannot handle 0 as a base address, so mark first 128K - * 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; - - bitmap_set(mfc_dev->mem_bitmap, 0, offset >> PAGE_SHIFT); - mfc_dev->dma_base[BANK1_CTX] += offset; - mfc_dev->dma_base[BANK2_CTX] += offset; - } - /* Firmware allocation cannot fail in this case */ s5p_mfc_alloc_firmware(mfc_dev); @@ -1234,6 +1237,7 @@ static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev) { struct device *dev = &mfc_dev->plat_dev->dev; + exynos_unconfigure_iommu(dev); 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_iommu.h b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h index 76667924ee2a..6962132ae8fa 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h @@ -11,13 +11,54 @@ #ifndef S5P_MFC_IOMMU_H_ #define S5P_MFC_IOMMU_H_ -#if defined(CONFIG_EXYNOS_IOMMU) +#define S5P_MFC_IOMMU_DMA_BASE 0x20000000lu +#define S5P_MFC_IOMMU_DMA_SIZE SZ_256M + +#if defined(CONFIG_EXYNOS_IOMMU) && defined(CONFIG_ARM_DMA_USE_IOMMU) + +#include static inline bool exynos_is_iommu_available(struct device *dev) { return dev->archdata.iommu != NULL; } +static inline void exynos_unconfigure_iommu(struct device *dev) +{ + struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev); + + arm_iommu_detach_device(dev); + arm_iommu_release_mapping(mapping); +} + +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + struct dma_iommu_mapping *mapping = NULL; + int ret; + + /* Disable the default mapping created by device core */ + if (to_dma_iommu_mapping(dev)) + exynos_unconfigure_iommu(dev); + + mapping = arm_iommu_create_mapping(dev->bus, base, size); + if (IS_ERR(mapping)) { + pr_warn("Failed to create IOMMU mapping for device %s\n", + dev_name(dev)); + return PTR_ERR(mapping); + } + + ret = arm_iommu_attach_device(dev, mapping); + if (ret) { + pr_warn("Failed to attached device %s to IOMMU_mapping\n", + dev_name(dev)); + arm_iommu_release_mapping(mapping); + return ret; + } + + return 0; +} + #else static inline bool exynos_is_iommu_available(struct device *dev) @@ -25,6 +66,14 @@ static inline bool exynos_is_iommu_available(struct device *dev) return false; } +static inline int exynos_configure_iommu(struct device *dev, + unsigned int base, unsigned int size) +{ + return -ENOSYS; +} + +static inline void exynos_unconfigure_iommu(struct device *dev) { } + #endif #endif /* S5P_MFC_IOMMU_H_ */