12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21c248b7dSInki Dae /* exynos_drm_gem.c
31c248b7dSInki Dae  *
41c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
51c248b7dSInki Dae  * Author: Inki Dae <inki.dae@samsung.com>
61c248b7dSInki Dae  */
71c248b7dSInki Dae 
81c248b7dSInki Dae 
901ed50ddSJoonyoung Shim #include <linux/dma-buf.h>
1001c8f1c4SDan Williams #include <linux/pfn_t.h>
112bda34d7SSam Ravnborg #include <linux/shmem_fs.h>
122bda34d7SSam Ravnborg 
132bda34d7SSam Ravnborg #include <drm/drm_prime.h>
142bda34d7SSam Ravnborg #include <drm/drm_vma_manager.h>
151c248b7dSInki Dae #include <drm/exynos_drm.h>
161c248b7dSInki Dae 
171c248b7dSInki Dae #include "exynos_drm_drv.h"
181c248b7dSInki Dae #include "exynos_drm_gem.h"
191c248b7dSInki Dae 
20813fd67bSJoonyoung Shim static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
212a8cb489SJoonyoung Shim {
22813fd67bSJoonyoung Shim 	struct drm_device *dev = exynos_gem->base.dev;
2300085f1eSKrzysztof Kozlowski 	unsigned long attr;
242a8cb489SJoonyoung Shim 	unsigned int nr_pages;
25df547bf7SMarek Szyprowski 	struct sg_table sgt;
26df547bf7SMarek Szyprowski 	int ret = -ENOMEM;
272a8cb489SJoonyoung Shim 
28813fd67bSJoonyoung Shim 	if (exynos_gem->dma_addr) {
296be90056SInki Dae 		DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "already allocated.\n");
302a8cb489SJoonyoung Shim 		return 0;
312a8cb489SJoonyoung Shim 	}
322a8cb489SJoonyoung Shim 
3300085f1eSKrzysztof Kozlowski 	exynos_gem->dma_attrs = 0;
342a8cb489SJoonyoung Shim 
352a8cb489SJoonyoung Shim 	/*
362a8cb489SJoonyoung Shim 	 * if EXYNOS_BO_CONTIG, fully physically contiguous memory
372a8cb489SJoonyoung Shim 	 * region will be allocated else physically contiguous
382a8cb489SJoonyoung Shim 	 * as possible.
392a8cb489SJoonyoung Shim 	 */
40813fd67bSJoonyoung Shim 	if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG))
4100085f1eSKrzysztof Kozlowski 		exynos_gem->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
422a8cb489SJoonyoung Shim 
432a8cb489SJoonyoung Shim 	/*
442a8cb489SJoonyoung Shim 	 * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping
452a8cb489SJoonyoung Shim 	 * else cachable mapping.
462a8cb489SJoonyoung Shim 	 */
47813fd67bSJoonyoung Shim 	if (exynos_gem->flags & EXYNOS_BO_WC ||
48813fd67bSJoonyoung Shim 			!(exynos_gem->flags & EXYNOS_BO_CACHABLE))
492a8cb489SJoonyoung Shim 		attr = DMA_ATTR_WRITE_COMBINE;
502a8cb489SJoonyoung Shim 	else
512a8cb489SJoonyoung Shim 		attr = DMA_ATTR_NON_CONSISTENT;
522a8cb489SJoonyoung Shim 
5300085f1eSKrzysztof Kozlowski 	exynos_gem->dma_attrs |= attr;
5400085f1eSKrzysztof Kozlowski 	exynos_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
552a8cb489SJoonyoung Shim 
56813fd67bSJoonyoung Shim 	nr_pages = exynos_gem->size >> PAGE_SHIFT;
572a8cb489SJoonyoung Shim 
582098105eSMichal Hocko 	exynos_gem->pages = kvmalloc_array(nr_pages, sizeof(struct page *),
592098105eSMichal Hocko 			GFP_KERNEL | __GFP_ZERO);
60813fd67bSJoonyoung Shim 	if (!exynos_gem->pages) {
616f83d208SInki Dae 		DRM_DEV_ERROR(to_dma_dev(dev), "failed to allocate pages.\n");
622a8cb489SJoonyoung Shim 		return -ENOMEM;
632a8cb489SJoonyoung Shim 	}
642a8cb489SJoonyoung Shim 
65f43c3596SMarek Szyprowski 	exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size,
66813fd67bSJoonyoung Shim 					     &exynos_gem->dma_addr, GFP_KERNEL,
6700085f1eSKrzysztof Kozlowski 					     exynos_gem->dma_attrs);
68813fd67bSJoonyoung Shim 	if (!exynos_gem->cookie) {
696f83d208SInki Dae 		DRM_DEV_ERROR(to_dma_dev(dev), "failed to allocate buffer.\n");
70df547bf7SMarek Szyprowski 		goto err_free;
712a8cb489SJoonyoung Shim 	}
722a8cb489SJoonyoung Shim 
73f43c3596SMarek Szyprowski 	ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt, exynos_gem->cookie,
74df547bf7SMarek Szyprowski 				    exynos_gem->dma_addr, exynos_gem->size,
7500085f1eSKrzysztof Kozlowski 				    exynos_gem->dma_attrs);
76df547bf7SMarek Szyprowski 	if (ret < 0) {
776f83d208SInki Dae 		DRM_DEV_ERROR(to_dma_dev(dev), "failed to get sgtable.\n");
78df547bf7SMarek Szyprowski 		goto err_dma_free;
79df547bf7SMarek Szyprowski 	}
80333e8e58SJoonyoung Shim 
81df547bf7SMarek Szyprowski 	if (drm_prime_sg_to_page_addr_arrays(&sgt, exynos_gem->pages, NULL,
82df547bf7SMarek Szyprowski 					     nr_pages)) {
836f83d208SInki Dae 		DRM_DEV_ERROR(to_dma_dev(dev), "invalid sgtable.\n");
84df547bf7SMarek Szyprowski 		ret = -EINVAL;
85df547bf7SMarek Szyprowski 		goto err_sgt_free;
862a8cb489SJoonyoung Shim 	}
87df547bf7SMarek Szyprowski 
88df547bf7SMarek Szyprowski 	sg_free_table(&sgt);
892a8cb489SJoonyoung Shim 
906be90056SInki Dae 	DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "dma_addr(0x%lx), size(0x%lx)\n",
91813fd67bSJoonyoung Shim 			(unsigned long)exynos_gem->dma_addr, exynos_gem->size);
922a8cb489SJoonyoung Shim 
932a8cb489SJoonyoung Shim 	return 0;
94df547bf7SMarek Szyprowski 
95df547bf7SMarek Szyprowski err_sgt_free:
96df547bf7SMarek Szyprowski 	sg_free_table(&sgt);
97df547bf7SMarek Szyprowski err_dma_free:
98f43c3596SMarek Szyprowski 	dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie,
9900085f1eSKrzysztof Kozlowski 		       exynos_gem->dma_addr, exynos_gem->dma_attrs);
100df547bf7SMarek Szyprowski err_free:
1012098105eSMichal Hocko 	kvfree(exynos_gem->pages);
102df547bf7SMarek Szyprowski 
103df547bf7SMarek Szyprowski 	return ret;
1042a8cb489SJoonyoung Shim }
1052a8cb489SJoonyoung Shim 
106813fd67bSJoonyoung Shim static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem)
1072a8cb489SJoonyoung Shim {
108813fd67bSJoonyoung Shim 	struct drm_device *dev = exynos_gem->base.dev;
1092a8cb489SJoonyoung Shim 
110813fd67bSJoonyoung Shim 	if (!exynos_gem->dma_addr) {
1116be90056SInki Dae 		DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n");
1122a8cb489SJoonyoung Shim 		return;
1132a8cb489SJoonyoung Shim 	}
1142a8cb489SJoonyoung Shim 
1156be90056SInki Dae 	DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr(0x%lx), size(0x%lx)\n",
116813fd67bSJoonyoung Shim 			(unsigned long)exynos_gem->dma_addr, exynos_gem->size);
1172a8cb489SJoonyoung Shim 
118f43c3596SMarek Szyprowski 	dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie,
119813fd67bSJoonyoung Shim 			(dma_addr_t)exynos_gem->dma_addr,
12000085f1eSKrzysztof Kozlowski 			exynos_gem->dma_attrs);
121333e8e58SJoonyoung Shim 
1222098105eSMichal Hocko 	kvfree(exynos_gem->pages);
1232a8cb489SJoonyoung Shim }
1242a8cb489SJoonyoung Shim 
1252364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
1262364839aSJoonyoung Shim 					struct drm_file *file_priv,
1272364839aSJoonyoung Shim 					unsigned int *handle)
1281c248b7dSInki Dae {
1291c248b7dSInki Dae 	int ret;
1301c248b7dSInki Dae 
1311c248b7dSInki Dae 	/*
1321c248b7dSInki Dae 	 * allocate a id of idr table where the obj is registered
1331c248b7dSInki Dae 	 * and handle has the id what user can see.
1341c248b7dSInki Dae 	 */
1351c248b7dSInki Dae 	ret = drm_gem_handle_create(file_priv, obj, handle);
1361c248b7dSInki Dae 	if (ret)
1372364839aSJoonyoung Shim 		return ret;
1381c248b7dSInki Dae 
1396be90056SInki Dae 	DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "gem handle = 0x%x\n", *handle);
1401c248b7dSInki Dae 
1411c248b7dSInki Dae 	/* drop reference from allocate - handle holds it now. */
142af7d9101SThomas Zimmermann 	drm_gem_object_put_unlocked(obj);
1431c248b7dSInki Dae 
1442364839aSJoonyoung Shim 	return 0;
1452364839aSJoonyoung Shim }
1461c248b7dSInki Dae 
147813fd67bSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem)
1482364839aSJoonyoung Shim {
149813fd67bSJoonyoung Shim 	struct drm_gem_object *obj = &exynos_gem->base;
1502364839aSJoonyoung Shim 
1516be90056SInki Dae 	DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "handle count = %d\n",
1526be90056SInki Dae 			  obj->handle_count);
1532364839aSJoonyoung Shim 
154c374e731SInki Dae 	/*
155c374e731SInki Dae 	 * do not release memory region from exporter.
156c374e731SInki Dae 	 *
157c374e731SInki Dae 	 * the region will be released by exporter
158c374e731SInki Dae 	 * once dmabuf's refcount becomes 0.
159c374e731SInki Dae 	 */
160c374e731SInki Dae 	if (obj->import_attach)
161813fd67bSJoonyoung Shim 		drm_prime_gem_destroy(obj, exynos_gem->sgt);
1627c93537aSJoonyoung Shim 	else
163813fd67bSJoonyoung Shim 		exynos_drm_free_buf(exynos_gem);
1642b35892eSInki Dae 
1652364839aSJoonyoung Shim 	/* release file pointer to gem object. */
1661c248b7dSInki Dae 	drm_gem_object_release(obj);
1671c248b7dSInki Dae 
168813fd67bSJoonyoung Shim 	kfree(exynos_gem);
1692364839aSJoonyoung Shim }
1702364839aSJoonyoung Shim 
171813fd67bSJoonyoung Shim static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev,
1722364839aSJoonyoung Shim 						  unsigned long size)
1732364839aSJoonyoung Shim {
174813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
1752364839aSJoonyoung Shim 	struct drm_gem_object *obj;
1762364839aSJoonyoung Shim 	int ret;
1772364839aSJoonyoung Shim 
178813fd67bSJoonyoung Shim 	exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL);
179813fd67bSJoonyoung Shim 	if (!exynos_gem)
1805f3f4266SJoonyoung Shim 		return ERR_PTR(-ENOMEM);
1812364839aSJoonyoung Shim 
182813fd67bSJoonyoung Shim 	exynos_gem->size = size;
183813fd67bSJoonyoung Shim 	obj = &exynos_gem->base;
1842364839aSJoonyoung Shim 
1852364839aSJoonyoung Shim 	ret = drm_gem_object_init(dev, obj, size);
1862364839aSJoonyoung Shim 	if (ret < 0) {
1876f83d208SInki Dae 		DRM_DEV_ERROR(dev->dev, "failed to initialize gem object\n");
188813fd67bSJoonyoung Shim 		kfree(exynos_gem);
1895f3f4266SJoonyoung Shim 		return ERR_PTR(ret);
1902364839aSJoonyoung Shim 	}
1912364839aSJoonyoung Shim 
19248cf53f4SJoonyoung Shim 	ret = drm_gem_create_mmap_offset(obj);
19348cf53f4SJoonyoung Shim 	if (ret < 0) {
19448cf53f4SJoonyoung Shim 		drm_gem_object_release(obj);
195813fd67bSJoonyoung Shim 		kfree(exynos_gem);
19648cf53f4SJoonyoung Shim 		return ERR_PTR(ret);
19748cf53f4SJoonyoung Shim 	}
19848cf53f4SJoonyoung Shim 
1996be90056SInki Dae 	DRM_DEV_DEBUG_KMS(dev->dev, "created file object = %pK\n", obj->filp);
2002364839aSJoonyoung Shim 
201813fd67bSJoonyoung Shim 	return exynos_gem;
2021c248b7dSInki Dae }
2031c248b7dSInki Dae 
204813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev,
2052b35892eSInki Dae 					     unsigned int flags,
206ee5e770eSJoonyoung Shim 					     unsigned long size)
207f088d5a9SInki Dae {
208813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
2092b35892eSInki Dae 	int ret;
210f088d5a9SInki Dae 
211c4130bcdSJoonyoung Shim 	if (flags & ~(EXYNOS_BO_MASK)) {
2126f83d208SInki Dae 		DRM_DEV_ERROR(dev->dev,
2136f83d208SInki Dae 			      "invalid GEM buffer flags: %u\n", flags);
214c4130bcdSJoonyoung Shim 		return ERR_PTR(-EINVAL);
215c4130bcdSJoonyoung Shim 	}
216c4130bcdSJoonyoung Shim 
217dcf9af82SInki Dae 	if (!size) {
2186f83d208SInki Dae 		DRM_DEV_ERROR(dev->dev, "invalid GEM buffer size: %lu\n", size);
219dcf9af82SInki Dae 		return ERR_PTR(-EINVAL);
220dcf9af82SInki Dae 	}
221f088d5a9SInki Dae 
222eb57da88SJoonyoung Shim 	size = roundup(size, PAGE_SIZE);
223dcf9af82SInki Dae 
224813fd67bSJoonyoung Shim 	exynos_gem = exynos_drm_gem_init(dev, size);
225813fd67bSJoonyoung Shim 	if (IS_ERR(exynos_gem))
226813fd67bSJoonyoung Shim 		return exynos_gem;
2272b35892eSInki Dae 
228120a264fSMarek Szyprowski 	if (!is_drm_iommu_supported(dev) && (flags & EXYNOS_BO_NONCONTIG)) {
229120a264fSMarek Szyprowski 		/*
230120a264fSMarek Szyprowski 		 * when no IOMMU is available, all allocated buffers are
231120a264fSMarek Szyprowski 		 * contiguous anyway, so drop EXYNOS_BO_NONCONTIG flag
232120a264fSMarek Szyprowski 		 */
233120a264fSMarek Szyprowski 		flags &= ~EXYNOS_BO_NONCONTIG;
234120a264fSMarek Szyprowski 		DRM_WARN("Non-contiguous allocation is not supported without IOMMU, falling back to contiguous buffer\n");
235120a264fSMarek Szyprowski 	}
236120a264fSMarek Szyprowski 
2372b35892eSInki Dae 	/* set memory type and cache attribute from user side. */
238813fd67bSJoonyoung Shim 	exynos_gem->flags = flags;
2392b35892eSInki Dae 
240813fd67bSJoonyoung Shim 	ret = exynos_drm_alloc_buf(exynos_gem);
2412a8cb489SJoonyoung Shim 	if (ret < 0) {
242813fd67bSJoonyoung Shim 		drm_gem_object_release(&exynos_gem->base);
243813fd67bSJoonyoung Shim 		kfree(exynos_gem);
2442b35892eSInki Dae 		return ERR_PTR(ret);
245f088d5a9SInki Dae 	}
246f088d5a9SInki Dae 
247813fd67bSJoonyoung Shim 	return exynos_gem;
2482a8cb489SJoonyoung Shim }
2492a8cb489SJoonyoung Shim 
2501c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
2511c248b7dSInki Dae 				struct drm_file *file_priv)
2521c248b7dSInki Dae {
2531c248b7dSInki Dae 	struct drm_exynos_gem_create *args = data;
254813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
2552364839aSJoonyoung Shim 	int ret;
2561c248b7dSInki Dae 
257813fd67bSJoonyoung Shim 	exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size);
258813fd67bSJoonyoung Shim 	if (IS_ERR(exynos_gem))
259813fd67bSJoonyoung Shim 		return PTR_ERR(exynos_gem);
2601c248b7dSInki Dae 
261813fd67bSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv,
2622364839aSJoonyoung Shim 					   &args->handle);
2632364839aSJoonyoung Shim 	if (ret) {
264813fd67bSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem);
2652364839aSJoonyoung Shim 		return ret;
2662364839aSJoonyoung Shim 	}
2672364839aSJoonyoung Shim 
2681c248b7dSInki Dae 	return 0;
2691c248b7dSInki Dae }
2701c248b7dSInki Dae 
2716564c65fSJoonyoung Shim int exynos_drm_gem_map_ioctl(struct drm_device *dev, void *data,
2726564c65fSJoonyoung Shim 			     struct drm_file *file_priv)
2736564c65fSJoonyoung Shim {
2746564c65fSJoonyoung Shim 	struct drm_exynos_gem_map *args = data;
2756564c65fSJoonyoung Shim 
2764d12c233SNoralf Trønnes 	return drm_gem_dumb_map_offset(file_priv, dev, args->handle,
2776564c65fSJoonyoung Shim 				       &args->offset);
2786564c65fSJoonyoung Shim }
2796564c65fSJoonyoung Shim 
2803aa2a5c1SMarek Szyprowski struct exynos_drm_gem *exynos_drm_gem_get(struct drm_file *filp,
2813aa2a5c1SMarek Szyprowski 					  unsigned int gem_handle)
282f0b1bda7SInki Dae {
283f0b1bda7SInki Dae 	struct drm_gem_object *obj;
284f0b1bda7SInki Dae 
285a8ad0bd8SChris Wilson 	obj = drm_gem_object_lookup(filp, gem_handle);
2863aa2a5c1SMarek Szyprowski 	if (!obj)
2873aa2a5c1SMarek Szyprowski 		return NULL;
2883aa2a5c1SMarek Szyprowski 	return to_exynos_gem(obj);
289f0b1bda7SInki Dae }
290f0b1bda7SInki Dae 
291813fd67bSJoonyoung Shim static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem,
2921c248b7dSInki Dae 				      struct vm_area_struct *vma)
2931c248b7dSInki Dae {
294813fd67bSJoonyoung Shim 	struct drm_device *drm_dev = exynos_gem->base.dev;
2950519f9a1SInki Dae 	unsigned long vm_size;
2965b07c660SInki Dae 	int ret;
2971c248b7dSInki Dae 
298832316c7SInki Dae 	vma->vm_flags &= ~VM_PFNMAP;
299832316c7SInki Dae 	vma->vm_pgoff = 0;
3001c248b7dSInki Dae 
3010519f9a1SInki Dae 	vm_size = vma->vm_end - vma->vm_start;
3022b35892eSInki Dae 
3031c248b7dSInki Dae 	/* check if user-requested size is valid. */
304813fd67bSJoonyoung Shim 	if (vm_size > exynos_gem->size)
3051c248b7dSInki Dae 		return -EINVAL;
3061c248b7dSInki Dae 
307f43c3596SMarek Szyprowski 	ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie,
308813fd67bSJoonyoung Shim 			     exynos_gem->dma_addr, exynos_gem->size,
30900085f1eSKrzysztof Kozlowski 			     exynos_gem->dma_attrs);
3105b07c660SInki Dae 	if (ret < 0) {
3115b07c660SInki Dae 		DRM_ERROR("failed to mmap.\n");
3125b07c660SInki Dae 		return ret;
3135b07c660SInki Dae 	}
3145b07c660SInki Dae 
3151c248b7dSInki Dae 	return 0;
3161c248b7dSInki Dae }
3171c248b7dSInki Dae 
31840cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
31940cd7e0cSInki Dae 				      struct drm_file *file_priv)
320b4cfd4ddSJoonyoung Shim {
321813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
32240cd7e0cSInki Dae 	struct drm_exynos_gem_info *args = data;
32340cd7e0cSInki Dae 	struct drm_gem_object *obj;
32440cd7e0cSInki Dae 
325a8ad0bd8SChris Wilson 	obj = drm_gem_object_lookup(file_priv, args->handle);
32640cd7e0cSInki Dae 	if (!obj) {
3276f83d208SInki Dae 		DRM_DEV_ERROR(dev->dev, "failed to lookup gem object.\n");
32840cd7e0cSInki Dae 		return -EINVAL;
32940cd7e0cSInki Dae 	}
33040cd7e0cSInki Dae 
331813fd67bSJoonyoung Shim 	exynos_gem = to_exynos_gem(obj);
33240cd7e0cSInki Dae 
333813fd67bSJoonyoung Shim 	args->flags = exynos_gem->flags;
334813fd67bSJoonyoung Shim 	args->size = exynos_gem->size;
33540cd7e0cSInki Dae 
336af7d9101SThomas Zimmermann 	drm_gem_object_put_unlocked(obj);
33740cd7e0cSInki Dae 
33840cd7e0cSInki Dae 	return 0;
33940cd7e0cSInki Dae }
34040cd7e0cSInki Dae 
341ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj)
3421c248b7dSInki Dae {
343813fd67bSJoonyoung Shim 	exynos_drm_gem_destroy(to_exynos_gem(obj));
3441c248b7dSInki Dae }
3451c248b7dSInki Dae 
3461c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
347ee5e770eSJoonyoung Shim 			       struct drm_device *dev,
348ee5e770eSJoonyoung Shim 			       struct drm_mode_create_dumb *args)
3491c248b7dSInki Dae {
350813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
351333e8e58SJoonyoung Shim 	unsigned int flags;
3522364839aSJoonyoung Shim 	int ret;
3531c248b7dSInki Dae 
3541c248b7dSInki Dae 	/*
355c6b78bc8SMasanari Iida 	 * allocate memory to be used for framebuffer.
3561c248b7dSInki Dae 	 * - this callback would be called by user application
3571c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
3581c248b7dSInki Dae 	 */
3591c248b7dSInki Dae 
3603fd6b694SCooper Yuan 	args->pitch = args->width * ((args->bpp + 7) / 8);
3617da5907cSInki Dae 	args->size = args->pitch * args->height;
3621c248b7dSInki Dae 
363333e8e58SJoonyoung Shim 	if (is_drm_iommu_supported(dev))
364333e8e58SJoonyoung Shim 		flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC;
365333e8e58SJoonyoung Shim 	else
366333e8e58SJoonyoung Shim 		flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC;
3673fec4532SVikas Sajjan 
368813fd67bSJoonyoung Shim 	exynos_gem = exynos_drm_gem_create(dev, flags, args->size);
369813fd67bSJoonyoung Shim 	if (IS_ERR(exynos_gem)) {
370122beea8SRahul Sharma 		dev_warn(dev->dev, "FB allocation failed.\n");
371813fd67bSJoonyoung Shim 		return PTR_ERR(exynos_gem);
372122beea8SRahul Sharma 	}
3731c248b7dSInki Dae 
374813fd67bSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv,
3752364839aSJoonyoung Shim 					   &args->handle);
3762364839aSJoonyoung Shim 	if (ret) {
377813fd67bSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem);
3782364839aSJoonyoung Shim 		return ret;
3792364839aSJoonyoung Shim 	}
3802364839aSJoonyoung Shim 
3811c248b7dSInki Dae 	return 0;
3821c248b7dSInki Dae }
3831c248b7dSInki Dae 
3845a0202f7SJoonyoung Shim static int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj,
3855a0202f7SJoonyoung Shim 				   struct vm_area_struct *vma)
3861c248b7dSInki Dae {
3875a0202f7SJoonyoung Shim 	struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj);
3881c248b7dSInki Dae 	int ret;
3891c248b7dSInki Dae 
3906be90056SInki Dae 	DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "flags = 0x%x\n",
3916be90056SInki Dae 			  exynos_gem->flags);
392211b8878SJoonyoung Shim 
393211b8878SJoonyoung Shim 	/* non-cachable as default. */
394813fd67bSJoonyoung Shim 	if (exynos_gem->flags & EXYNOS_BO_CACHABLE)
395211b8878SJoonyoung Shim 		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
396813fd67bSJoonyoung Shim 	else if (exynos_gem->flags & EXYNOS_BO_WC)
397211b8878SJoonyoung Shim 		vma->vm_page_prot =
398211b8878SJoonyoung Shim 			pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
399211b8878SJoonyoung Shim 	else
400211b8878SJoonyoung Shim 		vma->vm_page_prot =
401211b8878SJoonyoung Shim 			pgprot_noncached(vm_get_page_prot(vma->vm_flags));
402c01d73faSInki Dae 
403813fd67bSJoonyoung Shim 	ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma);
404832316c7SInki Dae 	if (ret)
405832316c7SInki Dae 		goto err_close_vm;
406832316c7SInki Dae 
407832316c7SInki Dae 	return ret;
408832316c7SInki Dae 
409832316c7SInki Dae err_close_vm:
410832316c7SInki Dae 	drm_gem_vm_close(vma);
411832316c7SInki Dae 
4121c248b7dSInki Dae 	return ret;
4131c248b7dSInki Dae }
41401ed50ddSJoonyoung Shim 
4155a0202f7SJoonyoung Shim int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
4165a0202f7SJoonyoung Shim {
4175a0202f7SJoonyoung Shim 	struct drm_gem_object *obj;
4185a0202f7SJoonyoung Shim 	int ret;
4195a0202f7SJoonyoung Shim 
4205a0202f7SJoonyoung Shim 	/* set vm_area_struct. */
4215a0202f7SJoonyoung Shim 	ret = drm_gem_mmap(filp, vma);
4225a0202f7SJoonyoung Shim 	if (ret < 0) {
4235a0202f7SJoonyoung Shim 		DRM_ERROR("failed to mmap.\n");
4245a0202f7SJoonyoung Shim 		return ret;
4255a0202f7SJoonyoung Shim 	}
4265a0202f7SJoonyoung Shim 
4275a0202f7SJoonyoung Shim 	obj = vma->vm_private_data;
4285a0202f7SJoonyoung Shim 
42955b19fc7SJoonyoung Shim 	if (obj->import_attach)
43055b19fc7SJoonyoung Shim 		return dma_buf_mmap(obj->dma_buf, vma, 0);
43155b19fc7SJoonyoung Shim 
4325a0202f7SJoonyoung Shim 	return exynos_drm_gem_mmap_obj(obj, vma);
4335a0202f7SJoonyoung Shim }
4345a0202f7SJoonyoung Shim 
43501ed50ddSJoonyoung Shim /* low-level interface prime helpers */
43689452d4aSMarek Szyprowski struct drm_gem_object *exynos_drm_gem_prime_import(struct drm_device *dev,
43789452d4aSMarek Szyprowski 					    struct dma_buf *dma_buf)
43889452d4aSMarek Szyprowski {
43989452d4aSMarek Szyprowski 	return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev));
44089452d4aSMarek Szyprowski }
44189452d4aSMarek Szyprowski 
44201ed50ddSJoonyoung Shim struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj)
44301ed50ddSJoonyoung Shim {
444813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj);
44501ed50ddSJoonyoung Shim 	int npages;
44601ed50ddSJoonyoung Shim 
447813fd67bSJoonyoung Shim 	npages = exynos_gem->size >> PAGE_SHIFT;
44801ed50ddSJoonyoung Shim 
449813fd67bSJoonyoung Shim 	return drm_prime_pages_to_sg(exynos_gem->pages, npages);
45001ed50ddSJoonyoung Shim }
45101ed50ddSJoonyoung Shim 
45201ed50ddSJoonyoung Shim struct drm_gem_object *
45301ed50ddSJoonyoung Shim exynos_drm_gem_prime_import_sg_table(struct drm_device *dev,
45401ed50ddSJoonyoung Shim 				     struct dma_buf_attachment *attach,
45501ed50ddSJoonyoung Shim 				     struct sg_table *sgt)
45601ed50ddSJoonyoung Shim {
457813fd67bSJoonyoung Shim 	struct exynos_drm_gem *exynos_gem;
45801ed50ddSJoonyoung Shim 	int npages;
45901ed50ddSJoonyoung Shim 	int ret;
46001ed50ddSJoonyoung Shim 
46124f6fe79SMarek Szyprowski 	if (sgt->nents < 1)
46224f6fe79SMarek Szyprowski 		return ERR_PTR(-EINVAL);
46324f6fe79SMarek Szyprowski 
46424f6fe79SMarek Szyprowski 	/*
46524f6fe79SMarek Szyprowski 	 * Check if the provided buffer has been mapped as contiguous
46624f6fe79SMarek Szyprowski 	 * into DMA address space.
46724f6fe79SMarek Szyprowski 	 */
46824f6fe79SMarek Szyprowski 	if (sgt->nents > 1) {
46924f6fe79SMarek Szyprowski 		dma_addr_t next_addr = sg_dma_address(sgt->sgl);
47024f6fe79SMarek Szyprowski 		struct scatterlist *s;
47124f6fe79SMarek Szyprowski 		unsigned int i;
47224f6fe79SMarek Szyprowski 
47324f6fe79SMarek Szyprowski 		for_each_sg(sgt->sgl, s, sgt->nents, i) {
47424f6fe79SMarek Szyprowski 			if (!sg_dma_len(s))
47524f6fe79SMarek Szyprowski 				break;
47624f6fe79SMarek Szyprowski 			if (sg_dma_address(s) != next_addr) {
47724f6fe79SMarek Szyprowski 				DRM_ERROR("buffer chunks must be mapped contiguously");
47824f6fe79SMarek Szyprowski 				return ERR_PTR(-EINVAL);
47924f6fe79SMarek Szyprowski 			}
48024f6fe79SMarek Szyprowski 			next_addr = sg_dma_address(s) + sg_dma_len(s);
48124f6fe79SMarek Szyprowski 		}
48224f6fe79SMarek Szyprowski 	}
48324f6fe79SMarek Szyprowski 
484813fd67bSJoonyoung Shim 	exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size);
485813fd67bSJoonyoung Shim 	if (IS_ERR(exynos_gem)) {
486813fd67bSJoonyoung Shim 		ret = PTR_ERR(exynos_gem);
48750002d4cSInki Dae 		return ERR_PTR(ret);
48801ed50ddSJoonyoung Shim 	}
48901ed50ddSJoonyoung Shim 
490813fd67bSJoonyoung Shim 	exynos_gem->dma_addr = sg_dma_address(sgt->sgl);
4912a8cb489SJoonyoung Shim 
492813fd67bSJoonyoung Shim 	npages = exynos_gem->size >> PAGE_SHIFT;
4932098105eSMichal Hocko 	exynos_gem->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
494813fd67bSJoonyoung Shim 	if (!exynos_gem->pages) {
4952a8cb489SJoonyoung Shim 		ret = -ENOMEM;
4962a8cb489SJoonyoung Shim 		goto err;
4972a8cb489SJoonyoung Shim 	}
4982a8cb489SJoonyoung Shim 
499813fd67bSJoonyoung Shim 	ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem->pages, NULL,
5002a8cb489SJoonyoung Shim 					       npages);
5012a8cb489SJoonyoung Shim 	if (ret < 0)
5022a8cb489SJoonyoung Shim 		goto err_free_large;
5032a8cb489SJoonyoung Shim 
504813fd67bSJoonyoung Shim 	exynos_gem->sgt = sgt;
5057c93537aSJoonyoung Shim 
50601ed50ddSJoonyoung Shim 	/*
50724f6fe79SMarek Szyprowski 	 * Buffer has been mapped as contiguous into DMA address space,
50824f6fe79SMarek Szyprowski 	 * but if there is IOMMU, it can be either CONTIG or NONCONTIG.
50924f6fe79SMarek Szyprowski 	 * We assume a simplified logic below:
51001ed50ddSJoonyoung Shim 	 */
51124f6fe79SMarek Szyprowski 	if (is_drm_iommu_supported(dev))
512813fd67bSJoonyoung Shim 		exynos_gem->flags |= EXYNOS_BO_NONCONTIG;
51324f6fe79SMarek Szyprowski 	else
51424f6fe79SMarek Szyprowski 		exynos_gem->flags |= EXYNOS_BO_CONTIG;
51501ed50ddSJoonyoung Shim 
516813fd67bSJoonyoung Shim 	return &exynos_gem->base;
51701ed50ddSJoonyoung Shim 
51801ed50ddSJoonyoung Shim err_free_large:
5192098105eSMichal Hocko 	kvfree(exynos_gem->pages);
52001ed50ddSJoonyoung Shim err:
521813fd67bSJoonyoung Shim 	drm_gem_object_release(&exynos_gem->base);
522813fd67bSJoonyoung Shim 	kfree(exynos_gem);
52301ed50ddSJoonyoung Shim 	return ERR_PTR(ret);
52401ed50ddSJoonyoung Shim }
52501ed50ddSJoonyoung Shim 
52601ed50ddSJoonyoung Shim void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj)
52701ed50ddSJoonyoung Shim {
52801ed50ddSJoonyoung Shim 	return NULL;
52901ed50ddSJoonyoung Shim }
53001ed50ddSJoonyoung Shim 
53101ed50ddSJoonyoung Shim void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
53201ed50ddSJoonyoung Shim {
53301ed50ddSJoonyoung Shim 	/* Nothing to do */
53401ed50ddSJoonyoung Shim }
5355a0202f7SJoonyoung Shim 
5365a0202f7SJoonyoung Shim int exynos_drm_gem_prime_mmap(struct drm_gem_object *obj,
5375a0202f7SJoonyoung Shim 			      struct vm_area_struct *vma)
5385a0202f7SJoonyoung Shim {
5395a0202f7SJoonyoung Shim 	int ret;
5405a0202f7SJoonyoung Shim 
5415a0202f7SJoonyoung Shim 	ret = drm_gem_mmap_obj(obj, obj->size, vma);
5425a0202f7SJoonyoung Shim 	if (ret < 0)
5435a0202f7SJoonyoung Shim 		return ret;
5445a0202f7SJoonyoung Shim 
5455a0202f7SJoonyoung Shim 	return exynos_drm_gem_mmap_obj(obj, vma);
5465a0202f7SJoonyoung Shim }
547