[COMMON] g2d: add task management
authorCho KyongHo <pullip.cho@samsung.com>
Fri, 21 Apr 2017 14:28:04 +0000 (23:28 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:27:11 +0000 (14:27 +0900)
G2D driver does not configure SFRs of G2D from the image processing
information from userspace. It just verifies a set of SFR values from
userspace and forwards to H/W the SFR values.
The possible number of SFR values is 830, and H/W limits the number to
1024, we have decided to maintain the pools of the buffers to store
the SFR values from userspace because allocation of 8KB at run-time
may consume a lot of time.
We also decided to create 16 buffers to store the SFR values which is
called 'Command list' because G2D H/W accepts 16 compositing jobs in
maximum.
We call entries in the pool as 'task' and a task has the following
elements:
- JOB ID that is used by H/W to distinguish jobs
- Descriptions of 16 source layers and the target image
- Buffer descriptions of each layer
- Command list

The life time of a task:
A new task is registered to tasks_free list of g2d_device. If a
compositing job is requested by userspace, G2D driver pulls a task
from tasks_free list and configures the task for the requested
compositing. When the configuration is finished, the task is
registered to tasks_prepared list. Therefore all tasks registered to
tasks_prepared are ready to be pushed to H/W. If a task should be wait
for fences to be signaled, it is not registered to tasks_prepared list
until the fence is signaled. As a result, tasks are listed in
tasks_prepared in a very short time except the system is falling into
suspend to RAM. The tasks remained in tasks_prepared are pushed to the
H/W as soon as the system is woken up from supend to RAM.
When H/W completes composting of a pushed task, it is removed from
tasks_active and returned to tasks_free.

The implemetation of the life cycle of tasks is included in the
following patch series.

Change-Id: I38f11020fc27e48c05d05e39ed9dc9ae28f9eb58
Signed-off-by: hyesoo.yu <hyesoo.yu@samsung.com>
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
drivers/gpu/exynos/g2d/Makefile
drivers/gpu/exynos/g2d/g2d.h
drivers/gpu/exynos/g2d/g2d_drv.c
drivers/gpu/exynos/g2d/g2d_format.h [new file with mode: 0644]
drivers/gpu/exynos/g2d/g2d_task.c [new file with mode: 0644]
drivers/gpu/exynos/g2d/g2d_task.h [new file with mode: 0644]

index 6d7a44cc87a12dc2eaa1c1850266ffd829307316..d47295bd8c3d807322e0a746073d3310088d89eb 100644 (file)
@@ -1 +1 @@
-obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_drv.o
+obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_drv.o g2d_task.o
index 39acfd5459062fff930e06e2da75e28a544a359e..d5e74f8b270d054142af24d346ba04c61e434ceb 100644 (file)
 #include <linux/device.h>
 #include <linux/miscdevice.h>
 
+struct g2d_task; /* defined in g2d_task.h */
+
 struct g2d_device {
        struct miscdevice       misc;
        struct device           *dev;
        struct clk              *clock;
        void __iomem            *reg;
+
+       /* task management */
+       spinlock_t              lock_task;
+       struct g2d_task         *tasks;
+       struct list_head        tasks_free;
+       struct list_head        tasks_prepared;
+       struct list_head        tasks_active;
 };
 
 struct g2d_context {
        struct g2d_device       *g2d_dev;
 };
 
+/* job mask (hwfc or not) */
+#define G2D_JOBMASK_HWFC 0x7
+#define G2D_JOBMASK_DEFAULT 0xFFF8
+#define g2d_job_full(id, job_mask) ((id & job_mask) == job_mask)
+#define g2d_job_empty(id, job_mask) ((id & job_mask) == 0)
+
 #endif /* __EXYNOS_G2D_H__ */
index ea5352ce7d18ab6ea05e2a9ff8141418e6668595..49b2b6e11822d2fa06a062429745cf0155e6fd9c 100644 (file)
 #include <linux/pm_runtime.h>
 #include <linux/exynos_iovmm.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 
 #include "g2d.h"
 #include "g2d_regs.h"
+#include "g2d_task.h"
 
 #define MODULE_NAME "exynos-g2d"
 
@@ -165,9 +167,23 @@ static int g2d_probe(struct platform_device *pdev)
                goto err;
        }
 
+       spin_lock_init(&g2d_dev->lock_task);
+
+       INIT_LIST_HEAD(&g2d_dev->tasks_free);
+       INIT_LIST_HEAD(&g2d_dev->tasks_prepared);
+       INIT_LIST_HEAD(&g2d_dev->tasks_active);
+
+       ret = g2d_create_tasks(g2d_dev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to create tasks");
+               goto err_task;
+       }
+
        dev_info(&pdev->dev, "Probed FIMG2D version %#010x\n", version);
 
        return 0;
+err_task:
+       misc_deregister(&g2d_dev->misc);
 err:
        pm_runtime_disable(&pdev->dev);
        iovmm_deactivate(g2d_dev->dev);
@@ -181,6 +197,8 @@ static int g2d_remove(struct platform_device *pdev)
 {
        struct g2d_device *g2d_dev = platform_get_drvdata(pdev);
 
+       g2d_destroy_tasks(g2d_dev);
+
        misc_deregister(&g2d_dev->misc);
 
        pm_runtime_disable(&pdev->dev);
diff --git a/drivers/gpu/exynos/g2d/g2d_format.h b/drivers/gpu/exynos/g2d/g2d_format.h
new file mode 100644 (file)
index 0000000..5a00b2e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * linux/drivers/gpu/exynos/g2d/g2d_format.h
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Samsung Graphics 2D driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __G2D_FORMAT_H__
+#define __G2D_FORMAT_H__
+
+/* TODO: check the ARGB order */
+#define G2D_SWZ_MASK           0xFFFF
+
+#define G2D_SWZ_ARGB           (0x3210)
+#define G2D_SWZ_ABGR           (0x3012)
+#define G2D_SWZ_xBGR           (0x5012)
+#define G2D_SWZ_xRGB           (0x5210)
+
+#define G2D_YUVORDER_MASK      (0x3 << 24)
+
+#define G2D_YUV_UV             ((0 << 24) | G2D_SWZ_ARGB)
+#define G2D_YUV_VU             ((1 << 24) | G2D_SWZ_ARGB)
+#define G2D_YUV_YC             (0 << 25)
+#define G2D_YUV_CY             (1 << 25)
+
+#define G2D_DATAFMT_SHIFT      16
+#define G2D_DATAFMT_MASK       (0xF << G2D_DATAFMT_SHIFT)
+
+#define G2D_DATAFMT_8888       (0 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_565                (1 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_4444       (2 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_888                (3 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_1555       (4 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_5551       (5 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_8          (6 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_RESERVED   (7 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_YUV420SP   (8 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_YUV420P    (9 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_YUV422I    (10 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_YUV422SP   (11 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_P010       (12 << G2D_DATAFMT_SHIFT)
+#define G2D_DATAFMT_YUV420SP82 (13 << G2D_DATAFMT_SHIFT)
+
+#define G2D_FMT_ARGB8888       (G2D_DATAFMT_8888 | G2D_SWZ_ARGB)
+#define G2D_FMT_ABGR8888       (G2D_DATAFMT_8888 | G2D_SWZ_ABGR)
+#define G2D_FMT_XBGR8888       (G2D_DATAFMT_8888 | G2D_SWZ_xBGR)
+#define G2D_FMT_XRGB8888       (G2D_DATAFMT_8888 | G2D_SWZ_xRGB)
+#define G2D_FMT_ARGB4444       (G2D_DATAFMT_4444 | G2D_SWZ_ARGB)
+#define G2D_FMT_ARGB1555       (G2D_DATAFMT_1555 | G2D_SWZ_ARGB)
+#define G2D_FMT_RGB565         (G2D_DATAFMT_565 | G2D_SWZ_xBGR)
+#define G2D_FMT_RGB888         (G2D_DATAFMT_888 | G2D_SWZ_xRGB)
+#define G2D_FMT_BGR888         (G2D_DATAFMT_888 | G2D_SWZ_BGR)
+#define G2D_FMT_NV12           (G2D_DATAFMT_YUV420SP | G2D_YUV_UV)
+#define G2D_FMT_NV21           (G2D_DATAFMT_YUV420SP | G2D_YUV_VU)
+#define G2D_FMT_YV12           (G2D_DATAFMT_YUV420P | G2D_YUV_VU)
+#define G2D_FMT_YUYV           (G2D_DATAFMT_YUV422I | G2D_YUV_YC | G2D_YUV_UV)
+#define G2D_FMT_YVYU           (G2D_DATAFMT_YUV422I | G2D_YUV_YC | G2D_YUV_VU)
+#define G2D_FMT_UYVY           (G2D_DATAFMT_YUV422I | G2D_YUV_CY | G2D_YUV_UV)
+#define G2D_FMT_VYUY           (G2D_DATAFMT_YUV422I | G2D_YUV_CY | G2D_YUV_VU)
+#define G2D_FMT_NV16           (G2D_DATAFMT_YUV422SP | G2D_YUV_UV)
+#define G2D_FMT_NV61           (G2D_DATAFMT_YUV422SP | G2D_YUV_VU)
+#define G2D_FMT_NV12_82                (G2D_DATAFMT_YUV420SP82 | G2D_YUV_UV)
+#define G2D_FMT_NV21_82                (G2D_DATAFMT_YUV420SP82 | G2D_YUV_VU)
+#define G2D_FMT_NV12_P010      (G2D_DATAFMT_P010 | G2D_YUV_UV)
+#define G2D_FMT_NV21_P010      (G2D_DATAFMT_P010 | G2D_YUV_VU)
+
+#define G2D_DATAFORMAT_AFBC    (1 << 20)
+#define G2D_DATAFORMAT_UORDER  (1 << 21)
+
+#define IS_AFBC(fmt)   ((fmt & G2D_DATAFORMAT_AFBC) != 0)
+#define IS_UORDER(fmt) ((fmt & G2D_DATAFORMAT_UORDER) != 0)
+#define IS_YUV(fmt)    (((fmt) & G2D_DATAFMT_MASK) > G2D_DATAFMT_RESERVED)
+#define IS_YUV422(fmt) ((((fmt) >> G2D_DATAFMT_SHIFT) & 0xE) == 0xA)
+#define IS_RGB(fmt)    (((fmt) & G2D_DATAFMT_MASK) < G2D_DATAFMT_8)
+#define IS_YUV420_82(fmt) (((fmt) & G2D_DATAFMT_MASK) == G2D_DATAFMT_YUV420SP82)
+
+#define IS_AFBC_WIDTH_ALIGNED(width)   IS_ALIGNED((width), 16)
+#define IS_AFBC_HEIGHT_ALIGNED(height) IS_ALIGNED((height), 4)
+
+#define G2D_IMGFMT(value) ((value) & \
+                       (G2D_DATAFMT_MASK | G2D_YUVORDER_MASK | G2D_SWZ_MASK))
+
+#define G2D_MAX_PLANES 4
+
+#define G2D_MAX_SIZE   8192
+
+#endif /* __G2D_FORMAT_H__ */
diff --git a/drivers/gpu/exynos/g2d/g2d_task.c b/drivers/gpu/exynos/g2d/g2d_task.c
new file mode 100644 (file)
index 0000000..3b99531
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * linux/drivers/gpu/exynos/g2d/g2d_task.c
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/exynos_iovmm.h>
+
+#include "g2d.h"
+#include "g2d_task.h"
+
+void g2d_destroy_tasks(struct g2d_device *g2d_dev)
+{
+       struct g2d_task *task, *next;
+       unsigned long flags;
+
+       spin_lock_irqsave(&g2d_dev->lock_task, flags);
+
+       task = g2d_dev->tasks;
+       while (task != NULL) {
+               next = task->next;
+
+               list_del(&task->node);
+
+               iovmm_unmap(g2d_dev->dev, task->cmd_addr);
+
+               __free_pages(task->cmd_page, get_order(G2D_CMD_LIST_SIZE));
+
+               kfree(task);
+
+               task = next;
+       }
+
+       spin_unlock_irqrestore(&g2d_dev->lock_task, flags);
+}
+
+static struct g2d_task *g2d_create_task(struct g2d_device *g2d_dev)
+{
+       struct g2d_task *task;
+       struct scatterlist sgl;
+       int i;
+
+       task = kzalloc(sizeof(*task), GFP_KERNEL);
+       if (!task)
+               return ERR_PTR(-ENOMEM);
+
+       INIT_LIST_HEAD(&task->node);
+
+       task->cmd_page = alloc_pages(GFP_KERNEL, get_order(G2D_CMD_LIST_SIZE));
+       if (!task->cmd_page)
+               goto err_page;
+
+       /* mapping the command data */
+       sg_init_table(&sgl, 1);
+       sg_set_page(&sgl, task->cmd_page, G2D_CMD_LIST_SIZE, 0);
+       task->cmd_addr = iovmm_map(g2d_dev->dev, &sgl, 0, G2D_CMD_LIST_SIZE,
+                                  DMA_TO_DEVICE, IOMMU_READ | IOMMU_CACHE);
+
+       for (i = 0; i < G2D_MAX_IMAGES; i++)
+               task->source[i].task = task;
+       task->target.task = task;
+
+       task->g2d_dev = g2d_dev;
+
+       return task;
+err_page:
+       kfree(task);
+
+       return ERR_PTR(-ENOMEM);
+}
+
+int g2d_create_tasks(struct g2d_device *g2d_dev)
+{
+       struct g2d_task *task;
+       unsigned int i;
+
+       for (i = 0; i < G2D_MAX_JOBS; i++) {
+               task = g2d_create_task(g2d_dev);
+
+               if (IS_ERR(task)) {
+                       g2d_destroy_tasks(g2d_dev);
+                       return PTR_ERR(task);
+               }
+
+               task->job_id = i;
+
+               task->next = g2d_dev->tasks;
+               g2d_dev->tasks = task;
+               list_add(&task->node, &g2d_dev->tasks_free);
+       }
+
+       return 0;
+}
diff --git a/drivers/gpu/exynos/g2d/g2d_task.h b/drivers/gpu/exynos/g2d/g2d_task.h
new file mode 100644 (file)
index 0000000..e2bcb62
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * linux/drivers/gpu/exynos/g2d/g2d_task.h
+ *
+ * Copyright (C) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Contact: Hyesoo Yu <hyesoo.yu@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __EXYNOS_G2D_TASK_H__
+#define __EXYNOS_G2D_TASK_H__
+
+#include <linux/dma-buf.h>
+
+#include "g2d_format.h"
+
+#define G2D_MAX_IMAGES         16
+#define G2D_MAX_JOBS           16
+#define G2D_CMD_LIST_SIZE      8192
+
+struct g2d_buffer {
+       union {
+               struct {
+                       unsigned int                    offset;
+                       struct dma_buf                  *dmabuf;
+                       struct dma_buf_attachment       *attachment;
+                       struct sg_table                 *sgt;
+               } dmabuf;
+               struct {
+                       unsigned long                   addr;
+                       struct vm_area_struct           *vma;
+               } userptr;
+       };
+       unsigned int    length;
+       unsigned int    payload;
+       dma_addr_t      dma_addr;
+};
+
+struct g2d_layer {
+       struct g2d_task         *task;
+       int                     flags;
+       int                     buffer_type;
+       int                     num_buffers;
+       struct g2d_buffer       buffer[G2D_MAX_PLANES];
+};
+
+#define G2D_TASKSTATE_WAITING          1
+#define G2D_TASKSTATE_UNPREPARED       2
+#define G2D_TASKSTATE_PREPARED         3
+#define G2D_TASKSTATE_ACTIVE           4
+#define G2D_TASKSTATE_PROCESSED                5
+#define G2D_TASKSTATE_ERROR            6
+#define G2D_TASKSTATE_KILLED           7
+#define G2D_TASKSTATE_TIMEOUT          8
+
+struct g2d_context;
+struct g2d_device;
+
+struct g2d_task {
+       struct list_head        node;
+       struct g2d_task         *next;
+       struct g2d_device       *g2d_dev;
+
+       unsigned int            job_id;
+
+       struct g2d_layer        source[G2D_MAX_IMAGES];
+       struct g2d_layer        target;
+       unsigned int            num_source;
+
+       /* Command list */
+       struct page             *cmd_page;
+       dma_addr_t              cmd_addr;
+       unsigned int            cmd_count;
+
+       unsigned int            priority;
+};
+
+void g2d_destroy_tasks(struct g2d_device *g2d_dev);
+int g2d_create_tasks(struct g2d_device *g2d_dev);
+
+#endif /*__EXYNOS_G2D_TASK_H__*/