| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  | /* exynos_drm_iommu.c
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2012 Samsung Electronics Co., Ltd. | 
					
						
							|  |  |  |  * Author: Inki Dae <inki.dae@samsung.com> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-12-18 02:30:17 +09:00
										 |  |  |  * 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. | 
					
						
							| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <drmP.h>
 | 
					
						
							|  |  |  | #include <drm/exynos_drm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/dma-mapping.h>
 | 
					
						
							|  |  |  | #include <linux/iommu.h>
 | 
					
						
							|  |  |  | #include <linux/kref.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <asm/dma-iommu.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "exynos_drm_drv.h"
 | 
					
						
							|  |  |  | #include "exynos_drm_iommu.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * drm_create_iommu_mapping - create a mapping structure | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @drm_dev: DRM device | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int drm_create_iommu_mapping(struct drm_device *drm_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dma_iommu_mapping *mapping = NULL; | 
					
						
							|  |  |  | 	struct exynos_drm_private *priv = drm_dev->dev_private; | 
					
						
							|  |  |  | 	struct device *dev = drm_dev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!priv->da_start) | 
					
						
							|  |  |  | 		priv->da_start = EXYNOS_DEV_ADDR_START; | 
					
						
							|  |  |  | 	if (!priv->da_space_size) | 
					
						
							|  |  |  | 		priv->da_space_size = EXYNOS_DEV_ADDR_SIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mapping = arm_iommu_create_mapping(&platform_bus_type, priv->da_start, | 
					
						
							| 
									
										
										
										
											2014-02-25 13:01:09 +01:00
										 |  |  | 						priv->da_space_size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-10 02:11:13 -05:00
										 |  |  | 	if (IS_ERR(mapping)) | 
					
						
							|  |  |  | 		return PTR_ERR(mapping); | 
					
						
							| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), | 
					
						
							|  |  |  | 					GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2013-08-14 16:38:03 +05:30
										 |  |  | 	if (!dev->dma_parms) | 
					
						
							|  |  |  | 		goto error; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  | 	dma_set_max_seg_size(dev, 0xffffffffu); | 
					
						
							|  |  |  | 	dev->archdata.mapping = mapping; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-08-14 16:38:03 +05:30
										 |  |  | error: | 
					
						
							|  |  |  | 	arm_iommu_release_mapping(mapping); | 
					
						
							|  |  |  | 	return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * drm_release_iommu_mapping - release iommu mapping structure | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @drm_dev: DRM device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * if mapping->kref becomes 0 then all things related to iommu mapping | 
					
						
							|  |  |  |  * will be released | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void drm_release_iommu_mapping(struct drm_device *drm_dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct device *dev = drm_dev->dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	arm_iommu_release_mapping(dev->archdata.mapping); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * 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 device *dev = drm_dev->dev; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!dev->archdata.mapping) { | 
					
						
							|  |  |  | 		DRM_ERROR("iommu_mapping is null.\n"); | 
					
						
							|  |  |  | 		return -EFAULT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev, | 
					
						
							|  |  |  | 					sizeof(*subdrv_dev->dma_parms), | 
					
						
							|  |  |  | 					GFP_KERNEL); | 
					
						
							| 
									
										
										
										
											2013-08-14 16:38:03 +05:30
										 |  |  | 	if (!subdrv_dev->dma_parms) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-20 07:53:42 -07:00
										 |  |  | 	dma_set_max_seg_size(subdrv_dev, 0xffffffffu); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		DRM_DEBUG_KMS("failed iommu attach.\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Set dma_ops to drm_device just one time. | 
					
						
							|  |  |  | 	 * | 
					
						
							|  |  |  | 	 * The dma mapping api needs device object and the api is used | 
					
						
							|  |  |  | 	 * to allocate physial memory and map it with iommu table. | 
					
						
							|  |  |  | 	 * If iommu attach succeeded, the sub driver would have dma_ops | 
					
						
							|  |  |  | 	 * for iommu and also all sub drivers have same dma_ops. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!dev->archdata.dma_ops) | 
					
						
							|  |  |  | 		dev->archdata.dma_ops = subdrv_dev->archdata.dma_ops; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 device *dev = drm_dev->dev; | 
					
						
							|  |  |  | 	struct dma_iommu_mapping *mapping = dev->archdata.mapping; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!mapping || !mapping->domain) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iommu_detach_device(mapping->domain, subdrv_dev); | 
					
						
							|  |  |  | 	drm_release_iommu_mapping(drm_dev); | 
					
						
							|  |  |  | } |