Skip to content

Commit

Permalink
drm/tegra: Add PRIME support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Thierry Reding committed Dec 20, 2013
1 parent 72d3028 commit 3800391
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 2 deletions.
8 changes: 7 additions & 1 deletion drivers/gpu/drm/tegra/drm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
180 changes: 179 additions & 1 deletion drivers/gpu/drm/tegra/gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* GNU General Public License for more details.
*/

#include <linux/dma-buf.h>
#include <drm/tegra_drm.h>

#include "gem.h"
Expand Down Expand Up @@ -173,13 +174,87 @@ struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file,
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);
}
Expand Down Expand Up @@ -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;
}
7 changes: 7 additions & 0 deletions drivers/gpu/drm/tegra/gem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand All @@ -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

0 comments on commit 3800391

Please sign in to comment.