diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_iommu.c')
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_iommu.c | 212 |
1 files changed, 60 insertions, 152 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index f2ffa68..b0a8e1c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -37,13 +37,13 @@ static DEFINE_MUTEX(iommu_mutex); struct exynos_iommu_ops { - int (*setup)(struct device *dev); - void (*cleanup)(struct device *dev); - int (*activate)(struct device *dev); - void (*deactivate)(struct device *dev); - dma_addr_t (*map)(struct device *dev, struct scatterlist *sg, + void *(*setup)(unsigned long s_iova, unsigned long size); + void (*cleanup)(void *in_vmm); + int (*activate)(void *in_vmm, struct device *dev); + void (*deactivate)(void *in_vmm, struct device *dev); + dma_addr_t (*map)(void *in_vmm, struct scatterlist *sg, off_t offset, size_t size); - void (*unmap)(struct device *dev, dma_addr_t iova); + void (*unmap)(void *in_vmm, dma_addr_t iova); }; static const struct exynos_iommu_ops iommu_ops = { @@ -55,74 +55,20 @@ static const struct exynos_iommu_ops iommu_ops = { .unmap = iovmm_unmap }; -static bool check_iommu_map_params(struct iommu_gem_map_params *params) -{ - if (!params) { - DRM_ERROR("params is null.\n"); - return false; - } - - if (!params->dev || !params->drm_dev || !params->file) { - DRM_ERROR("invalid params.\n"); - return false; - } - - return true; -} - -void exynos_drm_remove_iommu_list(struct list_head *iommu_list, - void *gem_obj) -{ - struct iommu_info_node *im, *t_im; - - list_for_each_entry_safe(im, t_im, iommu_list, list) { - if (im->gem_obj == gem_obj) { - list_del(&im->list); - kfree(im); - im = NULL; - break; - } - } - -} - -dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, - struct list_head *iommu_list, - unsigned int gem_handle, - enum iommu_types type) +dma_addr_t exynos_drm_iommu_map_gem(struct drm_device *drm_dev, + struct drm_gem_object *obj) { + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; struct sg_table *sgt; - struct iommu_info_node *node; - struct exynos_drm_gem_obj *obj; - dma_addr_t dma_addr; - - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); - return 0; - } - - if (!check_iommu_map_params(params)) - return 0; - - /* get gem object from specific gem framework. */ - obj = exynos_drm_gem_get_obj(params->drm_dev, gem_handle, - params->file); - if (IS_ERR(obj)) - return 0; + dma_addr_t dev_addr; mutex_lock(&iommu_mutex); - /* - * if this gem object had already been mapped to iommu then - * return dma address mapped before this time. - */ - if (obj->iommu_info.mapped & (1 << type)) { - DRM_DEBUG_KMS("already mapped to iommu"); - mutex_unlock(&iommu_mutex); - return obj->iommu_info.dma_addrs[type]; - } + exynos_gem_obj = to_exynos_gem_obj(obj); - sgt = obj->buffer->sgt; + buf = exynos_gem_obj->buffer; + sgt = buf->sgt; /* * if not using iommu, just return base address to physical @@ -132,102 +78,64 @@ dma_addr_t exynos_drm_iommu_map_gem(struct iommu_gem_map_params *params, mutex_unlock(&iommu_mutex); return sg_dma_address(&sgt->sgl[0]); } - mutex_unlock(&iommu_mutex); /* - * allocate device address space for this driver and then - * map all pages contained in sg list to iommu table. + * if a gem buffer was already mapped with iommu table then + * just return dev_addr; + * + * Note: device address is unique to system globally. */ - dma_addr = iommu_ops.map(params->dev, sgt->sgl, (off_t)0, - (size_t)obj->size); - if (!dma_addr) { + if (buf->dev_addr) { mutex_unlock(&iommu_mutex); - return dma_addr; + return buf->dev_addr; } - mutex_lock(&iommu_mutex); - /* - * check map flag bit and device address mapped to iommu. - * this data would be used to avoid duplicated mapping. - */ - obj->iommu_info.mapped |= (1 << type); - obj->iommu_info.dma_addrs[type] = dma_addr; - obj->iommu_info.devs[type] = params->dev; - obj->iommu_info.iommu_lists[type] = iommu_list; - - params->gem_obj = obj; - - /* - * this gem object is referenced by this driver so - * the object refcount should be increased. + * allocate device address space for this driver and then + * map all pages contained in sg list to iommu table. */ - drm_gem_object_reference(&obj->base); - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - DRM_ERROR("failed to allocate iommu node.\n"); - dma_addr = 0; - goto err; + dev_addr = iommu_ops.map(exynos_gem_obj->vmm, sgt->sgl, (off_t)0, + (size_t)obj->size); + if (!dev_addr) { + mutex_unlock(&iommu_mutex); + return dev_addr; } - node->gem_obj = obj; - node->dma_addr = dma_addr; mutex_unlock(&iommu_mutex); - list_add_tail(&node->list, iommu_list); - - return dma_addr; -err: - mutex_unlock(&iommu_mutex); - iommu_ops.unmap(params->dev, dma_addr); - return dma_addr; + return dev_addr; } -void exynos_drm_iommu_unmap_gem(struct iommu_gem_map_params *params, - dma_addr_t dma_addr, - enum iommu_types type) +void exynos_drm_iommu_unmap_gem(struct drm_gem_object *obj) { - struct exynos_drm_gem_obj *obj; - - if (!iommu_ops.unmap) - return; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_gem_buf *buf; - if (!is_iommu_type_valid(type)) { - DRM_ERROR("invalid iommu type.\n"); + if (!iommu_ops.unmap || !obj) return; - } - if (!check_iommu_map_params(params)) - return; - - if (!params->gem_obj) - return; + exynos_gem_obj = to_exynos_gem_obj(obj); + buf = exynos_gem_obj->buffer; - obj = (struct exynos_drm_gem_obj *)params->gem_obj; + /* workaround */ + usleep_range(15000, 20000); mutex_lock(&iommu_mutex); - if (!(obj->iommu_info.mapped & (1 << type))) { - DRM_DEBUG_KMS("not already mapped to iommu so just return\n"); + + if (!buf->dev_addr) { mutex_unlock(&iommu_mutex); + DRM_DEBUG_KMS("not mapped with iommu table.\n"); return; } - /* uncheck map flag bit. */ - obj->iommu_info.mapped &= ~(1 << type); - obj->iommu_info.dma_addrs[type] = 0; - mutex_unlock(&iommu_mutex); - - iommu_ops.unmap(params->dev, dma_addr); + if (exynos_gem_obj->vmm) + iommu_ops.unmap(exynos_gem_obj->vmm, buf->dev_addr); - /* - * drop this gem object refcount to release allocated buffer - * and resources. - */ - drm_gem_object_unreference_unlocked(&obj->base); + buf->dev_addr = 0; + mutex_unlock(&iommu_mutex); } -dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, +dma_addr_t exynos_drm_iommu_map(void *in_vmm, dma_addr_t paddr, size_t size) { struct sg_table *sgt; @@ -244,13 +152,13 @@ dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, sgt = kzalloc(sizeof(struct sg_table) * npages, GFP_KERNEL); if (!sgt) { - dev_err(dev, "failed to allocate sg table.\n"); + DRM_ERROR("failed to allocate sg table.\n"); return dma_addr; } ret = sg_alloc_table(sgt, npages, GFP_KERNEL); if (ret < 0) { - dev_err(dev, "failed to initialize sg table.\n"); + DRM_ERROR("failed to initialize sg table.\n"); goto err; } @@ -270,9 +178,9 @@ dma_addr_t exynos_drm_iommu_map(struct device *dev, dma_addr_t paddr, * allocate device address space for this driver and then * map all pages contained in sg list to iommu table. */ - dma_addr = iommu_ops.map(dev, sgt->sgl, (off_t)0, (size_t)size); + dma_addr = iommu_ops.map(in_vmm, sgt->sgl, (off_t)0, (size_t)size); if (!dma_addr) - dev_err(dev, "failed to map cmdlist pool.\n"); + DRM_ERROR("failed to map cmdlist pool.\n"); sg_free_table(sgt); err: @@ -283,45 +191,45 @@ err: } -void exynos_drm_iommu_unmap(struct device *dev, dma_addr_t dma_addr) +void exynos_drm_iommu_unmap(void *in_vmm, dma_addr_t dma_addr) { if (iommu_ops.unmap) - iommu_ops.unmap(dev, dma_addr); + iommu_ops.unmap(in_vmm, dma_addr); } -int exynos_drm_iommu_setup(struct device *dev) +void *exynos_drm_iommu_setup(unsigned long s_iova, unsigned long size) { /* * allocate device address space to this driver and add vmm object * to s5p_iovmm_list. please know that each iommu will use * 1GB as its own device address apace. * - * the device address space : 0x80000000 ~ 0xA0000000 + * the device address space : s_iova ~ s_iova + size */ if (iommu_ops.setup) - return iommu_ops.setup(dev); + return iommu_ops.setup(s_iova, size); - return 0; + return ERR_PTR(-EINVAL); } -int exynos_drm_iommu_activate(struct device *dev) +int exynos_drm_iommu_activate(void *in_vmm, struct device *dev) { if (iommu_ops.activate) - return iovmm_activate(dev); + return iovmm_activate(in_vmm, dev); return 0; } -void exynos_drm_iommu_deactivate(struct device *dev) +void exynos_drm_iommu_deactivate(void *in_vmm, struct device *dev) { if (iommu_ops.deactivate) - iommu_ops.deactivate(dev); + iommu_ops.deactivate(in_vmm, dev); } -void exynos_drm_iommu_cleanup(struct device *dev) +void exynos_drm_iommu_cleanup(void *in_vmm) { if (iommu_ops.cleanup) - iommu_ops.cleanup(dev); + iommu_ops.cleanup(in_vmm); } MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); |