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 
562364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
572364839aSJoonyoung Shim 					struct drm_file *file_priv,
582364839aSJoonyoung Shim 					unsigned int *handle)
591c248b7dSInki Dae {
601c248b7dSInki Dae 	int ret;
611c248b7dSInki Dae 
621c248b7dSInki Dae 	/*
631c248b7dSInki Dae 	 * allocate a id of idr table where the obj is registered
641c248b7dSInki Dae 	 * and handle has the id what user can see.
651c248b7dSInki Dae 	 */
661c248b7dSInki Dae 	ret = drm_gem_handle_create(file_priv, obj, handle);
671c248b7dSInki Dae 	if (ret)
682364839aSJoonyoung Shim 		return ret;
691c248b7dSInki Dae 
701c248b7dSInki Dae 	DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
711c248b7dSInki Dae 
721c248b7dSInki Dae 	/* drop reference from allocate - handle holds it now. */
731c248b7dSInki Dae 	drm_gem_object_unreference_unlocked(obj);
741c248b7dSInki Dae 
752364839aSJoonyoung Shim 	return 0;
762364839aSJoonyoung Shim }
771c248b7dSInki Dae 
782364839aSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
792364839aSJoonyoung Shim {
802364839aSJoonyoung Shim 	struct drm_gem_object *obj;
81c01d73faSInki Dae 	struct exynos_drm_gem_buf *buf;
822364839aSJoonyoung Shim 
832364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
84c01d73faSInki Dae 	buf = exynos_gem_obj->buffer;
852364839aSJoonyoung Shim 
86a8e11d1cSDaniel Vetter 	DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count);
872364839aSJoonyoung Shim 
88c374e731SInki Dae 	/*
89c374e731SInki Dae 	 * do not release memory region from exporter.
90c374e731SInki Dae 	 *
91c374e731SInki Dae 	 * the region will be released by exporter
92c374e731SInki Dae 	 * once dmabuf's refcount becomes 0.
93c374e731SInki Dae 	 */
94c374e731SInki Dae 	if (obj->import_attach)
95c374e731SInki Dae 		goto out;
96c374e731SInki Dae 
97c01d73faSInki Dae 	exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
982b35892eSInki Dae 
99c374e731SInki Dae out:
100c01d73faSInki Dae 	exynos_drm_fini_buf(obj->dev, buf);
1012b35892eSInki Dae 	exynos_gem_obj->buffer = NULL;
1022364839aSJoonyoung Shim 
1032364839aSJoonyoung Shim 	drm_gem_free_mmap_offset(obj);
1042364839aSJoonyoung Shim 
1052364839aSJoonyoung Shim 	/* release file pointer to gem object. */
1061c248b7dSInki Dae 	drm_gem_object_release(obj);
1071c248b7dSInki Dae 
1081c248b7dSInki Dae 	kfree(exynos_gem_obj);
1092b35892eSInki Dae 	exynos_gem_obj = NULL;
1102364839aSJoonyoung Shim }
1112364839aSJoonyoung Shim 
112a4f19aaaSInki Dae unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
113a4f19aaaSInki Dae 						unsigned int gem_handle,
114a4f19aaaSInki Dae 						struct drm_file *file_priv)
115a4f19aaaSInki Dae {
116a4f19aaaSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
117a4f19aaaSInki Dae 	struct drm_gem_object *obj;
118a4f19aaaSInki Dae 
119a4f19aaaSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
120a4f19aaaSInki Dae 	if (!obj) {
121a4f19aaaSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
122a4f19aaaSInki Dae 		return 0;
123a4f19aaaSInki Dae 	}
124a4f19aaaSInki Dae 
125a4f19aaaSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
126a4f19aaaSInki Dae 
127a4f19aaaSInki Dae 	drm_gem_object_unreference_unlocked(obj);
128a4f19aaaSInki Dae 
129a4f19aaaSInki Dae 	return exynos_gem_obj->buffer->size;
130a4f19aaaSInki Dae }
131a4f19aaaSInki Dae 
132a4f19aaaSInki Dae 
133b2df26c1SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
1342364839aSJoonyoung Shim 						      unsigned long size)
1352364839aSJoonyoung Shim {
1362364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1372364839aSJoonyoung Shim 	struct drm_gem_object *obj;
1382364839aSJoonyoung Shim 	int ret;
1392364839aSJoonyoung Shim 
1402364839aSJoonyoung Shim 	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
14138bb5253SSachin Kamat 	if (!exynos_gem_obj)
1425f3f4266SJoonyoung Shim 		return ERR_PTR(-ENOMEM);
1432364839aSJoonyoung Shim 
1442b35892eSInki Dae 	exynos_gem_obj->size = size;
1452364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
1462364839aSJoonyoung Shim 
1472364839aSJoonyoung Shim 	ret = drm_gem_object_init(dev, obj, size);
1482364839aSJoonyoung Shim 	if (ret < 0) {
1492364839aSJoonyoung Shim 		DRM_ERROR("failed to initialize gem object\n");
1502364839aSJoonyoung Shim 		kfree(exynos_gem_obj);
1515f3f4266SJoonyoung Shim 		return ERR_PTR(ret);
1522364839aSJoonyoung Shim 	}
1532364839aSJoonyoung Shim 
1542364839aSJoonyoung Shim 	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
1552364839aSJoonyoung Shim 
1562364839aSJoonyoung Shim 	return exynos_gem_obj;
1571c248b7dSInki Dae }
1581c248b7dSInki Dae 
159f088d5a9SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
1602b35892eSInki Dae 						unsigned int flags,
161ee5e770eSJoonyoung Shim 						unsigned long size)
162f088d5a9SInki Dae {
1632364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1642b35892eSInki Dae 	struct exynos_drm_gem_buf *buf;
1652b35892eSInki Dae 	int ret;
166f088d5a9SInki Dae 
167dcf9af82SInki Dae 	if (!size) {
168dcf9af82SInki Dae 		DRM_ERROR("invalid size.\n");
169dcf9af82SInki Dae 		return ERR_PTR(-EINVAL);
170dcf9af82SInki Dae 	}
171f088d5a9SInki Dae 
172dcf9af82SInki Dae 	size = roundup_gem_size(size, flags);
173dcf9af82SInki Dae 
174dcf9af82SInki Dae 	ret = check_gem_flags(flags);
175dcf9af82SInki Dae 	if (ret)
176dcf9af82SInki Dae 		return ERR_PTR(ret);
1772b35892eSInki Dae 
1782b35892eSInki Dae 	buf = exynos_drm_init_buf(dev, size);
1792b35892eSInki Dae 	if (!buf)
180ee5e770eSJoonyoung Shim 		return ERR_PTR(-ENOMEM);
181f088d5a9SInki Dae 
1822364839aSJoonyoung Shim 	exynos_gem_obj = exynos_drm_gem_init(dev, size);
1835f3f4266SJoonyoung Shim 	if (IS_ERR(exynos_gem_obj)) {
1845f3f4266SJoonyoung Shim 		ret = PTR_ERR(exynos_gem_obj);
185dcf9af82SInki Dae 		goto err_fini_buf;
186f088d5a9SInki Dae 	}
187f088d5a9SInki Dae 
1882b35892eSInki Dae 	exynos_gem_obj->buffer = buf;
1892b35892eSInki Dae 
1902b35892eSInki Dae 	/* set memory type and cache attribute from user side. */
1912b35892eSInki Dae 	exynos_gem_obj->flags = flags;
1922b35892eSInki Dae 
1932b35892eSInki Dae 	ret = exynos_drm_alloc_buf(dev, buf, flags);
194c58c1599SYoungJun Cho 	if (ret < 0)
195c58c1599SYoungJun Cho 		goto err_gem_fini;
196f088d5a9SInki Dae 
197f088d5a9SInki Dae 	return exynos_gem_obj;
198dcf9af82SInki Dae 
199c58c1599SYoungJun Cho err_gem_fini:
200c58c1599SYoungJun Cho 	drm_gem_object_release(&exynos_gem_obj->base);
201c58c1599SYoungJun Cho 	kfree(exynos_gem_obj);
202dcf9af82SInki Dae err_fini_buf:
2032b35892eSInki Dae 	exynos_drm_fini_buf(dev, buf);
2042b35892eSInki Dae 	return ERR_PTR(ret);
205f088d5a9SInki Dae }
206f088d5a9SInki Dae 
2071c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
2081c248b7dSInki Dae 				struct drm_file *file_priv)
2091c248b7dSInki Dae {
2101c248b7dSInki Dae 	struct drm_exynos_gem_create *args = data;
211ee5e770eSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
2122364839aSJoonyoung Shim 	int ret;
2131c248b7dSInki Dae 
2142b35892eSInki Dae 	exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
2151c248b7dSInki Dae 	if (IS_ERR(exynos_gem_obj))
2161c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
2171c248b7dSInki Dae 
2182364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
2192364839aSJoonyoung Shim 			&args->handle);
2202364839aSJoonyoung Shim 	if (ret) {
2212364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
2222364839aSJoonyoung Shim 		return ret;
2232364839aSJoonyoung Shim 	}
2242364839aSJoonyoung Shim 
2251c248b7dSInki Dae 	return 0;
2261c248b7dSInki Dae }
2271c248b7dSInki Dae 
228d87342c1SInki Dae dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
229f0b1bda7SInki Dae 					unsigned int gem_handle,
230d87342c1SInki Dae 					struct drm_file *filp)
231f0b1bda7SInki Dae {
232f0b1bda7SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
233f0b1bda7SInki Dae 	struct drm_gem_object *obj;
234f0b1bda7SInki Dae 
235d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
236f0b1bda7SInki Dae 	if (!obj) {
237f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
238f0b1bda7SInki Dae 		return ERR_PTR(-EINVAL);
239f0b1bda7SInki Dae 	}
240f0b1bda7SInki Dae 
241f0b1bda7SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
242f0b1bda7SInki Dae 
243f0b1bda7SInki Dae 	return &exynos_gem_obj->buffer->dma_addr;
244f0b1bda7SInki Dae }
245f0b1bda7SInki Dae 
246f0b1bda7SInki Dae void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
247f0b1bda7SInki Dae 					unsigned int gem_handle,
248d87342c1SInki Dae 					struct drm_file *filp)
249f0b1bda7SInki Dae {
250f0b1bda7SInki Dae 	struct drm_gem_object *obj;
251f0b1bda7SInki Dae 
252d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
253f0b1bda7SInki Dae 	if (!obj) {
254f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
255f0b1bda7SInki Dae 		return;
256f0b1bda7SInki Dae 	}
257f0b1bda7SInki Dae 
258f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
259f0b1bda7SInki Dae 
260f0b1bda7SInki Dae 	/*
261f0b1bda7SInki Dae 	 * decrease obj->refcount one more time because we has already
262f0b1bda7SInki Dae 	 * increased it at exynos_drm_gem_get_dma_addr().
263f0b1bda7SInki Dae 	 */
264f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
265f0b1bda7SInki Dae }
266f0b1bda7SInki Dae 
267832316c7SInki Dae int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
2681c248b7dSInki Dae 				      struct vm_area_struct *vma)
2691c248b7dSInki Dae {
270832316c7SInki Dae 	struct drm_device *drm_dev = exynos_gem_obj->base.dev;
2712c871127SInki Dae 	struct exynos_drm_gem_buf *buffer;
2720519f9a1SInki Dae 	unsigned long vm_size;
2735b07c660SInki Dae 	int ret;
2741c248b7dSInki Dae 
275832316c7SInki Dae 	vma->vm_flags &= ~VM_PFNMAP;
276832316c7SInki Dae 	vma->vm_pgoff = 0;
2771c248b7dSInki Dae 
2780519f9a1SInki Dae 	vm_size = vma->vm_end - vma->vm_start;
2792b35892eSInki Dae 
2801c248b7dSInki Dae 	/*
2812c871127SInki Dae 	 * a buffer contains information to physically continuous memory
2821c248b7dSInki Dae 	 * allocated by user request or at framebuffer creation.
2831c248b7dSInki Dae 	 */
2842c871127SInki Dae 	buffer = exynos_gem_obj->buffer;
2851c248b7dSInki Dae 
2861c248b7dSInki Dae 	/* check if user-requested size is valid. */
2872c871127SInki Dae 	if (vm_size > buffer->size)
2881c248b7dSInki Dae 		return -EINVAL;
2891c248b7dSInki Dae 
2904744ad24SInki Dae 	ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
2910519f9a1SInki Dae 				buffer->dma_addr, buffer->size,
2920519f9a1SInki Dae 				&buffer->dma_attrs);
2935b07c660SInki Dae 	if (ret < 0) {
2945b07c660SInki Dae 		DRM_ERROR("failed to mmap.\n");
2955b07c660SInki Dae 		return ret;
2965b07c660SInki Dae 	}
2975b07c660SInki Dae 
2981c248b7dSInki Dae 	return 0;
2991c248b7dSInki Dae }
3001c248b7dSInki Dae 
30140cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
30240cd7e0cSInki Dae 				      struct drm_file *file_priv)
30340cd7e0cSInki Dae {	struct exynos_drm_gem_obj *exynos_gem_obj;
30440cd7e0cSInki Dae 	struct drm_exynos_gem_info *args = data;
30540cd7e0cSInki Dae 	struct drm_gem_object *obj;
30640cd7e0cSInki Dae 
30740cd7e0cSInki Dae 	mutex_lock(&dev->struct_mutex);
30840cd7e0cSInki Dae 
30940cd7e0cSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
31040cd7e0cSInki Dae 	if (!obj) {
31140cd7e0cSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
31240cd7e0cSInki Dae 		mutex_unlock(&dev->struct_mutex);
31340cd7e0cSInki Dae 		return -EINVAL;
31440cd7e0cSInki Dae 	}
31540cd7e0cSInki Dae 
31640cd7e0cSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
31740cd7e0cSInki Dae 
31840cd7e0cSInki Dae 	args->flags = exynos_gem_obj->flags;
31940cd7e0cSInki Dae 	args->size = exynos_gem_obj->size;
32040cd7e0cSInki Dae 
32140cd7e0cSInki Dae 	drm_gem_object_unreference(obj);
32240cd7e0cSInki Dae 	mutex_unlock(&dev->struct_mutex);
32340cd7e0cSInki Dae 
32440cd7e0cSInki Dae 	return 0;
32540cd7e0cSInki Dae }
32640cd7e0cSInki Dae 
3272a3098ffSInki Dae struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
3282a3098ffSInki Dae {
3292a3098ffSInki Dae 	struct vm_area_struct *vma_copy;
3302a3098ffSInki Dae 
3312a3098ffSInki Dae 	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
3322a3098ffSInki Dae 	if (!vma_copy)
3332a3098ffSInki Dae 		return NULL;
3342a3098ffSInki Dae 
3352a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->open)
3362a3098ffSInki Dae 		vma->vm_ops->open(vma);
3372a3098ffSInki Dae 
3382a3098ffSInki Dae 	if (vma->vm_file)
3392a3098ffSInki Dae 		get_file(vma->vm_file);
3402a3098ffSInki Dae 
3412a3098ffSInki Dae 	memcpy(vma_copy, vma, sizeof(*vma));
3422a3098ffSInki Dae 
3432a3098ffSInki Dae 	vma_copy->vm_mm = NULL;
3442a3098ffSInki Dae 	vma_copy->vm_next = NULL;
3452a3098ffSInki Dae 	vma_copy->vm_prev = NULL;
3462a3098ffSInki Dae 
3472a3098ffSInki Dae 	return vma_copy;
3482a3098ffSInki Dae }
3492a3098ffSInki Dae 
3502a3098ffSInki Dae void exynos_gem_put_vma(struct vm_area_struct *vma)
3512a3098ffSInki Dae {
3522a3098ffSInki Dae 	if (!vma)
3532a3098ffSInki Dae 		return;
3542a3098ffSInki Dae 
3552a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->close)
3562a3098ffSInki Dae 		vma->vm_ops->close(vma);
3572a3098ffSInki Dae 
3582a3098ffSInki Dae 	if (vma->vm_file)
3592a3098ffSInki Dae 		fput(vma->vm_file);
3602a3098ffSInki Dae 
3612a3098ffSInki Dae 	kfree(vma);
3622a3098ffSInki Dae }
3632a3098ffSInki Dae 
3642a3098ffSInki Dae int exynos_gem_get_pages_from_userptr(unsigned long start,
3652a3098ffSInki Dae 						unsigned int npages,
3662a3098ffSInki Dae 						struct page **pages,
3672a3098ffSInki Dae 						struct vm_area_struct *vma)
3682a3098ffSInki Dae {
3692a3098ffSInki Dae 	int get_npages;
3702a3098ffSInki Dae 
3712a3098ffSInki Dae 	/* the memory region mmaped with VM_PFNMAP. */
3722a3098ffSInki Dae 	if (vma_is_io(vma)) {
3732a3098ffSInki Dae 		unsigned int i;
3742a3098ffSInki Dae 
3752a3098ffSInki Dae 		for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
3762a3098ffSInki Dae 			unsigned long pfn;
3772a3098ffSInki Dae 			int ret = follow_pfn(vma, start, &pfn);
3782a3098ffSInki Dae 			if (ret)
3792a3098ffSInki Dae 				return ret;
3802a3098ffSInki Dae 
3812a3098ffSInki Dae 			pages[i] = pfn_to_page(pfn);
3822a3098ffSInki Dae 		}
3832a3098ffSInki Dae 
3842a3098ffSInki Dae 		if (i != npages) {
3852a3098ffSInki Dae 			DRM_ERROR("failed to get user_pages.\n");
3862a3098ffSInki Dae 			return -EINVAL;
3872a3098ffSInki Dae 		}
3882a3098ffSInki Dae 
3892a3098ffSInki Dae 		return 0;
3902a3098ffSInki Dae 	}
3912a3098ffSInki Dae 
3922a3098ffSInki Dae 	get_npages = get_user_pages(current, current->mm, start,
3932a3098ffSInki Dae 					npages, 1, 1, pages, NULL);
3942a3098ffSInki Dae 	get_npages = max(get_npages, 0);
3952a3098ffSInki Dae 	if (get_npages != npages) {
3962a3098ffSInki Dae 		DRM_ERROR("failed to get user_pages.\n");
3972a3098ffSInki Dae 		while (get_npages)
3982a3098ffSInki Dae 			put_page(pages[--get_npages]);
3992a3098ffSInki Dae 		return -EFAULT;
4002a3098ffSInki Dae 	}
4012a3098ffSInki Dae 
4022a3098ffSInki Dae 	return 0;
4032a3098ffSInki Dae }
4042a3098ffSInki Dae 
4052a3098ffSInki Dae void exynos_gem_put_pages_to_userptr(struct page **pages,
4062a3098ffSInki Dae 					unsigned int npages,
4072a3098ffSInki Dae 					struct vm_area_struct *vma)
4082a3098ffSInki Dae {
4092a3098ffSInki Dae 	if (!vma_is_io(vma)) {
4102a3098ffSInki Dae 		unsigned int i;
4112a3098ffSInki Dae 
4122a3098ffSInki Dae 		for (i = 0; i < npages; i++) {
4132a3098ffSInki Dae 			set_page_dirty_lock(pages[i]);
4142a3098ffSInki Dae 
4152a3098ffSInki Dae 			/*
4162a3098ffSInki Dae 			 * undo the reference we took when populating
4172a3098ffSInki Dae 			 * the table.
4182a3098ffSInki Dae 			 */
4192a3098ffSInki Dae 			put_page(pages[i]);
4202a3098ffSInki Dae 		}
4212a3098ffSInki Dae 	}
4222a3098ffSInki Dae }
4232a3098ffSInki Dae 
4242a3098ffSInki Dae int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
4252a3098ffSInki Dae 				struct sg_table *sgt,
4262a3098ffSInki Dae 				enum dma_data_direction dir)
4272a3098ffSInki Dae {
4282a3098ffSInki Dae 	int nents;
4292a3098ffSInki Dae 
4302a3098ffSInki Dae 	mutex_lock(&drm_dev->struct_mutex);
4312a3098ffSInki Dae 
4322a3098ffSInki Dae 	nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4332a3098ffSInki Dae 	if (!nents) {
4342a3098ffSInki Dae 		DRM_ERROR("failed to map sgl with dma.\n");
4352a3098ffSInki Dae 		mutex_unlock(&drm_dev->struct_mutex);
4362a3098ffSInki Dae 		return nents;
4372a3098ffSInki Dae 	}
4382a3098ffSInki Dae 
4392a3098ffSInki Dae 	mutex_unlock(&drm_dev->struct_mutex);
4402a3098ffSInki Dae 	return 0;
4412a3098ffSInki Dae }
4422a3098ffSInki Dae 
4432a3098ffSInki Dae void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
4442a3098ffSInki Dae 				struct sg_table *sgt,
4452a3098ffSInki Dae 				enum dma_data_direction dir)
4462a3098ffSInki Dae {
4472a3098ffSInki Dae 	dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4482a3098ffSInki Dae }
4492a3098ffSInki Dae 
450ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj)
4511c248b7dSInki Dae {
452b2df26c1SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
453b2df26c1SInki Dae 	struct exynos_drm_gem_buf *buf;
454b2df26c1SInki Dae 
455b2df26c1SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
456b2df26c1SInki Dae 	buf = exynos_gem_obj->buffer;
457b2df26c1SInki Dae 
4582364839aSJoonyoung Shim 	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
4591c248b7dSInki Dae }
4601c248b7dSInki Dae 
4611c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
462ee5e770eSJoonyoung Shim 			       struct drm_device *dev,
463ee5e770eSJoonyoung Shim 			       struct drm_mode_create_dumb *args)
4641c248b7dSInki Dae {
4651c248b7dSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
4662364839aSJoonyoung Shim 	int ret;
4671c248b7dSInki Dae 
4681c248b7dSInki Dae 	/*
469c6b78bc8SMasanari Iida 	 * allocate memory to be used for framebuffer.
4701c248b7dSInki Dae 	 * - this callback would be called by user application
4711c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
4721c248b7dSInki Dae 	 */
4731c248b7dSInki Dae 
4743fd6b694SCooper Yuan 	args->pitch = args->width * ((args->bpp + 7) / 8);
4757da5907cSInki Dae 	args->size = args->pitch * args->height;
4761c248b7dSInki Dae 
477122beea8SRahul Sharma 	if (is_drm_iommu_supported(dev)) {
4783fec4532SVikas Sajjan 		exynos_gem_obj = exynos_drm_gem_create(dev,
4793fec4532SVikas Sajjan 			EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
4803fec4532SVikas Sajjan 			args->size);
481122beea8SRahul Sharma 	} else {
482122beea8SRahul Sharma 		exynos_gem_obj = exynos_drm_gem_create(dev,
483122beea8SRahul Sharma 			EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
484122beea8SRahul Sharma 			args->size);
4853fec4532SVikas Sajjan 	}
4863fec4532SVikas Sajjan 
487122beea8SRahul Sharma 	if (IS_ERR(exynos_gem_obj)) {
488122beea8SRahul Sharma 		dev_warn(dev->dev, "FB allocation failed.\n");
4891c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
490122beea8SRahul Sharma 	}
4911c248b7dSInki Dae 
4922364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
4932364839aSJoonyoung Shim 			&args->handle);
4942364839aSJoonyoung Shim 	if (ret) {
4952364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
4962364839aSJoonyoung Shim 		return ret;
4972364839aSJoonyoung Shim 	}
4982364839aSJoonyoung Shim 
4991c248b7dSInki Dae 	return 0;
5001c248b7dSInki Dae }
5011c248b7dSInki Dae 
5021c248b7dSInki Dae int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
503ee5e770eSJoonyoung Shim 				   struct drm_device *dev, uint32_t handle,
504ee5e770eSJoonyoung Shim 				   uint64_t *offset)
5051c248b7dSInki Dae {
5061c248b7dSInki Dae 	struct drm_gem_object *obj;
5072d91cf17SJoonyoung Shim 	int ret = 0;
5081c248b7dSInki Dae 
5091c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
5101c248b7dSInki Dae 
5111c248b7dSInki Dae 	/*
5121c248b7dSInki Dae 	 * get offset of memory allocated for drm framebuffer.
5131c248b7dSInki Dae 	 * - this callback would be called by user application
5141c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_MAP_DUMB command.
5151c248b7dSInki Dae 	 */
5161c248b7dSInki Dae 
5171c248b7dSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, handle);
5181c248b7dSInki Dae 	if (!obj) {
5191c248b7dSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
5202d91cf17SJoonyoung Shim 		ret = -EINVAL;
5212d91cf17SJoonyoung Shim 		goto unlock;
5221c248b7dSInki Dae 	}
5231c248b7dSInki Dae 
5246037bafaSLaurent Pinchart 	ret = drm_gem_create_mmap_offset(obj);
5252d91cf17SJoonyoung Shim 	if (ret)
5262d91cf17SJoonyoung Shim 		goto out;
5271c248b7dSInki Dae 
5280de23977SDavid Herrmann 	*offset = drm_vma_node_offset_addr(&obj->vma_node);
5291c248b7dSInki Dae 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
5301c248b7dSInki Dae 
5312d91cf17SJoonyoung Shim out:
5322d91cf17SJoonyoung Shim 	drm_gem_object_unreference(obj);
5332d91cf17SJoonyoung Shim unlock:
5341c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5352d91cf17SJoonyoung Shim 	return ret;
5361c248b7dSInki Dae }
5371c248b7dSInki Dae 
5381c248b7dSInki Dae int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
5391c248b7dSInki Dae {
5401c248b7dSInki Dae 	struct drm_gem_object *obj = vma->vm_private_data;
5410e9a2ee3SJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
5420e9a2ee3SJoonyoung Shim 	struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
5430e9a2ee3SJoonyoung Shim 	unsigned long pfn;
5441c248b7dSInki Dae 	pgoff_t page_offset;
5451c248b7dSInki Dae 	int ret;
5461c248b7dSInki Dae 
5471c248b7dSInki Dae 	page_offset = ((unsigned long)vmf->virtual_address -
5481c248b7dSInki Dae 			vma->vm_start) >> PAGE_SHIFT;
5491c248b7dSInki Dae 
5500e9a2ee3SJoonyoung Shim 	if (page_offset >= (buf->size >> PAGE_SHIFT)) {
5510e9a2ee3SJoonyoung Shim 		DRM_ERROR("invalid page offset\n");
5520e9a2ee3SJoonyoung Shim 		ret = -EINVAL;
5530e9a2ee3SJoonyoung Shim 		goto out;
5540e9a2ee3SJoonyoung Shim 	}
5551c248b7dSInki Dae 
5560e9a2ee3SJoonyoung Shim 	pfn = page_to_pfn(buf->pages[page_offset]);
5570e9a2ee3SJoonyoung Shim 	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
5580e9a2ee3SJoonyoung Shim 
5590e9a2ee3SJoonyoung Shim out:
56023597e26SJoonyoung Shim 	switch (ret) {
56123597e26SJoonyoung Shim 	case 0:
56223597e26SJoonyoung Shim 	case -ERESTARTSYS:
56323597e26SJoonyoung Shim 	case -EINTR:
56423597e26SJoonyoung Shim 		return VM_FAULT_NOPAGE;
56523597e26SJoonyoung Shim 	case -ENOMEM:
56623597e26SJoonyoung Shim 		return VM_FAULT_OOM;
56723597e26SJoonyoung Shim 	default:
56823597e26SJoonyoung Shim 		return VM_FAULT_SIGBUS;
56923597e26SJoonyoung Shim 	}
5701c248b7dSInki Dae }
5711c248b7dSInki Dae 
5721c248b7dSInki Dae int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
5731c248b7dSInki Dae {
574c01d73faSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
575c01d73faSInki Dae 	struct drm_gem_object *obj;
5761c248b7dSInki Dae 	int ret;
5771c248b7dSInki Dae 
5781c248b7dSInki Dae 	/* set vm_area_struct. */
5791c248b7dSInki Dae 	ret = drm_gem_mmap(filp, vma);
5801c248b7dSInki Dae 	if (ret < 0) {
5811c248b7dSInki Dae 		DRM_ERROR("failed to mmap.\n");
5821c248b7dSInki Dae 		return ret;
5831c248b7dSInki Dae 	}
5841c248b7dSInki Dae 
585c01d73faSInki Dae 	obj = vma->vm_private_data;
586c01d73faSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
587c01d73faSInki Dae 
588c01d73faSInki Dae 	ret = check_gem_flags(exynos_gem_obj->flags);
589832316c7SInki Dae 	if (ret)
590832316c7SInki Dae 		goto err_close_vm;
5911c248b7dSInki Dae 
592c01d73faSInki Dae 	update_vm_cache_attr(exynos_gem_obj, vma);
593c01d73faSInki Dae 
594832316c7SInki Dae 	ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);
595832316c7SInki Dae 	if (ret)
596832316c7SInki Dae 		goto err_close_vm;
597832316c7SInki Dae 
598832316c7SInki Dae 	return ret;
599832316c7SInki Dae 
600832316c7SInki Dae err_close_vm:
601832316c7SInki Dae 	drm_gem_vm_close(vma);
602832316c7SInki Dae 	drm_gem_free_mmap_offset(obj);
603832316c7SInki Dae 
6041c248b7dSInki Dae 	return ret;
6051c248b7dSInki Dae }
606