diff options
author | Thierry Reding <treding@nvidia.com> | 2013-12-12 04:00:43 -0500 |
---|---|---|
committer | Thierry Reding <treding@nvidia.com> | 2013-12-20 09:56:07 -0500 |
commit | 3800391db1b22a7f5d5ae92f9c54fa00327d682a (patch) | |
tree | 8f2ca9de23c23292f7f5e8ba08202d16a3a25b8f | |
parent | 72d302861530bcdb780ea57ebfc3dff6ec4f802c (diff) |
drm/tegra: Add PRIME support
Implement very basic PRIME support. This currently only works with
buffers that are contiguous in memory and will refuse to import any
physically non-contiguous buffers.
Signed-off-by: Thierry Reding <treding@nvidia.com>
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gem.c | 180 | ||||
-rw-r--r-- | drivers/gpu/drm/tegra/gem.h | 7 |
3 files changed, 193 insertions, 2 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index eec8d2e2db5c..88a529008ce0 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c | |||
@@ -580,7 +580,7 @@ static void tegra_debugfs_cleanup(struct drm_minor *minor) | |||
580 | #endif | 580 | #endif |
581 | 581 | ||
582 | static struct drm_driver tegra_drm_driver = { | 582 | static struct drm_driver tegra_drm_driver = { |
583 | .driver_features = DRIVER_MODESET | DRIVER_GEM, | 583 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, |
584 | .load = tegra_drm_load, | 584 | .load = tegra_drm_load, |
585 | .unload = tegra_drm_unload, | 585 | .unload = tegra_drm_unload, |
586 | .open = tegra_drm_open, | 586 | .open = tegra_drm_open, |
@@ -598,6 +598,12 @@ static struct drm_driver tegra_drm_driver = { | |||
598 | 598 | ||
599 | .gem_free_object = tegra_bo_free_object, | 599 | .gem_free_object = tegra_bo_free_object, |
600 | .gem_vm_ops = &tegra_bo_vm_ops, | 600 | .gem_vm_ops = &tegra_bo_vm_ops, |
601 | |||
602 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
603 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
604 | .gem_prime_export = tegra_gem_prime_export, | ||
605 | .gem_prime_import = tegra_gem_prime_import, | ||
606 | |||
601 | .dumb_create = tegra_bo_dumb_create, | 607 | .dumb_create = tegra_bo_dumb_create, |
602 | .dumb_map_offset = tegra_bo_dumb_map_offset, | 608 | .dumb_map_offset = tegra_bo_dumb_map_offset, |
603 | .dumb_destroy = drm_gem_dumb_destroy, | 609 | .dumb_destroy = drm_gem_dumb_destroy, |
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 7741e3355c98..ef853e558036 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * GNU General Public License for more details. | 18 | * GNU General Public License for more details. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/dma-buf.h> | ||
21 | #include <drm/tegra_drm.h> | 22 | #include <drm/tegra_drm.h> |
22 | 23 | ||
23 | #include "gem.h" | 24 | #include "gem.h" |
@@ -173,13 +174,87 @@ err: | |||
173 | return ERR_PTR(ret); | 174 | return ERR_PTR(ret); |
174 | } | 175 | } |
175 | 176 | ||
177 | struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf) | ||
178 | { | ||
179 | struct dma_buf_attachment *attach; | ||
180 | struct tegra_bo *bo; | ||
181 | ssize_t size; | ||
182 | int err; | ||
183 | |||
184 | bo = kzalloc(sizeof(*bo), GFP_KERNEL); | ||
185 | if (!bo) | ||
186 | return ERR_PTR(-ENOMEM); | ||
187 | |||
188 | host1x_bo_init(&bo->base, &tegra_bo_ops); | ||
189 | size = round_up(buf->size, PAGE_SIZE); | ||
190 | |||
191 | err = drm_gem_object_init(drm, &bo->gem, size); | ||
192 | if (err < 0) | ||
193 | goto free; | ||
194 | |||
195 | err = drm_gem_create_mmap_offset(&bo->gem); | ||
196 | if (err < 0) | ||
197 | goto release; | ||
198 | |||
199 | attach = dma_buf_attach(buf, drm->dev); | ||
200 | if (IS_ERR(attach)) { | ||
201 | err = PTR_ERR(attach); | ||
202 | goto free_mmap; | ||
203 | } | ||
204 | |||
205 | get_dma_buf(buf); | ||
206 | |||
207 | bo->sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); | ||
208 | if (!bo->sgt) { | ||
209 | err = -ENOMEM; | ||
210 | goto detach; | ||
211 | } | ||
212 | |||
213 | if (IS_ERR(bo->sgt)) { | ||
214 | err = PTR_ERR(bo->sgt); | ||
215 | goto detach; | ||
216 | } | ||
217 | |||
218 | if (bo->sgt->nents > 1) { | ||
219 | err = -EINVAL; | ||
220 | goto detach; | ||
221 | } | ||
222 | |||
223 | bo->paddr = sg_dma_address(bo->sgt->sgl); | ||
224 | bo->gem.import_attach = attach; | ||
225 | |||
226 | return bo; | ||
227 | |||
228 | detach: | ||
229 | if (!IS_ERR_OR_NULL(bo->sgt)) | ||
230 | dma_buf_unmap_attachment(attach, bo->sgt, DMA_TO_DEVICE); | ||
231 | |||
232 | dma_buf_detach(buf, attach); | ||
233 | dma_buf_put(buf); | ||
234 | free_mmap: | ||
235 | drm_gem_free_mmap_offset(&bo->gem); | ||
236 | release: | ||
237 | drm_gem_object_release(&bo->gem); | ||
238 | free: | ||
239 | kfree(bo); | ||
240 | |||
241 | return ERR_PTR(err); | ||
242 | } | ||
243 | |||
176 | void tegra_bo_free_object(struct drm_gem_object *gem) | 244 | void tegra_bo_free_object(struct drm_gem_object *gem) |
177 | { | 245 | { |
178 | struct tegra_bo *bo = to_tegra_bo(gem); | 246 | struct tegra_bo *bo = to_tegra_bo(gem); |
179 | 247 | ||
248 | if (gem->import_attach) { | ||
249 | dma_buf_unmap_attachment(gem->import_attach, bo->sgt, | ||
250 | DMA_TO_DEVICE); | ||
251 | drm_prime_gem_destroy(gem, NULL); | ||
252 | } else { | ||
253 | tegra_bo_destroy(gem->dev, bo); | ||
254 | } | ||
255 | |||
180 | drm_gem_free_mmap_offset(gem); | 256 | drm_gem_free_mmap_offset(gem); |
181 | drm_gem_object_release(gem); | 257 | drm_gem_object_release(gem); |
182 | tegra_bo_destroy(gem->dev, bo); | ||
183 | 258 | ||
184 | kfree(bo); | 259 | kfree(bo); |
185 | } | 260 | } |
@@ -255,3 +330,106 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) | |||
255 | 330 | ||
256 | return ret; | 331 | return ret; |
257 | } | 332 | } |
333 | |||
334 | static struct sg_table * | ||
335 | tegra_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, | ||
336 | enum dma_data_direction dir) | ||
337 | { | ||
338 | struct drm_gem_object *gem = attach->dmabuf->priv; | ||
339 | struct tegra_bo *bo = to_tegra_bo(gem); | ||
340 | struct sg_table *sgt; | ||
341 | |||
342 | sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); | ||
343 | if (!sgt) | ||
344 | return NULL; | ||
345 | |||
346 | if (sg_alloc_table(sgt, 1, GFP_KERNEL)) { | ||
347 | kfree(sgt); | ||
348 | return NULL; | ||
349 | } | ||
350 | |||
351 | sg_dma_address(sgt->sgl) = bo->paddr; | ||
352 | sg_dma_len(sgt->sgl) = gem->size; | ||
353 | |||
354 | return sgt; | ||
355 | } | ||
356 | |||
357 | static void tegra_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, | ||
358 | struct sg_table *sgt, | ||
359 | enum dma_data_direction dir) | ||
360 | { | ||
361 | sg_free_table(sgt); | ||
362 | kfree(sgt); | ||
363 | } | ||
364 | |||
365 | static void tegra_gem_prime_release(struct dma_buf *buf) | ||
366 | { | ||
367 | drm_gem_dmabuf_release(buf); | ||
368 | } | ||
369 | |||
370 | static void *tegra_gem_prime_kmap_atomic(struct dma_buf *buf, | ||
371 | unsigned long page) | ||
372 | { | ||
373 | return NULL; | ||
374 | } | ||
375 | |||
376 | static void tegra_gem_prime_kunmap_atomic(struct dma_buf *buf, | ||
377 | unsigned long page, | ||
378 | void *addr) | ||
379 | { | ||
380 | } | ||
381 | |||
382 | static void *tegra_gem_prime_kmap(struct dma_buf *buf, unsigned long page) | ||
383 | { | ||
384 | return NULL; | ||
385 | } | ||
386 | |||
387 | static void tegra_gem_prime_kunmap(struct dma_buf *buf, unsigned long page, | ||
388 | void *addr) | ||
389 | { | ||
390 | } | ||
391 | |||
392 | static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma) | ||
393 | { | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | |||
397 | static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = { | ||
398 | .map_dma_buf = tegra_gem_prime_map_dma_buf, | ||
399 | .unmap_dma_buf = tegra_gem_prime_unmap_dma_buf, | ||
400 | .release = tegra_gem_prime_release, | ||
401 | .kmap_atomic = tegra_gem_prime_kmap_atomic, | ||
402 | .kunmap_atomic = tegra_gem_prime_kunmap_atomic, | ||
403 | .kmap = tegra_gem_prime_kmap, | ||
404 | .kunmap = tegra_gem_prime_kunmap, | ||
405 | .mmap = tegra_gem_prime_mmap, | ||
406 | }; | ||
407 | |||
408 | struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, | ||
409 | struct drm_gem_object *gem, | ||
410 | int flags) | ||
411 | { | ||
412 | return dma_buf_export(gem, &tegra_gem_prime_dmabuf_ops, gem->size, | ||
413 | flags); | ||
414 | } | ||
415 | |||
416 | struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, | ||
417 | struct dma_buf *buf) | ||
418 | { | ||
419 | struct tegra_bo *bo; | ||
420 | |||
421 | if (buf->ops == &tegra_gem_prime_dmabuf_ops) { | ||
422 | struct drm_gem_object *gem = buf->priv; | ||
423 | |||
424 | if (gem->dev == drm) { | ||
425 | drm_gem_object_reference(gem); | ||
426 | return gem; | ||
427 | } | ||
428 | } | ||
429 | |||
430 | bo = tegra_bo_import(drm, buf); | ||
431 | if (IS_ERR(bo)) | ||
432 | return ERR_CAST(bo); | ||
433 | |||
434 | return &bo->gem; | ||
435 | } | ||
diff --git a/drivers/gpu/drm/tegra/gem.h b/drivers/gpu/drm/tegra/gem.h index 007e0befcb9c..ffd4f792b410 100644 --- a/drivers/gpu/drm/tegra/gem.h +++ b/drivers/gpu/drm/tegra/gem.h | |||
@@ -31,6 +31,7 @@ struct tegra_bo { | |||
31 | struct drm_gem_object gem; | 31 | struct drm_gem_object gem; |
32 | struct host1x_bo base; | 32 | struct host1x_bo base; |
33 | unsigned long flags; | 33 | unsigned long flags; |
34 | struct sg_table *sgt; | ||
34 | dma_addr_t paddr; | 35 | dma_addr_t paddr; |
35 | void *vaddr; | 36 | void *vaddr; |
36 | }; | 37 | }; |
@@ -57,4 +58,10 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); | |||
57 | 58 | ||
58 | extern const struct vm_operations_struct tegra_bo_vm_ops; | 59 | extern const struct vm_operations_struct tegra_bo_vm_ops; |
59 | 60 | ||
61 | struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, | ||
62 | struct drm_gem_object *gem, | ||
63 | int flags); | ||
64 | struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, | ||
65 | struct dma_buf *buf); | ||
66 | |||
60 | #endif | 67 | #endif |