These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / exynos / exynos_drm_g2d.c
index 81a2508..c17efdb 100644 (file)
 
 /* registers for base address */
 #define G2D_SRC_BASE_ADDR              0x0304
+#define G2D_SRC_STRIDE_REG             0x0308
 #define G2D_SRC_COLOR_MODE             0x030C
 #define G2D_SRC_LEFT_TOP               0x0310
 #define G2D_SRC_RIGHT_BOTTOM           0x0314
 #define G2D_SRC_PLANE2_BASE_ADDR       0x0318
 #define G2D_DST_BASE_ADDR              0x0404
+#define G2D_DST_STRIDE_REG             0x0408
 #define G2D_DST_COLOR_MODE             0x040C
 #define G2D_DST_LEFT_TOP               0x0410
 #define G2D_DST_RIGHT_BOTTOM           0x0414
@@ -148,6 +150,7 @@ struct g2d_cmdlist {
  * A structure of buffer description
  *
  * @format: color format
+ * @stride: buffer stride/pitch in bytes
  * @left_x: the x coordinates of left top corner
  * @top_y: the y coordinates of left top corner
  * @right_x: the x coordinates of right bottom corner
@@ -156,6 +159,7 @@ struct g2d_cmdlist {
  */
 struct g2d_buf_desc {
        unsigned int    format;
+       unsigned int    stride;
        unsigned int    left_x;
        unsigned int    top_y;
        unsigned int    right_x;
@@ -190,10 +194,8 @@ struct g2d_cmdlist_userptr {
        dma_addr_t              dma_addr;
        unsigned long           userptr;
        unsigned long           size;
-       struct page             **pages;
-       unsigned int            npages;
+       struct frame_vector     *vec;
        struct sg_table         *sgt;
-       struct vm_area_struct   *vma;
        atomic_t                refcount;
        bool                    in_pool;
        bool                    out_of_list;
@@ -363,6 +365,7 @@ static void g2d_userptr_put_dma_addr(struct drm_device *drm_dev,
 {
        struct g2d_cmdlist_userptr *g2d_userptr =
                                        (struct g2d_cmdlist_userptr *)obj;
+       struct page **pages;
 
        if (!obj)
                return;
@@ -382,19 +385,21 @@ out:
        exynos_gem_unmap_sgt_from_dma(drm_dev, g2d_userptr->sgt,
                                        DMA_BIDIRECTIONAL);
 
-       exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-                                       g2d_userptr->npages,
-                                       g2d_userptr->vma);
+       pages = frame_vector_pages(g2d_userptr->vec);
+       if (!IS_ERR(pages)) {
+               int i;
 
-       exynos_gem_put_vma(g2d_userptr->vma);
+               for (i = 0; i < frame_vector_count(g2d_userptr->vec); i++)
+                       set_page_dirty_lock(pages[i]);
+       }
+       put_vaddr_frames(g2d_userptr->vec);
+       frame_vector_destroy(g2d_userptr->vec);
 
        if (!g2d_userptr->out_of_list)
                list_del_init(&g2d_userptr->list);
 
        sg_free_table(g2d_userptr->sgt);
        kfree(g2d_userptr->sgt);
-
-       drm_free_large(g2d_userptr->pages);
        kfree(g2d_userptr);
 }
 
@@ -408,9 +413,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
        struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
        struct g2d_cmdlist_userptr *g2d_userptr;
        struct g2d_data *g2d;
-       struct page **pages;
        struct sg_table *sgt;
-       struct vm_area_struct *vma;
        unsigned long start, end;
        unsigned int npages, offset;
        int ret;
@@ -456,65 +459,40 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,
                return ERR_PTR(-ENOMEM);
 
        atomic_set(&g2d_userptr->refcount, 1);
+       g2d_userptr->size = size;
 
        start = userptr & PAGE_MASK;
        offset = userptr & ~PAGE_MASK;
        end = PAGE_ALIGN(userptr + size);
        npages = (end - start) >> PAGE_SHIFT;
-       g2d_userptr->npages = npages;
-
-       pages = drm_calloc_large(npages, sizeof(struct page *));
-       if (!pages) {
-               DRM_ERROR("failed to allocate pages.\n");
+       g2d_userptr->vec = frame_vector_create(npages);
+       if (!g2d_userptr->vec) {
                ret = -ENOMEM;
                goto err_free;
        }
 
-       down_read(&current->mm->mmap_sem);
-       vma = find_vma(current->mm, userptr);
-       if (!vma) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to get vm region.\n");
+       ret = get_vaddr_frames(start, npages, true, true, g2d_userptr->vec);
+       if (ret != npages) {
+               DRM_ERROR("failed to get user pages from userptr.\n");
+               if (ret < 0)
+                       goto err_destroy_framevec;
                ret = -EFAULT;
-               goto err_free_pages;
+               goto err_put_framevec;
        }
-
-       if (vma->vm_end < userptr + size) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("vma is too small.\n");
+       if (frame_vector_to_pages(g2d_userptr->vec) < 0) {
                ret = -EFAULT;
-               goto err_free_pages;
-       }
-
-       g2d_userptr->vma = exynos_gem_get_vma(vma);
-       if (!g2d_userptr->vma) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to copy vma.\n");
-               ret = -ENOMEM;
-               goto err_free_pages;
-       }
-
-       g2d_userptr->size = size;
-
-       ret = exynos_gem_get_pages_from_userptr(start & PAGE_MASK,
-                                               npages, pages, vma);
-       if (ret < 0) {
-               up_read(&current->mm->mmap_sem);
-               DRM_ERROR("failed to get user pages from userptr.\n");
-               goto err_put_vma;
+               goto err_put_framevec;
        }
 
-       up_read(&current->mm->mmap_sem);
-       g2d_userptr->pages = pages;
-
        sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
        if (!sgt) {
                ret = -ENOMEM;
-               goto err_free_userptr;
+               goto err_put_framevec;
        }
 
-       ret = sg_alloc_table_from_pages(sgt, pages, npages, offset,
-                                       size, GFP_KERNEL);
+       ret = sg_alloc_table_from_pages(sgt,
+                                       frame_vector_pages(g2d_userptr->vec),
+                                       npages, offset, size, GFP_KERNEL);
        if (ret < 0) {
                DRM_ERROR("failed to get sgt from pages.\n");
                goto err_free_sgt;
@@ -549,16 +527,11 @@ err_sg_free_table:
 err_free_sgt:
        kfree(sgt);
 
-err_free_userptr:
-       exynos_gem_put_pages_to_userptr(g2d_userptr->pages,
-                                       g2d_userptr->npages,
-                                       g2d_userptr->vma);
-
-err_put_vma:
-       exynos_gem_put_vma(g2d_userptr->vma);
+err_put_framevec:
+       put_vaddr_frames(g2d_userptr->vec);
 
-err_free_pages:
-       drm_free_large(pages);
+err_destroy_framevec:
+       frame_vector_destroy(g2d_userptr->vec);
 
 err_free:
        kfree(g2d_userptr);
@@ -589,6 +562,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)
 
        switch (reg_offset) {
        case G2D_SRC_BASE_ADDR:
+       case G2D_SRC_STRIDE_REG:
        case G2D_SRC_COLOR_MODE:
        case G2D_SRC_LEFT_TOP:
        case G2D_SRC_RIGHT_BOTTOM:
@@ -598,6 +572,7 @@ static enum g2d_reg_type g2d_get_reg_type(int reg_offset)
                reg_type = REG_TYPE_SRC_PLANE2;
                break;
        case G2D_DST_BASE_ADDR:
+       case G2D_DST_STRIDE_REG:
        case G2D_DST_COLOR_MODE:
        case G2D_DST_LEFT_TOP:
        case G2D_DST_RIGHT_BOTTOM:
@@ -652,8 +627,8 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,
                                                enum g2d_reg_type reg_type,
                                                unsigned long size)
 {
-       unsigned int width, height;
-       unsigned long area;
+       int width, height;
+       unsigned long bpp, last_pos;
 
        /*
         * check source and destination buffers only.
@@ -662,22 +637,37 @@ static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc,
        if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST)
                return true;
 
-       width = buf_desc->right_x - buf_desc->left_x;
+       /* This check also makes sure that right_x > left_x. */
+       width = (int)buf_desc->right_x - (int)buf_desc->left_x;
        if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) {
-               DRM_ERROR("width[%u] is out of range!\n", width);
+               DRM_ERROR("width[%d] is out of range!\n", width);
                return false;
        }
 
-       height = buf_desc->bottom_y - buf_desc->top_y;
+       /* This check also makes sure that bottom_y > top_y. */
+       height = (int)buf_desc->bottom_y - (int)buf_desc->top_y;
        if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) {
-               DRM_ERROR("height[%u] is out of range!\n", height);
+               DRM_ERROR("height[%d] is out of range!\n", height);
                return false;
        }
 
-       area = (unsigned long)width * (unsigned long)height *
-                                       g2d_get_buf_bpp(buf_desc->format);
-       if (area > size) {
-               DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size);
+       bpp = g2d_get_buf_bpp(buf_desc->format);
+
+       /* Compute the position of the last byte that the engine accesses. */
+       last_pos = ((unsigned long)buf_desc->bottom_y - 1) *
+               (unsigned long)buf_desc->stride +
+               (unsigned long)buf_desc->right_x * bpp - 1;
+
+       /*
+        * Since right_x > left_x and bottom_y > top_y we already know
+        * that the first_pos < last_pos (first_pos being the position
+        * of the first byte the engine accesses), it just remains to
+        * check if last_pos is smaller then the buffer size.
+        */
+
+       if (last_pos >= size) {
+               DRM_ERROR("last engine access position [%lu] "
+                       "is out of range [%lu]!\n", last_pos, size);
                return false;
        }
 
@@ -973,8 +963,6 @@ static int g2d_check_reg_offset(struct device *dev,
                                goto err;
 
                        reg_type = g2d_get_reg_type(reg_offset);
-                       if (reg_type == REG_TYPE_NONE)
-                               goto err;
 
                        /* check userptr buffer type. */
                        if ((cmdlist->data[index] & ~0x7fffffff) >> 31) {
@@ -983,14 +971,22 @@ static int g2d_check_reg_offset(struct device *dev,
                        } else
                                buf_info->types[reg_type] = BUF_TYPE_GEM;
                        break;
+               case G2D_SRC_STRIDE_REG:
+               case G2D_DST_STRIDE_REG:
+                       if (for_addr)
+                               goto err;
+
+                       reg_type = g2d_get_reg_type(reg_offset);
+
+                       buf_desc = &buf_info->descs[reg_type];
+                       buf_desc->stride = cmdlist->data[index + 1];
+                       break;
                case G2D_SRC_COLOR_MODE:
                case G2D_DST_COLOR_MODE:
                        if (for_addr)
                                goto err;
 
                        reg_type = g2d_get_reg_type(reg_offset);
-                       if (reg_type == REG_TYPE_NONE)
-                               goto err;
 
                        buf_desc = &buf_info->descs[reg_type];
                        value = cmdlist->data[index + 1];
@@ -1003,8 +999,6 @@ static int g2d_check_reg_offset(struct device *dev,
                                goto err;
 
                        reg_type = g2d_get_reg_type(reg_offset);
-                       if (reg_type == REG_TYPE_NONE)
-                               goto err;
 
                        buf_desc = &buf_info->descs[reg_type];
                        value = cmdlist->data[index + 1];
@@ -1018,8 +1012,6 @@ static int g2d_check_reg_offset(struct device *dev,
                                goto err;
 
                        reg_type = g2d_get_reg_type(reg_offset);
-                       if (reg_type == REG_TYPE_NONE)
-                               goto err;
 
                        buf_desc = &buf_info->descs[reg_type];
                        value = cmdlist->data[index + 1];
@@ -1067,7 +1059,6 @@ int exynos_g2d_get_ver_ioctl(struct drm_device *drm_dev, void *data,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(exynos_g2d_get_ver_ioctl);
 
 int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
                                 struct drm_file *file)
@@ -1238,7 +1229,6 @@ err:
        g2d_put_cmdlist(g2d, node);
        return ret;
 }
-EXPORT_SYMBOL_GPL(exynos_g2d_set_cmdlist_ioctl);
 
 int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
                          struct drm_file *file)
@@ -1301,7 +1291,6 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
 out:
        return 0;
 }
-EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl);
 
 static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 {
@@ -1319,9 +1308,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
                return ret;
        }
 
-       if (!is_drm_iommu_supported(drm_dev))
-               return 0;
-
        ret = drm_iommu_attach_device(drm_dev, dev);
        if (ret < 0) {
                dev_err(dev, "failed to enable iommu.\n");
@@ -1334,9 +1320,6 @@ static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
 
 static void g2d_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
 {
-       if (!is_drm_iommu_supported(drm_dev))
-               return;
-
        drm_iommu_detach_device(drm_dev, dev);
 }