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 unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
24dcf9af82SInki Dae {
250519f9a1SInki Dae 	/* TODO */
260519f9a1SInki Dae 
27dcf9af82SInki Dae 	return roundup(size, PAGE_SIZE);
282b35892eSInki Dae }
292b35892eSInki Dae 
302364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
312364839aSJoonyoung Shim 					struct drm_file *file_priv,
322364839aSJoonyoung Shim 					unsigned int *handle)
331c248b7dSInki Dae {
341c248b7dSInki Dae 	int ret;
351c248b7dSInki Dae 
361c248b7dSInki Dae 	/*
371c248b7dSInki Dae 	 * allocate a id of idr table where the obj is registered
381c248b7dSInki Dae 	 * and handle has the id what user can see.
391c248b7dSInki Dae 	 */
401c248b7dSInki Dae 	ret = drm_gem_handle_create(file_priv, obj, handle);
411c248b7dSInki Dae 	if (ret)
422364839aSJoonyoung Shim 		return ret;
431c248b7dSInki Dae 
441c248b7dSInki Dae 	DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
451c248b7dSInki Dae 
461c248b7dSInki Dae 	/* drop reference from allocate - handle holds it now. */
471c248b7dSInki Dae 	drm_gem_object_unreference_unlocked(obj);
481c248b7dSInki Dae 
492364839aSJoonyoung Shim 	return 0;
502364839aSJoonyoung Shim }
511c248b7dSInki Dae 
522364839aSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
532364839aSJoonyoung Shim {
542364839aSJoonyoung Shim 	struct drm_gem_object *obj;
55c01d73faSInki Dae 	struct exynos_drm_gem_buf *buf;
562364839aSJoonyoung Shim 
572364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
58c01d73faSInki Dae 	buf = exynos_gem_obj->buffer;
592364839aSJoonyoung Shim 
60a8e11d1cSDaniel Vetter 	DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count);
612364839aSJoonyoung Shim 
62c374e731SInki Dae 	/*
63c374e731SInki Dae 	 * do not release memory region from exporter.
64c374e731SInki Dae 	 *
65c374e731SInki Dae 	 * the region will be released by exporter
66c374e731SInki Dae 	 * once dmabuf's refcount becomes 0.
67c374e731SInki Dae 	 */
68c374e731SInki Dae 	if (obj->import_attach)
69c374e731SInki Dae 		goto out;
70c374e731SInki Dae 
71c01d73faSInki Dae 	exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
722b35892eSInki Dae 
73c374e731SInki Dae out:
74c01d73faSInki Dae 	exynos_drm_fini_buf(obj->dev, buf);
752b35892eSInki Dae 	exynos_gem_obj->buffer = NULL;
762364839aSJoonyoung Shim 
772364839aSJoonyoung Shim 	drm_gem_free_mmap_offset(obj);
782364839aSJoonyoung Shim 
792364839aSJoonyoung Shim 	/* release file pointer to gem object. */
801c248b7dSInki Dae 	drm_gem_object_release(obj);
811c248b7dSInki Dae 
821c248b7dSInki Dae 	kfree(exynos_gem_obj);
832b35892eSInki Dae 	exynos_gem_obj = NULL;
842364839aSJoonyoung Shim }
852364839aSJoonyoung Shim 
86a4f19aaaSInki Dae unsigned long exynos_drm_gem_get_size(struct drm_device *dev,
87a4f19aaaSInki Dae 						unsigned int gem_handle,
88a4f19aaaSInki Dae 						struct drm_file *file_priv)
89a4f19aaaSInki Dae {
90a4f19aaaSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
91a4f19aaaSInki Dae 	struct drm_gem_object *obj;
92a4f19aaaSInki Dae 
93a4f19aaaSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
94a4f19aaaSInki Dae 	if (!obj) {
95a4f19aaaSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
96a4f19aaaSInki Dae 		return 0;
97a4f19aaaSInki Dae 	}
98a4f19aaaSInki Dae 
99a4f19aaaSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
100a4f19aaaSInki Dae 
101a4f19aaaSInki Dae 	drm_gem_object_unreference_unlocked(obj);
102a4f19aaaSInki Dae 
103a4f19aaaSInki Dae 	return exynos_gem_obj->buffer->size;
104a4f19aaaSInki Dae }
105a4f19aaaSInki Dae 
106a4f19aaaSInki Dae 
107b2df26c1SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
1082364839aSJoonyoung Shim 						      unsigned long size)
1092364839aSJoonyoung Shim {
1102364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1112364839aSJoonyoung Shim 	struct drm_gem_object *obj;
1122364839aSJoonyoung Shim 	int ret;
1132364839aSJoonyoung Shim 
1142364839aSJoonyoung Shim 	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
11538bb5253SSachin Kamat 	if (!exynos_gem_obj)
1165f3f4266SJoonyoung Shim 		return ERR_PTR(-ENOMEM);
1172364839aSJoonyoung Shim 
1182b35892eSInki Dae 	exynos_gem_obj->size = size;
1192364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
1202364839aSJoonyoung Shim 
1212364839aSJoonyoung Shim 	ret = drm_gem_object_init(dev, obj, size);
1222364839aSJoonyoung Shim 	if (ret < 0) {
1232364839aSJoonyoung Shim 		DRM_ERROR("failed to initialize gem object\n");
1242364839aSJoonyoung Shim 		kfree(exynos_gem_obj);
1255f3f4266SJoonyoung Shim 		return ERR_PTR(ret);
1262364839aSJoonyoung Shim 	}
1272364839aSJoonyoung Shim 
1282364839aSJoonyoung Shim 	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
1292364839aSJoonyoung Shim 
1302364839aSJoonyoung Shim 	return exynos_gem_obj;
1311c248b7dSInki Dae }
1321c248b7dSInki Dae 
133f088d5a9SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
1342b35892eSInki Dae 						unsigned int flags,
135ee5e770eSJoonyoung Shim 						unsigned long size)
136f088d5a9SInki Dae {
1372364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1382b35892eSInki Dae 	struct exynos_drm_gem_buf *buf;
1392b35892eSInki Dae 	int ret;
140f088d5a9SInki Dae 
141c4130bcdSJoonyoung Shim 	if (flags & ~(EXYNOS_BO_MASK)) {
142c4130bcdSJoonyoung Shim 		DRM_ERROR("invalid flags.\n");
143c4130bcdSJoonyoung Shim 		return ERR_PTR(-EINVAL);
144c4130bcdSJoonyoung Shim 	}
145c4130bcdSJoonyoung Shim 
146dcf9af82SInki Dae 	if (!size) {
147dcf9af82SInki Dae 		DRM_ERROR("invalid size.\n");
148dcf9af82SInki Dae 		return ERR_PTR(-EINVAL);
149dcf9af82SInki Dae 	}
150f088d5a9SInki Dae 
151dcf9af82SInki Dae 	size = roundup_gem_size(size, flags);
152dcf9af82SInki Dae 
1532b35892eSInki Dae 	buf = exynos_drm_init_buf(dev, size);
1542b35892eSInki Dae 	if (!buf)
155ee5e770eSJoonyoung Shim 		return ERR_PTR(-ENOMEM);
156f088d5a9SInki Dae 
1572364839aSJoonyoung Shim 	exynos_gem_obj = exynos_drm_gem_init(dev, size);
1585f3f4266SJoonyoung Shim 	if (IS_ERR(exynos_gem_obj)) {
1595f3f4266SJoonyoung Shim 		ret = PTR_ERR(exynos_gem_obj);
160dcf9af82SInki Dae 		goto err_fini_buf;
161f088d5a9SInki Dae 	}
162f088d5a9SInki Dae 
1632b35892eSInki Dae 	exynos_gem_obj->buffer = buf;
1642b35892eSInki Dae 
1652b35892eSInki Dae 	/* set memory type and cache attribute from user side. */
1662b35892eSInki Dae 	exynos_gem_obj->flags = flags;
1672b35892eSInki Dae 
1682b35892eSInki Dae 	ret = exynos_drm_alloc_buf(dev, buf, flags);
169c58c1599SYoungJun Cho 	if (ret < 0)
170c58c1599SYoungJun Cho 		goto err_gem_fini;
171f088d5a9SInki Dae 
172f088d5a9SInki Dae 	return exynos_gem_obj;
173dcf9af82SInki Dae 
174c58c1599SYoungJun Cho err_gem_fini:
175c58c1599SYoungJun Cho 	drm_gem_object_release(&exynos_gem_obj->base);
176c58c1599SYoungJun Cho 	kfree(exynos_gem_obj);
177dcf9af82SInki Dae err_fini_buf:
1782b35892eSInki Dae 	exynos_drm_fini_buf(dev, buf);
1792b35892eSInki Dae 	return ERR_PTR(ret);
180f088d5a9SInki Dae }
181f088d5a9SInki Dae 
1821c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
1831c248b7dSInki Dae 				struct drm_file *file_priv)
1841c248b7dSInki Dae {
1851c248b7dSInki Dae 	struct drm_exynos_gem_create *args = data;
186ee5e770eSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1872364839aSJoonyoung Shim 	int ret;
1881c248b7dSInki Dae 
1892b35892eSInki Dae 	exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
1901c248b7dSInki Dae 	if (IS_ERR(exynos_gem_obj))
1911c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
1921c248b7dSInki Dae 
1932364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
1942364839aSJoonyoung Shim 			&args->handle);
1952364839aSJoonyoung Shim 	if (ret) {
1962364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
1972364839aSJoonyoung Shim 		return ret;
1982364839aSJoonyoung Shim 	}
1992364839aSJoonyoung Shim 
2001c248b7dSInki Dae 	return 0;
2011c248b7dSInki Dae }
2021c248b7dSInki Dae 
203d87342c1SInki Dae dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
204f0b1bda7SInki Dae 					unsigned int gem_handle,
205d87342c1SInki Dae 					struct drm_file *filp)
206f0b1bda7SInki Dae {
207f0b1bda7SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
208f0b1bda7SInki Dae 	struct drm_gem_object *obj;
209f0b1bda7SInki Dae 
210d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
211f0b1bda7SInki Dae 	if (!obj) {
212f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
213f0b1bda7SInki Dae 		return ERR_PTR(-EINVAL);
214f0b1bda7SInki Dae 	}
215f0b1bda7SInki Dae 
216f0b1bda7SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
217f0b1bda7SInki Dae 
218f0b1bda7SInki Dae 	return &exynos_gem_obj->buffer->dma_addr;
219f0b1bda7SInki Dae }
220f0b1bda7SInki Dae 
221f0b1bda7SInki Dae void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
222f0b1bda7SInki Dae 					unsigned int gem_handle,
223d87342c1SInki Dae 					struct drm_file *filp)
224f0b1bda7SInki Dae {
225f0b1bda7SInki Dae 	struct drm_gem_object *obj;
226f0b1bda7SInki Dae 
227d87342c1SInki Dae 	obj = drm_gem_object_lookup(dev, filp, gem_handle);
228f0b1bda7SInki Dae 	if (!obj) {
229f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
230f0b1bda7SInki Dae 		return;
231f0b1bda7SInki Dae 	}
232f0b1bda7SInki Dae 
233f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
234f0b1bda7SInki Dae 
235f0b1bda7SInki Dae 	/*
236f0b1bda7SInki Dae 	 * decrease obj->refcount one more time because we has already
237f0b1bda7SInki Dae 	 * increased it at exynos_drm_gem_get_dma_addr().
238f0b1bda7SInki Dae 	 */
239f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
240f0b1bda7SInki Dae }
241f0b1bda7SInki Dae 
242832316c7SInki Dae int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem_obj *exynos_gem_obj,
2431c248b7dSInki Dae 				      struct vm_area_struct *vma)
2441c248b7dSInki Dae {
245832316c7SInki Dae 	struct drm_device *drm_dev = exynos_gem_obj->base.dev;
2462c871127SInki Dae 	struct exynos_drm_gem_buf *buffer;
2470519f9a1SInki Dae 	unsigned long vm_size;
2485b07c660SInki Dae 	int ret;
2491c248b7dSInki Dae 
250832316c7SInki Dae 	vma->vm_flags &= ~VM_PFNMAP;
251832316c7SInki Dae 	vma->vm_pgoff = 0;
2521c248b7dSInki Dae 
2530519f9a1SInki Dae 	vm_size = vma->vm_end - vma->vm_start;
2542b35892eSInki Dae 
2551c248b7dSInki Dae 	/*
2562c871127SInki Dae 	 * a buffer contains information to physically continuous memory
2571c248b7dSInki Dae 	 * allocated by user request or at framebuffer creation.
2581c248b7dSInki Dae 	 */
2592c871127SInki Dae 	buffer = exynos_gem_obj->buffer;
2601c248b7dSInki Dae 
2611c248b7dSInki Dae 	/* check if user-requested size is valid. */
2622c871127SInki Dae 	if (vm_size > buffer->size)
2631c248b7dSInki Dae 		return -EINVAL;
2641c248b7dSInki Dae 
2654744ad24SInki Dae 	ret = dma_mmap_attrs(drm_dev->dev, vma, buffer->pages,
2660519f9a1SInki Dae 				buffer->dma_addr, buffer->size,
2670519f9a1SInki Dae 				&buffer->dma_attrs);
2685b07c660SInki Dae 	if (ret < 0) {
2695b07c660SInki Dae 		DRM_ERROR("failed to mmap.\n");
2705b07c660SInki Dae 		return ret;
2715b07c660SInki Dae 	}
2725b07c660SInki Dae 
2731c248b7dSInki Dae 	return 0;
2741c248b7dSInki Dae }
2751c248b7dSInki Dae 
27640cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
27740cd7e0cSInki Dae 				      struct drm_file *file_priv)
27840cd7e0cSInki Dae {	struct exynos_drm_gem_obj *exynos_gem_obj;
27940cd7e0cSInki Dae 	struct drm_exynos_gem_info *args = data;
28040cd7e0cSInki Dae 	struct drm_gem_object *obj;
28140cd7e0cSInki Dae 
28240cd7e0cSInki Dae 	mutex_lock(&dev->struct_mutex);
28340cd7e0cSInki Dae 
28440cd7e0cSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
28540cd7e0cSInki Dae 	if (!obj) {
28640cd7e0cSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
28740cd7e0cSInki Dae 		mutex_unlock(&dev->struct_mutex);
28840cd7e0cSInki Dae 		return -EINVAL;
28940cd7e0cSInki Dae 	}
29040cd7e0cSInki Dae 
29140cd7e0cSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
29240cd7e0cSInki Dae 
29340cd7e0cSInki Dae 	args->flags = exynos_gem_obj->flags;
29440cd7e0cSInki Dae 	args->size = exynos_gem_obj->size;
29540cd7e0cSInki Dae 
29640cd7e0cSInki Dae 	drm_gem_object_unreference(obj);
29740cd7e0cSInki Dae 	mutex_unlock(&dev->struct_mutex);
29840cd7e0cSInki Dae 
29940cd7e0cSInki Dae 	return 0;
30040cd7e0cSInki Dae }
30140cd7e0cSInki Dae 
3022a3098ffSInki Dae struct vm_area_struct *exynos_gem_get_vma(struct vm_area_struct *vma)
3032a3098ffSInki Dae {
3042a3098ffSInki Dae 	struct vm_area_struct *vma_copy;
3052a3098ffSInki Dae 
3062a3098ffSInki Dae 	vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL);
3072a3098ffSInki Dae 	if (!vma_copy)
3082a3098ffSInki Dae 		return NULL;
3092a3098ffSInki Dae 
3102a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->open)
3112a3098ffSInki Dae 		vma->vm_ops->open(vma);
3122a3098ffSInki Dae 
3132a3098ffSInki Dae 	if (vma->vm_file)
3142a3098ffSInki Dae 		get_file(vma->vm_file);
3152a3098ffSInki Dae 
3162a3098ffSInki Dae 	memcpy(vma_copy, vma, sizeof(*vma));
3172a3098ffSInki Dae 
3182a3098ffSInki Dae 	vma_copy->vm_mm = NULL;
3192a3098ffSInki Dae 	vma_copy->vm_next = NULL;
3202a3098ffSInki Dae 	vma_copy->vm_prev = NULL;
3212a3098ffSInki Dae 
3222a3098ffSInki Dae 	return vma_copy;
3232a3098ffSInki Dae }
3242a3098ffSInki Dae 
3252a3098ffSInki Dae void exynos_gem_put_vma(struct vm_area_struct *vma)
3262a3098ffSInki Dae {
3272a3098ffSInki Dae 	if (!vma)
3282a3098ffSInki Dae 		return;
3292a3098ffSInki Dae 
3302a3098ffSInki Dae 	if (vma->vm_ops && vma->vm_ops->close)
3312a3098ffSInki Dae 		vma->vm_ops->close(vma);
3322a3098ffSInki Dae 
3332a3098ffSInki Dae 	if (vma->vm_file)
3342a3098ffSInki Dae 		fput(vma->vm_file);
3352a3098ffSInki Dae 
3362a3098ffSInki Dae 	kfree(vma);
3372a3098ffSInki Dae }
3382a3098ffSInki Dae 
3392a3098ffSInki Dae int exynos_gem_get_pages_from_userptr(unsigned long start,
3402a3098ffSInki Dae 						unsigned int npages,
3412a3098ffSInki Dae 						struct page **pages,
3422a3098ffSInki Dae 						struct vm_area_struct *vma)
3432a3098ffSInki Dae {
3442a3098ffSInki Dae 	int get_npages;
3452a3098ffSInki Dae 
3462a3098ffSInki Dae 	/* the memory region mmaped with VM_PFNMAP. */
3472a3098ffSInki Dae 	if (vma_is_io(vma)) {
3482a3098ffSInki Dae 		unsigned int i;
3492a3098ffSInki Dae 
3502a3098ffSInki Dae 		for (i = 0; i < npages; ++i, start += PAGE_SIZE) {
3512a3098ffSInki Dae 			unsigned long pfn;
3522a3098ffSInki Dae 			int ret = follow_pfn(vma, start, &pfn);
3532a3098ffSInki Dae 			if (ret)
3542a3098ffSInki Dae 				return ret;
3552a3098ffSInki Dae 
3562a3098ffSInki Dae 			pages[i] = pfn_to_page(pfn);
3572a3098ffSInki Dae 		}
3582a3098ffSInki Dae 
3592a3098ffSInki Dae 		if (i != npages) {
3602a3098ffSInki Dae 			DRM_ERROR("failed to get user_pages.\n");
3612a3098ffSInki Dae 			return -EINVAL;
3622a3098ffSInki Dae 		}
3632a3098ffSInki Dae 
3642a3098ffSInki Dae 		return 0;
3652a3098ffSInki Dae 	}
3662a3098ffSInki Dae 
3672a3098ffSInki Dae 	get_npages = get_user_pages(current, current->mm, start,
3682a3098ffSInki Dae 					npages, 1, 1, pages, NULL);
3692a3098ffSInki Dae 	get_npages = max(get_npages, 0);
3702a3098ffSInki Dae 	if (get_npages != npages) {
3712a3098ffSInki Dae 		DRM_ERROR("failed to get user_pages.\n");
3722a3098ffSInki Dae 		while (get_npages)
3732a3098ffSInki Dae 			put_page(pages[--get_npages]);
3742a3098ffSInki Dae 		return -EFAULT;
3752a3098ffSInki Dae 	}
3762a3098ffSInki Dae 
3772a3098ffSInki Dae 	return 0;
3782a3098ffSInki Dae }
3792a3098ffSInki Dae 
3802a3098ffSInki Dae void exynos_gem_put_pages_to_userptr(struct page **pages,
3812a3098ffSInki Dae 					unsigned int npages,
3822a3098ffSInki Dae 					struct vm_area_struct *vma)
3832a3098ffSInki Dae {
3842a3098ffSInki Dae 	if (!vma_is_io(vma)) {
3852a3098ffSInki Dae 		unsigned int i;
3862a3098ffSInki Dae 
3872a3098ffSInki Dae 		for (i = 0; i < npages; i++) {
3882a3098ffSInki Dae 			set_page_dirty_lock(pages[i]);
3892a3098ffSInki Dae 
3902a3098ffSInki Dae 			/*
3912a3098ffSInki Dae 			 * undo the reference we took when populating
3922a3098ffSInki Dae 			 * the table.
3932a3098ffSInki Dae 			 */
3942a3098ffSInki Dae 			put_page(pages[i]);
3952a3098ffSInki Dae 		}
3962a3098ffSInki Dae 	}
3972a3098ffSInki Dae }
3982a3098ffSInki Dae 
3992a3098ffSInki Dae int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev,
4002a3098ffSInki Dae 				struct sg_table *sgt,
4012a3098ffSInki Dae 				enum dma_data_direction dir)
4022a3098ffSInki Dae {
4032a3098ffSInki Dae 	int nents;
4042a3098ffSInki Dae 
4052a3098ffSInki Dae 	mutex_lock(&drm_dev->struct_mutex);
4062a3098ffSInki Dae 
4072a3098ffSInki Dae 	nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4082a3098ffSInki Dae 	if (!nents) {
4092a3098ffSInki Dae 		DRM_ERROR("failed to map sgl with dma.\n");
4102a3098ffSInki Dae 		mutex_unlock(&drm_dev->struct_mutex);
4112a3098ffSInki Dae 		return nents;
4122a3098ffSInki Dae 	}
4132a3098ffSInki Dae 
4142a3098ffSInki Dae 	mutex_unlock(&drm_dev->struct_mutex);
4152a3098ffSInki Dae 	return 0;
4162a3098ffSInki Dae }
4172a3098ffSInki Dae 
4182a3098ffSInki Dae void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev,
4192a3098ffSInki Dae 				struct sg_table *sgt,
4202a3098ffSInki Dae 				enum dma_data_direction dir)
4212a3098ffSInki Dae {
4222a3098ffSInki Dae 	dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir);
4232a3098ffSInki Dae }
4242a3098ffSInki Dae 
425ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj)
4261c248b7dSInki Dae {
427b2df26c1SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
428b2df26c1SInki Dae 	struct exynos_drm_gem_buf *buf;
429b2df26c1SInki Dae 
430b2df26c1SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
431b2df26c1SInki Dae 	buf = exynos_gem_obj->buffer;
432b2df26c1SInki Dae 
4332364839aSJoonyoung Shim 	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
4341c248b7dSInki Dae }
4351c248b7dSInki Dae 
4361c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
437ee5e770eSJoonyoung Shim 			       struct drm_device *dev,
438ee5e770eSJoonyoung Shim 			       struct drm_mode_create_dumb *args)
4391c248b7dSInki Dae {
4401c248b7dSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
4412364839aSJoonyoung Shim 	int ret;
4421c248b7dSInki Dae 
4431c248b7dSInki Dae 	/*
444c6b78bc8SMasanari Iida 	 * allocate memory to be used for framebuffer.
4451c248b7dSInki Dae 	 * - this callback would be called by user application
4461c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
4471c248b7dSInki Dae 	 */
4481c248b7dSInki Dae 
4493fd6b694SCooper Yuan 	args->pitch = args->width * ((args->bpp + 7) / 8);
4507da5907cSInki Dae 	args->size = args->pitch * args->height;
4511c248b7dSInki Dae 
452122beea8SRahul Sharma 	if (is_drm_iommu_supported(dev)) {
4533fec4532SVikas Sajjan 		exynos_gem_obj = exynos_drm_gem_create(dev,
4543fec4532SVikas Sajjan 			EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC,
4553fec4532SVikas Sajjan 			args->size);
456122beea8SRahul Sharma 	} else {
457122beea8SRahul Sharma 		exynos_gem_obj = exynos_drm_gem_create(dev,
458122beea8SRahul Sharma 			EXYNOS_BO_CONTIG | EXYNOS_BO_WC,
459122beea8SRahul Sharma 			args->size);
4603fec4532SVikas Sajjan 	}
4613fec4532SVikas Sajjan 
462122beea8SRahul Sharma 	if (IS_ERR(exynos_gem_obj)) {
463122beea8SRahul Sharma 		dev_warn(dev->dev, "FB allocation failed.\n");
4641c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
465122beea8SRahul Sharma 	}
4661c248b7dSInki Dae 
4672364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
4682364839aSJoonyoung Shim 			&args->handle);
4692364839aSJoonyoung Shim 	if (ret) {
4702364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
4712364839aSJoonyoung Shim 		return ret;
4722364839aSJoonyoung Shim 	}
4732364839aSJoonyoung Shim 
4741c248b7dSInki Dae 	return 0;
4751c248b7dSInki Dae }
4761c248b7dSInki Dae 
4771c248b7dSInki Dae int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
478ee5e770eSJoonyoung Shim 				   struct drm_device *dev, uint32_t handle,
479ee5e770eSJoonyoung Shim 				   uint64_t *offset)
4801c248b7dSInki Dae {
4811c248b7dSInki Dae 	struct drm_gem_object *obj;
4822d91cf17SJoonyoung Shim 	int ret = 0;
4831c248b7dSInki Dae 
4841c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
4851c248b7dSInki Dae 
4861c248b7dSInki Dae 	/*
4871c248b7dSInki Dae 	 * get offset of memory allocated for drm framebuffer.
4881c248b7dSInki Dae 	 * - this callback would be called by user application
4891c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_MAP_DUMB command.
4901c248b7dSInki Dae 	 */
4911c248b7dSInki Dae 
4921c248b7dSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, handle);
4931c248b7dSInki Dae 	if (!obj) {
4941c248b7dSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
4952d91cf17SJoonyoung Shim 		ret = -EINVAL;
4962d91cf17SJoonyoung Shim 		goto unlock;
4971c248b7dSInki Dae 	}
4981c248b7dSInki Dae 
4996037bafaSLaurent Pinchart 	ret = drm_gem_create_mmap_offset(obj);
5002d91cf17SJoonyoung Shim 	if (ret)
5012d91cf17SJoonyoung Shim 		goto out;
5021c248b7dSInki Dae 
5030de23977SDavid Herrmann 	*offset = drm_vma_node_offset_addr(&obj->vma_node);
5041c248b7dSInki Dae 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
5051c248b7dSInki Dae 
5062d91cf17SJoonyoung Shim out:
5072d91cf17SJoonyoung Shim 	drm_gem_object_unreference(obj);
5082d91cf17SJoonyoung Shim unlock:
5091c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5102d91cf17SJoonyoung Shim 	return ret;
5111c248b7dSInki Dae }
5121c248b7dSInki Dae 
5131c248b7dSInki Dae int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
5141c248b7dSInki Dae {
5151c248b7dSInki Dae 	struct drm_gem_object *obj = vma->vm_private_data;
5160e9a2ee3SJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
5170e9a2ee3SJoonyoung Shim 	struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
5180e9a2ee3SJoonyoung Shim 	unsigned long pfn;
5191c248b7dSInki Dae 	pgoff_t page_offset;
5201c248b7dSInki Dae 	int ret;
5211c248b7dSInki Dae 
5221c248b7dSInki Dae 	page_offset = ((unsigned long)vmf->virtual_address -
5231c248b7dSInki Dae 			vma->vm_start) >> PAGE_SHIFT;
5241c248b7dSInki Dae 
5250e9a2ee3SJoonyoung Shim 	if (page_offset >= (buf->size >> PAGE_SHIFT)) {
5260e9a2ee3SJoonyoung Shim 		DRM_ERROR("invalid page offset\n");
5270e9a2ee3SJoonyoung Shim 		ret = -EINVAL;
5280e9a2ee3SJoonyoung Shim 		goto out;
5290e9a2ee3SJoonyoung Shim 	}
5301c248b7dSInki Dae 
5310e9a2ee3SJoonyoung Shim 	pfn = page_to_pfn(buf->pages[page_offset]);
5320e9a2ee3SJoonyoung Shim 	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
5330e9a2ee3SJoonyoung Shim 
5340e9a2ee3SJoonyoung Shim out:
53523597e26SJoonyoung Shim 	switch (ret) {
53623597e26SJoonyoung Shim 	case 0:
53723597e26SJoonyoung Shim 	case -ERESTARTSYS:
53823597e26SJoonyoung Shim 	case -EINTR:
53923597e26SJoonyoung Shim 		return VM_FAULT_NOPAGE;
54023597e26SJoonyoung Shim 	case -ENOMEM:
54123597e26SJoonyoung Shim 		return VM_FAULT_OOM;
54223597e26SJoonyoung Shim 	default:
54323597e26SJoonyoung Shim 		return VM_FAULT_SIGBUS;
54423597e26SJoonyoung Shim 	}
5451c248b7dSInki Dae }
5461c248b7dSInki Dae 
5471c248b7dSInki Dae int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
5481c248b7dSInki Dae {
549c01d73faSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
550c01d73faSInki Dae 	struct drm_gem_object *obj;
5511c248b7dSInki Dae 	int ret;
5521c248b7dSInki Dae 
5531c248b7dSInki Dae 	/* set vm_area_struct. */
5541c248b7dSInki Dae 	ret = drm_gem_mmap(filp, vma);
5551c248b7dSInki Dae 	if (ret < 0) {
5561c248b7dSInki Dae 		DRM_ERROR("failed to mmap.\n");
5571c248b7dSInki Dae 		return ret;
5581c248b7dSInki Dae 	}
5591c248b7dSInki Dae 
560c01d73faSInki Dae 	obj = vma->vm_private_data;
561c01d73faSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
562c01d73faSInki Dae 
563211b8878SJoonyoung Shim 	DRM_DEBUG_KMS("flags = 0x%x\n", exynos_gem_obj->flags);
564211b8878SJoonyoung Shim 
565211b8878SJoonyoung Shim 	/* non-cachable as default. */
566211b8878SJoonyoung Shim 	if (exynos_gem_obj->flags & EXYNOS_BO_CACHABLE)
567211b8878SJoonyoung Shim 		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
568211b8878SJoonyoung Shim 	else if (exynos_gem_obj->flags & EXYNOS_BO_WC)
569211b8878SJoonyoung Shim 		vma->vm_page_prot =
570211b8878SJoonyoung Shim 			pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
571211b8878SJoonyoung Shim 	else
572211b8878SJoonyoung Shim 		vma->vm_page_prot =
573211b8878SJoonyoung Shim 			pgprot_noncached(vm_get_page_prot(vma->vm_flags));
574c01d73faSInki Dae 
575832316c7SInki Dae 	ret = exynos_drm_gem_mmap_buffer(exynos_gem_obj, vma);
576832316c7SInki Dae 	if (ret)
577832316c7SInki Dae 		goto err_close_vm;
578832316c7SInki Dae 
579832316c7SInki Dae 	return ret;
580832316c7SInki Dae 
581832316c7SInki Dae err_close_vm:
582832316c7SInki Dae 	drm_gem_vm_close(vma);
583832316c7SInki Dae 	drm_gem_free_mmap_offset(obj);
584832316c7SInki Dae 
5851c248b7dSInki Dae 	return ret;
5861c248b7dSInki Dae }
587