@@ -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);
@@ -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>
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(-)