]>
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 | * | |
d81aecb5 ID |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. | |
1c248b7d ID |
10 | */ |
11 | ||
760285e7 | 12 | #include <drm/drmP.h> |
0de23977 | 13 | #include <drm/drm_vma_manager.h> |
1c248b7d | 14 | |
2b35892e | 15 | #include <linux/shmem_fs.h> |
01ed50dd | 16 | #include <linux/dma-buf.h> |
1c248b7d ID |
17 | #include <drm/exynos_drm.h> |
18 | ||
19 | #include "exynos_drm_drv.h" | |
20 | #include "exynos_drm_gem.h" | |
3fec4532 | 21 | #include "exynos_drm_iommu.h" |
1c248b7d | 22 | |
813fd67b | 23 | static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) |
2a8cb489 | 24 | { |
813fd67b | 25 | struct drm_device *dev = exynos_gem->base.dev; |
2a8cb489 JS |
26 | enum dma_attr attr; |
27 | unsigned int nr_pages; | |
28 | ||
813fd67b | 29 | if (exynos_gem->dma_addr) { |
2a8cb489 JS |
30 | DRM_DEBUG_KMS("already allocated.\n"); |
31 | return 0; | |
32 | } | |
33 | ||
813fd67b | 34 | init_dma_attrs(&exynos_gem->dma_attrs); |
2a8cb489 JS |
35 | |
36 | /* | |
37 | * if EXYNOS_BO_CONTIG, fully physically contiguous memory | |
38 | * region will be allocated else physically contiguous | |
39 | * as possible. | |
40 | */ | |
813fd67b JS |
41 | if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG)) |
42 | dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &exynos_gem->dma_attrs); | |
2a8cb489 JS |
43 | |
44 | /* | |
45 | * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping | |
46 | * else cachable mapping. | |
47 | */ | |
813fd67b JS |
48 | if (exynos_gem->flags & EXYNOS_BO_WC || |
49 | !(exynos_gem->flags & EXYNOS_BO_CACHABLE)) | |
2a8cb489 JS |
50 | attr = DMA_ATTR_WRITE_COMBINE; |
51 | else | |
52 | attr = DMA_ATTR_NON_CONSISTENT; | |
53 | ||
813fd67b JS |
54 | dma_set_attr(attr, &exynos_gem->dma_attrs); |
55 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &exynos_gem->dma_attrs); | |
2a8cb489 | 56 | |
813fd67b | 57 | nr_pages = exynos_gem->size >> PAGE_SHIFT; |
2a8cb489 JS |
58 | |
59 | if (!is_drm_iommu_supported(dev)) { | |
813fd67b JS |
60 | exynos_gem->pages = drm_calloc_large(nr_pages, |
61 | sizeof(struct page *)); | |
62 | if (!exynos_gem->pages) { | |
2a8cb489 JS |
63 | DRM_ERROR("failed to allocate pages.\n"); |
64 | return -ENOMEM; | |
65 | } | |
333e8e58 | 66 | } |
2a8cb489 | 67 | |
813fd67b JS |
68 | exynos_gem->cookie = dma_alloc_attrs(dev->dev, exynos_gem->size, |
69 | &exynos_gem->dma_addr, GFP_KERNEL, | |
70 | &exynos_gem->dma_attrs); | |
71 | if (!exynos_gem->cookie) { | |
333e8e58 | 72 | DRM_ERROR("failed to allocate buffer.\n"); |
813fd67b JS |
73 | if (exynos_gem->pages) |
74 | drm_free_large(exynos_gem->pages); | |
333e8e58 JS |
75 | return -ENOMEM; |
76 | } | |
77 | ||
813fd67b | 78 | if (exynos_gem->pages) { |
333e8e58 JS |
79 | dma_addr_t start_addr; |
80 | unsigned int i = 0; | |
2a8cb489 | 81 | |
813fd67b | 82 | start_addr = exynos_gem->dma_addr; |
2a8cb489 | 83 | while (i < nr_pages) { |
813fd67b JS |
84 | exynos_gem->pages[i] = |
85 | pfn_to_page(dma_to_pfn(dev->dev, start_addr)); | |
2a8cb489 JS |
86 | start_addr += PAGE_SIZE; |
87 | i++; | |
88 | } | |
89 | } else { | |
813fd67b | 90 | exynos_gem->pages = exynos_gem->cookie; |
2a8cb489 JS |
91 | } |
92 | ||
93 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", | |
813fd67b | 94 | (unsigned long)exynos_gem->dma_addr, exynos_gem->size); |
2a8cb489 JS |
95 | |
96 | return 0; | |
97 | } | |
98 | ||
813fd67b | 99 | static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem) |
2a8cb489 | 100 | { |
813fd67b | 101 | struct drm_device *dev = exynos_gem->base.dev; |
2a8cb489 | 102 | |
813fd67b | 103 | if (!exynos_gem->dma_addr) { |
2a8cb489 JS |
104 | DRM_DEBUG_KMS("dma_addr is invalid.\n"); |
105 | return; | |
106 | } | |
107 | ||
108 | DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", | |
813fd67b | 109 | (unsigned long)exynos_gem->dma_addr, exynos_gem->size); |
2a8cb489 | 110 | |
813fd67b JS |
111 | dma_free_attrs(dev->dev, exynos_gem->size, exynos_gem->cookie, |
112 | (dma_addr_t)exynos_gem->dma_addr, | |
113 | &exynos_gem->dma_attrs); | |
333e8e58 JS |
114 | |
115 | if (!is_drm_iommu_supported(dev)) | |
813fd67b | 116 | drm_free_large(exynos_gem->pages); |
2a8cb489 JS |
117 | } |
118 | ||
2364839a JS |
119 | static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, |
120 | struct drm_file *file_priv, | |
121 | unsigned int *handle) | |
1c248b7d | 122 | { |
1c248b7d ID |
123 | int ret; |
124 | ||
1c248b7d ID |
125 | /* |
126 | * allocate a id of idr table where the obj is registered | |
127 | * and handle has the id what user can see. | |
128 | */ | |
129 | ret = drm_gem_handle_create(file_priv, obj, handle); | |
130 | if (ret) | |
2364839a | 131 | return ret; |
1c248b7d ID |
132 | |
133 | DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); | |
134 | ||
135 | /* drop reference from allocate - handle holds it now. */ | |
136 | drm_gem_object_unreference_unlocked(obj); | |
137 | ||
2364839a JS |
138 | return 0; |
139 | } | |
140 | ||
813fd67b | 141 | void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem) |
2364839a | 142 | { |
813fd67b | 143 | struct drm_gem_object *obj = &exynos_gem->base; |
2364839a | 144 | |
a8e11d1c | 145 | DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count); |
1c248b7d | 146 | |
c374e731 ID |
147 | /* |
148 | * do not release memory region from exporter. | |
149 | * | |
150 | * the region will be released by exporter | |
151 | * once dmabuf's refcount becomes 0. | |
152 | */ | |
153 | if (obj->import_attach) | |
813fd67b | 154 | drm_prime_gem_destroy(obj, exynos_gem->sgt); |
7c93537a | 155 | else |
813fd67b | 156 | exynos_drm_free_buf(exynos_gem); |
2b35892e | 157 | |
2364839a | 158 | /* release file pointer to gem object. */ |
1c248b7d ID |
159 | drm_gem_object_release(obj); |
160 | ||
813fd67b | 161 | kfree(exynos_gem); |
2364839a JS |
162 | } |
163 | ||
a4f19aaa ID |
164 | unsigned long exynos_drm_gem_get_size(struct drm_device *dev, |
165 | unsigned int gem_handle, | |
166 | struct drm_file *file_priv) | |
167 | { | |
813fd67b | 168 | struct exynos_drm_gem *exynos_gem; |
a4f19aaa ID |
169 | struct drm_gem_object *obj; |
170 | ||
171 | obj = drm_gem_object_lookup(dev, file_priv, gem_handle); | |
172 | if (!obj) { | |
173 | DRM_ERROR("failed to lookup gem object.\n"); | |
174 | return 0; | |
175 | } | |
176 | ||
813fd67b | 177 | exynos_gem = to_exynos_gem(obj); |
a4f19aaa ID |
178 | |
179 | drm_gem_object_unreference_unlocked(obj); | |
180 | ||
813fd67b | 181 | return exynos_gem->size; |
a4f19aaa ID |
182 | } |
183 | ||
813fd67b JS |
184 | static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev, |
185 | unsigned long size) | |
2364839a | 186 | { |
813fd67b | 187 | struct exynos_drm_gem *exynos_gem; |
2364839a JS |
188 | struct drm_gem_object *obj; |
189 | int ret; | |
190 | ||
813fd67b JS |
191 | exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL); |
192 | if (!exynos_gem) | |
5f3f4266 | 193 | return ERR_PTR(-ENOMEM); |
2364839a | 194 | |
813fd67b JS |
195 | exynos_gem->size = size; |
196 | obj = &exynos_gem->base; | |
2364839a JS |
197 | |
198 | ret = drm_gem_object_init(dev, obj, size); | |
199 | if (ret < 0) { | |
200 | DRM_ERROR("failed to initialize gem object\n"); | |
813fd67b | 201 | kfree(exynos_gem); |
5f3f4266 | 202 | return ERR_PTR(ret); |
2364839a JS |
203 | } |
204 | ||
48cf53f4 JS |
205 | ret = drm_gem_create_mmap_offset(obj); |
206 | if (ret < 0) { | |
207 | drm_gem_object_release(obj); | |
813fd67b | 208 | kfree(exynos_gem); |
48cf53f4 JS |
209 | return ERR_PTR(ret); |
210 | } | |
211 | ||
2364839a JS |
212 | DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); |
213 | ||
813fd67b | 214 | return exynos_gem; |
1c248b7d ID |
215 | } |
216 | ||
813fd67b JS |
217 | struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev, |
218 | unsigned int flags, | |
219 | unsigned long size) | |
f088d5a9 | 220 | { |
813fd67b | 221 | struct exynos_drm_gem *exynos_gem; |
2b35892e | 222 | int ret; |
f088d5a9 | 223 | |
c4130bcd JS |
224 | if (flags & ~(EXYNOS_BO_MASK)) { |
225 | DRM_ERROR("invalid flags.\n"); | |
226 | return ERR_PTR(-EINVAL); | |
227 | } | |
228 | ||
dcf9af82 ID |
229 | if (!size) { |
230 | DRM_ERROR("invalid size.\n"); | |
231 | return ERR_PTR(-EINVAL); | |
232 | } | |
233 | ||
eb57da88 | 234 | size = roundup(size, PAGE_SIZE); |
f088d5a9 | 235 | |
813fd67b JS |
236 | exynos_gem = exynos_drm_gem_init(dev, size); |
237 | if (IS_ERR(exynos_gem)) | |
238 | return exynos_gem; | |
2b35892e ID |
239 | |
240 | /* set memory type and cache attribute from user side. */ | |
813fd67b | 241 | exynos_gem->flags = flags; |
2b35892e | 242 | |
813fd67b | 243 | ret = exynos_drm_alloc_buf(exynos_gem); |
2a8cb489 | 244 | if (ret < 0) { |
813fd67b JS |
245 | drm_gem_object_release(&exynos_gem->base); |
246 | kfree(exynos_gem); | |
2a8cb489 JS |
247 | return ERR_PTR(ret); |
248 | } | |
f088d5a9 | 249 | |
813fd67b | 250 | return exynos_gem; |
f088d5a9 ID |
251 | } |
252 | ||
1c248b7d | 253 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, |
ee5e770e | 254 | struct drm_file *file_priv) |
1c248b7d ID |
255 | { |
256 | struct drm_exynos_gem_create *args = data; | |
813fd67b | 257 | struct exynos_drm_gem *exynos_gem; |
2364839a | 258 | int ret; |
1c248b7d | 259 | |
813fd67b JS |
260 | exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size); |
261 | if (IS_ERR(exynos_gem)) | |
262 | return PTR_ERR(exynos_gem); | |
1c248b7d | 263 | |
813fd67b JS |
264 | ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, |
265 | &args->handle); | |
2364839a | 266 | if (ret) { |
813fd67b | 267 | exynos_drm_gem_destroy(exynos_gem); |
2364839a JS |
268 | return ret; |
269 | } | |
270 | ||
1c248b7d ID |
271 | return 0; |
272 | } | |
273 | ||
d87342c1 | 274 | dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev, |
f0b1bda7 | 275 | unsigned int gem_handle, |
d87342c1 | 276 | struct drm_file *filp) |
f0b1bda7 | 277 | { |
813fd67b | 278 | struct exynos_drm_gem *exynos_gem; |
f0b1bda7 ID |
279 | struct drm_gem_object *obj; |
280 | ||
d87342c1 | 281 | obj = drm_gem_object_lookup(dev, filp, gem_handle); |
f0b1bda7 ID |
282 | if (!obj) { |
283 | DRM_ERROR("failed to lookup gem object.\n"); | |
284 | return ERR_PTR(-EINVAL); | |
285 | } | |
286 | ||
813fd67b | 287 | exynos_gem = to_exynos_gem(obj); |
f0b1bda7 | 288 | |
813fd67b | 289 | return &exynos_gem->dma_addr; |
f0b1bda7 ID |
290 | } |
291 | ||
292 | void exynos_drm_gem_put_dma_addr(struct drm_device *dev, | |
293 | unsigned int gem_handle, | |
d87342c1 | 294 | struct drm_file *filp) |
f0b1bda7 | 295 | { |
f0b1bda7 ID |
296 | struct drm_gem_object *obj; |
297 | ||
d87342c1 | 298 | obj = drm_gem_object_lookup(dev, filp, gem_handle); |
f0b1bda7 ID |
299 | if (!obj) { |
300 | DRM_ERROR("failed to lookup gem object.\n"); | |
301 | return; | |
302 | } | |
303 | ||
f0b1bda7 ID |
304 | drm_gem_object_unreference_unlocked(obj); |
305 | ||
306 | /* | |
307 | * decrease obj->refcount one more time because we has already | |
308 | * increased it at exynos_drm_gem_get_dma_addr(). | |
309 | */ | |
310 | drm_gem_object_unreference_unlocked(obj); | |
311 | } | |
312 | ||
813fd67b | 313 | static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem, |
ee5e770e | 314 | struct vm_area_struct *vma) |
1c248b7d | 315 | { |
813fd67b | 316 | struct drm_device *drm_dev = exynos_gem->base.dev; |
0519f9a1 | 317 | unsigned long vm_size; |
5b07c660 | 318 | int ret; |
1c248b7d | 319 | |
832316c7 ID |
320 | vma->vm_flags &= ~VM_PFNMAP; |
321 | vma->vm_pgoff = 0; | |
1c248b7d | 322 | |
0519f9a1 | 323 | vm_size = vma->vm_end - vma->vm_start; |
2b35892e | 324 | |
1c248b7d | 325 | /* check if user-requested size is valid. */ |
813fd67b | 326 | if (vm_size > exynos_gem->size) |
1c248b7d ID |
327 | return -EINVAL; |
328 | ||
813fd67b JS |
329 | ret = dma_mmap_attrs(drm_dev->dev, vma, exynos_gem->pages, |
330 | exynos_gem->dma_addr, exynos_gem->size, | |
331 | &exynos_gem->dma_attrs); | |
5b07c660 ID |
332 | if (ret < 0) { |
333 | DRM_ERROR("failed to mmap.\n"); | |
334 | return ret; | |
335 | } | |
336 | ||
1c248b7d ID |
337 | return 0; |
338 | } | |
339 | ||
40cd7e0c ID |
340 | int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, |
341 | struct drm_file *file_priv) | |
b4cfd4dd | 342 | { |
813fd67b | 343 | struct exynos_drm_gem *exynos_gem; |
40cd7e0c ID |
344 | struct drm_exynos_gem_info *args = data; |
345 | struct drm_gem_object *obj; | |
346 | ||
347 | mutex_lock(&dev->struct_mutex); | |
348 | ||
349 | obj = drm_gem_object_lookup(dev, file_priv, args->handle); | |
350 | if (!obj) { | |
351 | DRM_ERROR("failed to lookup gem object.\n"); | |
352 | mutex_unlock(&dev->struct_mutex); | |
353 | return -EINVAL; | |
354 | } | |
355 | ||
813fd67b | 356 | exynos_gem = to_exynos_gem(obj); |
40cd7e0c | 357 | |
813fd67b JS |
358 | args->flags = exynos_gem->flags; |
359 | args->size = exynos_gem->size; | |
40cd7e0c ID |
360 | |
361 | drm_gem_object_unreference(obj); | |
362 | mutex_unlock(&dev->struct_mutex); | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
2a3098ff ID |
367 | int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, |
368 | struct sg_table *sgt, | |
369 | enum dma_data_direction dir) | |
370 | { | |
371 | int nents; | |
372 | ||
373 | mutex_lock(&drm_dev->struct_mutex); | |
374 | ||
375 | nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); | |
376 | if (!nents) { | |
377 | DRM_ERROR("failed to map sgl with dma.\n"); | |
378 | mutex_unlock(&drm_dev->struct_mutex); | |
379 | return nents; | |
380 | } | |
381 | ||
382 | mutex_unlock(&drm_dev->struct_mutex); | |
383 | return 0; | |
384 | } | |
385 | ||
386 | void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev, | |
387 | struct sg_table *sgt, | |
388 | enum dma_data_direction dir) | |
389 | { | |
390 | dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); | |
391 | } | |
392 | ||
ee5e770e | 393 | void exynos_drm_gem_free_object(struct drm_gem_object *obj) |
1c248b7d | 394 | { |
813fd67b | 395 | exynos_drm_gem_destroy(to_exynos_gem(obj)); |
1c248b7d ID |
396 | } |
397 | ||
398 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | |
ee5e770e JS |
399 | struct drm_device *dev, |
400 | struct drm_mode_create_dumb *args) | |
1c248b7d | 401 | { |
813fd67b | 402 | struct exynos_drm_gem *exynos_gem; |
333e8e58 | 403 | unsigned int flags; |
2364839a | 404 | int ret; |
1c248b7d | 405 | |
1c248b7d | 406 | /* |
c6b78bc8 | 407 | * allocate memory to be used for framebuffer. |
1c248b7d ID |
408 | * - this callback would be called by user application |
409 | * with DRM_IOCTL_MODE_CREATE_DUMB command. | |
410 | */ | |
411 | ||
3fd6b694 | 412 | args->pitch = args->width * ((args->bpp + 7) / 8); |
7da5907c | 413 | args->size = args->pitch * args->height; |
1c248b7d | 414 | |
333e8e58 JS |
415 | if (is_drm_iommu_supported(dev)) |
416 | flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; | |
417 | else | |
418 | flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC; | |
3fec4532 | 419 | |
813fd67b JS |
420 | exynos_gem = exynos_drm_gem_create(dev, flags, args->size); |
421 | if (IS_ERR(exynos_gem)) { | |
122beea8 | 422 | dev_warn(dev->dev, "FB allocation failed.\n"); |
813fd67b | 423 | return PTR_ERR(exynos_gem); |
122beea8 | 424 | } |
1c248b7d | 425 | |
813fd67b JS |
426 | ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, |
427 | &args->handle); | |
2364839a | 428 | if (ret) { |
813fd67b | 429 | exynos_drm_gem_destroy(exynos_gem); |
2364839a JS |
430 | return ret; |
431 | } | |
432 | ||
1c248b7d ID |
433 | return 0; |
434 | } | |
435 | ||
436 | int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, | |
ee5e770e JS |
437 | struct drm_device *dev, uint32_t handle, |
438 | uint64_t *offset) | |
1c248b7d | 439 | { |
1c248b7d | 440 | struct drm_gem_object *obj; |
2d91cf17 | 441 | int ret = 0; |
1c248b7d | 442 | |
1c248b7d ID |
443 | mutex_lock(&dev->struct_mutex); |
444 | ||
445 | /* | |
446 | * get offset of memory allocated for drm framebuffer. | |
447 | * - this callback would be called by user application | |
448 | * with DRM_IOCTL_MODE_MAP_DUMB command. | |
449 | */ | |
450 | ||
451 | obj = drm_gem_object_lookup(dev, file_priv, handle); | |
452 | if (!obj) { | |
453 | DRM_ERROR("failed to lookup gem object.\n"); | |
2d91cf17 JS |
454 | ret = -EINVAL; |
455 | goto unlock; | |
1c248b7d ID |
456 | } |
457 | ||
0de23977 | 458 | *offset = drm_vma_node_offset_addr(&obj->vma_node); |
1c248b7d ID |
459 | DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); |
460 | ||
2d91cf17 JS |
461 | drm_gem_object_unreference(obj); |
462 | unlock: | |
1c248b7d | 463 | mutex_unlock(&dev->struct_mutex); |
2d91cf17 | 464 | return ret; |
1c248b7d ID |
465 | } |
466 | ||
467 | int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | |
468 | { | |
469 | struct drm_gem_object *obj = vma->vm_private_data; | |
813fd67b | 470 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
0e9a2ee3 | 471 | unsigned long pfn; |
1c248b7d ID |
472 | pgoff_t page_offset; |
473 | int ret; | |
474 | ||
475 | page_offset = ((unsigned long)vmf->virtual_address - | |
476 | vma->vm_start) >> PAGE_SHIFT; | |
477 | ||
813fd67b | 478 | if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) { |
0e9a2ee3 JS |
479 | DRM_ERROR("invalid page offset\n"); |
480 | ret = -EINVAL; | |
481 | goto out; | |
482 | } | |
1c248b7d | 483 | |
813fd67b | 484 | pfn = page_to_pfn(exynos_gem->pages[page_offset]); |
0e9a2ee3 JS |
485 | ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); |
486 | ||
487 | out: | |
23597e26 JS |
488 | switch (ret) { |
489 | case 0: | |
490 | case -ERESTARTSYS: | |
491 | case -EINTR: | |
492 | return VM_FAULT_NOPAGE; | |
493 | case -ENOMEM: | |
494 | return VM_FAULT_OOM; | |
495 | default: | |
496 | return VM_FAULT_SIGBUS; | |
497 | } | |
1c248b7d ID |
498 | } |
499 | ||
500 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
501 | { | |
813fd67b | 502 | struct exynos_drm_gem *exynos_gem; |
c01d73fa | 503 | struct drm_gem_object *obj; |
1c248b7d ID |
504 | int ret; |
505 | ||
1c248b7d ID |
506 | /* set vm_area_struct. */ |
507 | ret = drm_gem_mmap(filp, vma); | |
508 | if (ret < 0) { | |
509 | DRM_ERROR("failed to mmap.\n"); | |
510 | return ret; | |
511 | } | |
512 | ||
c01d73fa | 513 | obj = vma->vm_private_data; |
813fd67b | 514 | exynos_gem = to_exynos_gem(obj); |
c01d73fa | 515 | |
813fd67b | 516 | DRM_DEBUG_KMS("flags = 0x%x\n", exynos_gem->flags); |
211b8878 JS |
517 | |
518 | /* non-cachable as default. */ | |
813fd67b | 519 | if (exynos_gem->flags & EXYNOS_BO_CACHABLE) |
211b8878 | 520 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); |
813fd67b | 521 | else if (exynos_gem->flags & EXYNOS_BO_WC) |
211b8878 JS |
522 | vma->vm_page_prot = |
523 | pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); | |
524 | else | |
525 | vma->vm_page_prot = | |
526 | pgprot_noncached(vm_get_page_prot(vma->vm_flags)); | |
c01d73fa | 527 | |
813fd67b | 528 | ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma); |
832316c7 ID |
529 | if (ret) |
530 | goto err_close_vm; | |
531 | ||
532 | return ret; | |
533 | ||
534 | err_close_vm: | |
535 | drm_gem_vm_close(vma); | |
832316c7 | 536 | |
1c248b7d ID |
537 | return ret; |
538 | } | |
01ed50dd JS |
539 | |
540 | /* low-level interface prime helpers */ | |
541 | struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) | |
542 | { | |
813fd67b | 543 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
01ed50dd JS |
544 | int npages; |
545 | ||
813fd67b | 546 | npages = exynos_gem->size >> PAGE_SHIFT; |
01ed50dd | 547 | |
813fd67b | 548 | return drm_prime_pages_to_sg(exynos_gem->pages, npages); |
01ed50dd JS |
549 | } |
550 | ||
551 | struct drm_gem_object * | |
552 | exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, | |
553 | struct dma_buf_attachment *attach, | |
554 | struct sg_table *sgt) | |
555 | { | |
813fd67b | 556 | struct exynos_drm_gem *exynos_gem; |
01ed50dd JS |
557 | int npages; |
558 | int ret; | |
559 | ||
813fd67b JS |
560 | exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size); |
561 | if (IS_ERR(exynos_gem)) { | |
562 | ret = PTR_ERR(exynos_gem); | |
50002d4c | 563 | return ERR_PTR(ret); |
2a8cb489 | 564 | } |
01ed50dd | 565 | |
813fd67b | 566 | exynos_gem->dma_addr = sg_dma_address(sgt->sgl); |
01ed50dd | 567 | |
813fd67b JS |
568 | npages = exynos_gem->size >> PAGE_SHIFT; |
569 | exynos_gem->pages = drm_malloc_ab(npages, sizeof(struct page *)); | |
570 | if (!exynos_gem->pages) { | |
01ed50dd JS |
571 | ret = -ENOMEM; |
572 | goto err; | |
573 | } | |
574 | ||
813fd67b JS |
575 | ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem->pages, NULL, |
576 | npages); | |
01ed50dd JS |
577 | if (ret < 0) |
578 | goto err_free_large; | |
579 | ||
813fd67b | 580 | exynos_gem->sgt = sgt; |
7c93537a | 581 | |
01ed50dd JS |
582 | if (sgt->nents == 1) { |
583 | /* always physically continuous memory if sgt->nents is 1. */ | |
813fd67b | 584 | exynos_gem->flags |= EXYNOS_BO_CONTIG; |
01ed50dd JS |
585 | } else { |
586 | /* | |
587 | * this case could be CONTIG or NONCONTIG type but for now | |
588 | * sets NONCONTIG. | |
589 | * TODO. we have to find a way that exporter can notify | |
590 | * the type of its own buffer to importer. | |
591 | */ | |
813fd67b | 592 | exynos_gem->flags |= EXYNOS_BO_NONCONTIG; |
01ed50dd JS |
593 | } |
594 | ||
813fd67b | 595 | return &exynos_gem->base; |
01ed50dd JS |
596 | |
597 | err_free_large: | |
813fd67b | 598 | drm_free_large(exynos_gem->pages); |
01ed50dd | 599 | err: |
813fd67b JS |
600 | drm_gem_object_release(&exynos_gem->base); |
601 | kfree(exynos_gem); | |
01ed50dd JS |
602 | return ERR_PTR(ret); |
603 | } | |
604 | ||
605 | void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj) | |
606 | { | |
607 | return NULL; | |
608 | } | |
609 | ||
610 | void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) | |
611 | { | |
612 | /* Nothing to do */ | |
613 | } |