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  *
61c248b7dSInki Dae  * Permission is hereby granted, free of charge, to any person obtaining a
71c248b7dSInki Dae  * copy of this software and associated documentation files (the "Software"),
81c248b7dSInki Dae  * to deal in the Software without restriction, including without limitation
91c248b7dSInki Dae  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
101c248b7dSInki Dae  * and/or sell copies of the Software, and to permit persons to whom the
111c248b7dSInki Dae  * Software is furnished to do so, subject to the following conditions:
121c248b7dSInki Dae  *
131c248b7dSInki Dae  * The above copyright notice and this permission notice (including the next
141c248b7dSInki Dae  * paragraph) shall be included in all copies or substantial portions of the
151c248b7dSInki Dae  * Software.
161c248b7dSInki Dae  *
171c248b7dSInki Dae  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
181c248b7dSInki Dae  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
191c248b7dSInki Dae  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
201c248b7dSInki Dae  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
211c248b7dSInki Dae  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
221c248b7dSInki Dae  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
231c248b7dSInki Dae  * OTHER DEALINGS IN THE SOFTWARE.
241c248b7dSInki Dae  */
251c248b7dSInki Dae 
26760285e7SDavid Howells #include <drm/drmP.h>
271c248b7dSInki Dae 
282b35892eSInki Dae #include <linux/shmem_fs.h>
291c248b7dSInki Dae #include <drm/exynos_drm.h>
301c248b7dSInki Dae 
311c248b7dSInki Dae #include "exynos_drm_drv.h"
321c248b7dSInki Dae #include "exynos_drm_gem.h"
331c248b7dSInki Dae #include "exynos_drm_buf.h"
341c248b7dSInki Dae 
351c248b7dSInki Dae static unsigned int convert_to_vm_err_msg(int msg)
361c248b7dSInki Dae {
371c248b7dSInki Dae 	unsigned int out_msg;
381c248b7dSInki Dae 
391c248b7dSInki Dae 	switch (msg) {
401c248b7dSInki Dae 	case 0:
411c248b7dSInki Dae 	case -ERESTARTSYS:
421c248b7dSInki Dae 	case -EINTR:
431c248b7dSInki Dae 		out_msg = VM_FAULT_NOPAGE;
441c248b7dSInki Dae 		break;
451c248b7dSInki Dae 
461c248b7dSInki Dae 	case -ENOMEM:
471c248b7dSInki Dae 		out_msg = VM_FAULT_OOM;
481c248b7dSInki Dae 		break;
491c248b7dSInki Dae 
501c248b7dSInki Dae 	default:
511c248b7dSInki Dae 		out_msg = VM_FAULT_SIGBUS;
521c248b7dSInki Dae 		break;
531c248b7dSInki Dae 	}
541c248b7dSInki Dae 
551c248b7dSInki Dae 	return out_msg;
561c248b7dSInki Dae }
571c248b7dSInki Dae 
58dcf9af82SInki Dae static int check_gem_flags(unsigned int flags)
592b35892eSInki Dae {
60dcf9af82SInki Dae 	if (flags & ~(EXYNOS_BO_MASK)) {
61dcf9af82SInki Dae 		DRM_ERROR("invalid flags.\n");
62dcf9af82SInki Dae 		return -EINVAL;
63dcf9af82SInki Dae 	}
64dcf9af82SInki Dae 
65dcf9af82SInki Dae 	return 0;
66dcf9af82SInki Dae }
67dcf9af82SInki Dae 
68c01d73faSInki Dae static void update_vm_cache_attr(struct exynos_drm_gem_obj *obj,
69c01d73faSInki Dae 					struct vm_area_struct *vma)
70c01d73faSInki Dae {
71c01d73faSInki Dae 	DRM_DEBUG_KMS("flags = 0x%x\n", obj->flags);
72c01d73faSInki Dae 
73c01d73faSInki Dae 	/* non-cachable as default. */
74c01d73faSInki Dae 	if (obj->flags & EXYNOS_BO_CACHABLE)
75c01d73faSInki Dae 		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
76c01d73faSInki Dae 	else if (obj->flags & EXYNOS_BO_WC)
77c01d73faSInki Dae 		vma->vm_page_prot =
78c01d73faSInki Dae 			pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
79c01d73faSInki Dae 	else
80c01d73faSInki Dae 		vma->vm_page_prot =
81c01d73faSInki Dae 			pgprot_noncached(vm_get_page_prot(vma->vm_flags));
82c01d73faSInki Dae }
83c01d73faSInki Dae 
84dcf9af82SInki Dae static unsigned long roundup_gem_size(unsigned long size, unsigned int flags)
85dcf9af82SInki Dae {
860519f9a1SInki Dae 	/* TODO */
870519f9a1SInki Dae 
88dcf9af82SInki Dae 	return roundup(size, PAGE_SIZE);
892b35892eSInki Dae }
902b35892eSInki Dae 
910519f9a1SInki Dae static int exynos_drm_gem_map_buf(struct drm_gem_object *obj,
922b35892eSInki Dae 					struct vm_area_struct *vma,
932b35892eSInki Dae 					unsigned long f_vaddr,
942b35892eSInki Dae 					pgoff_t page_offset)
952b35892eSInki Dae {
962b35892eSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
972b35892eSInki Dae 	struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
982b35892eSInki Dae 	unsigned long pfn;
992b35892eSInki Dae 
1002b35892eSInki Dae 	if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
1012b35892eSInki Dae 		if (!buf->pages)
1022b35892eSInki Dae 			return -EINTR;
1032b35892eSInki Dae 
1042b35892eSInki Dae 		pfn = page_to_pfn(buf->pages[page_offset++]);
105f6ead8deSInki Dae 	} else
1062b35892eSInki Dae 		pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset;
1072b35892eSInki Dae 
1082b35892eSInki Dae 	return vm_insert_mixed(vma, f_vaddr, pfn);
1092b35892eSInki Dae }
1102b35892eSInki Dae 
1112364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
1122364839aSJoonyoung Shim 					struct drm_file *file_priv,
1132364839aSJoonyoung Shim 					unsigned int *handle)
1141c248b7dSInki Dae {
1151c248b7dSInki Dae 	int ret;
1161c248b7dSInki Dae 
1171c248b7dSInki Dae 	/*
1181c248b7dSInki Dae 	 * allocate a id of idr table where the obj is registered
1191c248b7dSInki Dae 	 * and handle has the id what user can see.
1201c248b7dSInki Dae 	 */
1211c248b7dSInki Dae 	ret = drm_gem_handle_create(file_priv, obj, handle);
1221c248b7dSInki Dae 	if (ret)
1232364839aSJoonyoung Shim 		return ret;
1241c248b7dSInki Dae 
1251c248b7dSInki Dae 	DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
1261c248b7dSInki Dae 
1271c248b7dSInki Dae 	/* drop reference from allocate - handle holds it now. */
1281c248b7dSInki Dae 	drm_gem_object_unreference_unlocked(obj);
1291c248b7dSInki Dae 
1302364839aSJoonyoung Shim 	return 0;
1312364839aSJoonyoung Shim }
1321c248b7dSInki Dae 
1332364839aSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
1342364839aSJoonyoung Shim {
1352364839aSJoonyoung Shim 	struct drm_gem_object *obj;
136c01d73faSInki Dae 	struct exynos_drm_gem_buf *buf;
1372364839aSJoonyoung Shim 
1382364839aSJoonyoung Shim 	DRM_DEBUG_KMS("%s\n", __FILE__);
1392364839aSJoonyoung Shim 
1402364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
141c01d73faSInki Dae 	buf = exynos_gem_obj->buffer;
1422364839aSJoonyoung Shim 
1432364839aSJoonyoung Shim 	DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
1442364839aSJoonyoung Shim 
145c374e731SInki Dae 	/*
146c374e731SInki Dae 	 * do not release memory region from exporter.
147c374e731SInki Dae 	 *
148c374e731SInki Dae 	 * the region will be released by exporter
149c374e731SInki Dae 	 * once dmabuf's refcount becomes 0.
150c374e731SInki Dae 	 */
151c374e731SInki Dae 	if (obj->import_attach)
152c374e731SInki Dae 		goto out;
153c374e731SInki Dae 
154c01d73faSInki Dae 	exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, buf);
1552b35892eSInki Dae 
156c374e731SInki Dae out:
157c01d73faSInki Dae 	exynos_drm_fini_buf(obj->dev, buf);
1582b35892eSInki Dae 	exynos_gem_obj->buffer = NULL;
1592364839aSJoonyoung Shim 
1602364839aSJoonyoung Shim 	if (obj->map_list.map)
1612364839aSJoonyoung Shim 		drm_gem_free_mmap_offset(obj);
1622364839aSJoonyoung Shim 
1632364839aSJoonyoung Shim 	/* release file pointer to gem object. */
1641c248b7dSInki Dae 	drm_gem_object_release(obj);
1651c248b7dSInki Dae 
1661c248b7dSInki Dae 	kfree(exynos_gem_obj);
1672b35892eSInki Dae 	exynos_gem_obj = NULL;
1682364839aSJoonyoung Shim }
1692364839aSJoonyoung Shim 
170b2df26c1SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
1712364839aSJoonyoung Shim 						      unsigned long size)
1722364839aSJoonyoung Shim {
1732364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
1742364839aSJoonyoung Shim 	struct drm_gem_object *obj;
1752364839aSJoonyoung Shim 	int ret;
1762364839aSJoonyoung Shim 
1772364839aSJoonyoung Shim 	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
1782364839aSJoonyoung Shim 	if (!exynos_gem_obj) {
1792364839aSJoonyoung Shim 		DRM_ERROR("failed to allocate exynos gem object\n");
1802364839aSJoonyoung Shim 		return NULL;
1812364839aSJoonyoung Shim 	}
1822364839aSJoonyoung Shim 
1832b35892eSInki Dae 	exynos_gem_obj->size = size;
1842364839aSJoonyoung Shim 	obj = &exynos_gem_obj->base;
1852364839aSJoonyoung Shim 
1862364839aSJoonyoung Shim 	ret = drm_gem_object_init(dev, obj, size);
1872364839aSJoonyoung Shim 	if (ret < 0) {
1882364839aSJoonyoung Shim 		DRM_ERROR("failed to initialize gem object\n");
1892364839aSJoonyoung Shim 		kfree(exynos_gem_obj);
1902364839aSJoonyoung Shim 		return NULL;
1912364839aSJoonyoung Shim 	}
1922364839aSJoonyoung Shim 
1932364839aSJoonyoung Shim 	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
1942364839aSJoonyoung Shim 
1952364839aSJoonyoung Shim 	return exynos_gem_obj;
1961c248b7dSInki Dae }
1971c248b7dSInki Dae 
198f088d5a9SInki Dae struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
1992b35892eSInki Dae 						unsigned int flags,
200ee5e770eSJoonyoung Shim 						unsigned long size)
201f088d5a9SInki Dae {
2022364839aSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
2032b35892eSInki Dae 	struct exynos_drm_gem_buf *buf;
2042b35892eSInki Dae 	int ret;
205f088d5a9SInki Dae 
206dcf9af82SInki Dae 	if (!size) {
207dcf9af82SInki Dae 		DRM_ERROR("invalid size.\n");
208dcf9af82SInki Dae 		return ERR_PTR(-EINVAL);
209dcf9af82SInki Dae 	}
210f088d5a9SInki Dae 
211dcf9af82SInki Dae 	size = roundup_gem_size(size, flags);
212dcf9af82SInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
213dcf9af82SInki Dae 
214dcf9af82SInki Dae 	ret = check_gem_flags(flags);
215dcf9af82SInki Dae 	if (ret)
216dcf9af82SInki Dae 		return ERR_PTR(ret);
2172b35892eSInki Dae 
2182b35892eSInki Dae 	buf = exynos_drm_init_buf(dev, size);
2192b35892eSInki Dae 	if (!buf)
220ee5e770eSJoonyoung Shim 		return ERR_PTR(-ENOMEM);
221f088d5a9SInki Dae 
2222364839aSJoonyoung Shim 	exynos_gem_obj = exynos_drm_gem_init(dev, size);
2232364839aSJoonyoung Shim 	if (!exynos_gem_obj) {
2242b35892eSInki Dae 		ret = -ENOMEM;
225dcf9af82SInki Dae 		goto err_fini_buf;
226f088d5a9SInki Dae 	}
227f088d5a9SInki Dae 
2282b35892eSInki Dae 	exynos_gem_obj->buffer = buf;
2292b35892eSInki Dae 
2302b35892eSInki Dae 	/* set memory type and cache attribute from user side. */
2312b35892eSInki Dae 	exynos_gem_obj->flags = flags;
2322b35892eSInki Dae 
2332b35892eSInki Dae 	ret = exynos_drm_alloc_buf(dev, buf, flags);
2342b35892eSInki Dae 	if (ret < 0) {
2352b35892eSInki Dae 		drm_gem_object_release(&exynos_gem_obj->base);
236dcf9af82SInki Dae 		goto err_fini_buf;
2372b35892eSInki Dae 	}
238f088d5a9SInki Dae 
239f088d5a9SInki Dae 	return exynos_gem_obj;
240dcf9af82SInki Dae 
241dcf9af82SInki Dae err_fini_buf:
2422b35892eSInki Dae 	exynos_drm_fini_buf(dev, buf);
2432b35892eSInki Dae 	return ERR_PTR(ret);
244f088d5a9SInki Dae }
245f088d5a9SInki Dae 
2461c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
2471c248b7dSInki Dae 				struct drm_file *file_priv)
2481c248b7dSInki Dae {
2491c248b7dSInki Dae 	struct drm_exynos_gem_create *args = data;
250ee5e770eSJoonyoung Shim 	struct exynos_drm_gem_obj *exynos_gem_obj;
2512364839aSJoonyoung Shim 	int ret;
2521c248b7dSInki Dae 
253f088d5a9SInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
2541c248b7dSInki Dae 
2552b35892eSInki Dae 	exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
2561c248b7dSInki Dae 	if (IS_ERR(exynos_gem_obj))
2571c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
2581c248b7dSInki Dae 
2592364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
2602364839aSJoonyoung Shim 			&args->handle);
2612364839aSJoonyoung Shim 	if (ret) {
2622364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
2632364839aSJoonyoung Shim 		return ret;
2642364839aSJoonyoung Shim 	}
2652364839aSJoonyoung Shim 
2661c248b7dSInki Dae 	return 0;
2671c248b7dSInki Dae }
2681c248b7dSInki Dae 
269f0b1bda7SInki Dae void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
270f0b1bda7SInki Dae 					unsigned int gem_handle,
271f0b1bda7SInki Dae 					struct drm_file *file_priv)
272f0b1bda7SInki Dae {
273f0b1bda7SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
274f0b1bda7SInki Dae 	struct drm_gem_object *obj;
275f0b1bda7SInki Dae 
276f0b1bda7SInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
277f0b1bda7SInki Dae 	if (!obj) {
278f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
279f0b1bda7SInki Dae 		return ERR_PTR(-EINVAL);
280f0b1bda7SInki Dae 	}
281f0b1bda7SInki Dae 
282f0b1bda7SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
283f0b1bda7SInki Dae 
284f0b1bda7SInki Dae 	if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
285f0b1bda7SInki Dae 		DRM_DEBUG_KMS("not support NONCONTIG type.\n");
286f0b1bda7SInki Dae 		drm_gem_object_unreference_unlocked(obj);
287f0b1bda7SInki Dae 
288f0b1bda7SInki Dae 		/* TODO */
289f0b1bda7SInki Dae 		return ERR_PTR(-EINVAL);
290f0b1bda7SInki Dae 	}
291f0b1bda7SInki Dae 
292f0b1bda7SInki Dae 	return &exynos_gem_obj->buffer->dma_addr;
293f0b1bda7SInki Dae }
294f0b1bda7SInki Dae 
295f0b1bda7SInki Dae void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
296f0b1bda7SInki Dae 					unsigned int gem_handle,
297f0b1bda7SInki Dae 					struct drm_file *file_priv)
298f0b1bda7SInki Dae {
299f0b1bda7SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
300f0b1bda7SInki Dae 	struct drm_gem_object *obj;
301f0b1bda7SInki Dae 
302f0b1bda7SInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
303f0b1bda7SInki Dae 	if (!obj) {
304f0b1bda7SInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
305f0b1bda7SInki Dae 		return;
306f0b1bda7SInki Dae 	}
307f0b1bda7SInki Dae 
308f0b1bda7SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
309f0b1bda7SInki Dae 
310f0b1bda7SInki Dae 	if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
311f0b1bda7SInki Dae 		DRM_DEBUG_KMS("not support NONCONTIG type.\n");
312f0b1bda7SInki Dae 		drm_gem_object_unreference_unlocked(obj);
313f0b1bda7SInki Dae 
314f0b1bda7SInki Dae 		/* TODO */
315f0b1bda7SInki Dae 		return;
316f0b1bda7SInki Dae 	}
317f0b1bda7SInki Dae 
318f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
319f0b1bda7SInki Dae 
320f0b1bda7SInki Dae 	/*
321f0b1bda7SInki Dae 	 * decrease obj->refcount one more time because we has already
322f0b1bda7SInki Dae 	 * increased it at exynos_drm_gem_get_dma_addr().
323f0b1bda7SInki Dae 	 */
324f0b1bda7SInki Dae 	drm_gem_object_unreference_unlocked(obj);
325f0b1bda7SInki Dae }
326f0b1bda7SInki Dae 
3271c248b7dSInki Dae int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
3281c248b7dSInki Dae 				    struct drm_file *file_priv)
3291c248b7dSInki Dae {
3301c248b7dSInki Dae 	struct drm_exynos_gem_map_off *args = data;
3311c248b7dSInki Dae 
3321c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
3331c248b7dSInki Dae 
3341c248b7dSInki Dae 	DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
3351c248b7dSInki Dae 			args->handle, (unsigned long)args->offset);
3361c248b7dSInki Dae 
3371c248b7dSInki Dae 	if (!(dev->driver->driver_features & DRIVER_GEM)) {
3381c248b7dSInki Dae 		DRM_ERROR("does not support GEM.\n");
3391c248b7dSInki Dae 		return -ENODEV;
3401c248b7dSInki Dae 	}
3411c248b7dSInki Dae 
3421c248b7dSInki Dae 	return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
3431c248b7dSInki Dae 			&args->offset);
3441c248b7dSInki Dae }
3451c248b7dSInki Dae 
3461c248b7dSInki Dae static int exynos_drm_gem_mmap_buffer(struct file *filp,
3471c248b7dSInki Dae 				      struct vm_area_struct *vma)
3481c248b7dSInki Dae {
3491c248b7dSInki Dae 	struct drm_gem_object *obj = filp->private_data;
3501c248b7dSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
3512c871127SInki Dae 	struct exynos_drm_gem_buf *buffer;
3520519f9a1SInki Dae 	unsigned long vm_size;
3531c248b7dSInki Dae 
3541c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
3551c248b7dSInki Dae 
356314e51b9SKonstantin Khlebnikov 	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
3571c248b7dSInki Dae 
358c01d73faSInki Dae 	update_vm_cache_attr(exynos_gem_obj, vma);
3591c248b7dSInki Dae 
3600519f9a1SInki Dae 	vm_size = vma->vm_end - vma->vm_start;
3612b35892eSInki Dae 
3621c248b7dSInki Dae 	/*
3632c871127SInki Dae 	 * a buffer contains information to physically continuous memory
3641c248b7dSInki Dae 	 * allocated by user request or at framebuffer creation.
3651c248b7dSInki Dae 	 */
3662c871127SInki Dae 	buffer = exynos_gem_obj->buffer;
3671c248b7dSInki Dae 
3681c248b7dSInki Dae 	/* check if user-requested size is valid. */
3692c871127SInki Dae 	if (vm_size > buffer->size)
3701c248b7dSInki Dae 		return -EINVAL;
3711c248b7dSInki Dae 
3720519f9a1SInki Dae 	return dma_mmap_attrs(obj->dev->dev, vma, buffer->kvaddr,
3730519f9a1SInki Dae 				buffer->dma_addr, buffer->size,
3740519f9a1SInki Dae 				&buffer->dma_attrs);
3751c248b7dSInki Dae }
3761c248b7dSInki Dae 
3771c248b7dSInki Dae static const struct file_operations exynos_drm_gem_fops = {
3781c248b7dSInki Dae 	.mmap = exynos_drm_gem_mmap_buffer,
3791c248b7dSInki Dae };
3801c248b7dSInki Dae 
3811c248b7dSInki Dae int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
3821c248b7dSInki Dae 			      struct drm_file *file_priv)
3831c248b7dSInki Dae {
3841c248b7dSInki Dae 	struct drm_exynos_gem_mmap *args = data;
3851c248b7dSInki Dae 	struct drm_gem_object *obj;
3861c248b7dSInki Dae 	unsigned int addr;
3871c248b7dSInki Dae 
3881c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
3891c248b7dSInki Dae 
3901c248b7dSInki Dae 	if (!(dev->driver->driver_features & DRIVER_GEM)) {
3911c248b7dSInki Dae 		DRM_ERROR("does not support GEM.\n");
3921c248b7dSInki Dae 		return -ENODEV;
3931c248b7dSInki Dae 	}
3941c248b7dSInki Dae 
3951c248b7dSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
3961c248b7dSInki Dae 	if (!obj) {
3971c248b7dSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
3981c248b7dSInki Dae 		return -EINVAL;
3991c248b7dSInki Dae 	}
4001c248b7dSInki Dae 
4011c248b7dSInki Dae 	obj->filp->f_op = &exynos_drm_gem_fops;
4021c248b7dSInki Dae 	obj->filp->private_data = obj;
4031c248b7dSInki Dae 
4046be5ceb0SLinus Torvalds 	addr = vm_mmap(obj->filp, 0, args->size,
4051c248b7dSInki Dae 			PROT_READ | PROT_WRITE, MAP_SHARED, 0);
4061c248b7dSInki Dae 
4071c248b7dSInki Dae 	drm_gem_object_unreference_unlocked(obj);
4081c248b7dSInki Dae 
4091c248b7dSInki Dae 	if (IS_ERR((void *)addr))
4101c248b7dSInki Dae 		return PTR_ERR((void *)addr);
4111c248b7dSInki Dae 
4121c248b7dSInki Dae 	args->mapped = addr;
4131c248b7dSInki Dae 
4141c248b7dSInki Dae 	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
4151c248b7dSInki Dae 
4161c248b7dSInki Dae 	return 0;
4171c248b7dSInki Dae }
4181c248b7dSInki Dae 
41940cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,
42040cd7e0cSInki Dae 				      struct drm_file *file_priv)
42140cd7e0cSInki Dae {	struct exynos_drm_gem_obj *exynos_gem_obj;
42240cd7e0cSInki Dae 	struct drm_exynos_gem_info *args = data;
42340cd7e0cSInki Dae 	struct drm_gem_object *obj;
42440cd7e0cSInki Dae 
42540cd7e0cSInki Dae 	mutex_lock(&dev->struct_mutex);
42640cd7e0cSInki Dae 
42740cd7e0cSInki Dae 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
42840cd7e0cSInki Dae 	if (!obj) {
42940cd7e0cSInki Dae 		DRM_ERROR("failed to lookup gem object.\n");
43040cd7e0cSInki Dae 		mutex_unlock(&dev->struct_mutex);
43140cd7e0cSInki Dae 		return -EINVAL;
43240cd7e0cSInki Dae 	}
43340cd7e0cSInki Dae 
43440cd7e0cSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
43540cd7e0cSInki Dae 
43640cd7e0cSInki Dae 	args->flags = exynos_gem_obj->flags;
43740cd7e0cSInki Dae 	args->size = exynos_gem_obj->size;
43840cd7e0cSInki Dae 
43940cd7e0cSInki Dae 	drm_gem_object_unreference(obj);
44040cd7e0cSInki Dae 	mutex_unlock(&dev->struct_mutex);
44140cd7e0cSInki Dae 
44240cd7e0cSInki Dae 	return 0;
44340cd7e0cSInki Dae }
44440cd7e0cSInki Dae 
4451c248b7dSInki Dae int exynos_drm_gem_init_object(struct drm_gem_object *obj)
4461c248b7dSInki Dae {
4471c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
4481c248b7dSInki Dae 
4491c248b7dSInki Dae 	return 0;
4501c248b7dSInki Dae }
4511c248b7dSInki Dae 
452ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj)
4531c248b7dSInki Dae {
454b2df26c1SInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
455b2df26c1SInki Dae 	struct exynos_drm_gem_buf *buf;
456b2df26c1SInki Dae 
4571c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
4581c248b7dSInki Dae 
459b2df26c1SInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
460b2df26c1SInki Dae 	buf = exynos_gem_obj->buffer;
461b2df26c1SInki Dae 
462b2df26c1SInki Dae 	if (obj->import_attach)
463b2df26c1SInki Dae 		drm_prime_gem_destroy(obj, buf->sgt);
464b2df26c1SInki Dae 
4652364839aSJoonyoung Shim 	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
4661c248b7dSInki Dae }
4671c248b7dSInki Dae 
4681c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
469ee5e770eSJoonyoung Shim 			       struct drm_device *dev,
470ee5e770eSJoonyoung Shim 			       struct drm_mode_create_dumb *args)
4711c248b7dSInki Dae {
4721c248b7dSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
4732364839aSJoonyoung Shim 	int ret;
4741c248b7dSInki Dae 
4751c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
4761c248b7dSInki Dae 
4771c248b7dSInki Dae 	/*
4781c248b7dSInki Dae 	 * alocate memory to be used for framebuffer.
4791c248b7dSInki Dae 	 * - this callback would be called by user application
4801c248b7dSInki Dae 	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
4811c248b7dSInki Dae 	 */
4821c248b7dSInki Dae 
4833fd6b694SCooper Yuan 	args->pitch = args->width * ((args->bpp + 7) / 8);
4847da5907cSInki Dae 	args->size = args->pitch * args->height;
4851c248b7dSInki Dae 
4862b35892eSInki Dae 	exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
4871c248b7dSInki Dae 	if (IS_ERR(exynos_gem_obj))
4881c248b7dSInki Dae 		return PTR_ERR(exynos_gem_obj);
4891c248b7dSInki Dae 
4902364839aSJoonyoung Shim 	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
4912364839aSJoonyoung Shim 			&args->handle);
4922364839aSJoonyoung Shim 	if (ret) {
4932364839aSJoonyoung Shim 		exynos_drm_gem_destroy(exynos_gem_obj);
4942364839aSJoonyoung Shim 		return ret;
4952364839aSJoonyoung Shim 	}
4962364839aSJoonyoung Shim 
4971c248b7dSInki Dae 	return 0;
4981c248b7dSInki Dae }
4991c248b7dSInki Dae 
5001c248b7dSInki Dae int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
501ee5e770eSJoonyoung Shim 				   struct drm_device *dev, uint32_t handle,
502ee5e770eSJoonyoung Shim 				   uint64_t *offset)
5031c248b7dSInki Dae {
5041c248b7dSInki Dae 	struct drm_gem_object *obj;
5052d91cf17SJoonyoung Shim 	int ret = 0;
5061c248b7dSInki Dae 
5071c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
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 	if (!obj->map_list.map) {
5256037bafaSLaurent Pinchart 		ret = drm_gem_create_mmap_offset(obj);
5262d91cf17SJoonyoung Shim 		if (ret)
5272d91cf17SJoonyoung Shim 			goto out;
5282d91cf17SJoonyoung Shim 	}
5291c248b7dSInki Dae 
5306037bafaSLaurent Pinchart 	*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
5311c248b7dSInki Dae 	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
5321c248b7dSInki Dae 
5332d91cf17SJoonyoung Shim out:
5342d91cf17SJoonyoung Shim 	drm_gem_object_unreference(obj);
5352d91cf17SJoonyoung Shim unlock:
5361c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5372d91cf17SJoonyoung Shim 	return ret;
5381c248b7dSInki Dae }
5391c248b7dSInki Dae 
540ee5e770eSJoonyoung Shim int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
541ee5e770eSJoonyoung Shim 				struct drm_device *dev,
542ee5e770eSJoonyoung Shim 				unsigned int handle)
543ee5e770eSJoonyoung Shim {
544ee5e770eSJoonyoung Shim 	int ret;
545ee5e770eSJoonyoung Shim 
546ee5e770eSJoonyoung Shim 	DRM_DEBUG_KMS("%s\n", __FILE__);
547ee5e770eSJoonyoung Shim 
548ee5e770eSJoonyoung Shim 	/*
549ee5e770eSJoonyoung Shim 	 * obj->refcount and obj->handle_count are decreased and
550ee5e770eSJoonyoung Shim 	 * if both them are 0 then exynos_drm_gem_free_object()
551ee5e770eSJoonyoung Shim 	 * would be called by callback to release resources.
552ee5e770eSJoonyoung Shim 	 */
553ee5e770eSJoonyoung Shim 	ret = drm_gem_handle_delete(file_priv, handle);
554ee5e770eSJoonyoung Shim 	if (ret < 0) {
555ee5e770eSJoonyoung Shim 		DRM_ERROR("failed to delete drm_gem_handle.\n");
556ee5e770eSJoonyoung Shim 		return ret;
557ee5e770eSJoonyoung Shim 	}
558ee5e770eSJoonyoung Shim 
559ee5e770eSJoonyoung Shim 	return 0;
560ee5e770eSJoonyoung Shim }
561ee5e770eSJoonyoung Shim 
5621c248b7dSInki Dae int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
5631c248b7dSInki Dae {
5641c248b7dSInki Dae 	struct drm_gem_object *obj = vma->vm_private_data;
5651c248b7dSInki Dae 	struct drm_device *dev = obj->dev;
5662b35892eSInki Dae 	unsigned long f_vaddr;
5671c248b7dSInki Dae 	pgoff_t page_offset;
5681c248b7dSInki Dae 	int ret;
5691c248b7dSInki Dae 
5701c248b7dSInki Dae 	page_offset = ((unsigned long)vmf->virtual_address -
5711c248b7dSInki Dae 			vma->vm_start) >> PAGE_SHIFT;
5722b35892eSInki Dae 	f_vaddr = (unsigned long)vmf->virtual_address;
5731c248b7dSInki Dae 
5741c248b7dSInki Dae 	mutex_lock(&dev->struct_mutex);
5751c248b7dSInki Dae 
5760519f9a1SInki Dae 	ret = exynos_drm_gem_map_buf(obj, vma, f_vaddr, page_offset);
5772b35892eSInki Dae 	if (ret < 0)
5780519f9a1SInki Dae 		DRM_ERROR("failed to map a buffer with user.\n");
5791c248b7dSInki Dae 
5801c248b7dSInki Dae 	mutex_unlock(&dev->struct_mutex);
5811c248b7dSInki Dae 
5821c248b7dSInki Dae 	return convert_to_vm_err_msg(ret);
5831c248b7dSInki Dae }
5841c248b7dSInki Dae 
5851c248b7dSInki Dae int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
5861c248b7dSInki Dae {
587c01d73faSInki Dae 	struct exynos_drm_gem_obj *exynos_gem_obj;
588c01d73faSInki Dae 	struct drm_gem_object *obj;
5891c248b7dSInki Dae 	int ret;
5901c248b7dSInki Dae 
5911c248b7dSInki Dae 	DRM_DEBUG_KMS("%s\n", __FILE__);
5921c248b7dSInki Dae 
5931c248b7dSInki Dae 	/* set vm_area_struct. */
5941c248b7dSInki Dae 	ret = drm_gem_mmap(filp, vma);
5951c248b7dSInki Dae 	if (ret < 0) {
5961c248b7dSInki Dae 		DRM_ERROR("failed to mmap.\n");
5971c248b7dSInki Dae 		return ret;
5981c248b7dSInki Dae 	}
5991c248b7dSInki Dae 
600c01d73faSInki Dae 	obj = vma->vm_private_data;
601c01d73faSInki Dae 	exynos_gem_obj = to_exynos_gem_obj(obj);
602c01d73faSInki Dae 
603c01d73faSInki Dae 	ret = check_gem_flags(exynos_gem_obj->flags);
604c01d73faSInki Dae 	if (ret) {
605c01d73faSInki Dae 		drm_gem_vm_close(vma);
606c01d73faSInki Dae 		drm_gem_free_mmap_offset(obj);
607c01d73faSInki Dae 		return ret;
608c01d73faSInki Dae 	}
609c01d73faSInki Dae 
6101c248b7dSInki Dae 	vma->vm_flags &= ~VM_PFNMAP;
6111c248b7dSInki Dae 	vma->vm_flags |= VM_MIXEDMAP;
6121c248b7dSInki Dae 
613c01d73faSInki Dae 	update_vm_cache_attr(exynos_gem_obj, vma);
614c01d73faSInki Dae 
6151c248b7dSInki Dae 	return ret;
6161c248b7dSInki Dae }
617