| /* exynos_drm_iommu.c |
| * |
| * Copyright (c) 2012 Samsung Electronics Co., Ltd. |
| * Author: Inki Dae <inki.dae@samsung.com> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| */ |
| |
| #include <drm/drmP.h> |
| #include <drm/exynos_drm.h> |
| |
| #include <linux/dma-mapping.h> |
| #include <linux/iommu.h> |
| |
| #include "exynos_drm_drv.h" |
| #include "exynos_drm_iommu.h" |
| |
| static inline int configure_dma_max_seg_size(struct device *dev) |
| { |
| if (!dev->dma_parms) |
| dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL); |
| if (!dev->dma_parms) |
| return -ENOMEM; |
| |
| dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); |
| return 0; |
| } |
| |
| static inline void clear_dma_max_seg_size(struct device *dev) |
| { |
| kfree(dev->dma_parms); |
| dev->dma_parms = NULL; |
| } |
| |
| /* |
| * drm_create_iommu_mapping - create a mapping structure |
| * |
| * @drm_dev: DRM device |
| */ |
| int drm_create_iommu_mapping(struct drm_device *drm_dev) |
| { |
| struct exynos_drm_private *priv = drm_dev->dev_private; |
| |
| return __exynos_iommu_create_mapping(priv, EXYNOS_DEV_ADDR_START, |
| EXYNOS_DEV_ADDR_SIZE); |
| } |
| |
| /* |
| * drm_release_iommu_mapping - release iommu mapping structure |
| * |
| * @drm_dev: DRM device |
| */ |
| void drm_release_iommu_mapping(struct drm_device *drm_dev) |
| { |
| struct exynos_drm_private *priv = drm_dev->dev_private; |
| |
| __exynos_iommu_release_mapping(priv); |
| } |
| |
| /* |
| * drm_iommu_attach_device- attach device to iommu mapping |
| * |
| * @drm_dev: DRM device |
| * @subdrv_dev: device to be attach |
| * |
| * This function should be called by sub drivers to attach it to iommu |
| * mapping. |
| */ |
| int drm_iommu_attach_device(struct drm_device *drm_dev, |
| struct device *subdrv_dev) |
| { |
| struct exynos_drm_private *priv = drm_dev->dev_private; |
| int ret; |
| |
| if (get_dma_ops(priv->dma_dev) != get_dma_ops(subdrv_dev)) { |
| DRM_ERROR("Device %s lacks support for IOMMU\n", |
| dev_name(subdrv_dev)); |
| return -EINVAL; |
| } |
| |
| ret = configure_dma_max_seg_size(subdrv_dev); |
| if (ret) |
| return ret; |
| |
| ret = __exynos_iommu_attach(priv, subdrv_dev); |
| if (ret) |
| clear_dma_max_seg_size(subdrv_dev); |
| |
| return 0; |
| } |
| |
| /* |
| * drm_iommu_detach_device -detach device address space mapping from device |
| * |
| * @drm_dev: DRM device |
| * @subdrv_dev: device to be detached |
| * |
| * This function should be called by sub drivers to detach it from iommu |
| * mapping |
| */ |
| void drm_iommu_detach_device(struct drm_device *drm_dev, |
| struct device *subdrv_dev) |
| { |
| struct exynos_drm_private *priv = drm_dev->dev_private; |
| |
| __exynos_iommu_detach(priv, subdrv_dev); |
| clear_dma_max_seg_size(subdrv_dev); |
| } |