1/* exynos_drm_gem.c
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Author: Inki Dae <inki.dae@samsung.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include "drmP.h"
27#include "drm.h"
28
29#include <drm/exynos_drm.h>
30
31#include "exynos_drm_drv.h"
32#include "exynos_drm_gem.h"
33#include "exynos_drm_buf.h"
34
35static unsigned int convert_to_vm_err_msg(int msg)
36{
37	unsigned int out_msg;
38
39	switch (msg) {
40	case 0:
41	case -ERESTARTSYS:
42	case -EINTR:
43		out_msg = VM_FAULT_NOPAGE;
44		break;
45
46	case -ENOMEM:
47		out_msg = VM_FAULT_OOM;
48		break;
49
50	default:
51		out_msg = VM_FAULT_SIGBUS;
52		break;
53	}
54
55	return out_msg;
56}
57
58static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
59					struct drm_file *file_priv,
60					unsigned int *handle)
61{
62	int ret;
63
64	/*
65	 * allocate a id of idr table where the obj is registered
66	 * and handle has the id what user can see.
67	 */
68	ret = drm_gem_handle_create(file_priv, obj, handle);
69	if (ret)
70		return ret;
71
72	DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
73
74	/* drop reference from allocate - handle holds it now. */
75	drm_gem_object_unreference_unlocked(obj);
76
77	return 0;
78}
79
80void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
81{
82	struct drm_gem_object *obj;
83
84	DRM_DEBUG_KMS("%s\n", __FILE__);
85
86	if (!exynos_gem_obj)
87		return;
88
89	obj = &exynos_gem_obj->base;
90
91	DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
92
93	exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer);
94
95	if (obj->map_list.map)
96		drm_gem_free_mmap_offset(obj);
97
98	/* release file pointer to gem object. */
99	drm_gem_object_release(obj);
100
101	kfree(exynos_gem_obj);
102}
103
104static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
105						      unsigned long size)
106{
107	struct exynos_drm_gem_obj *exynos_gem_obj;
108	struct drm_gem_object *obj;
109	int ret;
110
111	exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
112	if (!exynos_gem_obj) {
113		DRM_ERROR("failed to allocate exynos gem object\n");
114		return NULL;
115	}
116
117	obj = &exynos_gem_obj->base;
118
119	ret = drm_gem_object_init(dev, obj, size);
120	if (ret < 0) {
121		DRM_ERROR("failed to initialize gem object\n");
122		kfree(exynos_gem_obj);
123		return NULL;
124	}
125
126	DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
127
128	return exynos_gem_obj;
129}
130
131struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
132						 unsigned long size)
133{
134	struct exynos_drm_gem_buf *buffer;
135	struct exynos_drm_gem_obj *exynos_gem_obj;
136
137	size = roundup(size, PAGE_SIZE);
138	DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
139
140	buffer = exynos_drm_buf_create(dev, size);
141	if (!buffer)
142		return ERR_PTR(-ENOMEM);
143
144	exynos_gem_obj = exynos_drm_gem_init(dev, size);
145	if (!exynos_gem_obj) {
146		exynos_drm_buf_destroy(dev, buffer);
147		return ERR_PTR(-ENOMEM);
148	}
149
150	exynos_gem_obj->buffer = buffer;
151
152	return exynos_gem_obj;
153}
154
155int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
156				struct drm_file *file_priv)
157{
158	struct drm_exynos_gem_create *args = data;
159	struct exynos_drm_gem_obj *exynos_gem_obj;
160	int ret;
161
162	DRM_DEBUG_KMS("%s\n", __FILE__);
163
164	exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
165	if (IS_ERR(exynos_gem_obj))
166		return PTR_ERR(exynos_gem_obj);
167
168	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
169			&args->handle);
170	if (ret) {
171		exynos_drm_gem_destroy(exynos_gem_obj);
172		return ret;
173	}
174
175	return 0;
176}
177
178int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
179				    struct drm_file *file_priv)
180{
181	struct drm_exynos_gem_map_off *args = data;
182
183	DRM_DEBUG_KMS("%s\n", __FILE__);
184
185	DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
186			args->handle, (unsigned long)args->offset);
187
188	if (!(dev->driver->driver_features & DRIVER_GEM)) {
189		DRM_ERROR("does not support GEM.\n");
190		return -ENODEV;
191	}
192
193	return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
194			&args->offset);
195}
196
197static int exynos_drm_gem_mmap_buffer(struct file *filp,
198				      struct vm_area_struct *vma)
199{
200	struct drm_gem_object *obj = filp->private_data;
201	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
202	struct exynos_drm_gem_buf *buffer;
203	unsigned long pfn, vm_size;
204
205	DRM_DEBUG_KMS("%s\n", __FILE__);
206
207	vma->vm_flags |= (VM_IO | VM_RESERVED);
208
209	/* in case of direct mapping, always having non-cachable attribute */
210	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
211	vma->vm_file = filp;
212
213	vm_size = vma->vm_end - vma->vm_start;
214	/*
215	 * a buffer contains information to physically continuous memory
216	 * allocated by user request or at framebuffer creation.
217	 */
218	buffer = exynos_gem_obj->buffer;
219
220	/* check if user-requested size is valid. */
221	if (vm_size > buffer->size)
222		return -EINVAL;
223
224	/*
225	 * get page frame number to physical memory to be mapped
226	 * to user space.
227	 */
228	pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT;
229
230	DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
231
232	if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
233				vma->vm_page_prot)) {
234		DRM_ERROR("failed to remap pfn range.\n");
235		return -EAGAIN;
236	}
237
238	return 0;
239}
240
241static const struct file_operations exynos_drm_gem_fops = {
242	.mmap = exynos_drm_gem_mmap_buffer,
243};
244
245int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
246			      struct drm_file *file_priv)
247{
248	struct drm_exynos_gem_mmap *args = data;
249	struct drm_gem_object *obj;
250	unsigned int addr;
251
252	DRM_DEBUG_KMS("%s\n", __FILE__);
253
254	if (!(dev->driver->driver_features & DRIVER_GEM)) {
255		DRM_ERROR("does not support GEM.\n");
256		return -ENODEV;
257	}
258
259	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
260	if (!obj) {
261		DRM_ERROR("failed to lookup gem object.\n");
262		return -EINVAL;
263	}
264
265	obj->filp->f_op = &exynos_drm_gem_fops;
266	obj->filp->private_data = obj;
267
268	down_write(&current->mm->mmap_sem);
269	addr = do_mmap(obj->filp, 0, args->size,
270			PROT_READ | PROT_WRITE, MAP_SHARED, 0);
271	up_write(&current->mm->mmap_sem);
272
273	drm_gem_object_unreference_unlocked(obj);
274
275	if (IS_ERR((void *)addr))
276		return PTR_ERR((void *)addr);
277
278	args->mapped = addr;
279
280	DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
281
282	return 0;
283}
284
285int exynos_drm_gem_init_object(struct drm_gem_object *obj)
286{
287	DRM_DEBUG_KMS("%s\n", __FILE__);
288
289	return 0;
290}
291
292void exynos_drm_gem_free_object(struct drm_gem_object *obj)
293{
294	DRM_DEBUG_KMS("%s\n", __FILE__);
295
296	exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
297}
298
299int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
300			       struct drm_device *dev,
301			       struct drm_mode_create_dumb *args)
302{
303	struct exynos_drm_gem_obj *exynos_gem_obj;
304	int ret;
305
306	DRM_DEBUG_KMS("%s\n", __FILE__);
307
308	/*
309	 * alocate memory to be used for framebuffer.
310	 * - this callback would be called by user application
311	 *	with DRM_IOCTL_MODE_CREATE_DUMB command.
312	 */
313
314	args->pitch = args->width * args->bpp >> 3;
315	args->size = args->pitch * args->height;
316
317	exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
318	if (IS_ERR(exynos_gem_obj))
319		return PTR_ERR(exynos_gem_obj);
320
321	ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
322			&args->handle);
323	if (ret) {
324		exynos_drm_gem_destroy(exynos_gem_obj);
325		return ret;
326	}
327
328	return 0;
329}
330
331int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
332				   struct drm_device *dev, uint32_t handle,
333				   uint64_t *offset)
334{
335	struct exynos_drm_gem_obj *exynos_gem_obj;
336	struct drm_gem_object *obj;
337	int ret = 0;
338
339	DRM_DEBUG_KMS("%s\n", __FILE__);
340
341	mutex_lock(&dev->struct_mutex);
342
343	/*
344	 * get offset of memory allocated for drm framebuffer.
345	 * - this callback would be called by user application
346	 *	with DRM_IOCTL_MODE_MAP_DUMB command.
347	 */
348
349	obj = drm_gem_object_lookup(dev, file_priv, handle);
350	if (!obj) {
351		DRM_ERROR("failed to lookup gem object.\n");
352		ret = -EINVAL;
353		goto unlock;
354	}
355
356	exynos_gem_obj = to_exynos_gem_obj(obj);
357
358	if (!exynos_gem_obj->base.map_list.map) {
359		ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base);
360		if (ret)
361			goto out;
362	}
363
364	*offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT;
365	DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
366
367out:
368	drm_gem_object_unreference(obj);
369unlock:
370	mutex_unlock(&dev->struct_mutex);
371	return ret;
372}
373
374int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
375				struct drm_device *dev,
376				unsigned int handle)
377{
378	int ret;
379
380	DRM_DEBUG_KMS("%s\n", __FILE__);
381
382	/*
383	 * obj->refcount and obj->handle_count are decreased and
384	 * if both them are 0 then exynos_drm_gem_free_object()
385	 * would be called by callback to release resources.
386	 */
387	ret = drm_gem_handle_delete(file_priv, handle);
388	if (ret < 0) {
389		DRM_ERROR("failed to delete drm_gem_handle.\n");
390		return ret;
391	}
392
393	return 0;
394}
395
396int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
397{
398	struct drm_gem_object *obj = vma->vm_private_data;
399	struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
400	struct drm_device *dev = obj->dev;
401	unsigned long pfn;
402	pgoff_t page_offset;
403	int ret;
404
405	page_offset = ((unsigned long)vmf->virtual_address -
406			vma->vm_start) >> PAGE_SHIFT;
407
408	mutex_lock(&dev->struct_mutex);
409
410	pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
411			PAGE_SHIFT) + page_offset;
412
413	ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
414
415	mutex_unlock(&dev->struct_mutex);
416
417	return convert_to_vm_err_msg(ret);
418}
419
420int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
421{
422	int ret;
423
424	DRM_DEBUG_KMS("%s\n", __FILE__);
425
426	/* set vm_area_struct. */
427	ret = drm_gem_mmap(filp, vma);
428	if (ret < 0) {
429		DRM_ERROR("failed to mmap.\n");
430		return ret;
431	}
432
433	vma->vm_flags &= ~VM_PFNMAP;
434	vma->vm_flags |= VM_MIXEDMAP;
435
436	return ret;
437}
438
439MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
440MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
441MODULE_LICENSE("GPL");
442