Commit | Line | Data |
---|---|---|
1c248b7d ID |
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 | ||
2b35892e | 29 | #include <linux/shmem_fs.h> |
1c248b7d ID |
30 | #include <drm/exynos_drm.h> |
31 | ||
32 | #include "exynos_drm_drv.h" | |
33 | #include "exynos_drm_gem.h" | |
34 | #include "exynos_drm_buf.h" | |
35 | ||
36 | static unsigned int convert_to_vm_err_msg(int msg) | |
37 | { | |
38 | unsigned int out_msg; | |
39 | ||
40 | switch (msg) { | |
41 | case 0: | |
42 | case -ERESTARTSYS: | |
43 | case -EINTR: | |
44 | out_msg = VM_FAULT_NOPAGE; | |
45 | break; | |
46 | ||
47 | case -ENOMEM: | |
48 | out_msg = VM_FAULT_OOM; | |
49 | break; | |
50 | ||
51 | default: | |
52 | out_msg = VM_FAULT_SIGBUS; | |
53 | break; | |
54 | } | |
55 | ||
56 | return out_msg; | |
57 | } | |
58 | ||
2b35892e ID |
59 | static unsigned int mask_gem_flags(unsigned int flags) |
60 | { | |
61 | return flags &= EXYNOS_BO_NONCONTIG; | |
62 | } | |
63 | ||
64 | static struct page **exynos_gem_get_pages(struct drm_gem_object *obj, | |
65 | gfp_t gfpmask) | |
66 | { | |
67 | struct inode *inode; | |
68 | struct address_space *mapping; | |
69 | struct page *p, **pages; | |
70 | int i, npages; | |
71 | ||
72 | /* This is the shared memory object that backs the GEM resource */ | |
73 | inode = obj->filp->f_path.dentry->d_inode; | |
74 | mapping = inode->i_mapping; | |
75 | ||
76 | npages = obj->size >> PAGE_SHIFT; | |
77 | ||
78 | pages = drm_malloc_ab(npages, sizeof(struct page *)); | |
79 | if (pages == NULL) | |
80 | return ERR_PTR(-ENOMEM); | |
81 | ||
82 | gfpmask |= mapping_gfp_mask(mapping); | |
83 | ||
84 | for (i = 0; i < npages; i++) { | |
85 | p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); | |
86 | if (IS_ERR(p)) | |
87 | goto fail; | |
88 | pages[i] = p; | |
89 | } | |
90 | ||
91 | return pages; | |
92 | ||
93 | fail: | |
94 | while (i--) | |
95 | page_cache_release(pages[i]); | |
96 | ||
97 | drm_free_large(pages); | |
98 | return ERR_PTR(PTR_ERR(p)); | |
99 | } | |
100 | ||
101 | static void exynos_gem_put_pages(struct drm_gem_object *obj, | |
102 | struct page **pages, | |
103 | bool dirty, bool accessed) | |
104 | { | |
105 | int i, npages; | |
106 | ||
107 | npages = obj->size >> PAGE_SHIFT; | |
108 | ||
109 | for (i = 0; i < npages; i++) { | |
110 | if (dirty) | |
111 | set_page_dirty(pages[i]); | |
112 | ||
113 | if (accessed) | |
114 | mark_page_accessed(pages[i]); | |
115 | ||
116 | /* Undo the reference we took when populating the table */ | |
117 | page_cache_release(pages[i]); | |
118 | } | |
119 | ||
120 | drm_free_large(pages); | |
121 | } | |
122 | ||
123 | static int exynos_drm_gem_map_pages(struct drm_gem_object *obj, | |
124 | struct vm_area_struct *vma, | |
125 | unsigned long f_vaddr, | |
126 | pgoff_t page_offset) | |
127 | { | |
128 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
129 | struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; | |
130 | unsigned long pfn; | |
131 | ||
132 | if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { | |
133 | unsigned long usize = buf->size; | |
134 | ||
135 | if (!buf->pages) | |
136 | return -EINTR; | |
137 | ||
138 | while (usize > 0) { | |
139 | pfn = page_to_pfn(buf->pages[page_offset++]); | |
140 | vm_insert_mixed(vma, f_vaddr, pfn); | |
141 | f_vaddr += PAGE_SIZE; | |
142 | usize -= PAGE_SIZE; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset; | |
149 | ||
150 | return vm_insert_mixed(vma, f_vaddr, pfn); | |
151 | } | |
152 | ||
153 | static int exynos_drm_gem_get_pages(struct drm_gem_object *obj) | |
154 | { | |
155 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
156 | struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; | |
157 | struct scatterlist *sgl; | |
158 | struct page **pages; | |
159 | unsigned int npages, i = 0; | |
160 | int ret; | |
161 | ||
162 | if (buf->pages) { | |
163 | DRM_DEBUG_KMS("already allocated.\n"); | |
164 | return -EINVAL; | |
165 | } | |
166 | ||
167 | pages = exynos_gem_get_pages(obj, GFP_KERNEL); | |
168 | if (IS_ERR(pages)) { | |
169 | DRM_ERROR("failed to get pages.\n"); | |
170 | return PTR_ERR(pages); | |
171 | } | |
172 | ||
173 | npages = obj->size >> PAGE_SHIFT; | |
174 | ||
175 | buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL); | |
176 | if (!buf->sgt) { | |
177 | DRM_ERROR("failed to allocate sg table.\n"); | |
178 | ret = -ENOMEM; | |
179 | goto err; | |
180 | } | |
181 | ||
182 | ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL); | |
183 | if (ret < 0) { | |
184 | DRM_ERROR("failed to initialize sg table.\n"); | |
185 | ret = -EFAULT; | |
186 | goto err1; | |
187 | } | |
188 | ||
189 | sgl = buf->sgt->sgl; | |
190 | ||
191 | /* set all pages to sg list. */ | |
192 | while (i < npages) { | |
193 | sg_set_page(sgl, pages[i], PAGE_SIZE, 0); | |
194 | sg_dma_address(sgl) = page_to_phys(pages[i]); | |
195 | i++; | |
196 | sgl = sg_next(sgl); | |
197 | } | |
198 | ||
199 | /* add some codes for UNCACHED type here. TODO */ | |
200 | ||
201 | buf->pages = pages; | |
202 | return ret; | |
203 | err1: | |
204 | kfree(buf->sgt); | |
205 | buf->sgt = NULL; | |
206 | err: | |
207 | exynos_gem_put_pages(obj, pages, true, false); | |
208 | return ret; | |
209 | ||
210 | } | |
211 | ||
212 | static void exynos_drm_gem_put_pages(struct drm_gem_object *obj) | |
213 | { | |
214 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
215 | struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer; | |
216 | ||
217 | /* | |
218 | * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages | |
219 | * allocated at gem fault handler. | |
220 | */ | |
221 | sg_free_table(buf->sgt); | |
222 | kfree(buf->sgt); | |
223 | buf->sgt = NULL; | |
224 | ||
225 | exynos_gem_put_pages(obj, buf->pages, true, false); | |
226 | buf->pages = NULL; | |
227 | ||
228 | /* add some codes for UNCACHED type here. TODO */ | |
229 | } | |
230 | ||
2364839a JS |
231 | static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, |
232 | struct drm_file *file_priv, | |
233 | unsigned int *handle) | |
1c248b7d | 234 | { |
1c248b7d ID |
235 | int ret; |
236 | ||
1c248b7d ID |
237 | /* |
238 | * allocate a id of idr table where the obj is registered | |
239 | * and handle has the id what user can see. | |
240 | */ | |
241 | ret = drm_gem_handle_create(file_priv, obj, handle); | |
242 | if (ret) | |
2364839a | 243 | return ret; |
1c248b7d ID |
244 | |
245 | DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); | |
246 | ||
247 | /* drop reference from allocate - handle holds it now. */ | |
248 | drm_gem_object_unreference_unlocked(obj); | |
249 | ||
2364839a JS |
250 | return 0; |
251 | } | |
252 | ||
253 | void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) | |
254 | { | |
255 | struct drm_gem_object *obj; | |
256 | ||
257 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
258 | ||
259 | if (!exynos_gem_obj) | |
260 | return; | |
261 | ||
262 | obj = &exynos_gem_obj->base; | |
263 | ||
264 | DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); | |
1c248b7d | 265 | |
2b35892e ID |
266 | if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) && |
267 | exynos_gem_obj->buffer->pages) | |
268 | exynos_drm_gem_put_pages(obj); | |
269 | else | |
270 | exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags, | |
271 | exynos_gem_obj->buffer); | |
272 | ||
273 | exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer); | |
274 | exynos_gem_obj->buffer = NULL; | |
2364839a JS |
275 | |
276 | if (obj->map_list.map) | |
277 | drm_gem_free_mmap_offset(obj); | |
278 | ||
279 | /* release file pointer to gem object. */ | |
1c248b7d ID |
280 | drm_gem_object_release(obj); |
281 | ||
1c248b7d | 282 | kfree(exynos_gem_obj); |
2b35892e | 283 | exynos_gem_obj = NULL; |
2364839a JS |
284 | } |
285 | ||
286 | static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, | |
287 | unsigned long size) | |
288 | { | |
289 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
290 | struct drm_gem_object *obj; | |
291 | int ret; | |
292 | ||
293 | exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); | |
294 | if (!exynos_gem_obj) { | |
295 | DRM_ERROR("failed to allocate exynos gem object\n"); | |
296 | return NULL; | |
297 | } | |
298 | ||
2b35892e | 299 | exynos_gem_obj->size = size; |
2364839a JS |
300 | obj = &exynos_gem_obj->base; |
301 | ||
302 | ret = drm_gem_object_init(dev, obj, size); | |
303 | if (ret < 0) { | |
304 | DRM_ERROR("failed to initialize gem object\n"); | |
305 | kfree(exynos_gem_obj); | |
306 | return NULL; | |
307 | } | |
308 | ||
309 | DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); | |
310 | ||
311 | return exynos_gem_obj; | |
1c248b7d ID |
312 | } |
313 | ||
f088d5a9 | 314 | struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, |
2b35892e ID |
315 | unsigned int flags, |
316 | unsigned long size) | |
f088d5a9 | 317 | { |
2364839a | 318 | struct exynos_drm_gem_obj *exynos_gem_obj; |
2b35892e ID |
319 | struct exynos_drm_gem_buf *buf; |
320 | int ret; | |
f088d5a9 ID |
321 | |
322 | size = roundup(size, PAGE_SIZE); | |
f088d5a9 ID |
323 | DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); |
324 | ||
2b35892e ID |
325 | flags = mask_gem_flags(flags); |
326 | ||
327 | buf = exynos_drm_init_buf(dev, size); | |
328 | if (!buf) | |
ee5e770e | 329 | return ERR_PTR(-ENOMEM); |
f088d5a9 | 330 | |
2364839a JS |
331 | exynos_gem_obj = exynos_drm_gem_init(dev, size); |
332 | if (!exynos_gem_obj) { | |
2b35892e ID |
333 | ret = -ENOMEM; |
334 | goto err; | |
f088d5a9 ID |
335 | } |
336 | ||
2b35892e ID |
337 | exynos_gem_obj->buffer = buf; |
338 | ||
339 | /* set memory type and cache attribute from user side. */ | |
340 | exynos_gem_obj->flags = flags; | |
341 | ||
342 | /* | |
343 | * allocate all pages as desired size if user wants to allocate | |
344 | * physically non-continuous memory. | |
345 | */ | |
346 | if (flags & EXYNOS_BO_NONCONTIG) { | |
347 | ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base); | |
348 | if (ret < 0) { | |
349 | drm_gem_object_release(&exynos_gem_obj->base); | |
350 | goto err; | |
351 | } | |
352 | } else { | |
353 | ret = exynos_drm_alloc_buf(dev, buf, flags); | |
354 | if (ret < 0) { | |
355 | drm_gem_object_release(&exynos_gem_obj->base); | |
356 | goto err; | |
357 | } | |
358 | } | |
f088d5a9 ID |
359 | |
360 | return exynos_gem_obj; | |
2b35892e ID |
361 | err: |
362 | exynos_drm_fini_buf(dev, buf); | |
363 | return ERR_PTR(ret); | |
f088d5a9 ID |
364 | } |
365 | ||
1c248b7d | 366 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, |
ee5e770e | 367 | struct drm_file *file_priv) |
1c248b7d ID |
368 | { |
369 | struct drm_exynos_gem_create *args = data; | |
ee5e770e | 370 | struct exynos_drm_gem_obj *exynos_gem_obj; |
2364839a | 371 | int ret; |
1c248b7d | 372 | |
f088d5a9 | 373 | DRM_DEBUG_KMS("%s\n", __FILE__); |
1c248b7d | 374 | |
2b35892e | 375 | exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); |
1c248b7d ID |
376 | if (IS_ERR(exynos_gem_obj)) |
377 | return PTR_ERR(exynos_gem_obj); | |
378 | ||
2364839a JS |
379 | ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, |
380 | &args->handle); | |
381 | if (ret) { | |
382 | exynos_drm_gem_destroy(exynos_gem_obj); | |
383 | return ret; | |
384 | } | |
385 | ||
1c248b7d ID |
386 | return 0; |
387 | } | |
388 | ||
f0b1bda7 ID |
389 | void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, |
390 | unsigned int gem_handle, | |
391 | struct drm_file *file_priv) | |
392 | { | |
393 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
394 | struct drm_gem_object *obj; | |
395 | ||
396 | obj = drm_gem_object_lookup(dev, file_priv, gem_handle); | |
397 | if (!obj) { | |
398 | DRM_ERROR("failed to lookup gem object.\n"); | |
399 | return ERR_PTR(-EINVAL); | |
400 | } | |
401 | ||
402 | exynos_gem_obj = to_exynos_gem_obj(obj); | |
403 | ||
404 | if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { | |
405 | DRM_DEBUG_KMS("not support NONCONTIG type.\n"); | |
406 | drm_gem_object_unreference_unlocked(obj); | |
407 | ||
408 | /* TODO */ | |
409 | return ERR_PTR(-EINVAL); | |
410 | } | |
411 | ||
412 | return &exynos_gem_obj->buffer->dma_addr; | |
413 | } | |
414 | ||
415 | void exynos_drm_gem_put_dma_addr(struct drm_device *dev, | |
416 | unsigned int gem_handle, | |
417 | struct drm_file *file_priv) | |
418 | { | |
419 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
420 | struct drm_gem_object *obj; | |
421 | ||
422 | obj = drm_gem_object_lookup(dev, file_priv, gem_handle); | |
423 | if (!obj) { | |
424 | DRM_ERROR("failed to lookup gem object.\n"); | |
425 | return; | |
426 | } | |
427 | ||
428 | exynos_gem_obj = to_exynos_gem_obj(obj); | |
429 | ||
430 | if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { | |
431 | DRM_DEBUG_KMS("not support NONCONTIG type.\n"); | |
432 | drm_gem_object_unreference_unlocked(obj); | |
433 | ||
434 | /* TODO */ | |
435 | return; | |
436 | } | |
437 | ||
438 | drm_gem_object_unreference_unlocked(obj); | |
439 | ||
440 | /* | |
441 | * decrease obj->refcount one more time because we has already | |
442 | * increased it at exynos_drm_gem_get_dma_addr(). | |
443 | */ | |
444 | drm_gem_object_unreference_unlocked(obj); | |
445 | } | |
446 | ||
1c248b7d | 447 | int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, |
ee5e770e | 448 | struct drm_file *file_priv) |
1c248b7d ID |
449 | { |
450 | struct drm_exynos_gem_map_off *args = data; | |
451 | ||
452 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
453 | ||
454 | DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", | |
455 | args->handle, (unsigned long)args->offset); | |
456 | ||
457 | if (!(dev->driver->driver_features & DRIVER_GEM)) { | |
458 | DRM_ERROR("does not support GEM.\n"); | |
459 | return -ENODEV; | |
460 | } | |
461 | ||
462 | return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, | |
463 | &args->offset); | |
464 | } | |
465 | ||
466 | static int exynos_drm_gem_mmap_buffer(struct file *filp, | |
ee5e770e | 467 | struct vm_area_struct *vma) |
1c248b7d ID |
468 | { |
469 | struct drm_gem_object *obj = filp->private_data; | |
470 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
2c871127 | 471 | struct exynos_drm_gem_buf *buffer; |
2b35892e ID |
472 | unsigned long pfn, vm_size, usize, uaddr = vma->vm_start; |
473 | int ret; | |
1c248b7d ID |
474 | |
475 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
476 | ||
477 | vma->vm_flags |= (VM_IO | VM_RESERVED); | |
478 | ||
2364839a | 479 | /* in case of direct mapping, always having non-cachable attribute */ |
1c248b7d | 480 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
1c248b7d | 481 | |
2b35892e ID |
482 | vm_size = usize = vma->vm_end - vma->vm_start; |
483 | ||
1c248b7d | 484 | /* |
2c871127 | 485 | * a buffer contains information to physically continuous memory |
1c248b7d ID |
486 | * allocated by user request or at framebuffer creation. |
487 | */ | |
2c871127 | 488 | buffer = exynos_gem_obj->buffer; |
1c248b7d ID |
489 | |
490 | /* check if user-requested size is valid. */ | |
2c871127 | 491 | if (vm_size > buffer->size) |
1c248b7d ID |
492 | return -EINVAL; |
493 | ||
2b35892e ID |
494 | if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { |
495 | int i = 0; | |
496 | ||
497 | if (!buffer->pages) | |
498 | return -EINVAL; | |
499 | ||
500 | do { | |
501 | ret = vm_insert_page(vma, uaddr, buffer->pages[i++]); | |
502 | if (ret) { | |
503 | DRM_ERROR("failed to remap user space.\n"); | |
504 | return ret; | |
505 | } | |
506 | ||
507 | uaddr += PAGE_SIZE; | |
508 | usize -= PAGE_SIZE; | |
509 | } while (usize > 0); | |
510 | } else { | |
511 | /* | |
512 | * get page frame number to physical memory to be mapped | |
513 | * to user space. | |
514 | */ | |
515 | pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> | |
516 | PAGE_SHIFT; | |
517 | ||
518 | DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); | |
519 | ||
520 | if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, | |
521 | vma->vm_page_prot)) { | |
522 | DRM_ERROR("failed to remap pfn range.\n"); | |
523 | return -EAGAIN; | |
524 | } | |
1c248b7d ID |
525 | } |
526 | ||
527 | return 0; | |
528 | } | |
529 | ||
530 | static const struct file_operations exynos_drm_gem_fops = { | |
531 | .mmap = exynos_drm_gem_mmap_buffer, | |
532 | }; | |
533 | ||
534 | int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, | |
ee5e770e | 535 | struct drm_file *file_priv) |
1c248b7d ID |
536 | { |
537 | struct drm_exynos_gem_mmap *args = data; | |
538 | struct drm_gem_object *obj; | |
539 | unsigned int addr; | |
540 | ||
541 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
542 | ||
543 | if (!(dev->driver->driver_features & DRIVER_GEM)) { | |
544 | DRM_ERROR("does not support GEM.\n"); | |
545 | return -ENODEV; | |
546 | } | |
547 | ||
548 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); | |
549 | if (!obj) { | |
550 | DRM_ERROR("failed to lookup gem object.\n"); | |
551 | return -EINVAL; | |
552 | } | |
553 | ||
554 | obj->filp->f_op = &exynos_drm_gem_fops; | |
555 | obj->filp->private_data = obj; | |
556 | ||
557 | down_write(¤t->mm->mmap_sem); | |
558 | addr = do_mmap(obj->filp, 0, args->size, | |
559 | PROT_READ | PROT_WRITE, MAP_SHARED, 0); | |
560 | up_write(¤t->mm->mmap_sem); | |
561 | ||
562 | drm_gem_object_unreference_unlocked(obj); | |
563 | ||
564 | if (IS_ERR((void *)addr)) | |
565 | return PTR_ERR((void *)addr); | |
566 | ||
567 | args->mapped = addr; | |
568 | ||
569 | DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); | |
570 | ||
571 | return 0; | |
572 | } | |
573 | ||
574 | int exynos_drm_gem_init_object(struct drm_gem_object *obj) | |
575 | { | |
576 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
577 | ||
578 | return 0; | |
579 | } | |
580 | ||
ee5e770e | 581 | void exynos_drm_gem_free_object(struct drm_gem_object *obj) |
1c248b7d | 582 | { |
1c248b7d ID |
583 | DRM_DEBUG_KMS("%s\n", __FILE__); |
584 | ||
2364839a | 585 | exynos_drm_gem_destroy(to_exynos_gem_obj(obj)); |
1c248b7d ID |
586 | } |
587 | ||
588 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | |
ee5e770e JS |
589 | struct drm_device *dev, |
590 | struct drm_mode_create_dumb *args) | |
1c248b7d ID |
591 | { |
592 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
2364839a | 593 | int ret; |
1c248b7d ID |
594 | |
595 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
596 | ||
597 | /* | |
598 | * alocate memory to be used for framebuffer. | |
599 | * - this callback would be called by user application | |
600 | * with DRM_IOCTL_MODE_CREATE_DUMB command. | |
601 | */ | |
602 | ||
603 | args->pitch = args->width * args->bpp >> 3; | |
2b35892e | 604 | args->size = PAGE_ALIGN(args->pitch * args->height); |
1c248b7d | 605 | |
2b35892e | 606 | exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size); |
1c248b7d ID |
607 | if (IS_ERR(exynos_gem_obj)) |
608 | return PTR_ERR(exynos_gem_obj); | |
609 | ||
2364839a JS |
610 | ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, |
611 | &args->handle); | |
612 | if (ret) { | |
613 | exynos_drm_gem_destroy(exynos_gem_obj); | |
614 | return ret; | |
615 | } | |
616 | ||
1c248b7d ID |
617 | return 0; |
618 | } | |
619 | ||
620 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | |
ee5e770e JS |
621 | struct drm_device *dev, uint32_t handle, |
622 | uint64_t *offset) | |
1c248b7d ID |
623 | { |
624 | struct exynos_drm_gem_obj *exynos_gem_obj; | |
625 | struct drm_gem_object *obj; | |
2d91cf17 | 626 | int ret = 0; |
1c248b7d ID |
627 | |
628 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
629 | ||
630 | mutex_lock(&dev->struct_mutex); | |
631 | ||
632 | /* | |
633 | * get offset of memory allocated for drm framebuffer. | |
634 | * - this callback would be called by user application | |
635 | * with DRM_IOCTL_MODE_MAP_DUMB command. | |
636 | */ | |
637 | ||
638 | obj = drm_gem_object_lookup(dev, file_priv, handle); | |
639 | if (!obj) { | |
640 | DRM_ERROR("failed to lookup gem object.\n"); | |
2d91cf17 JS |
641 | ret = -EINVAL; |
642 | goto unlock; | |
1c248b7d ID |
643 | } |
644 | ||
645 | exynos_gem_obj = to_exynos_gem_obj(obj); | |
646 | ||
2d91cf17 JS |
647 | if (!exynos_gem_obj->base.map_list.map) { |
648 | ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base); | |
649 | if (ret) | |
650 | goto out; | |
651 | } | |
1c248b7d | 652 | |
2d91cf17 | 653 | *offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT; |
1c248b7d ID |
654 | DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); |
655 | ||
2d91cf17 JS |
656 | out: |
657 | drm_gem_object_unreference(obj); | |
658 | unlock: | |
1c248b7d | 659 | mutex_unlock(&dev->struct_mutex); |
2d91cf17 | 660 | return ret; |
1c248b7d ID |
661 | } |
662 | ||
ee5e770e JS |
663 | int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, |
664 | struct drm_device *dev, | |
665 | unsigned int handle) | |
666 | { | |
667 | int ret; | |
668 | ||
669 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
670 | ||
671 | /* | |
672 | * obj->refcount and obj->handle_count are decreased and | |
673 | * if both them are 0 then exynos_drm_gem_free_object() | |
674 | * would be called by callback to release resources. | |
675 | */ | |
676 | ret = drm_gem_handle_delete(file_priv, handle); | |
677 | if (ret < 0) { | |
678 | DRM_ERROR("failed to delete drm_gem_handle.\n"); | |
679 | return ret; | |
680 | } | |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
1c248b7d ID |
685 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) |
686 | { | |
687 | struct drm_gem_object *obj = vma->vm_private_data; | |
688 | struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); | |
689 | struct drm_device *dev = obj->dev; | |
2b35892e | 690 | unsigned long f_vaddr; |
1c248b7d ID |
691 | pgoff_t page_offset; |
692 | int ret; | |
693 | ||
694 | page_offset = ((unsigned long)vmf->virtual_address - | |
695 | vma->vm_start) >> PAGE_SHIFT; | |
2b35892e | 696 | f_vaddr = (unsigned long)vmf->virtual_address; |
1c248b7d ID |
697 | |
698 | mutex_lock(&dev->struct_mutex); | |
699 | ||
2b35892e ID |
700 | /* |
701 | * allocate all pages as desired size if user wants to allocate | |
702 | * physically non-continuous memory. | |
703 | */ | |
704 | if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) { | |
705 | ret = exynos_drm_gem_get_pages(obj); | |
706 | if (ret < 0) | |
707 | goto err; | |
708 | } | |
1c248b7d | 709 | |
2b35892e ID |
710 | ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset); |
711 | if (ret < 0) | |
712 | DRM_ERROR("failed to map pages.\n"); | |
1c248b7d | 713 | |
2b35892e | 714 | err: |
1c248b7d ID |
715 | mutex_unlock(&dev->struct_mutex); |
716 | ||
717 | return convert_to_vm_err_msg(ret); | |
718 | } | |
719 | ||
720 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
721 | { | |
722 | int ret; | |
723 | ||
724 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
725 | ||
726 | /* set vm_area_struct. */ | |
727 | ret = drm_gem_mmap(filp, vma); | |
728 | if (ret < 0) { | |
729 | DRM_ERROR("failed to mmap.\n"); | |
730 | return ret; | |
731 | } | |
732 | ||
733 | vma->vm_flags &= ~VM_PFNMAP; | |
734 | vma->vm_flags |= VM_MIXEDMAP; | |
735 | ||
736 | return ret; | |
737 | } |