1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (c) 2012 Samsung Electronics Co., Ltd. 4 // Author: Inki Dae <inki.dae@samsung.com> 5 // Author: Andrzej Hajda <a.hajda@samsung.com> 6 7 #include <linux/dma-map-ops.h> 8 #include <linux/iommu.h> 9 #include <linux/platform_device.h> 10 11 #include <drm/drm_device.h> 12 #include <drm/drm_print.h> 13 #include <drm/exynos_drm.h> 14 15 #include "exynos_drm_drv.h" 16 17 #if defined(CONFIG_ARM_DMA_USE_IOMMU) 18 #include <asm/dma-iommu.h> 19 #else 20 #define arm_iommu_create_mapping(...) ({ NULL; }) 21 #define arm_iommu_attach_device(...) ({ -ENODEV; }) 22 #define arm_iommu_release_mapping(...) ({ }) 23 #define arm_iommu_detach_device(...) ({ }) 24 #define to_dma_iommu_mapping(dev) NULL 25 #endif 26 27 #define EXYNOS_DEV_ADDR_START 0x20000000 28 #define EXYNOS_DEV_ADDR_SIZE 0x40000000 29 30 /* 31 * drm_iommu_attach_device- attach device to iommu mapping 32 * 33 * @drm_dev: DRM device 34 * @subdrv_dev: device to be attach 35 * 36 * This function should be called by sub drivers to attach it to iommu 37 * mapping. 38 */ 39 static int drm_iommu_attach_device(struct drm_device *drm_dev, 40 struct device *subdrv_dev, void **dma_priv) 41 { 42 struct exynos_drm_private *priv = drm_dev->dev_private; 43 int ret = 0; 44 45 if (get_dma_ops(drm_dev_dma_dev(drm_dev)) != get_dma_ops(subdrv_dev)) { 46 DRM_DEV_ERROR(subdrv_dev, "Device %s lacks support for IOMMU\n", 47 dev_name(subdrv_dev)); 48 return -EINVAL; 49 } 50 51 dma_set_max_seg_size(subdrv_dev, DMA_BIT_MASK(32)); 52 if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) { 53 /* 54 * Keep the original DMA mapping of the sub-device and 55 * restore it on Exynos DRM detach, otherwise the DMA 56 * framework considers it as IOMMU-less during the next 57 * probe (in case of deferred probe or modular build) 58 */ 59 *dma_priv = to_dma_iommu_mapping(subdrv_dev); 60 if (*dma_priv) 61 arm_iommu_detach_device(subdrv_dev); 62 63 ret = arm_iommu_attach_device(subdrv_dev, priv->mapping); 64 } else if (IS_ENABLED(CONFIG_IOMMU_DMA)) { 65 ret = iommu_attach_device(priv->mapping, subdrv_dev); 66 } 67 68 return ret; 69 } 70 71 /* 72 * drm_iommu_detach_device -detach device address space mapping from device 73 * 74 * @drm_dev: DRM device 75 * @subdrv_dev: device to be detached 76 * 77 * This function should be called by sub drivers to detach it from iommu 78 * mapping 79 */ 80 static void drm_iommu_detach_device(struct drm_device *drm_dev, 81 struct device *subdrv_dev, void **dma_priv) 82 { 83 struct exynos_drm_private *priv = drm_dev->dev_private; 84 85 if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) { 86 arm_iommu_detach_device(subdrv_dev); 87 arm_iommu_attach_device(subdrv_dev, *dma_priv); 88 } else if (IS_ENABLED(CONFIG_IOMMU_DMA)) 89 iommu_detach_device(priv->mapping, subdrv_dev); 90 } 91 92 int exynos_drm_register_dma(struct drm_device *drm, struct device *dev, 93 void **dma_priv) 94 { 95 struct exynos_drm_private *priv = drm->dev_private; 96 97 if (drm_dev_dma_dev(drm) == drm->dev) { 98 drm_dev_set_dma_dev(drm, dev); 99 DRM_INFO("Exynos DRM: using %s device for DMA mapping operations\n", 100 dev_name(dev)); 101 } 102 103 if (!IS_ENABLED(CONFIG_EXYNOS_IOMMU)) 104 return 0; 105 106 if (!priv->mapping) { 107 void *mapping = NULL; 108 109 if (IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)) 110 mapping = arm_iommu_create_mapping(dev, 111 EXYNOS_DEV_ADDR_START, EXYNOS_DEV_ADDR_SIZE); 112 else if (IS_ENABLED(CONFIG_IOMMU_DMA)) 113 mapping = iommu_get_domain_for_dev(dev); 114 115 if (!mapping) 116 return -ENODEV; 117 priv->mapping = mapping; 118 } 119 120 return drm_iommu_attach_device(drm, dev, dma_priv); 121 } 122 123 void exynos_drm_unregister_dma(struct drm_device *drm, struct device *dev, 124 void **dma_priv) 125 { 126 if (IS_ENABLED(CONFIG_EXYNOS_IOMMU)) 127 drm_iommu_detach_device(drm, dev, dma_priv); 128 } 129 130 void exynos_drm_cleanup_dma(struct drm_device *drm) 131 { 132 struct exynos_drm_private *priv = drm->dev_private; 133 134 if (!IS_ENABLED(CONFIG_EXYNOS_IOMMU)) 135 return; 136 137 arm_iommu_release_mapping(priv->mapping); 138 priv->mapping = NULL; 139 drm_dev_set_dma_dev(drm, NULL); 140 } 141