11c248b7dSInki Dae /* exynos_drm_gem.c
21c248b7dSInki Dae  *
31c248b7dSInki Dae  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
41c248b7dSInki Dae  * Author: Inki Dae <inki.dae@samsung.com>
51c248b7dSInki Dae  *
6d81aecb5SInki Dae  * This program is free software; you can redistribute  it and/or modify it
7d81aecb5SInki Dae  * under  the terms of  the GNU General  Public License as published by the
8d81aecb5SInki Dae  * Free Software Foundation;  either version 2 of the  License, or (at your
9d81aecb5SInki Dae  * option) any later version.
101c248b7dSInki Dae  */
111c248b7dSInki Dae 
12760285e7SDavid Howells #include <drm/drmP.h>
130de23977SDavid Herrmann #include <drm/drm_vma_manager.h>
141c248b7dSInki Dae 
152b35892eSInki Dae #include <linux/shmem_fs.h>
161c248b7dSInki Dae #include <drm/exynos_drm.h>
171c248b7dSInki Dae 
181c248b7dSInki Dae #include "exynos_drm_drv.h"
191c248b7dSInki Dae #include "exynos_drm_gem.h"
201c248b7dSInki Dae #include "exynos_drm_buf.h"
213fec4532SVikas Sajjan #include "exynos_drm_iommu.h"
221c248b7dSInki Dae 
23dcf9af82SInki Dae static int check_gem_flags(unsigned int flags)
242b35892eSInki Dae {
25dcf9af82SInki Dae 	if (flags & ~(EXYNOS_BO_MASK)) {
26dcf9af82SInki Dae 		DRM_ERROR("invalid flags.\n");
27dcf9af82SInki Dae 		return -EINVAL;
28dcf9af82SInki Dae 	}
29dcf9af82SInki Dae 
30dcf9af82SInki Dae 	return 0;
31dcf9af82SInki Dae }
32dcf9af82SInki Dae 
33c01d73faSInki Dae static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj,
34c01d73faSInki Dae 					struct vm_area_struct *vma)
35c01d73faSInki Dae {
36c01d73faSInki Dae 	DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
37c01d73faSInki Dae 
38c01d73faSInki Dae 	/* non-cachable as default. */
39c01d73faSInki Dae 	if (obj->flags & EXYNOS_BO_CACHABLE)
40c01d73faSInki Dae 		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
41c01d73faSInki Dae 	else if (obj->flags & EXYNOS_BO_WC)
42c01d73faSInki Dae 		vma->vm_page_prot =
43c01d73faSInki Dae 			pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
44c01d73faSInki Dae 	else
45c01d73faSInki Dae 		vma->vm_page_prot =
46c01d73faSInki Dae 			pgprot_noncached(vm_get_page_prot(vma->vm_flags));
47c01d73faSInki Dae }
48c01d73faSInki Dae 
49dcf9af82SInki Dae static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
50dcf9af82SInki Dae {
510519f9a1SInki Dae 	/* TODO */
520519f9a1SInki Dae 
53dcf9af82SInki Dae 	return roundup(size, PAGE_SIZE);
542b35892eSInki Dae }
552b35892eSInki Dae 
560519f9a1SInki Dae static int exynos_drm_gem_map_buf(struct drm_gem_object *obj,
572b35892eSInki Dae 					struct vm_area_struct *vma,
582b35892eSInki Dae 					unsigned long f_vaddr,
592b35892eSInki Dae 					pgoff_t page_offset)
602b35892eSInki Dae {
612b35892eSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
622b35892eSInki Dae 	struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
632b35892eSInki Dae 	unsigned long pfn;
644ddc404bSRahul Sharma 
65465ed660SPrathyush K 	if (page_offset >= (buf->size >> PAGE_SHIFT)) {
66465ed660SPrathyush K 		DRM_ERROR("invalid page offset\n");
67465ed660SPrathyush K 		return -EINVAL;
68465ed660SPrathyush K 	}
69465ed660SPrathyush K 
708139951cSJoonyoung Shim 	pfn = page_to_pfn(buf->pages[page_offset]);
712b35892eSInki Dae 
722b35892eSInki Dae 	return vm_insert_mixed(vma, f_vaddr, pfn);
732b35892eSInki Dae }
742b35892eSInki Dae 
752364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
762364839aSJoonyoung Shim 					struct drm_file *file_priv,
772364839aSJoonyoung Shim 					unsigned int *handle)
781c248b7dSInki Dae {
791c248b7dSInki Dae 	int ret;
801c248b7dSInki Dae 
811c248b7dSInki Dae 	/*
821c248b7dSInki Dae 	 * allocate a id of idr table where the obj is registered
831c248b7dSInki Dae 	 * and handle has the id what user can see.
841c248b7dSInki Dae 	 */
851c248b7dSInki Dae 	ret = drm_gem_handle_create(file_priv, obj, handle);
861c248b7dSInki Dae 	if (ret)
872364839aSJoonyoung Shim 		return ret;
881c248b7dSInki Dae 
891c248b7dSInki Dae 	DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
901c248b7dSInki Dae 
911c248b7dSInki Dae 	/* drop reference from allocate - handle holds it now. */
921c248b7dSInki Dae 	drm_gem_object_unreference_unlocked(obj);
931c248b7dSInki Dae 
942364839aSJoonyoung Shim 	return 0;
952364839aSJoonyoung Shim }
961c248b7dSInki Dae 
972364839aSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
982364839aSJoonyoung Shim {
992364839aSJoonyoung Shim 	struct drm_gem_object *obj;
100c01d73faSInki Dae 	struct exynos_drm_gem_buf *buf;
1012364839aSJoonyoung Shim 
1022364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
103c01d73faSInki Dae 	buf = exynos_gem_obj->buffer;
1042364839aSJoonyoung Shim 
105a8e11d1cSDaniel Vetter 	DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count);
1062364839aSJoonyoung Shim 
107c374e731SInki Dae 	/*
108c374e731SInki Dae 	 * do not release memory region from exporter.
109c374e731SInki Dae 	 *
110c374e731SInki Dae 	 * the region will be released by exporter
111c374e731SInki Dae 	 * once dmabuf's refcount becomes 0.
112c374e731SInki Dae 	 */
113c374e731SInki Dae 	if (obj->import_attach)
114c374e731SInki Dae 		goto out;
115c374e731SInki Dae 
116c01d73faSInki Dae 	exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
1172b35892eSInki Dae 
118c374e731SInki Dae out:
119c01d73faSInki Dae 	exynos_drm_fini_buf(obj->dev, buf);
1202b35892eSInki Dae 	exynos_gem_obj->buffer = NULL;
1212364839aSJoonyoung Shim 
1222364839aSJoonyoung Shim 	drm_gem_free_mmap_offset(obj);
1232364839aSJoonyoung Shim 
1242364839aSJoonyoung Shim 	/* release file pointer to gem object. */
1251c248b7dSInki Dae 	drm_gem_object_release(obj);
1261c248b7dSInki Dae 
1271c248b7dSInki Dae 	kfree(exynos_gem_obj);
1282b35892eSInki Dae 	exynos_gem_obj = NULL;
1292364839aSJoonyoung Shim }
1302364839aSJoonyoung Shim 
131a4f19aaaSInki Dae unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
132a4f19aaaSInki Dae 						unsigned int gem_handle,
133a4f19aaaSInki Dae 						struct drm_file *file_priv)
134a4f19aaaSInki Dae {
135a4f19aaaSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
136a4f19aaaSInki Dae 	struct drm_gem_object *obj;
137a4f19aaaSInki Dae 
138a4f19aaaSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
139a4f19aaaSInki Dae 	if (!obj) {
140a4f19aaaSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
141a4f19aaaSInki Dae 		return 0;
142a4f19aaaSInki Dae 	}
143a4f19aaaSInki Dae 
144a4f19aaaSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
145a4f19aaaSInki Dae 
146a4f19aaaSInki Dae 	drm_gem_object_unreference_unlocked(obj);
147a4f19aaaSInki Dae 
148a4f19aaaSInki Dae 	return exynos_gem_obj->buffer->size;
149a4f19aaaSInki Dae }
150a4f19aaaSInki Dae 
151a4f19aaaSInki Dae 
152b2df26c1SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
1532364839aSJoonyoung Shim 						      unsigned long size)
1542364839aSJoonyoung Shim {
1552364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1562364839aSJoonyoung Shim 	struct drm_gem_object *obj;
1572364839aSJoonyoung Shim 	int ret;
1582364839aSJoonyoung Shim 
1592364839aSJoonyoung Shim 	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
16038bb5253SSachin Kamat 	if (!exynos_gem_obj)
1612364839aSJoonyoung Shim 		return NULL;
1622364839aSJoonyoung Shim 
1632b35892eSInki Dae 	exynos_gem_obj->size = size;
1642364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
1652364839aSJoonyoung Shim 
1662364839aSJoonyoung Shim 	ret = drm_gem_object_init(dev, obj, size);
1672364839aSJoonyoung Shim 	if (ret < 0) {
1682364839aSJoonyoung Shim 		DRM_ERROR("failed to initialize gem object\n");
1692364839aSJoonyoung Shim 		kfree(exynos_gem_obj);
1702364839aSJoonyoung Shim 		return NULL;
1712364839aSJoonyoung Shim 	}
1722364839aSJoonyoung Shim 
1732364839aSJoonyoung Shim 	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
1742364839aSJoonyoung Shim 
1752364839aSJoonyoung Shim 	return exynos_gem_obj;
1761c248b7dSInki Dae }
1771c248b7dSInki Dae 
178f088d5a9SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
1792b35892eSInki Dae 						unsigned int flags,
180ee5e770eSJoonyoung Shim 						unsigned long size)
181f088d5a9SInki Dae {
1822364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1832b35892eSInki Dae 	struct exynos_drm_gem_buf *buf;
1842b35892eSInki Dae 	int ret;
185f088d5a9SInki Dae 
186dcf9af82SInki Dae 	if (!size) {
187dcf9af82SInki Dae 		DRM_ERROR("invalid size.\n");
188dcf9af82SInki Dae 		return ERR_PTR(-EINVAL);
189dcf9af82SInki Dae 	}
190f088d5a9SInki Dae 
191dcf9af82SInki Dae 	size = roundup_gem_size(size, flags);
192dcf9af82SInki Dae 
193dcf9af82SInki Dae 	ret = check_gem_flags(flags);
194dcf9af82SInki Dae 	if (ret)
195dcf9af82SInki Dae 		return ERR_PTR(ret);
1962b35892eSInki Dae 
1972b35892eSInki Dae 	buf = exynos_drm_init_buf(dev, size);
1982b35892eSInki Dae 	if (!buf)
199ee5e770eSJoonyoung Shim 		return ERR_PTR(-ENOMEM);
200f088d5a9SInki Dae 
2012364839aSJoonyoung Shim 	exynos_gem_obj = exynos_drm_gem_init(dev, size);
2022364839aSJoonyoung Shim 	if (!exynos_gem_obj) {
2032b35892eSInki Dae 		ret = -ENOMEM;
204dcf9af82SInki Dae 		goto err_fini_buf;
205f088d5a9SInki Dae 	}
206f088d5a9SInki Dae 
2072b35892eSInki Dae 	exynos_gem_obj->buffer = buf;
2082b35892eSInki Dae 
2092b35892eSInki Dae 	/* set memory type and cache attribute from user side. */
2102b35892eSInki Dae 	exynos_gem_obj->flags = flags;
2112b35892eSInki Dae 
2122b35892eSInki Dae 	ret = exynos_drm_alloc_buf(dev, buf, flags);
213c58c1599SYoungJun Cho 	if (ret < 0)
214c58c1599SYoungJun Cho 		goto err_gem_fini;
215f088d5a9SInki Dae 
216f088d5a9SInki Dae 	return exynos_gem_obj;
217dcf9af82SInki Dae 
218c58c1599SYoungJun Cho err_gem_fini:
219c58c1599SYoungJun Cho 	drm_gem_object_release(&exynos_gem_obj->base);
220c58c1599SYoungJun Cho 	kfree(exynos_gem_obj);
221dcf9af82SInki Dae err_fini_buf:
2222b35892eSInki Dae 	exynos_drm_fini_buf(dev, buf);
2232b35892eSInki Dae 	return ERR_PTR(ret);
224f088d5a9SInki Dae }
225f088d5a9SInki Dae 
2261c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
2271c248b7dSInki Dae 				struct drm_file *file_priv)
2281c248b7dSInki Dae {
2291c248b7dSInki Dae 	struct drm_exynos_gem_create *args = data;
230ee5e770eSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
2312364839aSJoonyoung Shim 	int ret;
2321c248b7dSInki Dae 
2332b35892eSInki Dae 	exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
2341c248b7dSInki Dae 	if (IS_ERR(exynos_gem_obj))
2351c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
2361c248b7dSInki Dae 
2372364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
2382364839aSJoonyoung Shim 			&args->handle);
2392364839aSJoonyoung Shim 	if (ret) {
2402364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
2412364839aSJoonyoung Shim 		return ret;
2422364839aSJoonyoung Shim 	}
2432364839aSJoonyoung Shim 
2441c248b7dSInki Dae 	return 0;
2451c248b7dSInki Dae }
2461c248b7dSInki Dae 
247d87342c1SInki Dae dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
248f0b1bda7SInki Dae 					unsigned int gem_handle,
249d87342c1SInki Dae 					struct drm_file *filp)
250f0b1bda7SInki Dae {
251f0b1bda7SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
252f0b1bda7SInki Dae 	struct drm_gem_object *obj;
253f0b1bda7SInki Dae 
254d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
255f0b1bda7SInki Dae 	if (!obj) {
256f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
257f0b1bda7SInki Dae 		return ERR_PTR(-EINVAL);
258f0b1bda7SInki Dae 	}
259f0b1bda7SInki Dae 
260f0b1bda7SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
261f0b1bda7SInki Dae 
262f0b1bda7SInki Dae 	return &exynos_gem_obj->buffer->dma_addr;
263f0b1bda7SInki Dae }
264f0b1bda7SInki Dae 
265f0b1bda7SInki Dae void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
266f0b1bda7SInki Dae 					unsigned int gem_handle,
267d87342c1SInki Dae 					struct drm_file *filp)
268f0b1bda7SInki Dae {
269f0b1bda7SInki Dae 	struct drm_gem_object *obj;
270f0b1bda7SInki Dae 
271d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
272f0b1bda7SInki Dae 	if (!obj) {
273f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
274f0b1bda7SInki Dae 		return;
275f0b1bda7SInki Dae 	}
276f0b1bda7SInki Dae 
277f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
278f0b1bda7SInki Dae 
279f0b1bda7SInki Dae 	/*
280f0b1bda7SInki Dae 	 * decrease obj->refcount one more time because we has already
281f0b1bda7SInki Dae 	 * increased it at exynos_drm_gem_get_dma_addr().
282f0b1bda7SInki Dae 	 */
283f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
284f0b1bda7SInki Dae }
285f0b1bda7SInki Dae 
286832316c7SInki Dae int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
2871c248b7dSInki Dae 				      struct vm_area_struct *vma)
2881c248b7dSInki Dae {
289832316c7SInki Dae 	struct drm_device *drm_dev = exynos_gem_obj->base.dev;
2902c871127SInki Dae 	struct exynos_drm_gem_buf *buffer;
2910519f9a1SInki Dae 	unsigned long vm_size;
2925b07c660SInki Dae 	int ret;
2931c248b7dSInki Dae 
294832316c7SInki Dae 	vma->vm_flags &= ~VM_PFNMAP;
295832316c7SInki Dae 	vma->vm_pgoff = 0;
2961c248b7dSInki Dae 
2970519f9a1SInki Dae 	vm_size = vma->vm_end - vma->vm_start;
2982b35892eSInki Dae 
2991c248b7dSInki Dae 	/*
3002c871127SInki Dae 	 * a buffer contains information to physically continuous memory
3011c248b7dSInki Dae 	 * allocated by user request or at framebuffer creation.
3021c248b7dSInki Dae 	 */
3032c871127SInki Dae 	buffer = exynos_gem_obj->buffer;
3041c248b7dSInki Dae 
3051c248b7dSInki Dae 	/* check if user-requested size is valid. */
3062c871127SInki Dae 	if (vm_size > buffer->size)
3071c248b7dSInki Dae 		return -EINVAL;
3081c248b7dSInki Dae 
3094744ad24SInki Dae 	ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
3100519f9a1SInki Dae 				buffer->dma_addr, buffer->size,
3110519f9a1SInki Dae 				&buffer->dma_attrs);
3125b07c660SInki Dae 	if (ret < 0) {
3135b07c660SInki Dae 		DRM_ERROR("failed to mmap.\n");
3145b07c660SInki Dae 		return ret;
3155b07c660SInki Dae 	}
3165b07c660SInki Dae 
3171c248b7dSInki Dae 	return 0;
3181c248b7dSInki Dae }
3191c248b7dSInki Dae 
32040cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
32140cd7e0cSInki Dae 				      struct drm_file *file_priv)
32240cd7e0cSInki Dae {	struct exynos_drm_gem_obj *exynos_gem_obj;
32340cd7e0cSInki Dae 	struct drm_exynos_gem_info *args = data;
32440cd7e0cSInki Dae 	struct drm_gem_object *obj;
32540cd7e0cSInki Dae 
32640cd7e0cSInki Dae 	mutex_lock(&dev->struct_mutex);
32740cd7e0cSInki Dae 
32840cd7e0cSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
32940cd7e0cSInki Dae 	if (!obj) {
33040cd7e0cSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
33140cd7e0cSInki Dae 		mutex_unlock(&dev->struct_mutex);
33240cd7e0cSInki Dae 		return -EINVAL;
33340cd7e0cSInki Dae 	}
33440cd7e0cSInki Dae 
33540cd7e0cSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
33640cd7e0cSInki Dae 
33740cd7e0cSInki Dae 	args->flags = exynos_gem_obj->flags;
33840cd7e0cSInki Dae 	args->size = exynos_gem_obj->size;
33940cd7e0cSInki Dae 
34040cd7e0cSInki Dae 	drm_gem_object_unreference(obj);
34140cd7e0cSInki Dae 	mutex_unlock(&dev->struct_mutex);
34240cd7e0cSInki Dae 
34340cd7e0cSInki Dae 	return 0;
34440cd7e0cSInki Dae }
34540cd7e0cSInki Dae 
3462a3098ffSInki Dae struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
3472a3098ffSInki Dae {
3482a3098ffSInki Dae 	struct vm_area_struct *vma_copy;
3492a3098ffSInki Dae 
3502a3098ffSInki Dae 	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
3512a3098ffSInki Dae 	if (!vma_copy)
3522a3098ffSInki Dae 		return NULL;
3532a3098ffSInki Dae 
3542a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->open)
3552a3098ffSInki Dae 		vma->vm_ops->open(vma);
3562a3098ffSInki Dae 
3572a3098ffSInki Dae 	if (vma->vm_file)
3582a3098ffSInki Dae 		get_file(vma->vm_file);
3592a3098ffSInki Dae 
3602a3098ffSInki Dae 	memcpy(vma_copy, vma, sizeof(*vma));
3612a3098ffSInki Dae 
3622a3098ffSInki Dae 	vma_copy->vm_mm = NULL;
3632a3098ffSInki Dae 	vma_copy->vm_next = NULL;
3642a3098ffSInki Dae 	vma_copy->vm_prev = NULL;
3652a3098ffSInki Dae 
3662a3098ffSInki Dae 	return vma_copy;
3672a3098ffSInki Dae }
3682a3098ffSInki Dae 
3692a3098ffSInki Dae void exynos_gem_put_vma(struct vm_area_struct *vma)
3702a3098ffSInki Dae {
3712a3098ffSInki Dae 	if (!vma)
3722a3098ffSInki Dae 		return;
3732a3098ffSInki Dae 
3742a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->close)
3752a3098ffSInki Dae 		vma->vm_ops->close(vma);
3762a3098ffSInki Dae 
3772a3098ffSInki Dae 	if (vma->vm_file)
3782a3098ffSInki Dae 		fput(vma->vm_file);
3792a3098ffSInki Dae 
3802a3098ffSInki Dae 	kfree(vma);
3812a3098ffSInki Dae }
3822a3098ffSInki Dae 
3832a3098ffSInki Dae int exynos_gem_get_pages_from_userptr(unsigned long start,
3842a3098ffSInki Dae 						unsigned int npages,
3852a3098ffSInki Dae 						struct page **pages,
3862a3098ffSInki Dae 						struct vm_area_struct *vma)
3872a3098ffSInki Dae {
3882a3098ffSInki Dae 	int get_npages;
3892a3098ffSInki Dae 
3902a3098ffSInki Dae 	/* the memory region mmaped with VM_PFNMAP. */
3912a3098ffSInki Dae 	if (vma_is_io(vma)) {
3922a3098ffSInki Dae 		unsigned int i;
3932a3098ffSInki Dae 
3942a3098ffSInki Dae 		for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
3952a3098ffSInki Dae 			unsigned long pfn;
3962a3098ffSInki Dae 			int ret = follow_pfn(vma, start, &pfn);
3972a3098ffSInki Dae 			if (ret)
3982a3098ffSInki Dae 				return ret;
3992a3098ffSInki Dae 
4002a3098ffSInki Dae 			pages[i] = pfn_to_page(pfn);
4012a3098ffSInki Dae 		}
4022a3098ffSInki Dae 
4032a3098ffSInki Dae 		if (i != npages) {
4042a3098ffSInki Dae 			DRM_ERROR("failed to get user_pages.\n");
4052a3098ffSInki Dae 			return -EINVAL;
4062a3098ffSInki Dae 		}
4072a3098ffSInki Dae 
4082a3098ffSInki Dae 		return 0;
4092a3098ffSInki Dae 	}
4102a3098ffSInki Dae 
4112a3098ffSInki Dae 	get_npages = get_user_pages(current, current->mm, start,
4122a3098ffSInki Dae 					npages, 1, 1, pages, NULL);
4132a3098ffSInki Dae 	get_npages = max(get_npages, 0);
4142a3098ffSInki Dae 	if (get_npages != npages) {
4152a3098ffSInki Dae 		DRM_ERROR("failed to get user_pages.\n");
4162a3098ffSInki Dae 		while (get_npages)
4172a3098ffSInki Dae 			put_page(pages[--get_npages]);
4182a3098ffSInki Dae 		return -EFAULT;
4192a3098ffSInki Dae 	}
4202a3098ffSInki Dae 
4212a3098ffSInki Dae 	return 0;
4222a3098ffSInki Dae }
4232a3098ffSInki Dae 
4242a3098ffSInki Dae void exynos_gem_put_pages_to_userptr(struct page **pages,
4252a3098ffSInki Dae 					unsigned int npages,
4262a3098ffSInki Dae 					struct vm_area_struct *vma)
4272a3098ffSInki Dae {
4282a3098ffSInki Dae 	if (!vma_is_io(vma)) {
4292a3098ffSInki Dae 		unsigned int i;
4302a3098ffSInki Dae 
4312a3098ffSInki Dae 		for (i = 0; i < npages; i++) {
4322a3098ffSInki Dae 			set_page_dirty_lock(pages[i]);
4332a3098ffSInki Dae 
4342a3098ffSInki Dae 			/*
4352a3098ffSInki Dae 			 * undo the reference we took when populating
4362a3098ffSInki Dae 			 * the table.
4372a3098ffSInki Dae 			 */
4382a3098ffSInki Dae 			put_page(pages[i]);
4392a3098ffSInki Dae 		}
4402a3098ffSInki Dae 	}
4412a3098ffSInki Dae }
4422a3098ffSInki Dae 
4432a3098ffSInki Dae int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
4442a3098ffSInki Dae 				struct sg_table *sgt,
4452a3098ffSInki Dae 				enum dma_data_direction dir)
4462a3098ffSInki Dae {
4472a3098ffSInki Dae 	int nents;
4482a3098ffSInki Dae 
4492a3098ffSInki Dae 	mutex_lock(&drm_dev->struct_mutex);
4502a3098ffSInki Dae 
4512a3098ffSInki Dae 	nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4522a3098ffSInki Dae 	if (!nents) {
4532a3098ffSInki Dae 		DRM_ERROR("failed to map sgl with dma.\n");
4542a3098ffSInki Dae 		mutex_unlock(&drm_dev->struct_mutex);
4552a3098ffSInki Dae 		return nents;
4562a3098ffSInki Dae 	}
4572a3098ffSInki Dae 
4582a3098ffSInki Dae 	mutex_unlock(&drm_dev->struct_mutex);
4592a3098ffSInki Dae 	return 0;
4602a3098ffSInki Dae }
4612a3098ffSInki Dae 
4622a3098ffSInki Dae void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
4632a3098ffSInki Dae 				struct sg_table *sgt,
4642a3098ffSInki Dae 				enum dma_data_direction dir)
4652a3098ffSInki Dae {
4662a3098ffSInki Dae 	dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4672a3098ffSInki Dae }
4682a3098ffSInki Dae 
469ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj)
4701c248b7dSInki Dae {
471b2df26c1SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
472b2df26c1SInki Dae 	struct exynos_drm_gem_buf *buf;
473b2df26c1SInki Dae 
474b2df26c1SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
475b2df26c1SInki Dae 	buf = exynos_gem_obj->buffer;
476b2df26c1SInki Dae 
477b2df26c1SInki Dae 	if (obj->import_attach)
478b2df26c1SInki Dae 		drm_prime_gem_destroy(obj, buf->sgt);
479b2df26c1SInki Dae 
4802364839aSJoonyoung Shim 	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
4811c248b7dSInki Dae }
4821c248b7dSInki Dae 
4831c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
484ee5e770eSJoonyoung Shim 			       struct drm_device *dev,
485ee5e770eSJoonyoung Shim 			       struct drm_mode_create_dumb *args)
4861c248b7dSInki Dae {
4871c248b7dSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
4882364839aSJoonyoung Shim 	int ret;
4891c248b7dSInki Dae 
4901c248b7dSInki Dae 	/*
491c6b78bc8SMasanari Iida 	 * allocate memory to be used for framebuffer.
4921c248b7dSInki Dae 	 * - this callback would be called by user application
4931c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
4941c248b7dSInki Dae 	 */
4951c248b7dSInki Dae 
4963fd6b694SCooper Yuan 	args->pitch = args->width * ((args->bpp + 7) / 8);
4977da5907cSInki Dae 	args->size = args->pitch * args->height;
4981c248b7dSInki Dae 
499122beea8SRahul Sharma 	if (is_drm_iommu_supported(dev)) {
5003fec4532SVikas Sajjan 		exynos_gem_obj = exynos_drm_gem_create(dev,
5013fec4532SVikas Sajjan 			EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
5023fec4532SVikas Sajjan 			args->size);
503122beea8SRahul Sharma 	} else {
504122beea8SRahul Sharma 		exynos_gem_obj = exynos_drm_gem_create(dev,
505122beea8SRahul Sharma 			EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
506122beea8SRahul Sharma 			args->size);
5073fec4532SVikas Sajjan 	}
5083fec4532SVikas Sajjan 
509122beea8SRahul Sharma 	if (IS_ERR(exynos_gem_obj)) {
510122beea8SRahul Sharma 		dev_warn(dev->dev, "FB allocation failed.\n");
5111c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
512122beea8SRahul Sharma 	}
5131c248b7dSInki Dae 
5142364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
5152364839aSJoonyoung Shim 			&args->handle);
5162364839aSJoonyoung Shim 	if (ret) {
5172364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
5182364839aSJoonyoung Shim 		return ret;
5192364839aSJoonyoung Shim 	}
5202364839aSJoonyoung Shim 
5211c248b7dSInki Dae 	return 0;
5221c248b7dSInki Dae }
5231c248b7dSInki Dae 
5241c248b7dSInki Dae int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
525ee5e770eSJoonyoung Shim 				   struct drm_device *dev, uint32_t handle,
526ee5e770eSJoonyoung Shim 				   uint64_t *offset)
5271c248b7dSInki Dae {
5281c248b7dSInki Dae 	struct drm_gem_object *obj;
5292d91cf17SJoonyoung Shim 	int ret = 0;
5301c248b7dSInki Dae 
5311c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
5321c248b7dSInki Dae 
5331c248b7dSInki Dae 	/*
5341c248b7dSInki Dae 	 * get offset of memory allocated for drm framebuffer.
5351c248b7dSInki Dae 	 * - this callback would be called by user application
5361c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_MAP_DUMB command.
5371c248b7dSInki Dae 	 */
5381c248b7dSInki Dae 
5391c248b7dSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, handle);
5401c248b7dSInki Dae 	if (!obj) {
5411c248b7dSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
5422d91cf17SJoonyoung Shim 		ret = -EINVAL;
5432d91cf17SJoonyoung Shim 		goto unlock;
5441c248b7dSInki Dae 	}
5451c248b7dSInki Dae 
5466037bafaSLaurent Pinchart 	ret = drm_gem_create_mmap_offset(obj);
5472d91cf17SJoonyoung Shim 	if (ret)
5482d91cf17SJoonyoung Shim 		goto out;
5491c248b7dSInki Dae 
5500de23977SDavid Herrmann 	*offset = drm_vma_node_offset_addr(&obj->vma_node);
5511c248b7dSInki Dae 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
5521c248b7dSInki Dae 
5532d91cf17SJoonyoung Shim out:
5542d91cf17SJoonyoung Shim 	drm_gem_object_unreference(obj);
5552d91cf17SJoonyoung Shim unlock:
5561c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5572d91cf17SJoonyoung Shim 	return ret;
5581c248b7dSInki Dae }
5591c248b7dSInki Dae 
5601c248b7dSInki Dae int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
5611c248b7dSInki Dae {
5621c248b7dSInki Dae 	struct drm_gem_object *obj = vma->vm_private_data;
5631c248b7dSInki Dae 	struct drm_device *dev = obj->dev;
5642b35892eSInki Dae 	unsigned long f_vaddr;
5651c248b7dSInki Dae 	pgoff_t page_offset;
5661c248b7dSInki Dae 	int ret;
5671c248b7dSInki Dae 
5681c248b7dSInki Dae 	page_offset = ((unsigned long)vmf->virtual_address -
5691c248b7dSInki Dae 			vma->vm_start) >> PAGE_SHIFT;
5702b35892eSInki Dae 	f_vaddr = (unsigned long)vmf->virtual_address;
5711c248b7dSInki Dae 
5721c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
5731c248b7dSInki Dae 
5740519f9a1SInki Dae 	ret = exynos_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
5752b35892eSInki Dae 	if (ret < 0)
5760519f9a1SInki Dae 		DRM_ERROR("failed to map a buffer with user.\n");
5771c248b7dSInki Dae 
5781c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5791c248b7dSInki Dae 
58023597e26SJoonyoung Shim 	switch (ret) {
58123597e26SJoonyoung Shim 	case 0:
58223597e26SJoonyoung Shim 	case -ERESTARTSYS:
58323597e26SJoonyoung Shim 	case -EINTR:
58423597e26SJoonyoung Shim 		return VM_FAULT_NOPAGE;
58523597e26SJoonyoung Shim 	case -ENOMEM:
58623597e26SJoonyoung Shim 		return VM_FAULT_OOM;
58723597e26SJoonyoung Shim 	default:
58823597e26SJoonyoung Shim 		return VM_FAULT_SIGBUS;
58923597e26SJoonyoung Shim 	}
5901c248b7dSInki Dae }
5911c248b7dSInki Dae 
5921c248b7dSInki Dae int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
5931c248b7dSInki Dae {
594c01d73faSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
595c01d73faSInki Dae 	struct drm_gem_object *obj;
5961c248b7dSInki Dae 	int ret;
5971c248b7dSInki Dae 
5981c248b7dSInki Dae 	/* set vm_area_struct. */
5991c248b7dSInki Dae 	ret = drm_gem_mmap(filp, vma);
6001c248b7dSInki Dae 	if (ret < 0) {
6011c248b7dSInki Dae 		DRM_ERROR("failed to mmap.\n");
6021c248b7dSInki Dae 		return ret;
6031c248b7dSInki Dae 	}
6041c248b7dSInki Dae 
605c01d73faSInki Dae 	obj = vma->vm_private_data;
606c01d73faSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
607c01d73faSInki Dae 
608c01d73faSInki Dae 	ret = check_gem_flags(exynos_gem_obj->flags);
609832316c7SInki Dae 	if (ret)
610832316c7SInki Dae 		goto err_close_vm;
6111c248b7dSInki Dae 
612c01d73faSInki Dae 	update_vm_cache_attr(exynos_gem_obj, vma);
613c01d73faSInki Dae 
614832316c7SInki Dae 	ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);
615832316c7SInki Dae 	if (ret)
616832316c7SInki Dae 		goto err_close_vm;
617832316c7SInki Dae 
618832316c7SInki Dae 	return ret;
619832316c7SInki Dae 
620832316c7SInki Dae err_close_vm:
621832316c7SInki Dae 	drm_gem_vm_close(vma);
622832316c7SInki Dae 	drm_gem_free_mmap_offset(obj);
623832316c7SInki Dae 
6241c248b7dSInki Dae 	return ret;
6251c248b7dSInki Dae }
626