From 3800391db1b22a7f5d5ae92f9c54fa00327d682a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Dec 2013 10:00:43 +0100 Subject: [PATCH] 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 --- drivers/gpu/drm/tegra/drm.c | 8 +- drivers/gpu/drm/tegra/gem.c | 180 +++++++++++++++++++++++++++++++++++- 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) #endif static struct drm_driver tegra_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, .load = tegra_drm_load, .unload = tegra_drm_unload, .open = tegra_drm_open, @@ -598,6 +598,12 @@ static struct drm_driver tegra_drm_driver = { .gem_free_object = tegra_bo_free_object, .gem_vm_ops = &tegra_bo_vm_ops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = tegra_gem_prime_export, + .gem_prime_import = tegra_gem_prime_import, + .dumb_create = tegra_bo_dumb_create, .dumb_map_offset = tegra_bo_dumb_map_offset, .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 @@ * GNU General Public License for more details. */ +#include #include #include "gem.h" @@ -173,13 +174,87 @@ err: return ERR_PTR(ret); } +struct tegra_bo *tegra_bo_import(struct drm_device *drm, struct dma_buf *buf) +{ + struct dma_buf_attachment *attach; + struct tegra_bo *bo; + ssize_t size; + int err; + + bo = kzalloc(sizeof(*bo), GFP_KERNEL); + if (!bo) + return ERR_PTR(-ENOMEM); + + host1x_bo_init(&bo->base, &tegra_bo_ops); + size = round_up(buf->size, PAGE_SIZE); + + err = drm_gem_object_init(drm, &bo->gem, size); + if (err < 0) + goto free; + + err = drm_gem_create_mmap_offset(&bo->gem); + if (err < 0) + goto release; + + attach = dma_buf_attach(buf, drm->dev); + if (IS_ERR(attach)) { + err = PTR_ERR(attach); + goto free_mmap; + } + + get_dma_buf(buf); + + bo->sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); + if (!bo->sgt) { + err = -ENOMEM; + goto detach; + } + + if (IS_ERR(bo->sgt)) { + err = PTR_ERR(bo->sgt); + goto detach; + } + + if (bo->sgt->nents > 1) { + err = -EINVAL; + goto detach; + } + + bo->paddr = sg_dma_address(bo->sgt->sgl); + bo->gem.import_attach = attach; + + return bo; + +detach: + if (!IS_ERR_OR_NULL(bo->sgt)) + dma_buf_unmap_attachment(attach, bo->sgt, DMA_TO_DEVICE); + + dma_buf_detach(buf, attach); + dma_buf_put(buf); +free_mmap: + drm_gem_free_mmap_offset(&bo->gem); +release: + drm_gem_object_release(&bo->gem); +free: + kfree(bo); + + return ERR_PTR(err); +} + void tegra_bo_free_object(struct drm_gem_object *gem) { struct tegra_bo *bo = to_tegra_bo(gem); + if (gem->import_attach) { + dma_buf_unmap_attachment(gem->import_attach, bo->sgt, + DMA_TO_DEVICE); + drm_prime_gem_destroy(gem, NULL); + } else { + tegra_bo_destroy(gem->dev, bo); + } + drm_gem_free_mmap_offset(gem); drm_gem_object_release(gem); - tegra_bo_destroy(gem->dev, bo); kfree(bo); } @@ -255,3 +330,106 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) return ret; } + +static struct sg_table * +tegra_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + struct drm_gem_object *gem = attach->dmabuf->priv; + struct tegra_bo *bo = to_tegra_bo(gem); + struct sg_table *sgt; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return NULL; + + if (sg_alloc_table(sgt, 1, GFP_KERNEL)) { + kfree(sgt); + return NULL; + } + + sg_dma_address(sgt->sgl) = bo->paddr; + sg_dma_len(sgt->sgl) = gem->size; + + return sgt; +} + +static void tegra_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + sg_free_table(sgt); + kfree(sgt); +} + +static void tegra_gem_prime_release(struct dma_buf *buf) +{ + drm_gem_dmabuf_release(buf); +} + +static void *tegra_gem_prime_kmap_atomic(struct dma_buf *buf, + unsigned long page) +{ + return NULL; +} + +static void tegra_gem_prime_kunmap_atomic(struct dma_buf *buf, + unsigned long page, + void *addr) +{ +} + +static void *tegra_gem_prime_kmap(struct dma_buf *buf, unsigned long page) +{ + return NULL; +} + +static void tegra_gem_prime_kunmap(struct dma_buf *buf, unsigned long page, + void *addr) +{ +} + +static int tegra_gem_prime_mmap(struct dma_buf *buf, struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static const struct dma_buf_ops tegra_gem_prime_dmabuf_ops = { + .map_dma_buf = tegra_gem_prime_map_dma_buf, + .unmap_dma_buf = tegra_gem_prime_unmap_dma_buf, + .release = tegra_gem_prime_release, + .kmap_atomic = tegra_gem_prime_kmap_atomic, + .kunmap_atomic = tegra_gem_prime_kunmap_atomic, + .kmap = tegra_gem_prime_kmap, + .kunmap = tegra_gem_prime_kunmap, + .mmap = tegra_gem_prime_mmap, +}; + +struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, + struct drm_gem_object *gem, + int flags) +{ + return dma_buf_export(gem, &tegra_gem_prime_dmabuf_ops, gem->size, + flags); +} + +struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, + struct dma_buf *buf) +{ + struct tegra_bo *bo; + + if (buf->ops == &tegra_gem_prime_dmabuf_ops) { + struct drm_gem_object *gem = buf->priv; + + if (gem->dev == drm) { + drm_gem_object_reference(gem); + return gem; + } + } + + bo = tegra_bo_import(drm, buf); + if (IS_ERR(bo)) + return ERR_CAST(bo); + + return &bo->gem; +} 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 { struct drm_gem_object gem; struct host1x_bo base; unsigned long flags; + struct sg_table *sgt; dma_addr_t paddr; void *vaddr; }; @@ -57,4 +58,10 @@ int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma); extern const struct vm_operations_struct tegra_bo_vm_ops; +struct dma_buf *tegra_gem_prime_export(struct drm_device *drm, + struct drm_gem_object *gem, + int flags); +struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, + struct dma_buf *buf); + #endif -- 2.20.1