From a6e6adeee67e48eb7d8b7a487ca0d97a30a7c2ed Mon Sep 17 00:00:00 2001 From: Cho KyongHo Date: Fri, 21 Apr 2017 23:28:04 +0900 Subject: [PATCH] [COMMON] g2d: add task management 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 Signed-off-by: Cho KyongHo --- drivers/gpu/exynos/g2d/Makefile | 2 +- drivers/gpu/exynos/g2d/g2d.h | 15 ++++ drivers/gpu/exynos/g2d/g2d_drv.c | 18 +++++ drivers/gpu/exynos/g2d/g2d_format.h | 92 ++++++++++++++++++++++++ drivers/gpu/exynos/g2d/g2d_task.c | 104 ++++++++++++++++++++++++++++ drivers/gpu/exynos/g2d/g2d_task.h | 89 ++++++++++++++++++++++++ 6 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/exynos/g2d/g2d_format.h create mode 100644 drivers/gpu/exynos/g2d/g2d_task.c create mode 100644 drivers/gpu/exynos/g2d/g2d_task.h diff --git a/drivers/gpu/exynos/g2d/Makefile b/drivers/gpu/exynos/g2d/Makefile index 6d7a44cc87a1..d47295bd8c3d 100644 --- a/drivers/gpu/exynos/g2d/Makefile +++ b/drivers/gpu/exynos/g2d/Makefile @@ -1 +1 @@ -obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_drv.o +obj-$(CONFIG_EXYNOS_GRAPHICS_G2D) += g2d_drv.o g2d_task.o diff --git a/drivers/gpu/exynos/g2d/g2d.h b/drivers/gpu/exynos/g2d/g2d.h index 39acfd545906..d5e74f8b270d 100644 --- a/drivers/gpu/exynos/g2d/g2d.h +++ b/drivers/gpu/exynos/g2d/g2d.h @@ -18,15 +18,30 @@ #include #include +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__ */ diff --git a/drivers/gpu/exynos/g2d/g2d_drv.c b/drivers/gpu/exynos/g2d/g2d_drv.c index ea5352ce7d18..49b2b6e11822 100644 --- a/drivers/gpu/exynos/g2d/g2d_drv.c +++ b/drivers/gpu/exynos/g2d/g2d_drv.c @@ -19,11 +19,13 @@ #include #include #include +#include #include #include #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 index 000000000000..5a00b2ea2102 --- /dev/null +++ b/drivers/gpu/exynos/g2d/g2d_format.h @@ -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 index 000000000000..3b99531ae1dc --- /dev/null +++ b/drivers/gpu/exynos/g2d/g2d_task.c @@ -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 +#include +#include + +#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 index 000000000000..e2bcb62d31f6 --- /dev/null +++ b/drivers/gpu/exynos/g2d/g2d_task.h @@ -0,0 +1,89 @@ +/* + * linux/drivers/gpu/exynos/g2d/g2d_task.h + * + * Copyright (C) 2017 Samsung Electronics Co., Ltd. + * + * Contact: Hyesoo Yu + * + * 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 + +#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__*/ -- 2.20.1