]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1c248b7d ID |
2 | /* exynos_drm_gem.c |
3 | * | |
4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
5 | * Author: Inki Dae <inki.dae@samsung.com> | |
1c248b7d ID |
6 | */ |
7 | ||
760285e7 | 8 | #include <drm/drmP.h> |
0de23977 | 9 | #include <drm/drm_vma_manager.h> |
1c248b7d | 10 | |
2b35892e | 11 | #include <linux/shmem_fs.h> |
01ed50dd | 12 | #include <linux/dma-buf.h> |
01c8f1c4 | 13 | #include <linux/pfn_t.h> |
1c248b7d ID |
14 | #include <drm/exynos_drm.h> |
15 | ||
16 | #include "exynos_drm_drv.h" | |
17 | #include "exynos_drm_gem.h" | |
1c248b7d | 18 | |
813fd67b | 19 | static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) |
2a8cb489 | 20 | { |
813fd67b | 21 | struct drm_device *dev = exynos_gem->base.dev; |
00085f1e | 22 | unsigned long attr; |
2a8cb489 | 23 | unsigned int nr_pages; |
df547bf7 MS |
24 | struct sg_table sgt; |
25 | int ret = -ENOMEM; | |
2a8cb489 | 26 | |
813fd67b | 27 | if (exynos_gem->dma_addr) { |
6be90056 | 28 | DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "already allocated.\n"); |
2a8cb489 JS |
29 | return 0; |
30 | } | |
31 | ||
00085f1e | 32 | exynos_gem->dma_attrs = 0; |
2a8cb489 JS |
33 | |
34 | /* | |
35 | * if EXYNOS_BO_CONTIG, fully physically contiguous memory | |
36 | * region will be allocated else physically contiguous | |
37 | * as possible. | |
38 | */ | |
813fd67b | 39 | if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG)) |
00085f1e | 40 | exynos_gem->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS; |
2a8cb489 JS |
41 | |
42 | /* | |
43 | * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping | |
44 | * else cachable mapping. | |
45 | */ | |
813fd67b JS |
46 | if (exynos_gem->flags & EXYNOS_BO_WC || |
47 | !(exynos_gem->flags & EXYNOS_BO_CACHABLE)) | |
2a8cb489 JS |
48 | attr = DMA_ATTR_WRITE_COMBINE; |
49 | else | |
50 | attr = DMA_ATTR_NON_CONSISTENT; | |
51 | ||
00085f1e KK |
52 | exynos_gem->dma_attrs |= attr; |
53 | exynos_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; | |
2a8cb489 | 54 | |
813fd67b | 55 | nr_pages = exynos_gem->size >> PAGE_SHIFT; |
2a8cb489 | 56 | |
2098105e MH |
57 | exynos_gem->pages = kvmalloc_array(nr_pages, sizeof(struct page *), |
58 | GFP_KERNEL | __GFP_ZERO); | |
df547bf7 | 59 | if (!exynos_gem->pages) { |
6f83d208 | 60 | DRM_DEV_ERROR(to_dma_dev(dev), "failed to allocate pages.\n"); |
df547bf7 | 61 | return -ENOMEM; |
333e8e58 | 62 | } |
2a8cb489 | 63 | |
f43c3596 | 64 | exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size, |
813fd67b | 65 | &exynos_gem->dma_addr, GFP_KERNEL, |
00085f1e | 66 | exynos_gem->dma_attrs); |
813fd67b | 67 | if (!exynos_gem->cookie) { |
6f83d208 | 68 | DRM_DEV_ERROR(to_dma_dev(dev), "failed to allocate buffer.\n"); |
df547bf7 MS |
69 | goto err_free; |
70 | } | |
71 | ||
f43c3596 | 72 | ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt, exynos_gem->cookie, |
df547bf7 | 73 | exynos_gem->dma_addr, exynos_gem->size, |
00085f1e | 74 | exynos_gem->dma_attrs); |
df547bf7 | 75 | if (ret < 0) { |
6f83d208 | 76 | DRM_DEV_ERROR(to_dma_dev(dev), "failed to get sgtable.\n"); |
df547bf7 | 77 | goto err_dma_free; |
333e8e58 JS |
78 | } |
79 | ||
df547bf7 MS |
80 | if (drm_prime_sg_to_page_addr_arrays(&sgt, exynos_gem->pages, NULL, |
81 | nr_pages)) { | |
6f83d208 | 82 | DRM_DEV_ERROR(to_dma_dev(dev), "invalid sgtable.\n"); |
df547bf7 MS |
83 | ret = -EINVAL; |
84 | goto err_sgt_free; | |
2a8cb489 JS |
85 | } |
86 | ||
df547bf7 MS |
87 | sg_free_table(&sgt); |
88 | ||
6be90056 | 89 | DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "dma_addr(0x%lx), size(0x%lx)\n", |
813fd67b | 90 | (unsigned long)exynos_gem->dma_addr, exynos_gem->size); |
2a8cb489 JS |
91 | |
92 | return 0; | |
df547bf7 MS |
93 | |
94 | err_sgt_free: | |
95 | sg_free_table(&sgt); | |
96 | err_dma_free: | |
f43c3596 | 97 | dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, |
00085f1e | 98 | exynos_gem->dma_addr, exynos_gem->dma_attrs); |
df547bf7 | 99 | err_free: |
2098105e | 100 | kvfree(exynos_gem->pages); |
df547bf7 MS |
101 | |
102 | return ret; | |
2a8cb489 JS |
103 | } |
104 | ||
813fd67b | 105 | static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem) |
2a8cb489 | 106 | { |
813fd67b | 107 | struct drm_device *dev = exynos_gem->base.dev; |
2a8cb489 | 108 | |
813fd67b | 109 | if (!exynos_gem->dma_addr) { |
6be90056 | 110 | DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n"); |
2a8cb489 JS |
111 | return; |
112 | } | |
113 | ||
6be90056 | 114 | DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr(0x%lx), size(0x%lx)\n", |
813fd67b | 115 | (unsigned long)exynos_gem->dma_addr, exynos_gem->size); |
2a8cb489 | 116 | |
f43c3596 | 117 | dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, |
813fd67b | 118 | (dma_addr_t)exynos_gem->dma_addr, |
00085f1e | 119 | exynos_gem->dma_attrs); |
333e8e58 | 120 | |
2098105e | 121 | kvfree(exynos_gem->pages); |
2a8cb489 JS |
122 | } |
123 | ||
2364839a JS |
124 | static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, |
125 | struct drm_file *file_priv, | |
126 | unsigned int *handle) | |
1c248b7d | 127 | { |
1c248b7d ID |
128 | int ret; |
129 | ||
1c248b7d ID |
130 | /* |
131 | * allocate a id of idr table where the obj is registered | |
132 | * and handle has the id what user can see. | |
133 | */ | |
134 | ret = drm_gem_handle_create(file_priv, obj, handle); | |
135 | if (ret) | |
2364839a | 136 | return ret; |
1c248b7d | 137 | |
6be90056 | 138 | DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "gem handle = 0x%x\n", *handle); |
1c248b7d ID |
139 | |
140 | /* drop reference from allocate - handle holds it now. */ | |
af7d9101 | 141 | drm_gem_object_put_unlocked(obj); |
1c248b7d | 142 | |
2364839a JS |
143 | return 0; |
144 | } | |
145 | ||
813fd67b | 146 | void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem) |
2364839a | 147 | { |
813fd67b | 148 | struct drm_gem_object *obj = &exynos_gem->base; |
2364839a | 149 | |
6be90056 ID |
150 | DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "handle count = %d\n", |
151 | obj->handle_count); | |
1c248b7d | 152 | |
c374e731 ID |
153 | /* |
154 | * do not release memory region from exporter. | |
155 | * | |
156 | * the region will be released by exporter | |
157 | * once dmabuf's refcount becomes 0. | |
158 | */ | |
159 | if (obj->import_attach) | |
813fd67b | 160 | drm_prime_gem_destroy(obj, exynos_gem->sgt); |
7c93537a | 161 | else |
813fd67b | 162 | exynos_drm_free_buf(exynos_gem); |
2b35892e | 163 | |
2364839a | 164 | /* release file pointer to gem object. */ |
1c248b7d ID |
165 | drm_gem_object_release(obj); |
166 | ||
813fd67b | 167 | kfree(exynos_gem); |
2364839a JS |
168 | } |
169 | ||
813fd67b JS |
170 | static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev, |
171 | unsigned long size) | |
2364839a | 172 | { |
813fd67b | 173 | struct exynos_drm_gem *exynos_gem; |
2364839a JS |
174 | struct drm_gem_object *obj; |
175 | int ret; | |
176 | ||
813fd67b JS |
177 | exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL); |
178 | if (!exynos_gem) | |
5f3f4266 | 179 | return ERR_PTR(-ENOMEM); |
2364839a | 180 | |
813fd67b JS |
181 | exynos_gem->size = size; |
182 | obj = &exynos_gem->base; | |
2364839a JS |
183 | |
184 | ret = drm_gem_object_init(dev, obj, size); | |
185 | if (ret < 0) { | |
6f83d208 | 186 | DRM_DEV_ERROR(dev->dev, "failed to initialize gem object\n"); |
813fd67b | 187 | kfree(exynos_gem); |
5f3f4266 | 188 | return ERR_PTR(ret); |
2364839a JS |
189 | } |
190 | ||
48cf53f4 JS |
191 | ret = drm_gem_create_mmap_offset(obj); |
192 | if (ret < 0) { | |
193 | drm_gem_object_release(obj); | |
813fd67b | 194 | kfree(exynos_gem); |
48cf53f4 JS |
195 | return ERR_PTR(ret); |
196 | } | |
197 | ||
6be90056 | 198 | DRM_DEV_DEBUG_KMS(dev->dev, "created file object = %pK\n", obj->filp); |
2364839a | 199 | |
813fd67b | 200 | return exynos_gem; |
1c248b7d ID |
201 | } |
202 | ||
813fd67b JS |
203 | struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev, |
204 | unsigned int flags, | |
205 | unsigned long size) | |
f088d5a9 | 206 | { |
813fd67b | 207 | struct exynos_drm_gem *exynos_gem; |
2b35892e | 208 | int ret; |
f088d5a9 | 209 | |
c4130bcd | 210 | if (flags & ~(EXYNOS_BO_MASK)) { |
6f83d208 ID |
211 | DRM_DEV_ERROR(dev->dev, |
212 | "invalid GEM buffer flags: %u\n", flags); | |
c4130bcd JS |
213 | return ERR_PTR(-EINVAL); |
214 | } | |
215 | ||
dcf9af82 | 216 | if (!size) { |
6f83d208 | 217 | DRM_DEV_ERROR(dev->dev, "invalid GEM buffer size: %lu\n", size); |
dcf9af82 ID |
218 | return ERR_PTR(-EINVAL); |
219 | } | |
220 | ||
eb57da88 | 221 | size = roundup(size, PAGE_SIZE); |
f088d5a9 | 222 | |
813fd67b JS |
223 | exynos_gem = exynos_drm_gem_init(dev, size); |
224 | if (IS_ERR(exynos_gem)) | |
225 | return exynos_gem; | |
2b35892e | 226 | |
120a264f MS |
227 | if (!is_drm_iommu_supported(dev) && (flags & EXYNOS_BO_NONCONTIG)) { |
228 | /* | |
229 | * when no IOMMU is available, all allocated buffers are | |
230 | * contiguous anyway, so drop EXYNOS_BO_NONCONTIG flag | |
231 | */ | |
232 | flags &= ~EXYNOS_BO_NONCONTIG; | |
233 | DRM_WARN("Non-contiguous allocation is not supported without IOMMU, falling back to contiguous buffer\n"); | |
234 | } | |
235 | ||
2b35892e | 236 | /* set memory type and cache attribute from user side. */ |
813fd67b | 237 | exynos_gem->flags = flags; |
2b35892e | 238 | |
813fd67b | 239 | ret = exynos_drm_alloc_buf(exynos_gem); |
2a8cb489 | 240 | if (ret < 0) { |
813fd67b JS |
241 | drm_gem_object_release(&exynos_gem->base); |
242 | kfree(exynos_gem); | |
2a8cb489 JS |
243 | return ERR_PTR(ret); |
244 | } | |
f088d5a9 | 245 | |
813fd67b | 246 | return exynos_gem; |
f088d5a9 ID |
247 | } |
248 | ||
1c248b7d | 249 | int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, |
ee5e770e | 250 | struct drm_file *file_priv) |
1c248b7d ID |
251 | { |
252 | struct drm_exynos_gem_create *args = data; | |
813fd67b | 253 | struct exynos_drm_gem *exynos_gem; |
2364839a | 254 | int ret; |
1c248b7d | 255 | |
813fd67b JS |
256 | exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size); |
257 | if (IS_ERR(exynos_gem)) | |
258 | return PTR_ERR(exynos_gem); | |
1c248b7d | 259 | |
813fd67b JS |
260 | ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, |
261 | &args->handle); | |
2364839a | 262 | if (ret) { |
813fd67b | 263 | exynos_drm_gem_destroy(exynos_gem); |
2364839a JS |
264 | return ret; |
265 | } | |
266 | ||
1c248b7d ID |
267 | return 0; |
268 | } | |
269 | ||
6564c65f JS |
270 | int exynos_drm_gem_map_ioctl(struct drm_device *dev, void *data, |
271 | struct drm_file *file_priv) | |
272 | { | |
273 | struct drm_exynos_gem_map *args = data; | |
274 | ||
4d12c233 NT |
275 | return drm_gem_dumb_map_offset(file_priv, dev, args->handle, |
276 | &args->offset); | |
6564c65f JS |
277 | } |
278 | ||
3aa2a5c1 MS |
279 | struct exynos_drm_gem *exynos_drm_gem_get(struct drm_file *filp, |
280 | unsigned int gem_handle) | |
f0b1bda7 | 281 | { |
f0b1bda7 ID |
282 | struct drm_gem_object *obj; |
283 | ||
a8ad0bd8 | 284 | obj = drm_gem_object_lookup(filp, gem_handle); |
3aa2a5c1 MS |
285 | if (!obj) |
286 | return NULL; | |
287 | return to_exynos_gem(obj); | |
f0b1bda7 ID |
288 | } |
289 | ||
813fd67b | 290 | static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem, |
ee5e770e | 291 | struct vm_area_struct *vma) |
1c248b7d | 292 | { |
813fd67b | 293 | struct drm_device *drm_dev = exynos_gem->base.dev; |
0519f9a1 | 294 | unsigned long vm_size; |
5b07c660 | 295 | int ret; |
1c248b7d | 296 | |
832316c7 ID |
297 | vma->vm_flags &= ~VM_PFNMAP; |
298 | vma->vm_pgoff = 0; | |
1c248b7d | 299 | |
0519f9a1 | 300 | vm_size = vma->vm_end - vma->vm_start; |
2b35892e | 301 | |
1c248b7d | 302 | /* check if user-requested size is valid. */ |
813fd67b | 303 | if (vm_size > exynos_gem->size) |
1c248b7d ID |
304 | return -EINVAL; |
305 | ||
f43c3596 | 306 | ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie, |
813fd67b | 307 | exynos_gem->dma_addr, exynos_gem->size, |
00085f1e | 308 | exynos_gem->dma_attrs); |
5b07c660 ID |
309 | if (ret < 0) { |
310 | DRM_ERROR("failed to mmap.\n"); | |
311 | return ret; | |
312 | } | |
313 | ||
1c248b7d ID |
314 | return 0; |
315 | } | |
316 | ||
40cd7e0c ID |
317 | int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, |
318 | struct drm_file *file_priv) | |
b4cfd4dd | 319 | { |
813fd67b | 320 | struct exynos_drm_gem *exynos_gem; |
40cd7e0c ID |
321 | struct drm_exynos_gem_info *args = data; |
322 | struct drm_gem_object *obj; | |
323 | ||
a8ad0bd8 | 324 | obj = drm_gem_object_lookup(file_priv, args->handle); |
40cd7e0c | 325 | if (!obj) { |
6f83d208 | 326 | DRM_DEV_ERROR(dev->dev, "failed to lookup gem object.\n"); |
40cd7e0c ID |
327 | return -EINVAL; |
328 | } | |
329 | ||
813fd67b | 330 | exynos_gem = to_exynos_gem(obj); |
40cd7e0c | 331 | |
813fd67b JS |
332 | args->flags = exynos_gem->flags; |
333 | args->size = exynos_gem->size; | |
40cd7e0c | 334 | |
af7d9101 | 335 | drm_gem_object_put_unlocked(obj); |
40cd7e0c ID |
336 | |
337 | return 0; | |
338 | } | |
339 | ||
ee5e770e | 340 | void exynos_drm_gem_free_object(struct drm_gem_object *obj) |
1c248b7d | 341 | { |
813fd67b | 342 | exynos_drm_gem_destroy(to_exynos_gem(obj)); |
1c248b7d ID |
343 | } |
344 | ||
345 | int exynos_drm_gem_dumb_create(struct drm_file *file_priv, | |
ee5e770e JS |
346 | struct drm_device *dev, |
347 | struct drm_mode_create_dumb *args) | |
1c248b7d | 348 | { |
813fd67b | 349 | struct exynos_drm_gem *exynos_gem; |
333e8e58 | 350 | unsigned int flags; |
2364839a | 351 | int ret; |
1c248b7d | 352 | |
1c248b7d | 353 | /* |
c6b78bc8 | 354 | * allocate memory to be used for framebuffer. |
1c248b7d ID |
355 | * - this callback would be called by user application |
356 | * with DRM_IOCTL_MODE_CREATE_DUMB command. | |
357 | */ | |
358 | ||
3fd6b694 | 359 | args->pitch = args->width * ((args->bpp + 7) / 8); |
7da5907c | 360 | args->size = args->pitch * args->height; |
1c248b7d | 361 | |
333e8e58 JS |
362 | if (is_drm_iommu_supported(dev)) |
363 | flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; | |
364 | else | |
365 | flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC; | |
3fec4532 | 366 | |
813fd67b JS |
367 | exynos_gem = exynos_drm_gem_create(dev, flags, args->size); |
368 | if (IS_ERR(exynos_gem)) { | |
122beea8 | 369 | dev_warn(dev->dev, "FB allocation failed.\n"); |
813fd67b | 370 | return PTR_ERR(exynos_gem); |
122beea8 | 371 | } |
1c248b7d | 372 | |
813fd67b JS |
373 | ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, |
374 | &args->handle); | |
2364839a | 375 | if (ret) { |
813fd67b | 376 | exynos_drm_gem_destroy(exynos_gem); |
2364839a JS |
377 | return ret; |
378 | } | |
379 | ||
1c248b7d ID |
380 | return 0; |
381 | } | |
382 | ||
8a8d9b2c | 383 | vm_fault_t exynos_drm_gem_fault(struct vm_fault *vmf) |
1c248b7d | 384 | { |
11bac800 | 385 | struct vm_area_struct *vma = vmf->vma; |
1c248b7d | 386 | struct drm_gem_object *obj = vma->vm_private_data; |
813fd67b | 387 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
0e9a2ee3 | 388 | unsigned long pfn; |
1c248b7d | 389 | pgoff_t page_offset; |
1c248b7d | 390 | |
1a29d85e | 391 | page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; |
1c248b7d | 392 | |
813fd67b | 393 | if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) { |
0e9a2ee3 | 394 | DRM_ERROR("invalid page offset\n"); |
8a8d9b2c | 395 | return VM_FAULT_SIGBUS; |
0e9a2ee3 | 396 | } |
1c248b7d | 397 | |
813fd67b | 398 | pfn = page_to_pfn(exynos_gem->pages[page_offset]); |
8a8d9b2c SJ |
399 | return vmf_insert_mixed(vma, vmf->address, |
400 | __pfn_to_pfn_t(pfn, PFN_DEV)); | |
1c248b7d ID |
401 | } |
402 | ||
5a0202f7 JS |
403 | static int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj, |
404 | struct vm_area_struct *vma) | |
1c248b7d | 405 | { |
5a0202f7 | 406 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
1c248b7d ID |
407 | int ret; |
408 | ||
6be90056 ID |
409 | DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "flags = 0x%x\n", |
410 | exynos_gem->flags); | |
211b8878 JS |
411 | |
412 | /* non-cachable as default. */ | |
813fd67b | 413 | if (exynos_gem->flags & EXYNOS_BO_CACHABLE) |
211b8878 | 414 | vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); |
813fd67b | 415 | else if (exynos_gem->flags & EXYNOS_BO_WC) |
211b8878 JS |
416 | vma->vm_page_prot = |
417 | pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); | |
418 | else | |
419 | vma->vm_page_prot = | |
420 | pgprot_noncached(vm_get_page_prot(vma->vm_flags)); | |
c01d73fa | 421 | |
813fd67b | 422 | ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma); |
832316c7 ID |
423 | if (ret) |
424 | goto err_close_vm; | |
425 | ||
426 | return ret; | |
427 | ||
428 | err_close_vm: | |
429 | drm_gem_vm_close(vma); | |
832316c7 | 430 | |
1c248b7d ID |
431 | return ret; |
432 | } | |
01ed50dd | 433 | |
5a0202f7 JS |
434 | int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) |
435 | { | |
436 | struct drm_gem_object *obj; | |
437 | int ret; | |
438 | ||
439 | /* set vm_area_struct. */ | |
440 | ret = drm_gem_mmap(filp, vma); | |
441 | if (ret < 0) { | |
442 | DRM_ERROR("failed to mmap.\n"); | |
443 | return ret; | |
444 | } | |
445 | ||
446 | obj = vma->vm_private_data; | |
447 | ||
55b19fc7 JS |
448 | if (obj->import_attach) |
449 | return dma_buf_mmap(obj->dma_buf, vma, 0); | |
450 | ||
5a0202f7 JS |
451 | return exynos_drm_gem_mmap_obj(obj, vma); |
452 | } | |
453 | ||
01ed50dd | 454 | /* low-level interface prime helpers */ |
89452d4a MS |
455 | struct drm_gem_object *exynos_drm_gem_prime_import(struct drm_device *dev, |
456 | struct dma_buf *dma_buf) | |
457 | { | |
458 | return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev)); | |
459 | } | |
460 | ||
01ed50dd JS |
461 | struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) |
462 | { | |
813fd67b | 463 | struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); |
01ed50dd JS |
464 | int npages; |
465 | ||
813fd67b | 466 | npages = exynos_gem->size >> PAGE_SHIFT; |
01ed50dd | 467 | |
813fd67b | 468 | return drm_prime_pages_to_sg(exynos_gem->pages, npages); |
01ed50dd JS |
469 | } |
470 | ||
471 | struct drm_gem_object * | |
472 | exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, | |
473 | struct dma_buf_attachment *attach, | |
474 | struct sg_table *sgt) | |
475 | { | |
813fd67b | 476 | struct exynos_drm_gem *exynos_gem; |
01ed50dd JS |
477 | int npages; |
478 | int ret; | |
479 | ||
813fd67b JS |
480 | exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size); |
481 | if (IS_ERR(exynos_gem)) { | |
482 | ret = PTR_ERR(exynos_gem); | |
50002d4c | 483 | return ERR_PTR(ret); |
2a8cb489 | 484 | } |
01ed50dd | 485 | |
813fd67b | 486 | exynos_gem->dma_addr = sg_dma_address(sgt->sgl); |
01ed50dd | 487 | |
813fd67b | 488 | npages = exynos_gem->size >> PAGE_SHIFT; |
2098105e | 489 | exynos_gem->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); |
813fd67b | 490 | if (!exynos_gem->pages) { |
01ed50dd JS |
491 | ret = -ENOMEM; |
492 | goto err; | |
493 | } | |
494 | ||
813fd67b JS |
495 | ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem->pages, NULL, |
496 | npages); | |
01ed50dd JS |
497 | if (ret < 0) |
498 | goto err_free_large; | |
499 | ||
813fd67b | 500 | exynos_gem->sgt = sgt; |
7c93537a | 501 | |
01ed50dd JS |
502 | if (sgt->nents == 1) { |
503 | /* always physically continuous memory if sgt->nents is 1. */ | |
813fd67b | 504 | exynos_gem->flags |= EXYNOS_BO_CONTIG; |
01ed50dd JS |
505 | } else { |
506 | /* | |
507 | * this case could be CONTIG or NONCONTIG type but for now | |
508 | * sets NONCONTIG. | |
509 | * TODO. we have to find a way that exporter can notify | |
510 | * the type of its own buffer to importer. | |
511 | */ | |
813fd67b | 512 | exynos_gem->flags |= EXYNOS_BO_NONCONTIG; |
01ed50dd JS |
513 | } |
514 | ||
813fd67b | 515 | return &exynos_gem->base; |
01ed50dd JS |
516 | |
517 | err_free_large: | |
2098105e | 518 | kvfree(exynos_gem->pages); |
01ed50dd | 519 | err: |
813fd67b JS |
520 | drm_gem_object_release(&exynos_gem->base); |
521 | kfree(exynos_gem); | |
01ed50dd JS |
522 | return ERR_PTR(ret); |
523 | } | |
524 | ||
525 | void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj) | |
526 | { | |
527 | return NULL; | |
528 | } | |
529 | ||
530 | void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) | |
531 | { | |
532 | /* Nothing to do */ | |
533 | } | |
5a0202f7 JS |
534 | |
535 | int exynos_drm_gem_prime_mmap(struct drm_gem_object *obj, | |
536 | struct vm_area_struct *vma) | |
537 | { | |
538 | int ret; | |
539 | ||
540 | ret = drm_gem_mmap_obj(obj, obj->size, vma); | |
541 | if (ret < 0) | |
542 | return ret; | |
543 | ||
544 | return exynos_drm_gem_mmap_obj(obj, vma); | |
545 | } |