CHROMIUM: rockchip: support prime import sg table
authorHaixia Shi <hshi@chromium.org>
Fri, 29 Jan 2016 00:23:04 +0000 (16:23 -0800)
committerHuang, Tao <huangtao@rock-chips.com>
Wed, 7 Dec 2016 06:08:46 +0000 (14:08 +0800)
The prime fd to handle ioctl was not used with rockchip before. Support
was added in order to pass graphics_Gbm and to support potential uses
within Chrome OS (e.g. zero-copy video decode, camera).

Difference from kernel 3.14 implementation:
- prime_import_sg_table passes dma-buf as argument instead of size
- need to handle import sg_table for both DMA and IOMMU paths

TEST=test_that graphics_Gbm on kevin
BUG=chrome-os-partner:56526

Reviewed-on: https://chromium-review.googlesource.com/381991
Tested-by: Haixia Shi <hshi@chromium.org>
Commit-Queue: Haixia Shi <hshi@chromium.org>
Trybot-Ready: Haixia Shi <hshi@chromium.org>
Reviewed-by: Haixia Shi <hshi@chromium.org>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Conflicts:
drivers/gpu/drm/rockchip/rockchip_drm_gem.c

Change-Id: I7aae5b0e227de61ac17adf98996152da8db097d2
Signed-off-by: Randy Li <randy.li@rock-chips.com>
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_gem.c
drivers/gpu/drm/rockchip/rockchip_drm_gem.h

index 63104bcb318c2c5205fdec9eefb314d8faad64b9..e2a935de39bf2a63950a7074b3740ec69cf1858d 100644 (file)
@@ -1008,6 +1008,7 @@ static struct drm_driver rockchip_drm_driver = {
        .gem_prime_import       = drm_gem_prime_import,
        .gem_prime_export       = drm_gem_prime_export,
        .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
+       .gem_prime_import_sg_table      = rockchip_gem_prime_import_sg_table,
        .gem_prime_vmap         = rockchip_gem_prime_vmap,
        .gem_prime_vunmap       = rockchip_gem_prime_vunmap,
        .gem_prime_mmap         = rockchip_gem_mmap_buf,
index 380749d62ab6c62f370a78ddb83ea20bfac5c580..dbe08906f90be6fb9065ae8eae155392558751ec 100644 (file)
@@ -294,13 +294,11 @@ int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        return rockchip_drm_gem_object_mmap(obj, vma);
 }
 
-struct rockchip_gem_object *
-       rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
-                                  bool alloc_kmap)
+static struct rockchip_gem_object *
+rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
 {
        struct rockchip_gem_object *rk_obj;
        struct drm_gem_object *obj;
-       int ret;
 
        size = round_up(size, PAGE_SIZE);
 
@@ -312,6 +310,20 @@ struct rockchip_gem_object *
 
        drm_gem_object_init(drm, obj, size);
 
+       return rk_obj;
+}
+
+struct rockchip_gem_object *
+rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
+                          bool alloc_kmap)
+{
+       struct rockchip_gem_object *rk_obj;
+       int ret;
+
+       rk_obj = rockchip_gem_alloc_object(drm, size);
+       if (IS_ERR(rk_obj))
+               return rk_obj;
+
        ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
        if (ret)
                goto err_free_rk_obj;
@@ -329,13 +341,23 @@ err_free_rk_obj:
  */
 void rockchip_gem_free_object(struct drm_gem_object *obj)
 {
-       struct rockchip_gem_object *rk_obj;
+       struct drm_device *drm = obj->dev;
+       struct rockchip_drm_private *private = drm->dev_private;
+       struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
        drm_gem_free_mmap_offset(obj);
 
-       rk_obj = to_rockchip_obj(obj);
-
-       rockchip_gem_free_buf(rk_obj);
+       if (obj->import_attach) {
+               if (private->domain) {
+                       rockchip_gem_iommu_unmap(rk_obj);
+               } else {
+                       dma_unmap_sg(drm->dev, rk_obj->sgt->sgl,
+                                    rk_obj->sgt->nents, DMA_BIDIRECTIONAL);
+               }
+               drm_prime_gem_destroy(obj, rk_obj->sgt);
+       } else {
+               rockchip_gem_free_buf(rk_obj);
+       }
 
 #ifdef CONFIG_DRM_DMA_SYNC
        drm_fence_signal_and_put(&rk_obj->acquire_fence);
@@ -708,6 +730,86 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
        return sgt;
 }
 
+static unsigned long rockchip_sg_get_contiguous_size(struct sg_table *sgt,
+                                                    int count)
+{
+       struct scatterlist *s;
+       dma_addr_t expected = sg_dma_address(sgt->sgl);
+       unsigned int i;
+       unsigned long size = 0;
+
+       for_each_sg(sgt->sgl, s, count, i) {
+               if (sg_dma_address(s) != expected)
+                       break;
+               expected = sg_dma_address(s) + sg_dma_len(s);
+               size += sg_dma_len(s);
+       }
+       return size;
+}
+
+static int
+rockchip_gem_iommu_map_sg(struct drm_device *drm,
+                         struct dma_buf_attachment *attach,
+                         struct sg_table *sg,
+                         struct rockchip_gem_object *rk_obj)
+{
+       rk_obj->sgt = sg;
+       return rockchip_gem_iommu_map(rk_obj);
+}
+
+static int
+rockchip_gem_dma_map_sg(struct drm_device *drm,
+                       struct dma_buf_attachment *attach,
+                       struct sg_table *sg,
+                       struct rockchip_gem_object *rk_obj)
+{
+       int count = dma_map_sg(drm->dev, sg->sgl, sg->nents,
+                              DMA_BIDIRECTIONAL);
+       if (!count)
+               return -EINVAL;
+
+       if (rockchip_sg_get_contiguous_size(sg, count) < attach->dmabuf->size) {
+               DRM_ERROR("failed to map sg_table to contiguous linear address.\n");
+               dma_unmap_sg(drm->dev, sg->sgl, sg->nents,
+                            DMA_BIDIRECTIONAL);
+               return -EINVAL;
+       }
+
+       rk_obj->dma_addr = sg_dma_address(sg->sgl);
+       rk_obj->sgt = sg;
+       return 0;
+}
+
+struct drm_gem_object *
+rockchip_gem_prime_import_sg_table(struct drm_device *drm,
+                                  struct dma_buf_attachment *attach,
+                                  struct sg_table *sg)
+{
+       struct rockchip_drm_private *private = drm->dev_private;
+       struct rockchip_gem_object *rk_obj;
+       int ret;
+
+       rk_obj = rockchip_gem_alloc_object(drm, attach->dmabuf->size);
+       if (IS_ERR(rk_obj))
+               return ERR_CAST(rk_obj);
+
+       if (private->domain)
+               ret = rockchip_gem_iommu_map_sg(drm, attach, sg, rk_obj);
+       else
+               ret = rockchip_gem_dma_map_sg(drm, attach, sg, rk_obj);
+
+       if (ret < 0) {
+               DRM_ERROR("failed to import sg table: %d\n", ret);
+               goto err_free_rk_obj;
+       }
+
+       return &rk_obj->base;
+
+err_free_rk_obj:
+       kfree(rk_obj);
+       return ERR_PTR(ret);
+}
+
 void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
 {
        struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
index 888c5a9c3e774dd56c44e582ec2b4be6820b6430..144c519320f9ee543ff7b17a03dbd2cdb4e0d22f 100644 (file)
@@ -53,8 +53,9 @@ struct rockchip_gem_object_node {
 
 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
 struct drm_gem_object *
-rockchip_gem_prime_import_sg_table(struct drm_device *dev, size_t size,
-                                  struct sg_table *sgt);
+rockchip_gem_prime_import_sg_table(struct drm_device *dev,
+                                  struct dma_buf_attachment *attach,
+                                  struct sg_table *sg);
 void *rockchip_gem_prime_vmap(struct drm_gem_object *obj);
 void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);