diff options
author | Dave Airlie <airlied@redhat.com> | 2018-01-04 18:29:20 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-01-04 18:29:20 -0500 |
commit | 4ef0bef2ec56a63bd5f51dd07d5f6bedf1123fa3 (patch) | |
tree | ea3e3ab6fdbfce772bc16d40ef7e26e59b922f61 /drivers | |
parent | a9742b794aeea2abfbc12d1384de05b29f169cb9 (diff) | |
parent | 8ded59413ccc58fe138ab4bf337d0d0b3131d46b (diff) |
Merge tag 'exynos-drm-next-for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos into drm-next
Remove lagacy IPP driver
- This driver isn't used anymore so remove it. Marek is preparing new one
which includes completely rewritten API so this driver will be replaced
with the new version[1] later.
And cleanups.
[1] https://patches.linaro.org/cover/118386/
* tag 'exynos-drm-next-for-v4.16' of git://git.kernel.org/pub/scm/linux/kernel/git/daeinki/drm-exynos:
drm/exynos: ipp: Remove Exynos DRM IPP subsystem
drm/exynos/decon: Add include guard to the Exynos7 header
drm/exynos/decon: Move headers from global to local place
drm/exynos: decon5433: Remove unnecessary platform_get_resource() error check
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/exynos/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos5433_drm_decon.c | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos7_drm_decon.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_ipp.c | 1806 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_ipp.h | 252 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-decon5433.h | 209 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-decon7.h | 353 |
10 files changed, 567 insertions, 2089 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 5a7c9d8abd6b..735ce47688f9 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
@@ -95,26 +95,21 @@ config DRM_EXYNOS_G2D | |||
95 | help | 95 | help |
96 | Choose this option if you want to use Exynos G2D for DRM. | 96 | Choose this option if you want to use Exynos G2D for DRM. |
97 | 97 | ||
98 | config DRM_EXYNOS_IPP | ||
99 | bool "Image Post Processor" | ||
100 | help | ||
101 | Choose this option if you want to use IPP feature for DRM. | ||
102 | |||
103 | config DRM_EXYNOS_FIMC | 98 | config DRM_EXYNOS_FIMC |
104 | bool "FIMC" | 99 | bool "FIMC" |
105 | depends on DRM_EXYNOS_IPP && MFD_SYSCON | 100 | depends on BROKEN && MFD_SYSCON |
106 | help | 101 | help |
107 | Choose this option if you want to use Exynos FIMC for DRM. | 102 | Choose this option if you want to use Exynos FIMC for DRM. |
108 | 103 | ||
109 | config DRM_EXYNOS_ROTATOR | 104 | config DRM_EXYNOS_ROTATOR |
110 | bool "Rotator" | 105 | bool "Rotator" |
111 | depends on DRM_EXYNOS_IPP | 106 | depends on BROKEN |
112 | help | 107 | help |
113 | Choose this option if you want to use Exynos Rotator for DRM. | 108 | Choose this option if you want to use Exynos Rotator for DRM. |
114 | 109 | ||
115 | config DRM_EXYNOS_GSC | 110 | config DRM_EXYNOS_GSC |
116 | bool "GScaler" | 111 | bool "GScaler" |
117 | depends on DRM_EXYNOS_IPP && ARCH_EXYNOS5 && VIDEO_SAMSUNG_EXYNOS_GSC=n | 112 | depends on BROKEN && ARCH_EXYNOS5 && VIDEO_SAMSUNG_EXYNOS_GSC=n |
118 | help | 113 | help |
119 | Choose this option if you want to use Exynos GSC for DRM. | 114 | Choose this option if you want to use Exynos GSC for DRM. |
120 | 115 | ||
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index bdf4212dde7b..a51c5459bb13 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile | |||
@@ -18,7 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER) += exynos_mixer.o | |||
18 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o | 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o |
19 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o | 19 | exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o |
20 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o | 20 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o |
21 | exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o | ||
22 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o | 21 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o |
23 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o | 22 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o |
24 | exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o | 23 | exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o |
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 6be5b53c3b27..1c330f2a7a5d 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c | |||
@@ -21,13 +21,12 @@ | |||
21 | #include <linux/pm_runtime.h> | 21 | #include <linux/pm_runtime.h> |
22 | #include <linux/regmap.h> | 22 | #include <linux/regmap.h> |
23 | 23 | ||
24 | #include <video/exynos5433_decon.h> | ||
25 | |||
26 | #include "exynos_drm_drv.h" | 24 | #include "exynos_drm_drv.h" |
27 | #include "exynos_drm_crtc.h" | 25 | #include "exynos_drm_crtc.h" |
28 | #include "exynos_drm_fb.h" | 26 | #include "exynos_drm_fb.h" |
29 | #include "exynos_drm_plane.h" | 27 | #include "exynos_drm_plane.h" |
30 | #include "exynos_drm_iommu.h" | 28 | #include "exynos_drm_iommu.h" |
29 | #include "regs-decon5433.h" | ||
31 | 30 | ||
32 | #define DSD_CFG_MUX 0x1004 | 31 | #define DSD_CFG_MUX 0x1004 |
33 | #define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) | 32 | #define DSD_CFG_MUX_TE_UNMASK_GLOBAL BIT(13) |
@@ -744,11 +743,6 @@ static int exynos5433_decon_probe(struct platform_device *pdev) | |||
744 | } | 743 | } |
745 | 744 | ||
746 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 745 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
747 | if (!res) { | ||
748 | dev_err(dev, "cannot find IO resource\n"); | ||
749 | return -ENXIO; | ||
750 | } | ||
751 | |||
752 | ctx->addr = devm_ioremap_resource(dev, res); | 746 | ctx->addr = devm_ioremap_resource(dev, res); |
753 | if (IS_ERR(ctx->addr)) { | 747 | if (IS_ERR(ctx->addr)) { |
754 | dev_err(dev, "ioremap failed\n"); | 748 | dev_err(dev, "ioremap failed\n"); |
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 615efcf7782a..3931d5e33fe0 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c | |||
@@ -25,13 +25,13 @@ | |||
25 | 25 | ||
26 | #include <video/of_display_timing.h> | 26 | #include <video/of_display_timing.h> |
27 | #include <video/of_videomode.h> | 27 | #include <video/of_videomode.h> |
28 | #include <video/exynos7_decon.h> | ||
29 | 28 | ||
30 | #include "exynos_drm_crtc.h" | 29 | #include "exynos_drm_crtc.h" |
31 | #include "exynos_drm_plane.h" | 30 | #include "exynos_drm_plane.h" |
32 | #include "exynos_drm_drv.h" | 31 | #include "exynos_drm_drv.h" |
33 | #include "exynos_drm_fb.h" | 32 | #include "exynos_drm_fb.h" |
34 | #include "exynos_drm_iommu.h" | 33 | #include "exynos_drm_iommu.h" |
34 | #include "regs-decon7.h" | ||
35 | 35 | ||
36 | /* | 36 | /* |
37 | * DECON stands for Display and Enhancement controller. | 37 | * DECON stands for Display and Enhancement controller. |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index b96bd5a781b2..a518e9c6d6cc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -29,7 +29,6 @@ | |||
29 | #include "exynos_drm_plane.h" | 29 | #include "exynos_drm_plane.h" |
30 | #include "exynos_drm_vidi.h" | 30 | #include "exynos_drm_vidi.h" |
31 | #include "exynos_drm_g2d.h" | 31 | #include "exynos_drm_g2d.h" |
32 | #include "exynos_drm_ipp.h" | ||
33 | #include "exynos_drm_iommu.h" | 32 | #include "exynos_drm_iommu.h" |
34 | 33 | ||
35 | #define DRIVER_NAME "exynos" | 34 | #define DRIVER_NAME "exynos" |
@@ -109,14 +108,6 @@ static const struct drm_ioctl_desc exynos_ioctls[] = { | |||
109 | DRM_AUTH | DRM_RENDER_ALLOW), | 108 | DRM_AUTH | DRM_RENDER_ALLOW), |
110 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, | 109 | DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, |
111 | DRM_AUTH | DRM_RENDER_ALLOW), | 110 | DRM_AUTH | DRM_RENDER_ALLOW), |
112 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_PROPERTY, exynos_drm_ipp_get_property, | ||
113 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
114 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_SET_PROPERTY, exynos_drm_ipp_set_property, | ||
115 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
116 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_QUEUE_BUF, exynos_drm_ipp_queue_buf, | ||
117 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
118 | DRM_IOCTL_DEF_DRV(EXYNOS_IPP_CMD_CTRL, exynos_drm_ipp_cmd_ctrl, | ||
119 | DRM_AUTH | DRM_RENDER_ALLOW), | ||
120 | }; | 111 | }; |
121 | 112 | ||
122 | static const struct file_operations exynos_drm_driver_fops = { | 113 | static const struct file_operations exynos_drm_driver_fops = { |
@@ -257,9 +248,6 @@ static struct exynos_drm_driver_info exynos_drm_drivers[] = { | |||
257 | }, { | 248 | }, { |
258 | DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), | 249 | DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), |
259 | }, { | 250 | }, { |
260 | DRV_PTR(ipp_driver, CONFIG_DRM_EXYNOS_IPP), | ||
261 | DRM_VIRTUAL_DEVICE | ||
262 | }, { | ||
263 | &exynos_drm_platform_driver, | 251 | &exynos_drm_platform_driver, |
264 | DRM_VIRTUAL_DEVICE | 252 | DRM_VIRTUAL_DEVICE |
265 | } | 253 | } |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 589d465a7f88..df2262f70d91 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -188,7 +188,6 @@ struct exynos_drm_g2d_private { | |||
188 | 188 | ||
189 | struct drm_exynos_file_private { | 189 | struct drm_exynos_file_private { |
190 | struct exynos_drm_g2d_private *g2d_priv; | 190 | struct exynos_drm_g2d_private *g2d_priv; |
191 | struct device *ipp_dev; | ||
192 | }; | 191 | }; |
193 | 192 | ||
194 | /* | 193 | /* |
@@ -291,6 +290,5 @@ extern struct platform_driver g2d_driver; | |||
291 | extern struct platform_driver fimc_driver; | 290 | extern struct platform_driver fimc_driver; |
292 | extern struct platform_driver rotator_driver; | 291 | extern struct platform_driver rotator_driver; |
293 | extern struct platform_driver gsc_driver; | 292 | extern struct platform_driver gsc_driver; |
294 | extern struct platform_driver ipp_driver; | ||
295 | extern struct platform_driver mic_driver; | 293 | extern struct platform_driver mic_driver; |
296 | #endif | 294 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c deleted file mode 100644 index 3edda18cc2d2..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ /dev/null | |||
@@ -1,1806 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Samsung Electronics Co.Ltd | ||
3 | * Authors: | ||
4 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
5 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
6 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | * | ||
13 | */ | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/clk.h> | ||
18 | #include <linux/pm_runtime.h> | ||
19 | |||
20 | #include <drm/drmP.h> | ||
21 | #include <drm/exynos_drm.h> | ||
22 | #include "exynos_drm_drv.h" | ||
23 | #include "exynos_drm_gem.h" | ||
24 | #include "exynos_drm_ipp.h" | ||
25 | #include "exynos_drm_iommu.h" | ||
26 | |||
27 | /* | ||
28 | * IPP stands for Image Post Processing and | ||
29 | * supports image scaler/rotator and input/output DMA operations. | ||
30 | * using FIMC, GSC, Rotator, so on. | ||
31 | * IPP is integration device driver of same attribute h/w | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * TODO | ||
36 | * 1. expand command control id. | ||
37 | * 2. integrate property and config. | ||
38 | * 3. removed send_event id check routine. | ||
39 | * 4. compare send_event id if needed. | ||
40 | * 5. free subdrv_remove notifier callback list if needed. | ||
41 | * 6. need to check subdrv_open about multi-open. | ||
42 | * 7. need to power_on implement power and sysmmu ctrl. | ||
43 | */ | ||
44 | |||
45 | #define get_ipp_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
46 | #define ipp_is_m2m_cmd(c) (c == IPP_CMD_M2M) | ||
47 | |||
48 | /* | ||
49 | * A structure of event. | ||
50 | * | ||
51 | * @base: base of event. | ||
52 | * @event: ipp event. | ||
53 | */ | ||
54 | struct drm_exynos_ipp_send_event { | ||
55 | struct drm_pending_event base; | ||
56 | struct drm_exynos_ipp_event event; | ||
57 | }; | ||
58 | |||
59 | /* | ||
60 | * A structure of memory node. | ||
61 | * | ||
62 | * @list: list head to memory queue information. | ||
63 | * @ops_id: id of operations. | ||
64 | * @prop_id: id of property. | ||
65 | * @buf_id: id of buffer. | ||
66 | * @buf_info: gem objects and dma address, size. | ||
67 | * @filp: a pointer to drm_file. | ||
68 | */ | ||
69 | struct drm_exynos_ipp_mem_node { | ||
70 | struct list_head list; | ||
71 | enum drm_exynos_ops_id ops_id; | ||
72 | u32 prop_id; | ||
73 | u32 buf_id; | ||
74 | struct drm_exynos_ipp_buf_info buf_info; | ||
75 | }; | ||
76 | |||
77 | /* | ||
78 | * A structure of ipp context. | ||
79 | * | ||
80 | * @subdrv: prepare initialization using subdrv. | ||
81 | * @ipp_lock: lock for synchronization of access to ipp_idr. | ||
82 | * @prop_lock: lock for synchronization of access to prop_idr. | ||
83 | * @ipp_idr: ipp driver idr. | ||
84 | * @prop_idr: property idr. | ||
85 | * @event_workq: event work queue. | ||
86 | * @cmd_workq: command work queue. | ||
87 | */ | ||
88 | struct ipp_context { | ||
89 | struct exynos_drm_subdrv subdrv; | ||
90 | struct mutex ipp_lock; | ||
91 | struct mutex prop_lock; | ||
92 | struct idr ipp_idr; | ||
93 | struct idr prop_idr; | ||
94 | struct workqueue_struct *event_workq; | ||
95 | struct workqueue_struct *cmd_workq; | ||
96 | }; | ||
97 | |||
98 | static LIST_HEAD(exynos_drm_ippdrv_list); | ||
99 | static DEFINE_MUTEX(exynos_drm_ippdrv_lock); | ||
100 | static BLOCKING_NOTIFIER_HEAD(exynos_drm_ippnb_list); | ||
101 | |||
102 | int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) | ||
103 | { | ||
104 | mutex_lock(&exynos_drm_ippdrv_lock); | ||
105 | list_add_tail(&ippdrv->drv_list, &exynos_drm_ippdrv_list); | ||
106 | mutex_unlock(&exynos_drm_ippdrv_lock); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) | ||
112 | { | ||
113 | mutex_lock(&exynos_drm_ippdrv_lock); | ||
114 | list_del(&ippdrv->drv_list); | ||
115 | mutex_unlock(&exynos_drm_ippdrv_lock); | ||
116 | |||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int ipp_create_id(struct idr *id_idr, struct mutex *lock, void *obj) | ||
121 | { | ||
122 | int ret; | ||
123 | |||
124 | mutex_lock(lock); | ||
125 | ret = idr_alloc(id_idr, obj, 1, 0, GFP_KERNEL); | ||
126 | mutex_unlock(lock); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static void ipp_remove_id(struct idr *id_idr, struct mutex *lock, u32 id) | ||
132 | { | ||
133 | mutex_lock(lock); | ||
134 | idr_remove(id_idr, id); | ||
135 | mutex_unlock(lock); | ||
136 | } | ||
137 | |||
138 | static void *ipp_find_obj(struct idr *id_idr, struct mutex *lock, u32 id) | ||
139 | { | ||
140 | void *obj; | ||
141 | |||
142 | mutex_lock(lock); | ||
143 | obj = idr_find(id_idr, id); | ||
144 | mutex_unlock(lock); | ||
145 | |||
146 | return obj; | ||
147 | } | ||
148 | |||
149 | static int ipp_check_driver(struct exynos_drm_ippdrv *ippdrv, | ||
150 | struct drm_exynos_ipp_property *property) | ||
151 | { | ||
152 | if (ippdrv->dedicated || (!ipp_is_m2m_cmd(property->cmd) && | ||
153 | !pm_runtime_suspended(ippdrv->dev))) | ||
154 | return -EBUSY; | ||
155 | |||
156 | if (ippdrv->check_property && | ||
157 | ippdrv->check_property(ippdrv->dev, property)) | ||
158 | return -EINVAL; | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | static struct exynos_drm_ippdrv *ipp_find_driver(struct ipp_context *ctx, | ||
164 | struct drm_exynos_ipp_property *property) | ||
165 | { | ||
166 | struct exynos_drm_ippdrv *ippdrv; | ||
167 | u32 ipp_id = property->ipp_id; | ||
168 | int ret; | ||
169 | |||
170 | if (ipp_id) { | ||
171 | ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, ipp_id); | ||
172 | if (!ippdrv) { | ||
173 | DRM_DEBUG("ipp%d driver not found\n", ipp_id); | ||
174 | return ERR_PTR(-ENODEV); | ||
175 | } | ||
176 | |||
177 | ret = ipp_check_driver(ippdrv, property); | ||
178 | if (ret < 0) { | ||
179 | DRM_DEBUG("ipp%d driver check error %d\n", ipp_id, ret); | ||
180 | return ERR_PTR(ret); | ||
181 | } | ||
182 | |||
183 | return ippdrv; | ||
184 | } else { | ||
185 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
186 | ret = ipp_check_driver(ippdrv, property); | ||
187 | if (ret == 0) | ||
188 | return ippdrv; | ||
189 | } | ||
190 | |||
191 | DRM_DEBUG("cannot find driver suitable for given property.\n"); | ||
192 | } | ||
193 | |||
194 | return ERR_PTR(-ENODEV); | ||
195 | } | ||
196 | |||
197 | static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) | ||
198 | { | ||
199 | struct exynos_drm_ippdrv *ippdrv; | ||
200 | struct drm_exynos_ipp_cmd_node *c_node; | ||
201 | int count = 0; | ||
202 | |||
203 | DRM_DEBUG_KMS("prop_id[%d]\n", prop_id); | ||
204 | |||
205 | /* | ||
206 | * This case is search ipp driver by prop_id handle. | ||
207 | * sometimes, ipp subsystem find driver by prop_id. | ||
208 | * e.g PAUSE state, queue buf, command control. | ||
209 | */ | ||
210 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
211 | DRM_DEBUG_KMS("count[%d]ippdrv[%pK]\n", count++, ippdrv); | ||
212 | |||
213 | mutex_lock(&ippdrv->cmd_lock); | ||
214 | list_for_each_entry(c_node, &ippdrv->cmd_list, list) { | ||
215 | if (c_node->property.prop_id == prop_id) { | ||
216 | mutex_unlock(&ippdrv->cmd_lock); | ||
217 | return ippdrv; | ||
218 | } | ||
219 | } | ||
220 | mutex_unlock(&ippdrv->cmd_lock); | ||
221 | } | ||
222 | |||
223 | return ERR_PTR(-ENODEV); | ||
224 | } | ||
225 | |||
226 | int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, | ||
227 | struct drm_file *file) | ||
228 | { | ||
229 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
230 | struct device *dev = file_priv->ipp_dev; | ||
231 | struct ipp_context *ctx = get_ipp_context(dev); | ||
232 | struct drm_exynos_ipp_prop_list *prop_list = data; | ||
233 | struct exynos_drm_ippdrv *ippdrv; | ||
234 | int count = 0; | ||
235 | |||
236 | if (!ctx) { | ||
237 | DRM_ERROR("invalid context.\n"); | ||
238 | return -EINVAL; | ||
239 | } | ||
240 | |||
241 | if (!prop_list) { | ||
242 | DRM_ERROR("invalid property parameter.\n"); | ||
243 | return -EINVAL; | ||
244 | } | ||
245 | |||
246 | DRM_DEBUG_KMS("ipp_id[%d]\n", prop_list->ipp_id); | ||
247 | |||
248 | if (!prop_list->ipp_id) { | ||
249 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) | ||
250 | count++; | ||
251 | |||
252 | /* | ||
253 | * Supports ippdrv list count for user application. | ||
254 | * First step user application getting ippdrv count. | ||
255 | * and second step getting ippdrv capability using ipp_id. | ||
256 | */ | ||
257 | prop_list->count = count; | ||
258 | } else { | ||
259 | /* | ||
260 | * Getting ippdrv capability by ipp_id. | ||
261 | * some device not supported wb, output interface. | ||
262 | * so, user application detect correct ipp driver | ||
263 | * using this ioctl. | ||
264 | */ | ||
265 | ippdrv = ipp_find_obj(&ctx->ipp_idr, &ctx->ipp_lock, | ||
266 | prop_list->ipp_id); | ||
267 | if (!ippdrv) { | ||
268 | DRM_ERROR("not found ipp%d driver.\n", | ||
269 | prop_list->ipp_id); | ||
270 | return -ENODEV; | ||
271 | } | ||
272 | |||
273 | *prop_list = ippdrv->prop_list; | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static void ipp_print_property(struct drm_exynos_ipp_property *property, | ||
280 | int idx) | ||
281 | { | ||
282 | struct drm_exynos_ipp_config *config = &property->config[idx]; | ||
283 | struct drm_exynos_pos *pos = &config->pos; | ||
284 | struct drm_exynos_sz *sz = &config->sz; | ||
285 | |||
286 | DRM_DEBUG_KMS("prop_id[%d]ops[%s]fmt[0x%x]\n", | ||
287 | property->prop_id, idx ? "dst" : "src", config->fmt); | ||
288 | |||
289 | DRM_DEBUG_KMS("pos[%d %d %d %d]sz[%d %d]f[%d]r[%d]\n", | ||
290 | pos->x, pos->y, pos->w, pos->h, | ||
291 | sz->hsize, sz->vsize, config->flip, config->degree); | ||
292 | } | ||
293 | |||
294 | static struct drm_exynos_ipp_cmd_work *ipp_create_cmd_work(void) | ||
295 | { | ||
296 | struct drm_exynos_ipp_cmd_work *cmd_work; | ||
297 | |||
298 | cmd_work = kzalloc(sizeof(*cmd_work), GFP_KERNEL); | ||
299 | if (!cmd_work) | ||
300 | return ERR_PTR(-ENOMEM); | ||
301 | |||
302 | INIT_WORK((struct work_struct *)cmd_work, ipp_sched_cmd); | ||
303 | |||
304 | return cmd_work; | ||
305 | } | ||
306 | |||
307 | static struct drm_exynos_ipp_event_work *ipp_create_event_work(void) | ||
308 | { | ||
309 | struct drm_exynos_ipp_event_work *event_work; | ||
310 | |||
311 | event_work = kzalloc(sizeof(*event_work), GFP_KERNEL); | ||
312 | if (!event_work) | ||
313 | return ERR_PTR(-ENOMEM); | ||
314 | |||
315 | INIT_WORK(&event_work->work, ipp_sched_event); | ||
316 | |||
317 | return event_work; | ||
318 | } | ||
319 | |||
320 | int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, | ||
321 | struct drm_file *file) | ||
322 | { | ||
323 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
324 | struct device *dev = file_priv->ipp_dev; | ||
325 | struct ipp_context *ctx = get_ipp_context(dev); | ||
326 | struct drm_exynos_ipp_property *property = data; | ||
327 | struct exynos_drm_ippdrv *ippdrv; | ||
328 | struct drm_exynos_ipp_cmd_node *c_node; | ||
329 | u32 prop_id; | ||
330 | int ret, i; | ||
331 | |||
332 | if (!ctx) { | ||
333 | DRM_ERROR("invalid context.\n"); | ||
334 | return -EINVAL; | ||
335 | } | ||
336 | |||
337 | if (!property) { | ||
338 | DRM_ERROR("invalid property parameter.\n"); | ||
339 | return -EINVAL; | ||
340 | } | ||
341 | |||
342 | prop_id = property->prop_id; | ||
343 | |||
344 | /* | ||
345 | * This is log print for user application property. | ||
346 | * user application set various property. | ||
347 | */ | ||
348 | for_each_ipp_ops(i) | ||
349 | ipp_print_property(property, i); | ||
350 | |||
351 | /* | ||
352 | * In case prop_id is not zero try to set existing property. | ||
353 | */ | ||
354 | if (prop_id) { | ||
355 | c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, prop_id); | ||
356 | |||
357 | if (!c_node || c_node->filp != file) { | ||
358 | DRM_DEBUG_KMS("prop_id[%d] not found\n", prop_id); | ||
359 | return -EINVAL; | ||
360 | } | ||
361 | |||
362 | if (c_node->state != IPP_STATE_STOP) { | ||
363 | DRM_DEBUG_KMS("prop_id[%d] not stopped\n", prop_id); | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | |||
367 | c_node->property = *property; | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | /* find ipp driver using ipp id */ | ||
373 | ippdrv = ipp_find_driver(ctx, property); | ||
374 | if (IS_ERR(ippdrv)) { | ||
375 | DRM_ERROR("failed to get ipp driver.\n"); | ||
376 | return -EINVAL; | ||
377 | } | ||
378 | |||
379 | /* allocate command node */ | ||
380 | c_node = kzalloc(sizeof(*c_node), GFP_KERNEL); | ||
381 | if (!c_node) | ||
382 | return -ENOMEM; | ||
383 | |||
384 | ret = ipp_create_id(&ctx->prop_idr, &ctx->prop_lock, c_node); | ||
385 | if (ret < 0) { | ||
386 | DRM_ERROR("failed to create id.\n"); | ||
387 | goto err_clear; | ||
388 | } | ||
389 | property->prop_id = ret; | ||
390 | |||
391 | DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[%pK]\n", | ||
392 | property->prop_id, property->cmd, ippdrv); | ||
393 | |||
394 | /* stored property information and ippdrv in private data */ | ||
395 | c_node->property = *property; | ||
396 | c_node->state = IPP_STATE_IDLE; | ||
397 | c_node->filp = file; | ||
398 | |||
399 | c_node->start_work = ipp_create_cmd_work(); | ||
400 | if (IS_ERR(c_node->start_work)) { | ||
401 | DRM_ERROR("failed to create start work.\n"); | ||
402 | ret = PTR_ERR(c_node->start_work); | ||
403 | goto err_remove_id; | ||
404 | } | ||
405 | |||
406 | c_node->stop_work = ipp_create_cmd_work(); | ||
407 | if (IS_ERR(c_node->stop_work)) { | ||
408 | DRM_ERROR("failed to create stop work.\n"); | ||
409 | ret = PTR_ERR(c_node->stop_work); | ||
410 | goto err_free_start; | ||
411 | } | ||
412 | |||
413 | c_node->event_work = ipp_create_event_work(); | ||
414 | if (IS_ERR(c_node->event_work)) { | ||
415 | DRM_ERROR("failed to create event work.\n"); | ||
416 | ret = PTR_ERR(c_node->event_work); | ||
417 | goto err_free_stop; | ||
418 | } | ||
419 | |||
420 | mutex_init(&c_node->lock); | ||
421 | mutex_init(&c_node->mem_lock); | ||
422 | mutex_init(&c_node->event_lock); | ||
423 | |||
424 | init_completion(&c_node->start_complete); | ||
425 | init_completion(&c_node->stop_complete); | ||
426 | |||
427 | for_each_ipp_ops(i) | ||
428 | INIT_LIST_HEAD(&c_node->mem_list[i]); | ||
429 | |||
430 | INIT_LIST_HEAD(&c_node->event_list); | ||
431 | mutex_lock(&ippdrv->cmd_lock); | ||
432 | list_add_tail(&c_node->list, &ippdrv->cmd_list); | ||
433 | mutex_unlock(&ippdrv->cmd_lock); | ||
434 | |||
435 | /* make dedicated state without m2m */ | ||
436 | if (!ipp_is_m2m_cmd(property->cmd)) | ||
437 | ippdrv->dedicated = true; | ||
438 | |||
439 | return 0; | ||
440 | |||
441 | err_free_stop: | ||
442 | kfree(c_node->stop_work); | ||
443 | err_free_start: | ||
444 | kfree(c_node->start_work); | ||
445 | err_remove_id: | ||
446 | ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, property->prop_id); | ||
447 | err_clear: | ||
448 | kfree(c_node); | ||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | static int ipp_validate_mem_node(struct drm_device *drm_dev, | ||
453 | struct drm_exynos_ipp_mem_node *m_node, | ||
454 | struct drm_exynos_ipp_cmd_node *c_node) | ||
455 | { | ||
456 | struct drm_exynos_ipp_config *ipp_cfg; | ||
457 | unsigned int num_plane; | ||
458 | unsigned long size, buf_size = 0, plane_size, img_size = 0; | ||
459 | unsigned int bpp, width, height; | ||
460 | int i; | ||
461 | |||
462 | ipp_cfg = &c_node->property.config[m_node->ops_id]; | ||
463 | num_plane = drm_format_num_planes(ipp_cfg->fmt); | ||
464 | |||
465 | /** | ||
466 | * This is a rather simplified validation of a memory node. | ||
467 | * It basically verifies provided gem object handles | ||
468 | * and the buffer sizes with respect to current configuration. | ||
469 | * This is not the best that can be done | ||
470 | * but it seems more than enough | ||
471 | */ | ||
472 | for (i = 0; i < num_plane; ++i) { | ||
473 | width = ipp_cfg->sz.hsize; | ||
474 | height = ipp_cfg->sz.vsize; | ||
475 | bpp = drm_format_plane_cpp(ipp_cfg->fmt, i); | ||
476 | |||
477 | /* | ||
478 | * The result of drm_format_plane_cpp() for chroma planes must | ||
479 | * be used with drm_format_xxxx_chroma_subsampling() for | ||
480 | * correct result. | ||
481 | */ | ||
482 | if (i > 0) { | ||
483 | width /= drm_format_horz_chroma_subsampling( | ||
484 | ipp_cfg->fmt); | ||
485 | height /= drm_format_vert_chroma_subsampling( | ||
486 | ipp_cfg->fmt); | ||
487 | } | ||
488 | plane_size = width * height * bpp; | ||
489 | img_size += plane_size; | ||
490 | |||
491 | if (m_node->buf_info.handles[i]) { | ||
492 | size = exynos_drm_gem_get_size(drm_dev, | ||
493 | m_node->buf_info.handles[i], | ||
494 | c_node->filp); | ||
495 | if (plane_size > size) { | ||
496 | DRM_ERROR( | ||
497 | "buffer %d is smaller than required\n", | ||
498 | i); | ||
499 | return -EINVAL; | ||
500 | } | ||
501 | |||
502 | buf_size += size; | ||
503 | } | ||
504 | } | ||
505 | |||
506 | if (buf_size < img_size) { | ||
507 | DRM_ERROR("size of buffers(%lu) is smaller than image(%lu)\n", | ||
508 | buf_size, img_size); | ||
509 | return -EINVAL; | ||
510 | } | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static int ipp_put_mem_node(struct drm_device *drm_dev, | ||
516 | struct drm_exynos_ipp_cmd_node *c_node, | ||
517 | struct drm_exynos_ipp_mem_node *m_node) | ||
518 | { | ||
519 | int i; | ||
520 | |||
521 | DRM_DEBUG_KMS("node[%pK]\n", m_node); | ||
522 | |||
523 | if (!m_node) { | ||
524 | DRM_ERROR("invalid dequeue node.\n"); | ||
525 | return -EFAULT; | ||
526 | } | ||
527 | |||
528 | DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); | ||
529 | |||
530 | /* put gem buffer */ | ||
531 | for_each_ipp_planar(i) { | ||
532 | unsigned long handle = m_node->buf_info.handles[i]; | ||
533 | if (handle) | ||
534 | exynos_drm_gem_put_dma_addr(drm_dev, handle, | ||
535 | c_node->filp); | ||
536 | } | ||
537 | |||
538 | list_del(&m_node->list); | ||
539 | kfree(m_node); | ||
540 | |||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static struct drm_exynos_ipp_mem_node | ||
545 | *ipp_get_mem_node(struct drm_device *drm_dev, | ||
546 | struct drm_exynos_ipp_cmd_node *c_node, | ||
547 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
548 | { | ||
549 | struct drm_exynos_ipp_mem_node *m_node; | ||
550 | struct drm_exynos_ipp_buf_info *buf_info; | ||
551 | int i; | ||
552 | |||
553 | m_node = kzalloc(sizeof(*m_node), GFP_KERNEL); | ||
554 | if (!m_node) | ||
555 | return ERR_PTR(-ENOMEM); | ||
556 | |||
557 | buf_info = &m_node->buf_info; | ||
558 | |||
559 | /* operations, buffer id */ | ||
560 | m_node->ops_id = qbuf->ops_id; | ||
561 | m_node->prop_id = qbuf->prop_id; | ||
562 | m_node->buf_id = qbuf->buf_id; | ||
563 | INIT_LIST_HEAD(&m_node->list); | ||
564 | |||
565 | DRM_DEBUG_KMS("m_node[%pK]ops_id[%d]\n", m_node, qbuf->ops_id); | ||
566 | DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id); | ||
567 | |||
568 | for_each_ipp_planar(i) { | ||
569 | DRM_DEBUG_KMS("i[%d]handle[0x%x]\n", i, qbuf->handle[i]); | ||
570 | |||
571 | /* get dma address by handle */ | ||
572 | if (qbuf->handle[i]) { | ||
573 | dma_addr_t *addr; | ||
574 | |||
575 | addr = exynos_drm_gem_get_dma_addr(drm_dev, | ||
576 | qbuf->handle[i], c_node->filp); | ||
577 | if (IS_ERR(addr)) { | ||
578 | DRM_ERROR("failed to get addr.\n"); | ||
579 | ipp_put_mem_node(drm_dev, c_node, m_node); | ||
580 | return ERR_PTR(-EFAULT); | ||
581 | } | ||
582 | |||
583 | buf_info->handles[i] = qbuf->handle[i]; | ||
584 | buf_info->base[i] = *addr; | ||
585 | DRM_DEBUG_KMS("i[%d]base[%pad]hd[0x%lx]\n", i, | ||
586 | &buf_info->base[i], buf_info->handles[i]); | ||
587 | } | ||
588 | } | ||
589 | |||
590 | mutex_lock(&c_node->mem_lock); | ||
591 | if (ipp_validate_mem_node(drm_dev, m_node, c_node)) { | ||
592 | ipp_put_mem_node(drm_dev, c_node, m_node); | ||
593 | mutex_unlock(&c_node->mem_lock); | ||
594 | return ERR_PTR(-EFAULT); | ||
595 | } | ||
596 | list_add_tail(&m_node->list, &c_node->mem_list[qbuf->ops_id]); | ||
597 | mutex_unlock(&c_node->mem_lock); | ||
598 | |||
599 | return m_node; | ||
600 | } | ||
601 | |||
602 | static void ipp_clean_mem_nodes(struct drm_device *drm_dev, | ||
603 | struct drm_exynos_ipp_cmd_node *c_node, int ops) | ||
604 | { | ||
605 | struct drm_exynos_ipp_mem_node *m_node, *tm_node; | ||
606 | struct list_head *head = &c_node->mem_list[ops]; | ||
607 | |||
608 | mutex_lock(&c_node->mem_lock); | ||
609 | |||
610 | list_for_each_entry_safe(m_node, tm_node, head, list) { | ||
611 | int ret; | ||
612 | |||
613 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
614 | if (ret) | ||
615 | DRM_ERROR("failed to put m_node.\n"); | ||
616 | } | ||
617 | |||
618 | mutex_unlock(&c_node->mem_lock); | ||
619 | } | ||
620 | |||
621 | static int ipp_get_event(struct drm_device *drm_dev, | ||
622 | struct drm_exynos_ipp_cmd_node *c_node, | ||
623 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
624 | { | ||
625 | struct drm_exynos_ipp_send_event *e; | ||
626 | int ret; | ||
627 | |||
628 | DRM_DEBUG_KMS("ops_id[%d]buf_id[%d]\n", qbuf->ops_id, qbuf->buf_id); | ||
629 | |||
630 | e = kzalloc(sizeof(*e), GFP_KERNEL); | ||
631 | if (!e) | ||
632 | return -ENOMEM; | ||
633 | |||
634 | /* make event */ | ||
635 | e->event.base.type = DRM_EXYNOS_IPP_EVENT; | ||
636 | e->event.base.length = sizeof(e->event); | ||
637 | e->event.user_data = qbuf->user_data; | ||
638 | e->event.prop_id = qbuf->prop_id; | ||
639 | e->event.buf_id[EXYNOS_DRM_OPS_DST] = qbuf->buf_id; | ||
640 | |||
641 | ret = drm_event_reserve_init(drm_dev, c_node->filp, &e->base, &e->event.base); | ||
642 | if (ret) { | ||
643 | kfree(e); | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | mutex_lock(&c_node->event_lock); | ||
648 | list_add_tail(&e->base.link, &c_node->event_list); | ||
649 | mutex_unlock(&c_node->event_lock); | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, | ||
655 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
656 | { | ||
657 | struct drm_exynos_ipp_send_event *e, *te; | ||
658 | int count = 0; | ||
659 | |||
660 | mutex_lock(&c_node->event_lock); | ||
661 | list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { | ||
662 | DRM_DEBUG_KMS("count[%d]e[%pK]\n", count++, e); | ||
663 | |||
664 | /* | ||
665 | * qbuf == NULL condition means all event deletion. | ||
666 | * stop operations want to delete all event list. | ||
667 | * another case delete only same buf id. | ||
668 | */ | ||
669 | if (!qbuf) { | ||
670 | /* delete list */ | ||
671 | list_del(&e->base.link); | ||
672 | kfree(e); | ||
673 | } | ||
674 | |||
675 | /* compare buffer id */ | ||
676 | if (qbuf && (qbuf->buf_id == | ||
677 | e->event.buf_id[EXYNOS_DRM_OPS_DST])) { | ||
678 | /* delete list */ | ||
679 | list_del(&e->base.link); | ||
680 | kfree(e); | ||
681 | goto out_unlock; | ||
682 | } | ||
683 | } | ||
684 | |||
685 | out_unlock: | ||
686 | mutex_unlock(&c_node->event_lock); | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | static void ipp_clean_cmd_node(struct ipp_context *ctx, | ||
691 | struct drm_exynos_ipp_cmd_node *c_node) | ||
692 | { | ||
693 | int i; | ||
694 | |||
695 | /* cancel works */ | ||
696 | cancel_work_sync(&c_node->start_work->work); | ||
697 | cancel_work_sync(&c_node->stop_work->work); | ||
698 | cancel_work_sync(&c_node->event_work->work); | ||
699 | |||
700 | /* put event */ | ||
701 | ipp_put_event(c_node, NULL); | ||
702 | |||
703 | for_each_ipp_ops(i) | ||
704 | ipp_clean_mem_nodes(ctx->subdrv.drm_dev, c_node, i); | ||
705 | |||
706 | /* delete list */ | ||
707 | list_del(&c_node->list); | ||
708 | |||
709 | ipp_remove_id(&ctx->prop_idr, &ctx->prop_lock, | ||
710 | c_node->property.prop_id); | ||
711 | |||
712 | /* destroy mutex */ | ||
713 | mutex_destroy(&c_node->lock); | ||
714 | mutex_destroy(&c_node->mem_lock); | ||
715 | mutex_destroy(&c_node->event_lock); | ||
716 | |||
717 | /* free command node */ | ||
718 | kfree(c_node->start_work); | ||
719 | kfree(c_node->stop_work); | ||
720 | kfree(c_node->event_work); | ||
721 | kfree(c_node); | ||
722 | } | ||
723 | |||
724 | static bool ipp_check_mem_list(struct drm_exynos_ipp_cmd_node *c_node) | ||
725 | { | ||
726 | switch (c_node->property.cmd) { | ||
727 | case IPP_CMD_WB: | ||
728 | return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); | ||
729 | case IPP_CMD_OUTPUT: | ||
730 | return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]); | ||
731 | case IPP_CMD_M2M: | ||
732 | default: | ||
733 | return !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_SRC]) && | ||
734 | !list_empty(&c_node->mem_list[EXYNOS_DRM_OPS_DST]); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | static struct drm_exynos_ipp_mem_node | ||
739 | *ipp_find_mem_node(struct drm_exynos_ipp_cmd_node *c_node, | ||
740 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
741 | { | ||
742 | struct drm_exynos_ipp_mem_node *m_node; | ||
743 | struct list_head *head; | ||
744 | int count = 0; | ||
745 | |||
746 | DRM_DEBUG_KMS("buf_id[%d]\n", qbuf->buf_id); | ||
747 | |||
748 | /* source/destination memory list */ | ||
749 | head = &c_node->mem_list[qbuf->ops_id]; | ||
750 | |||
751 | /* find memory node from memory list */ | ||
752 | list_for_each_entry(m_node, head, list) { | ||
753 | DRM_DEBUG_KMS("count[%d]m_node[%pK]\n", count++, m_node); | ||
754 | |||
755 | /* compare buffer id */ | ||
756 | if (m_node->buf_id == qbuf->buf_id) | ||
757 | return m_node; | ||
758 | } | ||
759 | |||
760 | return NULL; | ||
761 | } | ||
762 | |||
763 | static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, | ||
764 | struct drm_exynos_ipp_cmd_node *c_node, | ||
765 | struct drm_exynos_ipp_mem_node *m_node) | ||
766 | { | ||
767 | struct exynos_drm_ipp_ops *ops = NULL; | ||
768 | int ret = 0; | ||
769 | |||
770 | DRM_DEBUG_KMS("node[%pK]\n", m_node); | ||
771 | |||
772 | if (!m_node) { | ||
773 | DRM_ERROR("invalid queue node.\n"); | ||
774 | return -EFAULT; | ||
775 | } | ||
776 | |||
777 | DRM_DEBUG_KMS("ops_id[%d]\n", m_node->ops_id); | ||
778 | |||
779 | /* get operations callback */ | ||
780 | ops = ippdrv->ops[m_node->ops_id]; | ||
781 | if (!ops) { | ||
782 | DRM_ERROR("not support ops.\n"); | ||
783 | return -EFAULT; | ||
784 | } | ||
785 | |||
786 | /* set address and enable irq */ | ||
787 | if (ops->set_addr) { | ||
788 | ret = ops->set_addr(ippdrv->dev, &m_node->buf_info, | ||
789 | m_node->buf_id, IPP_BUF_ENQUEUE); | ||
790 | if (ret) { | ||
791 | DRM_ERROR("failed to set addr.\n"); | ||
792 | return ret; | ||
793 | } | ||
794 | } | ||
795 | |||
796 | return ret; | ||
797 | } | ||
798 | |||
799 | static void ipp_handle_cmd_work(struct device *dev, | ||
800 | struct exynos_drm_ippdrv *ippdrv, | ||
801 | struct drm_exynos_ipp_cmd_work *cmd_work, | ||
802 | struct drm_exynos_ipp_cmd_node *c_node) | ||
803 | { | ||
804 | struct ipp_context *ctx = get_ipp_context(dev); | ||
805 | |||
806 | cmd_work->ippdrv = ippdrv; | ||
807 | cmd_work->c_node = c_node; | ||
808 | queue_work(ctx->cmd_workq, &cmd_work->work); | ||
809 | } | ||
810 | |||
811 | static int ipp_queue_buf_with_run(struct device *dev, | ||
812 | struct drm_exynos_ipp_cmd_node *c_node, | ||
813 | struct drm_exynos_ipp_mem_node *m_node, | ||
814 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
815 | { | ||
816 | struct exynos_drm_ippdrv *ippdrv; | ||
817 | struct drm_exynos_ipp_property *property; | ||
818 | struct exynos_drm_ipp_ops *ops; | ||
819 | int ret; | ||
820 | |||
821 | ippdrv = ipp_find_drv_by_handle(qbuf->prop_id); | ||
822 | if (IS_ERR(ippdrv)) { | ||
823 | DRM_ERROR("failed to get ipp driver.\n"); | ||
824 | return -EFAULT; | ||
825 | } | ||
826 | |||
827 | ops = ippdrv->ops[qbuf->ops_id]; | ||
828 | if (!ops) { | ||
829 | DRM_ERROR("failed to get ops.\n"); | ||
830 | return -EFAULT; | ||
831 | } | ||
832 | |||
833 | property = &c_node->property; | ||
834 | |||
835 | if (c_node->state != IPP_STATE_START) { | ||
836 | DRM_DEBUG_KMS("bypass for invalid state.\n"); | ||
837 | return 0; | ||
838 | } | ||
839 | |||
840 | mutex_lock(&c_node->mem_lock); | ||
841 | if (!ipp_check_mem_list(c_node)) { | ||
842 | mutex_unlock(&c_node->mem_lock); | ||
843 | DRM_DEBUG_KMS("empty memory.\n"); | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | /* | ||
848 | * If set destination buffer and enabled clock, | ||
849 | * then m2m operations need start operations at queue_buf | ||
850 | */ | ||
851 | if (ipp_is_m2m_cmd(property->cmd)) { | ||
852 | struct drm_exynos_ipp_cmd_work *cmd_work = c_node->start_work; | ||
853 | |||
854 | cmd_work->ctrl = IPP_CTRL_PLAY; | ||
855 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
856 | } else { | ||
857 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
858 | if (ret) { | ||
859 | mutex_unlock(&c_node->mem_lock); | ||
860 | DRM_ERROR("failed to set m node.\n"); | ||
861 | return ret; | ||
862 | } | ||
863 | } | ||
864 | mutex_unlock(&c_node->mem_lock); | ||
865 | |||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static void ipp_clean_queue_buf(struct drm_device *drm_dev, | ||
870 | struct drm_exynos_ipp_cmd_node *c_node, | ||
871 | struct drm_exynos_ipp_queue_buf *qbuf) | ||
872 | { | ||
873 | struct drm_exynos_ipp_mem_node *m_node, *tm_node; | ||
874 | |||
875 | /* delete list */ | ||
876 | mutex_lock(&c_node->mem_lock); | ||
877 | list_for_each_entry_safe(m_node, tm_node, | ||
878 | &c_node->mem_list[qbuf->ops_id], list) { | ||
879 | if (m_node->buf_id == qbuf->buf_id && | ||
880 | m_node->ops_id == qbuf->ops_id) | ||
881 | ipp_put_mem_node(drm_dev, c_node, m_node); | ||
882 | } | ||
883 | mutex_unlock(&c_node->mem_lock); | ||
884 | } | ||
885 | |||
886 | int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, | ||
887 | struct drm_file *file) | ||
888 | { | ||
889 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
890 | struct device *dev = file_priv->ipp_dev; | ||
891 | struct ipp_context *ctx = get_ipp_context(dev); | ||
892 | struct drm_exynos_ipp_queue_buf *qbuf = data; | ||
893 | struct drm_exynos_ipp_cmd_node *c_node; | ||
894 | struct drm_exynos_ipp_mem_node *m_node; | ||
895 | int ret; | ||
896 | |||
897 | if (!qbuf) { | ||
898 | DRM_ERROR("invalid buf parameter.\n"); | ||
899 | return -EINVAL; | ||
900 | } | ||
901 | |||
902 | if (qbuf->ops_id >= EXYNOS_DRM_OPS_MAX) { | ||
903 | DRM_ERROR("invalid ops parameter.\n"); | ||
904 | return -EINVAL; | ||
905 | } | ||
906 | |||
907 | DRM_DEBUG_KMS("prop_id[%d]ops_id[%s]buf_id[%d]buf_type[%d]\n", | ||
908 | qbuf->prop_id, qbuf->ops_id ? "dst" : "src", | ||
909 | qbuf->buf_id, qbuf->buf_type); | ||
910 | |||
911 | /* find command node */ | ||
912 | c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, | ||
913 | qbuf->prop_id); | ||
914 | if (!c_node || c_node->filp != file) { | ||
915 | DRM_ERROR("failed to get command node.\n"); | ||
916 | return -ENODEV; | ||
917 | } | ||
918 | |||
919 | /* buffer control */ | ||
920 | switch (qbuf->buf_type) { | ||
921 | case IPP_BUF_ENQUEUE: | ||
922 | /* get memory node */ | ||
923 | m_node = ipp_get_mem_node(drm_dev, c_node, qbuf); | ||
924 | if (IS_ERR(m_node)) { | ||
925 | DRM_ERROR("failed to get m_node.\n"); | ||
926 | return PTR_ERR(m_node); | ||
927 | } | ||
928 | |||
929 | /* | ||
930 | * first step get event for destination buffer. | ||
931 | * and second step when M2M case run with destination buffer | ||
932 | * if needed. | ||
933 | */ | ||
934 | if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) { | ||
935 | /* get event for destination buffer */ | ||
936 | ret = ipp_get_event(drm_dev, c_node, qbuf); | ||
937 | if (ret) { | ||
938 | DRM_ERROR("failed to get event.\n"); | ||
939 | goto err_clean_node; | ||
940 | } | ||
941 | |||
942 | /* | ||
943 | * M2M case run play control for streaming feature. | ||
944 | * other case set address and waiting. | ||
945 | */ | ||
946 | ret = ipp_queue_buf_with_run(dev, c_node, m_node, qbuf); | ||
947 | if (ret) { | ||
948 | DRM_ERROR("failed to run command.\n"); | ||
949 | goto err_clean_node; | ||
950 | } | ||
951 | } | ||
952 | break; | ||
953 | case IPP_BUF_DEQUEUE: | ||
954 | mutex_lock(&c_node->lock); | ||
955 | |||
956 | /* put event for destination buffer */ | ||
957 | if (qbuf->ops_id == EXYNOS_DRM_OPS_DST) | ||
958 | ipp_put_event(c_node, qbuf); | ||
959 | |||
960 | ipp_clean_queue_buf(drm_dev, c_node, qbuf); | ||
961 | |||
962 | mutex_unlock(&c_node->lock); | ||
963 | break; | ||
964 | default: | ||
965 | DRM_ERROR("invalid buffer control.\n"); | ||
966 | return -EINVAL; | ||
967 | } | ||
968 | |||
969 | return 0; | ||
970 | |||
971 | err_clean_node: | ||
972 | DRM_ERROR("clean memory nodes.\n"); | ||
973 | |||
974 | ipp_clean_queue_buf(drm_dev, c_node, qbuf); | ||
975 | return ret; | ||
976 | } | ||
977 | |||
978 | static bool exynos_drm_ipp_check_valid(struct device *dev, | ||
979 | enum drm_exynos_ipp_ctrl ctrl, enum drm_exynos_ipp_state state) | ||
980 | { | ||
981 | if (ctrl != IPP_CTRL_PLAY) { | ||
982 | if (pm_runtime_suspended(dev)) { | ||
983 | DRM_ERROR("pm:runtime_suspended.\n"); | ||
984 | goto err_status; | ||
985 | } | ||
986 | } | ||
987 | |||
988 | switch (ctrl) { | ||
989 | case IPP_CTRL_PLAY: | ||
990 | if (state != IPP_STATE_IDLE) | ||
991 | goto err_status; | ||
992 | break; | ||
993 | case IPP_CTRL_STOP: | ||
994 | if (state == IPP_STATE_STOP) | ||
995 | goto err_status; | ||
996 | break; | ||
997 | case IPP_CTRL_PAUSE: | ||
998 | if (state != IPP_STATE_START) | ||
999 | goto err_status; | ||
1000 | break; | ||
1001 | case IPP_CTRL_RESUME: | ||
1002 | if (state != IPP_STATE_STOP) | ||
1003 | goto err_status; | ||
1004 | break; | ||
1005 | default: | ||
1006 | DRM_ERROR("invalid state.\n"); | ||
1007 | goto err_status; | ||
1008 | } | ||
1009 | |||
1010 | return true; | ||
1011 | |||
1012 | err_status: | ||
1013 | DRM_ERROR("invalid status:ctrl[%d]state[%d]\n", ctrl, state); | ||
1014 | return false; | ||
1015 | } | ||
1016 | |||
1017 | int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, | ||
1018 | struct drm_file *file) | ||
1019 | { | ||
1020 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
1021 | struct exynos_drm_ippdrv *ippdrv = NULL; | ||
1022 | struct device *dev = file_priv->ipp_dev; | ||
1023 | struct ipp_context *ctx = get_ipp_context(dev); | ||
1024 | struct drm_exynos_ipp_cmd_ctrl *cmd_ctrl = data; | ||
1025 | struct drm_exynos_ipp_cmd_work *cmd_work; | ||
1026 | struct drm_exynos_ipp_cmd_node *c_node; | ||
1027 | |||
1028 | if (!ctx) { | ||
1029 | DRM_ERROR("invalid context.\n"); | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | |||
1033 | if (!cmd_ctrl) { | ||
1034 | DRM_ERROR("invalid control parameter.\n"); | ||
1035 | return -EINVAL; | ||
1036 | } | ||
1037 | |||
1038 | DRM_DEBUG_KMS("ctrl[%d]prop_id[%d]\n", | ||
1039 | cmd_ctrl->ctrl, cmd_ctrl->prop_id); | ||
1040 | |||
1041 | ippdrv = ipp_find_drv_by_handle(cmd_ctrl->prop_id); | ||
1042 | if (IS_ERR(ippdrv)) { | ||
1043 | DRM_ERROR("failed to get ipp driver.\n"); | ||
1044 | return PTR_ERR(ippdrv); | ||
1045 | } | ||
1046 | |||
1047 | c_node = ipp_find_obj(&ctx->prop_idr, &ctx->prop_lock, | ||
1048 | cmd_ctrl->prop_id); | ||
1049 | if (!c_node || c_node->filp != file) { | ||
1050 | DRM_ERROR("invalid command node list.\n"); | ||
1051 | return -ENODEV; | ||
1052 | } | ||
1053 | |||
1054 | if (!exynos_drm_ipp_check_valid(ippdrv->dev, cmd_ctrl->ctrl, | ||
1055 | c_node->state)) { | ||
1056 | DRM_ERROR("invalid state.\n"); | ||
1057 | return -EINVAL; | ||
1058 | } | ||
1059 | |||
1060 | switch (cmd_ctrl->ctrl) { | ||
1061 | case IPP_CTRL_PLAY: | ||
1062 | if (pm_runtime_suspended(ippdrv->dev)) | ||
1063 | pm_runtime_get_sync(ippdrv->dev); | ||
1064 | |||
1065 | c_node->state = IPP_STATE_START; | ||
1066 | |||
1067 | cmd_work = c_node->start_work; | ||
1068 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
1069 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
1070 | break; | ||
1071 | case IPP_CTRL_STOP: | ||
1072 | cmd_work = c_node->stop_work; | ||
1073 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
1074 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
1075 | |||
1076 | if (!wait_for_completion_timeout(&c_node->stop_complete, | ||
1077 | msecs_to_jiffies(300))) { | ||
1078 | DRM_ERROR("timeout stop:prop_id[%d]\n", | ||
1079 | c_node->property.prop_id); | ||
1080 | } | ||
1081 | |||
1082 | c_node->state = IPP_STATE_STOP; | ||
1083 | ippdrv->dedicated = false; | ||
1084 | mutex_lock(&ippdrv->cmd_lock); | ||
1085 | ipp_clean_cmd_node(ctx, c_node); | ||
1086 | |||
1087 | if (list_empty(&ippdrv->cmd_list)) | ||
1088 | pm_runtime_put_sync(ippdrv->dev); | ||
1089 | mutex_unlock(&ippdrv->cmd_lock); | ||
1090 | break; | ||
1091 | case IPP_CTRL_PAUSE: | ||
1092 | cmd_work = c_node->stop_work; | ||
1093 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
1094 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
1095 | |||
1096 | if (!wait_for_completion_timeout(&c_node->stop_complete, | ||
1097 | msecs_to_jiffies(200))) { | ||
1098 | DRM_ERROR("timeout stop:prop_id[%d]\n", | ||
1099 | c_node->property.prop_id); | ||
1100 | } | ||
1101 | |||
1102 | c_node->state = IPP_STATE_STOP; | ||
1103 | break; | ||
1104 | case IPP_CTRL_RESUME: | ||
1105 | c_node->state = IPP_STATE_START; | ||
1106 | cmd_work = c_node->start_work; | ||
1107 | cmd_work->ctrl = cmd_ctrl->ctrl; | ||
1108 | ipp_handle_cmd_work(dev, ippdrv, cmd_work, c_node); | ||
1109 | break; | ||
1110 | default: | ||
1111 | DRM_ERROR("could not support this state currently.\n"); | ||
1112 | return -EINVAL; | ||
1113 | } | ||
1114 | |||
1115 | DRM_DEBUG_KMS("done ctrl[%d]prop_id[%d]\n", | ||
1116 | cmd_ctrl->ctrl, cmd_ctrl->prop_id); | ||
1117 | |||
1118 | return 0; | ||
1119 | } | ||
1120 | |||
1121 | int exynos_drm_ippnb_register(struct notifier_block *nb) | ||
1122 | { | ||
1123 | return blocking_notifier_chain_register( | ||
1124 | &exynos_drm_ippnb_list, nb); | ||
1125 | } | ||
1126 | |||
1127 | int exynos_drm_ippnb_unregister(struct notifier_block *nb) | ||
1128 | { | ||
1129 | return blocking_notifier_chain_unregister( | ||
1130 | &exynos_drm_ippnb_list, nb); | ||
1131 | } | ||
1132 | |||
1133 | int exynos_drm_ippnb_send_event(unsigned long val, void *v) | ||
1134 | { | ||
1135 | return blocking_notifier_call_chain( | ||
1136 | &exynos_drm_ippnb_list, val, v); | ||
1137 | } | ||
1138 | |||
1139 | static int ipp_set_property(struct exynos_drm_ippdrv *ippdrv, | ||
1140 | struct drm_exynos_ipp_property *property) | ||
1141 | { | ||
1142 | struct exynos_drm_ipp_ops *ops = NULL; | ||
1143 | bool swap = false; | ||
1144 | int ret, i; | ||
1145 | |||
1146 | if (!property) { | ||
1147 | DRM_ERROR("invalid property parameter.\n"); | ||
1148 | return -EINVAL; | ||
1149 | } | ||
1150 | |||
1151 | DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); | ||
1152 | |||
1153 | /* reset h/w block */ | ||
1154 | if (ippdrv->reset && | ||
1155 | ippdrv->reset(ippdrv->dev)) { | ||
1156 | return -EINVAL; | ||
1157 | } | ||
1158 | |||
1159 | /* set source,destination operations */ | ||
1160 | for_each_ipp_ops(i) { | ||
1161 | struct drm_exynos_ipp_config *config = | ||
1162 | &property->config[i]; | ||
1163 | |||
1164 | ops = ippdrv->ops[i]; | ||
1165 | if (!ops || !config) { | ||
1166 | DRM_ERROR("not support ops and config.\n"); | ||
1167 | return -EINVAL; | ||
1168 | } | ||
1169 | |||
1170 | /* set format */ | ||
1171 | if (ops->set_fmt) { | ||
1172 | ret = ops->set_fmt(ippdrv->dev, config->fmt); | ||
1173 | if (ret) | ||
1174 | return ret; | ||
1175 | } | ||
1176 | |||
1177 | /* set transform for rotation, flip */ | ||
1178 | if (ops->set_transf) { | ||
1179 | ret = ops->set_transf(ippdrv->dev, config->degree, | ||
1180 | config->flip, &swap); | ||
1181 | if (ret) | ||
1182 | return ret; | ||
1183 | } | ||
1184 | |||
1185 | /* set size */ | ||
1186 | if (ops->set_size) { | ||
1187 | ret = ops->set_size(ippdrv->dev, swap, &config->pos, | ||
1188 | &config->sz); | ||
1189 | if (ret) | ||
1190 | return ret; | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1194 | return 0; | ||
1195 | } | ||
1196 | |||
1197 | static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, | ||
1198 | struct drm_exynos_ipp_cmd_node *c_node) | ||
1199 | { | ||
1200 | struct drm_exynos_ipp_mem_node *m_node; | ||
1201 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
1202 | struct list_head *head; | ||
1203 | int ret, i; | ||
1204 | |||
1205 | DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); | ||
1206 | |||
1207 | /* store command info in ippdrv */ | ||
1208 | ippdrv->c_node = c_node; | ||
1209 | |||
1210 | mutex_lock(&c_node->mem_lock); | ||
1211 | if (!ipp_check_mem_list(c_node)) { | ||
1212 | DRM_DEBUG_KMS("empty memory.\n"); | ||
1213 | ret = -ENOMEM; | ||
1214 | goto err_unlock; | ||
1215 | } | ||
1216 | |||
1217 | /* set current property in ippdrv */ | ||
1218 | ret = ipp_set_property(ippdrv, property); | ||
1219 | if (ret) { | ||
1220 | DRM_ERROR("failed to set property.\n"); | ||
1221 | ippdrv->c_node = NULL; | ||
1222 | goto err_unlock; | ||
1223 | } | ||
1224 | |||
1225 | /* check command */ | ||
1226 | switch (property->cmd) { | ||
1227 | case IPP_CMD_M2M: | ||
1228 | for_each_ipp_ops(i) { | ||
1229 | /* source/destination memory list */ | ||
1230 | head = &c_node->mem_list[i]; | ||
1231 | |||
1232 | m_node = list_first_entry(head, | ||
1233 | struct drm_exynos_ipp_mem_node, list); | ||
1234 | |||
1235 | DRM_DEBUG_KMS("m_node[%pK]\n", m_node); | ||
1236 | |||
1237 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
1238 | if (ret) { | ||
1239 | DRM_ERROR("failed to set m node.\n"); | ||
1240 | goto err_unlock; | ||
1241 | } | ||
1242 | } | ||
1243 | break; | ||
1244 | case IPP_CMD_WB: | ||
1245 | /* destination memory list */ | ||
1246 | head = &c_node->mem_list[EXYNOS_DRM_OPS_DST]; | ||
1247 | |||
1248 | list_for_each_entry(m_node, head, list) { | ||
1249 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
1250 | if (ret) { | ||
1251 | DRM_ERROR("failed to set m node.\n"); | ||
1252 | goto err_unlock; | ||
1253 | } | ||
1254 | } | ||
1255 | break; | ||
1256 | case IPP_CMD_OUTPUT: | ||
1257 | /* source memory list */ | ||
1258 | head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; | ||
1259 | |||
1260 | list_for_each_entry(m_node, head, list) { | ||
1261 | ret = ipp_set_mem_node(ippdrv, c_node, m_node); | ||
1262 | if (ret) { | ||
1263 | DRM_ERROR("failed to set m node.\n"); | ||
1264 | goto err_unlock; | ||
1265 | } | ||
1266 | } | ||
1267 | break; | ||
1268 | default: | ||
1269 | DRM_ERROR("invalid operations.\n"); | ||
1270 | ret = -EINVAL; | ||
1271 | goto err_unlock; | ||
1272 | } | ||
1273 | mutex_unlock(&c_node->mem_lock); | ||
1274 | |||
1275 | DRM_DEBUG_KMS("cmd[%d]\n", property->cmd); | ||
1276 | |||
1277 | /* start operations */ | ||
1278 | if (ippdrv->start) { | ||
1279 | ret = ippdrv->start(ippdrv->dev, property->cmd); | ||
1280 | if (ret) { | ||
1281 | DRM_ERROR("failed to start ops.\n"); | ||
1282 | ippdrv->c_node = NULL; | ||
1283 | return ret; | ||
1284 | } | ||
1285 | } | ||
1286 | |||
1287 | return 0; | ||
1288 | |||
1289 | err_unlock: | ||
1290 | mutex_unlock(&c_node->mem_lock); | ||
1291 | ippdrv->c_node = NULL; | ||
1292 | return ret; | ||
1293 | } | ||
1294 | |||
1295 | static int ipp_stop_property(struct drm_device *drm_dev, | ||
1296 | struct exynos_drm_ippdrv *ippdrv, | ||
1297 | struct drm_exynos_ipp_cmd_node *c_node) | ||
1298 | { | ||
1299 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
1300 | int i; | ||
1301 | |||
1302 | DRM_DEBUG_KMS("prop_id[%d]\n", property->prop_id); | ||
1303 | |||
1304 | /* stop operations */ | ||
1305 | if (ippdrv->stop) | ||
1306 | ippdrv->stop(ippdrv->dev, property->cmd); | ||
1307 | |||
1308 | /* check command */ | ||
1309 | switch (property->cmd) { | ||
1310 | case IPP_CMD_M2M: | ||
1311 | for_each_ipp_ops(i) | ||
1312 | ipp_clean_mem_nodes(drm_dev, c_node, i); | ||
1313 | break; | ||
1314 | case IPP_CMD_WB: | ||
1315 | ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_DST); | ||
1316 | break; | ||
1317 | case IPP_CMD_OUTPUT: | ||
1318 | ipp_clean_mem_nodes(drm_dev, c_node, EXYNOS_DRM_OPS_SRC); | ||
1319 | break; | ||
1320 | default: | ||
1321 | DRM_ERROR("invalid operations.\n"); | ||
1322 | return -EINVAL; | ||
1323 | } | ||
1324 | |||
1325 | return 0; | ||
1326 | } | ||
1327 | |||
1328 | void ipp_sched_cmd(struct work_struct *work) | ||
1329 | { | ||
1330 | struct drm_exynos_ipp_cmd_work *cmd_work = | ||
1331 | container_of(work, struct drm_exynos_ipp_cmd_work, work); | ||
1332 | struct exynos_drm_ippdrv *ippdrv; | ||
1333 | struct drm_exynos_ipp_cmd_node *c_node; | ||
1334 | struct drm_exynos_ipp_property *property; | ||
1335 | int ret; | ||
1336 | |||
1337 | ippdrv = cmd_work->ippdrv; | ||
1338 | if (!ippdrv) { | ||
1339 | DRM_ERROR("invalid ippdrv list.\n"); | ||
1340 | return; | ||
1341 | } | ||
1342 | |||
1343 | c_node = cmd_work->c_node; | ||
1344 | if (!c_node) { | ||
1345 | DRM_ERROR("invalid command node list.\n"); | ||
1346 | return; | ||
1347 | } | ||
1348 | |||
1349 | mutex_lock(&c_node->lock); | ||
1350 | |||
1351 | property = &c_node->property; | ||
1352 | |||
1353 | switch (cmd_work->ctrl) { | ||
1354 | case IPP_CTRL_PLAY: | ||
1355 | case IPP_CTRL_RESUME: | ||
1356 | ret = ipp_start_property(ippdrv, c_node); | ||
1357 | if (ret) { | ||
1358 | DRM_ERROR("failed to start property:prop_id[%d]\n", | ||
1359 | c_node->property.prop_id); | ||
1360 | goto err_unlock; | ||
1361 | } | ||
1362 | |||
1363 | /* | ||
1364 | * M2M case supports wait_completion of transfer. | ||
1365 | * because M2M case supports single unit operation | ||
1366 | * with multiple queue. | ||
1367 | * M2M need to wait completion of data transfer. | ||
1368 | */ | ||
1369 | if (ipp_is_m2m_cmd(property->cmd)) { | ||
1370 | if (!wait_for_completion_timeout | ||
1371 | (&c_node->start_complete, msecs_to_jiffies(200))) { | ||
1372 | DRM_ERROR("timeout event:prop_id[%d]\n", | ||
1373 | c_node->property.prop_id); | ||
1374 | goto err_unlock; | ||
1375 | } | ||
1376 | } | ||
1377 | break; | ||
1378 | case IPP_CTRL_STOP: | ||
1379 | case IPP_CTRL_PAUSE: | ||
1380 | ret = ipp_stop_property(ippdrv->drm_dev, ippdrv, | ||
1381 | c_node); | ||
1382 | if (ret) { | ||
1383 | DRM_ERROR("failed to stop property.\n"); | ||
1384 | goto err_unlock; | ||
1385 | } | ||
1386 | |||
1387 | complete(&c_node->stop_complete); | ||
1388 | break; | ||
1389 | default: | ||
1390 | DRM_ERROR("unknown control type\n"); | ||
1391 | break; | ||
1392 | } | ||
1393 | |||
1394 | DRM_DEBUG_KMS("ctrl[%d] done.\n", cmd_work->ctrl); | ||
1395 | |||
1396 | err_unlock: | ||
1397 | mutex_unlock(&c_node->lock); | ||
1398 | } | ||
1399 | |||
1400 | static int ipp_send_event(struct exynos_drm_ippdrv *ippdrv, | ||
1401 | struct drm_exynos_ipp_cmd_node *c_node, int *buf_id) | ||
1402 | { | ||
1403 | struct drm_device *drm_dev = ippdrv->drm_dev; | ||
1404 | struct drm_exynos_ipp_property *property = &c_node->property; | ||
1405 | struct drm_exynos_ipp_mem_node *m_node; | ||
1406 | struct drm_exynos_ipp_queue_buf qbuf; | ||
1407 | struct drm_exynos_ipp_send_event *e; | ||
1408 | struct list_head *head; | ||
1409 | struct timeval now; | ||
1410 | u32 tbuf_id[EXYNOS_DRM_OPS_MAX] = {0, }; | ||
1411 | int ret, i; | ||
1412 | |||
1413 | for_each_ipp_ops(i) | ||
1414 | DRM_DEBUG_KMS("%s buf_id[%d]\n", i ? "dst" : "src", buf_id[i]); | ||
1415 | |||
1416 | if (!drm_dev) { | ||
1417 | DRM_ERROR("failed to get drm_dev.\n"); | ||
1418 | return -EINVAL; | ||
1419 | } | ||
1420 | |||
1421 | if (!property) { | ||
1422 | DRM_ERROR("failed to get property.\n"); | ||
1423 | return -EINVAL; | ||
1424 | } | ||
1425 | |||
1426 | mutex_lock(&c_node->event_lock); | ||
1427 | if (list_empty(&c_node->event_list)) { | ||
1428 | DRM_DEBUG_KMS("event list is empty.\n"); | ||
1429 | ret = 0; | ||
1430 | goto err_event_unlock; | ||
1431 | } | ||
1432 | |||
1433 | mutex_lock(&c_node->mem_lock); | ||
1434 | if (!ipp_check_mem_list(c_node)) { | ||
1435 | DRM_DEBUG_KMS("empty memory.\n"); | ||
1436 | ret = 0; | ||
1437 | goto err_mem_unlock; | ||
1438 | } | ||
1439 | |||
1440 | /* check command */ | ||
1441 | switch (property->cmd) { | ||
1442 | case IPP_CMD_M2M: | ||
1443 | for_each_ipp_ops(i) { | ||
1444 | /* source/destination memory list */ | ||
1445 | head = &c_node->mem_list[i]; | ||
1446 | |||
1447 | m_node = list_first_entry(head, | ||
1448 | struct drm_exynos_ipp_mem_node, list); | ||
1449 | |||
1450 | tbuf_id[i] = m_node->buf_id; | ||
1451 | DRM_DEBUG_KMS("%s buf_id[%d]\n", | ||
1452 | i ? "dst" : "src", tbuf_id[i]); | ||
1453 | |||
1454 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
1455 | if (ret) | ||
1456 | DRM_ERROR("failed to put m_node.\n"); | ||
1457 | } | ||
1458 | break; | ||
1459 | case IPP_CMD_WB: | ||
1460 | /* clear buf for finding */ | ||
1461 | memset(&qbuf, 0x0, sizeof(qbuf)); | ||
1462 | qbuf.ops_id = EXYNOS_DRM_OPS_DST; | ||
1463 | qbuf.buf_id = buf_id[EXYNOS_DRM_OPS_DST]; | ||
1464 | |||
1465 | /* get memory node entry */ | ||
1466 | m_node = ipp_find_mem_node(c_node, &qbuf); | ||
1467 | if (!m_node) { | ||
1468 | DRM_ERROR("empty memory node.\n"); | ||
1469 | ret = -ENOMEM; | ||
1470 | goto err_mem_unlock; | ||
1471 | } | ||
1472 | |||
1473 | tbuf_id[EXYNOS_DRM_OPS_DST] = m_node->buf_id; | ||
1474 | |||
1475 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
1476 | if (ret) | ||
1477 | DRM_ERROR("failed to put m_node.\n"); | ||
1478 | break; | ||
1479 | case IPP_CMD_OUTPUT: | ||
1480 | /* source memory list */ | ||
1481 | head = &c_node->mem_list[EXYNOS_DRM_OPS_SRC]; | ||
1482 | |||
1483 | m_node = list_first_entry(head, | ||
1484 | struct drm_exynos_ipp_mem_node, list); | ||
1485 | |||
1486 | tbuf_id[EXYNOS_DRM_OPS_SRC] = m_node->buf_id; | ||
1487 | |||
1488 | ret = ipp_put_mem_node(drm_dev, c_node, m_node); | ||
1489 | if (ret) | ||
1490 | DRM_ERROR("failed to put m_node.\n"); | ||
1491 | break; | ||
1492 | default: | ||
1493 | DRM_ERROR("invalid operations.\n"); | ||
1494 | ret = -EINVAL; | ||
1495 | goto err_mem_unlock; | ||
1496 | } | ||
1497 | mutex_unlock(&c_node->mem_lock); | ||
1498 | |||
1499 | if (tbuf_id[EXYNOS_DRM_OPS_DST] != buf_id[EXYNOS_DRM_OPS_DST]) | ||
1500 | DRM_ERROR("failed to match buf_id[%d %d]prop_id[%d]\n", | ||
1501 | tbuf_id[1], buf_id[1], property->prop_id); | ||
1502 | |||
1503 | /* | ||
1504 | * command node have event list of destination buffer | ||
1505 | * If destination buffer enqueue to mem list, | ||
1506 | * then we make event and link to event list tail. | ||
1507 | * so, we get first event for first enqueued buffer. | ||
1508 | */ | ||
1509 | e = list_first_entry(&c_node->event_list, | ||
1510 | struct drm_exynos_ipp_send_event, base.link); | ||
1511 | |||
1512 | do_gettimeofday(&now); | ||
1513 | DRM_DEBUG_KMS("tv_sec[%ld]tv_usec[%ld]\n", now.tv_sec, now.tv_usec); | ||
1514 | e->event.tv_sec = now.tv_sec; | ||
1515 | e->event.tv_usec = now.tv_usec; | ||
1516 | e->event.prop_id = property->prop_id; | ||
1517 | |||
1518 | /* set buffer id about source destination */ | ||
1519 | for_each_ipp_ops(i) | ||
1520 | e->event.buf_id[i] = tbuf_id[i]; | ||
1521 | |||
1522 | drm_send_event(drm_dev, &e->base); | ||
1523 | mutex_unlock(&c_node->event_lock); | ||
1524 | |||
1525 | DRM_DEBUG_KMS("done cmd[%d]prop_id[%d]buf_id[%d]\n", | ||
1526 | property->cmd, property->prop_id, tbuf_id[EXYNOS_DRM_OPS_DST]); | ||
1527 | |||
1528 | return 0; | ||
1529 | |||
1530 | err_mem_unlock: | ||
1531 | mutex_unlock(&c_node->mem_lock); | ||
1532 | err_event_unlock: | ||
1533 | mutex_unlock(&c_node->event_lock); | ||
1534 | return ret; | ||
1535 | } | ||
1536 | |||
1537 | void ipp_sched_event(struct work_struct *work) | ||
1538 | { | ||
1539 | struct drm_exynos_ipp_event_work *event_work = | ||
1540 | container_of(work, struct drm_exynos_ipp_event_work, work); | ||
1541 | struct exynos_drm_ippdrv *ippdrv; | ||
1542 | struct drm_exynos_ipp_cmd_node *c_node; | ||
1543 | int ret; | ||
1544 | |||
1545 | if (!event_work) { | ||
1546 | DRM_ERROR("failed to get event_work.\n"); | ||
1547 | return; | ||
1548 | } | ||
1549 | |||
1550 | DRM_DEBUG_KMS("buf_id[%d]\n", event_work->buf_id[EXYNOS_DRM_OPS_DST]); | ||
1551 | |||
1552 | ippdrv = event_work->ippdrv; | ||
1553 | if (!ippdrv) { | ||
1554 | DRM_ERROR("failed to get ipp driver.\n"); | ||
1555 | return; | ||
1556 | } | ||
1557 | |||
1558 | c_node = ippdrv->c_node; | ||
1559 | if (!c_node) { | ||
1560 | DRM_ERROR("failed to get command node.\n"); | ||
1561 | return; | ||
1562 | } | ||
1563 | |||
1564 | /* | ||
1565 | * IPP supports command thread, event thread synchronization. | ||
1566 | * If IPP close immediately from user land, then IPP make | ||
1567 | * synchronization with command thread, so make complete event. | ||
1568 | * or going out operations. | ||
1569 | */ | ||
1570 | if (c_node->state != IPP_STATE_START) { | ||
1571 | DRM_DEBUG_KMS("bypass state[%d]prop_id[%d]\n", | ||
1572 | c_node->state, c_node->property.prop_id); | ||
1573 | goto err_completion; | ||
1574 | } | ||
1575 | |||
1576 | ret = ipp_send_event(ippdrv, c_node, event_work->buf_id); | ||
1577 | if (ret) { | ||
1578 | DRM_ERROR("failed to send event.\n"); | ||
1579 | goto err_completion; | ||
1580 | } | ||
1581 | |||
1582 | err_completion: | ||
1583 | if (ipp_is_m2m_cmd(c_node->property.cmd)) | ||
1584 | complete(&c_node->start_complete); | ||
1585 | } | ||
1586 | |||
1587 | static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) | ||
1588 | { | ||
1589 | struct ipp_context *ctx = get_ipp_context(dev); | ||
1590 | struct exynos_drm_ippdrv *ippdrv; | ||
1591 | int ret, count = 0; | ||
1592 | |||
1593 | /* get ipp driver entry */ | ||
1594 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
1595 | ippdrv->drm_dev = drm_dev; | ||
1596 | |||
1597 | ret = ipp_create_id(&ctx->ipp_idr, &ctx->ipp_lock, ippdrv); | ||
1598 | if (ret < 0) { | ||
1599 | DRM_ERROR("failed to create id.\n"); | ||
1600 | goto err; | ||
1601 | } | ||
1602 | ippdrv->prop_list.ipp_id = ret; | ||
1603 | |||
1604 | DRM_DEBUG_KMS("count[%d]ippdrv[%pK]ipp_id[%d]\n", | ||
1605 | count++, ippdrv, ret); | ||
1606 | |||
1607 | /* store parent device for node */ | ||
1608 | ippdrv->parent_dev = dev; | ||
1609 | |||
1610 | /* store event work queue and handler */ | ||
1611 | ippdrv->event_workq = ctx->event_workq; | ||
1612 | ippdrv->sched_event = ipp_sched_event; | ||
1613 | INIT_LIST_HEAD(&ippdrv->cmd_list); | ||
1614 | mutex_init(&ippdrv->cmd_lock); | ||
1615 | |||
1616 | ret = drm_iommu_attach_device(drm_dev, ippdrv->dev); | ||
1617 | if (ret) { | ||
1618 | DRM_ERROR("failed to activate iommu\n"); | ||
1619 | goto err; | ||
1620 | } | ||
1621 | } | ||
1622 | |||
1623 | return 0; | ||
1624 | |||
1625 | err: | ||
1626 | /* get ipp driver entry */ | ||
1627 | list_for_each_entry_continue_reverse(ippdrv, &exynos_drm_ippdrv_list, | ||
1628 | drv_list) { | ||
1629 | drm_iommu_detach_device(drm_dev, ippdrv->dev); | ||
1630 | |||
1631 | ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock, | ||
1632 | ippdrv->prop_list.ipp_id); | ||
1633 | } | ||
1634 | |||
1635 | return ret; | ||
1636 | } | ||
1637 | |||
1638 | static void ipp_subdrv_remove(struct drm_device *drm_dev, struct device *dev) | ||
1639 | { | ||
1640 | struct exynos_drm_ippdrv *ippdrv, *t; | ||
1641 | struct ipp_context *ctx = get_ipp_context(dev); | ||
1642 | |||
1643 | /* get ipp driver entry */ | ||
1644 | list_for_each_entry_safe(ippdrv, t, &exynos_drm_ippdrv_list, drv_list) { | ||
1645 | drm_iommu_detach_device(drm_dev, ippdrv->dev); | ||
1646 | |||
1647 | ipp_remove_id(&ctx->ipp_idr, &ctx->ipp_lock, | ||
1648 | ippdrv->prop_list.ipp_id); | ||
1649 | |||
1650 | ippdrv->drm_dev = NULL; | ||
1651 | exynos_drm_ippdrv_unregister(ippdrv); | ||
1652 | } | ||
1653 | } | ||
1654 | |||
1655 | static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, | ||
1656 | struct drm_file *file) | ||
1657 | { | ||
1658 | struct drm_exynos_file_private *file_priv = file->driver_priv; | ||
1659 | |||
1660 | file_priv->ipp_dev = dev; | ||
1661 | |||
1662 | DRM_DEBUG_KMS("done priv[%pK]\n", dev); | ||
1663 | |||
1664 | return 0; | ||
1665 | } | ||
1666 | |||
1667 | static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, | ||
1668 | struct drm_file *file) | ||
1669 | { | ||
1670 | struct exynos_drm_ippdrv *ippdrv = NULL; | ||
1671 | struct ipp_context *ctx = get_ipp_context(dev); | ||
1672 | struct drm_exynos_ipp_cmd_node *c_node, *tc_node; | ||
1673 | int count = 0; | ||
1674 | |||
1675 | list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { | ||
1676 | mutex_lock(&ippdrv->cmd_lock); | ||
1677 | list_for_each_entry_safe(c_node, tc_node, | ||
1678 | &ippdrv->cmd_list, list) { | ||
1679 | DRM_DEBUG_KMS("count[%d]ippdrv[%pK]\n", | ||
1680 | count++, ippdrv); | ||
1681 | |||
1682 | if (c_node->filp == file) { | ||
1683 | /* | ||
1684 | * userland goto unnormal state. process killed. | ||
1685 | * and close the file. | ||
1686 | * so, IPP didn't called stop cmd ctrl. | ||
1687 | * so, we are make stop operation in this state. | ||
1688 | */ | ||
1689 | if (c_node->state == IPP_STATE_START) { | ||
1690 | ipp_stop_property(drm_dev, ippdrv, | ||
1691 | c_node); | ||
1692 | c_node->state = IPP_STATE_STOP; | ||
1693 | } | ||
1694 | |||
1695 | ippdrv->dedicated = false; | ||
1696 | ipp_clean_cmd_node(ctx, c_node); | ||
1697 | if (list_empty(&ippdrv->cmd_list)) | ||
1698 | pm_runtime_put_sync(ippdrv->dev); | ||
1699 | } | ||
1700 | } | ||
1701 | mutex_unlock(&ippdrv->cmd_lock); | ||
1702 | } | ||
1703 | |||
1704 | return; | ||
1705 | } | ||
1706 | |||
1707 | static int ipp_probe(struct platform_device *pdev) | ||
1708 | { | ||
1709 | struct device *dev = &pdev->dev; | ||
1710 | struct ipp_context *ctx; | ||
1711 | struct exynos_drm_subdrv *subdrv; | ||
1712 | int ret; | ||
1713 | |||
1714 | ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); | ||
1715 | if (!ctx) | ||
1716 | return -ENOMEM; | ||
1717 | |||
1718 | mutex_init(&ctx->ipp_lock); | ||
1719 | mutex_init(&ctx->prop_lock); | ||
1720 | |||
1721 | idr_init(&ctx->ipp_idr); | ||
1722 | idr_init(&ctx->prop_idr); | ||
1723 | |||
1724 | /* | ||
1725 | * create single thread for ipp event | ||
1726 | * IPP supports event thread for IPP drivers. | ||
1727 | * IPP driver send event_work to this thread. | ||
1728 | * and IPP event thread send event to user process. | ||
1729 | */ | ||
1730 | ctx->event_workq = create_singlethread_workqueue("ipp_event"); | ||
1731 | if (!ctx->event_workq) { | ||
1732 | dev_err(dev, "failed to create event workqueue\n"); | ||
1733 | return -EINVAL; | ||
1734 | } | ||
1735 | |||
1736 | /* | ||
1737 | * create single thread for ipp command | ||
1738 | * IPP supports command thread for user process. | ||
1739 | * user process make command node using set property ioctl. | ||
1740 | * and make start_work and send this work to command thread. | ||
1741 | * and then this command thread start property. | ||
1742 | */ | ||
1743 | ctx->cmd_workq = create_singlethread_workqueue("ipp_cmd"); | ||
1744 | if (!ctx->cmd_workq) { | ||
1745 | dev_err(dev, "failed to create cmd workqueue\n"); | ||
1746 | ret = -EINVAL; | ||
1747 | goto err_event_workq; | ||
1748 | } | ||
1749 | |||
1750 | /* set sub driver informations */ | ||
1751 | subdrv = &ctx->subdrv; | ||
1752 | subdrv->dev = dev; | ||
1753 | subdrv->probe = ipp_subdrv_probe; | ||
1754 | subdrv->remove = ipp_subdrv_remove; | ||
1755 | subdrv->open = ipp_subdrv_open; | ||
1756 | subdrv->close = ipp_subdrv_close; | ||
1757 | |||
1758 | platform_set_drvdata(pdev, ctx); | ||
1759 | |||
1760 | ret = exynos_drm_subdrv_register(subdrv); | ||
1761 | if (ret < 0) { | ||
1762 | DRM_ERROR("failed to register drm ipp device.\n"); | ||
1763 | goto err_cmd_workq; | ||
1764 | } | ||
1765 | |||
1766 | dev_info(dev, "drm ipp registered successfully.\n"); | ||
1767 | |||
1768 | return 0; | ||
1769 | |||
1770 | err_cmd_workq: | ||
1771 | destroy_workqueue(ctx->cmd_workq); | ||
1772 | err_event_workq: | ||
1773 | destroy_workqueue(ctx->event_workq); | ||
1774 | return ret; | ||
1775 | } | ||
1776 | |||
1777 | static int ipp_remove(struct platform_device *pdev) | ||
1778 | { | ||
1779 | struct ipp_context *ctx = platform_get_drvdata(pdev); | ||
1780 | |||
1781 | /* unregister sub driver */ | ||
1782 | exynos_drm_subdrv_unregister(&ctx->subdrv); | ||
1783 | |||
1784 | /* remove,destroy ipp idr */ | ||
1785 | idr_destroy(&ctx->ipp_idr); | ||
1786 | idr_destroy(&ctx->prop_idr); | ||
1787 | |||
1788 | mutex_destroy(&ctx->ipp_lock); | ||
1789 | mutex_destroy(&ctx->prop_lock); | ||
1790 | |||
1791 | /* destroy command, event work queue */ | ||
1792 | destroy_workqueue(ctx->cmd_workq); | ||
1793 | destroy_workqueue(ctx->event_workq); | ||
1794 | |||
1795 | return 0; | ||
1796 | } | ||
1797 | |||
1798 | struct platform_driver ipp_driver = { | ||
1799 | .probe = ipp_probe, | ||
1800 | .remove = ipp_remove, | ||
1801 | .driver = { | ||
1802 | .name = "exynos-drm-ipp", | ||
1803 | .owner = THIS_MODULE, | ||
1804 | }, | ||
1805 | }; | ||
1806 | |||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h deleted file mode 100644 index 2a61547a39d0..000000000000 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.h +++ /dev/null | |||
@@ -1,252 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * Authors: | ||
5 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
6 | * Jinyoung Jeon <jy0.jeon@samsung.com> | ||
7 | * Sangmin Lee <lsmin.lee@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | |||
15 | #ifndef _EXYNOS_DRM_IPP_H_ | ||
16 | #define _EXYNOS_DRM_IPP_H_ | ||
17 | |||
18 | #define for_each_ipp_ops(pos) \ | ||
19 | for (pos = 0; pos < EXYNOS_DRM_OPS_MAX; pos++) | ||
20 | #define for_each_ipp_planar(pos) \ | ||
21 | for (pos = 0; pos < EXYNOS_DRM_PLANAR_MAX; pos++) | ||
22 | |||
23 | #define IPP_GET_LCD_WIDTH _IOR('F', 302, int) | ||
24 | #define IPP_GET_LCD_HEIGHT _IOR('F', 303, int) | ||
25 | #define IPP_SET_WRITEBACK _IOW('F', 304, u32) | ||
26 | |||
27 | /* definition of state */ | ||
28 | enum drm_exynos_ipp_state { | ||
29 | IPP_STATE_IDLE, | ||
30 | IPP_STATE_START, | ||
31 | IPP_STATE_STOP, | ||
32 | }; | ||
33 | |||
34 | /* | ||
35 | * A structure of command work information. | ||
36 | * @work: work structure. | ||
37 | * @ippdrv: current work ippdrv. | ||
38 | * @c_node: command node information. | ||
39 | * @ctrl: command control. | ||
40 | */ | ||
41 | struct drm_exynos_ipp_cmd_work { | ||
42 | struct work_struct work; | ||
43 | struct exynos_drm_ippdrv *ippdrv; | ||
44 | struct drm_exynos_ipp_cmd_node *c_node; | ||
45 | enum drm_exynos_ipp_ctrl ctrl; | ||
46 | }; | ||
47 | |||
48 | /* | ||
49 | * A structure of command node. | ||
50 | * | ||
51 | * @list: list head to command queue information. | ||
52 | * @event_list: list head of event. | ||
53 | * @mem_list: list head to source,destination memory queue information. | ||
54 | * @lock: lock for synchronization of access to ioctl. | ||
55 | * @mem_lock: lock for synchronization of access to memory nodes. | ||
56 | * @event_lock: lock for synchronization of access to scheduled event. | ||
57 | * @start_complete: completion of start of command. | ||
58 | * @stop_complete: completion of stop of command. | ||
59 | * @property: property information. | ||
60 | * @start_work: start command work structure. | ||
61 | * @stop_work: stop command work structure. | ||
62 | * @event_work: event work structure. | ||
63 | * @state: state of command node. | ||
64 | * @filp: associated file pointer. | ||
65 | */ | ||
66 | struct drm_exynos_ipp_cmd_node { | ||
67 | struct list_head list; | ||
68 | struct list_head event_list; | ||
69 | struct list_head mem_list[EXYNOS_DRM_OPS_MAX]; | ||
70 | struct mutex lock; | ||
71 | struct mutex mem_lock; | ||
72 | struct mutex event_lock; | ||
73 | struct completion start_complete; | ||
74 | struct completion stop_complete; | ||
75 | struct drm_exynos_ipp_property property; | ||
76 | struct drm_exynos_ipp_cmd_work *start_work; | ||
77 | struct drm_exynos_ipp_cmd_work *stop_work; | ||
78 | struct drm_exynos_ipp_event_work *event_work; | ||
79 | enum drm_exynos_ipp_state state; | ||
80 | struct drm_file *filp; | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * A structure of buffer information. | ||
85 | * | ||
86 | * @handles: Y, Cb, Cr each gem object handle. | ||
87 | * @base: Y, Cb, Cr each planar address. | ||
88 | */ | ||
89 | struct drm_exynos_ipp_buf_info { | ||
90 | unsigned long handles[EXYNOS_DRM_PLANAR_MAX]; | ||
91 | dma_addr_t base[EXYNOS_DRM_PLANAR_MAX]; | ||
92 | }; | ||
93 | |||
94 | /* | ||
95 | * A structure of wb setting information. | ||
96 | * | ||
97 | * @enable: enable flag for wb. | ||
98 | * @refresh: HZ of the refresh rate. | ||
99 | */ | ||
100 | struct drm_exynos_ipp_set_wb { | ||
101 | __u32 enable; | ||
102 | __u32 refresh; | ||
103 | }; | ||
104 | |||
105 | /* | ||
106 | * A structure of event work information. | ||
107 | * | ||
108 | * @work: work structure. | ||
109 | * @ippdrv: current work ippdrv. | ||
110 | * @buf_id: id of src, dst buffer. | ||
111 | */ | ||
112 | struct drm_exynos_ipp_event_work { | ||
113 | struct work_struct work; | ||
114 | struct exynos_drm_ippdrv *ippdrv; | ||
115 | u32 buf_id[EXYNOS_DRM_OPS_MAX]; | ||
116 | }; | ||
117 | |||
118 | /* | ||
119 | * A structure of source,destination operations. | ||
120 | * | ||
121 | * @set_fmt: set format of image. | ||
122 | * @set_transf: set transform(rotations, flip). | ||
123 | * @set_size: set size of region. | ||
124 | * @set_addr: set address for dma. | ||
125 | */ | ||
126 | struct exynos_drm_ipp_ops { | ||
127 | int (*set_fmt)(struct device *dev, u32 fmt); | ||
128 | int (*set_transf)(struct device *dev, | ||
129 | enum drm_exynos_degree degree, | ||
130 | enum drm_exynos_flip flip, bool *swap); | ||
131 | int (*set_size)(struct device *dev, int swap, | ||
132 | struct drm_exynos_pos *pos, struct drm_exynos_sz *sz); | ||
133 | int (*set_addr)(struct device *dev, | ||
134 | struct drm_exynos_ipp_buf_info *buf_info, u32 buf_id, | ||
135 | enum drm_exynos_ipp_buf_type buf_type); | ||
136 | }; | ||
137 | |||
138 | /* | ||
139 | * A structure of ipp driver. | ||
140 | * | ||
141 | * @drv_list: list head for registed sub driver information. | ||
142 | * @parent_dev: parent device information. | ||
143 | * @dev: platform device. | ||
144 | * @drm_dev: drm device. | ||
145 | * @dedicated: dedicated ipp device. | ||
146 | * @ops: source, destination operations. | ||
147 | * @event_workq: event work queue. | ||
148 | * @c_node: current command information. | ||
149 | * @cmd_list: list head for command information. | ||
150 | * @cmd_lock: lock for synchronization of access to cmd_list. | ||
151 | * @prop_list: property informations of current ipp driver. | ||
152 | * @check_property: check property about format, size, buffer. | ||
153 | * @reset: reset ipp block. | ||
154 | * @start: ipp each device start. | ||
155 | * @stop: ipp each device stop. | ||
156 | * @sched_event: work schedule handler. | ||
157 | */ | ||
158 | struct exynos_drm_ippdrv { | ||
159 | struct list_head drv_list; | ||
160 | struct device *parent_dev; | ||
161 | struct device *dev; | ||
162 | struct drm_device *drm_dev; | ||
163 | bool dedicated; | ||
164 | struct exynos_drm_ipp_ops *ops[EXYNOS_DRM_OPS_MAX]; | ||
165 | struct workqueue_struct *event_workq; | ||
166 | struct drm_exynos_ipp_cmd_node *c_node; | ||
167 | struct list_head cmd_list; | ||
168 | struct mutex cmd_lock; | ||
169 | struct drm_exynos_ipp_prop_list prop_list; | ||
170 | |||
171 | int (*check_property)(struct device *dev, | ||
172 | struct drm_exynos_ipp_property *property); | ||
173 | int (*reset)(struct device *dev); | ||
174 | int (*start)(struct device *dev, enum drm_exynos_ipp_cmd cmd); | ||
175 | void (*stop)(struct device *dev, enum drm_exynos_ipp_cmd cmd); | ||
176 | void (*sched_event)(struct work_struct *work); | ||
177 | }; | ||
178 | |||
179 | #ifdef CONFIG_DRM_EXYNOS_IPP | ||
180 | extern int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv); | ||
181 | extern int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv); | ||
182 | extern int exynos_drm_ipp_get_property(struct drm_device *drm_dev, void *data, | ||
183 | struct drm_file *file); | ||
184 | extern int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, | ||
185 | struct drm_file *file); | ||
186 | extern int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, void *data, | ||
187 | struct drm_file *file); | ||
188 | extern int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, void *data, | ||
189 | struct drm_file *file); | ||
190 | extern int exynos_drm_ippnb_register(struct notifier_block *nb); | ||
191 | extern int exynos_drm_ippnb_unregister(struct notifier_block *nb); | ||
192 | extern int exynos_drm_ippnb_send_event(unsigned long val, void *v); | ||
193 | extern void ipp_sched_cmd(struct work_struct *work); | ||
194 | extern void ipp_sched_event(struct work_struct *work); | ||
195 | |||
196 | #else | ||
197 | static inline int exynos_drm_ippdrv_register(struct exynos_drm_ippdrv *ippdrv) | ||
198 | { | ||
199 | return -ENODEV; | ||
200 | } | ||
201 | |||
202 | static inline int exynos_drm_ippdrv_unregister(struct exynos_drm_ippdrv *ippdrv) | ||
203 | { | ||
204 | return -ENODEV; | ||
205 | } | ||
206 | |||
207 | static inline int exynos_drm_ipp_get_property(struct drm_device *drm_dev, | ||
208 | void *data, | ||
209 | struct drm_file *file_priv) | ||
210 | { | ||
211 | return -ENOTTY; | ||
212 | } | ||
213 | |||
214 | static inline int exynos_drm_ipp_set_property(struct drm_device *drm_dev, | ||
215 | void *data, | ||
216 | struct drm_file *file_priv) | ||
217 | { | ||
218 | return -ENOTTY; | ||
219 | } | ||
220 | |||
221 | static inline int exynos_drm_ipp_queue_buf(struct drm_device *drm_dev, | ||
222 | void *data, | ||
223 | struct drm_file *file) | ||
224 | { | ||
225 | return -ENOTTY; | ||
226 | } | ||
227 | |||
228 | static inline int exynos_drm_ipp_cmd_ctrl(struct drm_device *drm_dev, | ||
229 | void *data, | ||
230 | struct drm_file *file) | ||
231 | { | ||
232 | return -ENOTTY; | ||
233 | } | ||
234 | |||
235 | static inline int exynos_drm_ippnb_register(struct notifier_block *nb) | ||
236 | { | ||
237 | return -ENODEV; | ||
238 | } | ||
239 | |||
240 | static inline int exynos_drm_ippnb_unregister(struct notifier_block *nb) | ||
241 | { | ||
242 | return -ENODEV; | ||
243 | } | ||
244 | |||
245 | static inline int exynos_drm_ippnb_send_event(unsigned long val, void *v) | ||
246 | { | ||
247 | return -ENOTTY; | ||
248 | } | ||
249 | #endif | ||
250 | |||
251 | #endif /* _EXYNOS_DRM_IPP_H_ */ | ||
252 | |||
diff --git a/drivers/gpu/drm/exynos/regs-decon5433.h b/drivers/gpu/drm/exynos/regs-decon5433.h new file mode 100644 index 000000000000..19ad9e47945e --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-decon5433.h | |||
@@ -0,0 +1,209 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Samsung Electronics Co.Ltd | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundationr | ||
7 | */ | ||
8 | |||
9 | #ifndef EXYNOS_REGS_DECON5433_H | ||
10 | #define EXYNOS_REGS_DECON5433_H | ||
11 | |||
12 | /* Exynos543X DECON */ | ||
13 | #define DECON_VIDCON0 0x0000 | ||
14 | #define DECON_VIDOUTCON0 0x0010 | ||
15 | #define DECON_WINCONx(n) (0x0020 + ((n) * 4)) | ||
16 | #define DECON_VIDOSDxH(n) (0x0080 + ((n) * 4)) | ||
17 | #define DECON_SHADOWCON 0x00A0 | ||
18 | #define DECON_VIDOSDxA(n) (0x00B0 + ((n) * 0x20)) | ||
19 | #define DECON_VIDOSDxB(n) (0x00B4 + ((n) * 0x20)) | ||
20 | #define DECON_VIDOSDxC(n) (0x00B8 + ((n) * 0x20)) | ||
21 | #define DECON_VIDOSDxD(n) (0x00BC + ((n) * 0x20)) | ||
22 | #define DECON_VIDOSDxE(n) (0x00C0 + ((n) * 0x20)) | ||
23 | #define DECON_VIDW0xADD0B0(n) (0x0150 + ((n) * 0x10)) | ||
24 | #define DECON_VIDW0xADD0B1(n) (0x0154 + ((n) * 0x10)) | ||
25 | #define DECON_VIDW0xADD0B2(n) (0x0158 + ((n) * 0x10)) | ||
26 | #define DECON_VIDW0xADD1B0(n) (0x01A0 + ((n) * 0x10)) | ||
27 | #define DECON_VIDW0xADD1B1(n) (0x01A4 + ((n) * 0x10)) | ||
28 | #define DECON_VIDW0xADD1B2(n) (0x01A8 + ((n) * 0x10)) | ||
29 | #define DECON_VIDW0xADD2(n) (0x0200 + ((n) * 4)) | ||
30 | #define DECON_LOCALxSIZE(n) (0x0214 + ((n) * 4)) | ||
31 | #define DECON_VIDINTCON0 0x0220 | ||
32 | #define DECON_VIDINTCON1 0x0224 | ||
33 | #define DECON_WxKEYCON0(n) (0x0230 + ((n - 1) * 8)) | ||
34 | #define DECON_WxKEYCON1(n) (0x0234 + ((n - 1) * 8)) | ||
35 | #define DECON_WxKEYALPHA(n) (0x0250 + ((n - 1) * 4)) | ||
36 | #define DECON_WINxMAP(n) (0x0270 + ((n) * 4)) | ||
37 | #define DECON_QOSLUT07_00 0x02C0 | ||
38 | #define DECON_QOSLUT15_08 0x02C4 | ||
39 | #define DECON_QOSCTRL 0x02C8 | ||
40 | #define DECON_BLENDERQx(n) (0x0300 + ((n - 1) * 4)) | ||
41 | #define DECON_BLENDCON 0x0310 | ||
42 | #define DECON_OPE_VIDW0xADD0(n) (0x0400 + ((n) * 4)) | ||
43 | #define DECON_OPE_VIDW0xADD1(n) (0x0414 + ((n) * 4)) | ||
44 | #define DECON_FRAMEFIFO_REG7 0x051C | ||
45 | #define DECON_FRAMEFIFO_REG8 0x0520 | ||
46 | #define DECON_FRAMEFIFO_STATUS 0x0524 | ||
47 | #define DECON_CMU 0x1404 | ||
48 | #define DECON_UPDATE 0x1410 | ||
49 | #define DECON_CRFMID 0x1414 | ||
50 | #define DECON_UPDATE_SCHEME 0x1438 | ||
51 | #define DECON_VIDCON1 0x2000 | ||
52 | #define DECON_VIDCON2 0x2004 | ||
53 | #define DECON_VIDCON3 0x2008 | ||
54 | #define DECON_VIDCON4 0x200C | ||
55 | #define DECON_VIDTCON2 0x2028 | ||
56 | #define DECON_FRAME_SIZE 0x2038 | ||
57 | #define DECON_LINECNT_OP_THRESHOLD 0x203C | ||
58 | #define DECON_TRIGCON 0x2040 | ||
59 | #define DECON_TRIGSKIP 0x2050 | ||
60 | #define DECON_CRCRDATA 0x20B0 | ||
61 | #define DECON_CRCCTRL 0x20B4 | ||
62 | |||
63 | /* Exynos5430 DECON */ | ||
64 | #define DECON_VIDTCON0 0x2020 | ||
65 | #define DECON_VIDTCON1 0x2024 | ||
66 | |||
67 | /* Exynos5433 DECON */ | ||
68 | #define DECON_VIDTCON00 0x2010 | ||
69 | #define DECON_VIDTCON01 0x2014 | ||
70 | #define DECON_VIDTCON10 0x2018 | ||
71 | #define DECON_VIDTCON11 0x201C | ||
72 | |||
73 | /* Exynos543X DECON Internal */ | ||
74 | #define DECON_W013DSTREOCON 0x0320 | ||
75 | #define DECON_W233DSTREOCON 0x0324 | ||
76 | #define DECON_FRAMEFIFO_REG0 0x0500 | ||
77 | #define DECON_ENHANCER_CTRL 0x2100 | ||
78 | |||
79 | /* Exynos543X DECON TV */ | ||
80 | #define DECON_VCLKCON0 0x0014 | ||
81 | #define DECON_VIDINTCON2 0x0228 | ||
82 | #define DECON_VIDINTCON3 0x022C | ||
83 | |||
84 | /* VIDCON0 */ | ||
85 | #define VIDCON0_SWRESET (1 << 28) | ||
86 | #define VIDCON0_CLKVALUP (1 << 14) | ||
87 | #define VIDCON0_VLCKFREE (1 << 5) | ||
88 | #define VIDCON0_STOP_STATUS (1 << 2) | ||
89 | #define VIDCON0_ENVID (1 << 1) | ||
90 | #define VIDCON0_ENVID_F (1 << 0) | ||
91 | |||
92 | /* VIDOUTCON0 */ | ||
93 | #define VIDOUT_INTERLACE_FIELD_F (1 << 29) | ||
94 | #define VIDOUT_INTERLACE_EN_F (1 << 28) | ||
95 | #define VIDOUT_LCD_ON (1 << 24) | ||
96 | #define VIDOUT_IF_F_MASK (0x3 << 20) | ||
97 | #define VIDOUT_RGB_IF (0x0 << 20) | ||
98 | #define VIDOUT_COMMAND_IF (0x2 << 20) | ||
99 | |||
100 | /* WINCONx */ | ||
101 | #define WINCONx_HAWSWP_F (1 << 16) | ||
102 | #define WINCONx_WSWP_F (1 << 15) | ||
103 | #define WINCONx_BURSTLEN_MASK (0x3 << 10) | ||
104 | #define WINCONx_BURSTLEN_16WORD (0x0 << 10) | ||
105 | #define WINCONx_BURSTLEN_8WORD (0x1 << 10) | ||
106 | #define WINCONx_BURSTLEN_4WORD (0x2 << 10) | ||
107 | #define WINCONx_BLD_PIX_F (1 << 6) | ||
108 | #define WINCONx_BPPMODE_MASK (0xf << 2) | ||
109 | #define WINCONx_BPPMODE_16BPP_565 (0x5 << 2) | ||
110 | #define WINCONx_BPPMODE_16BPP_A1555 (0x6 << 2) | ||
111 | #define WINCONx_BPPMODE_16BPP_I1555 (0x7 << 2) | ||
112 | #define WINCONx_BPPMODE_24BPP_888 (0xb << 2) | ||
113 | #define WINCONx_BPPMODE_24BPP_A1887 (0xc << 2) | ||
114 | #define WINCONx_BPPMODE_25BPP_A1888 (0xd << 2) | ||
115 | #define WINCONx_BPPMODE_32BPP_A8888 (0xd << 2) | ||
116 | #define WINCONx_BPPMODE_16BPP_A4444 (0xe << 2) | ||
117 | #define WINCONx_ALPHA_SEL_F (1 << 1) | ||
118 | #define WINCONx_ENWIN_F (1 << 0) | ||
119 | |||
120 | /* SHADOWCON */ | ||
121 | #define SHADOWCON_PROTECT_MASK GENMASK(14, 10) | ||
122 | #define SHADOWCON_Wx_PROTECT(n) (1 << (10 + (n))) | ||
123 | |||
124 | /* VIDOSDxD */ | ||
125 | #define VIDOSD_Wx_ALPHA_R_F(n) (((n) & 0xff) << 16) | ||
126 | #define VIDOSD_Wx_ALPHA_G_F(n) (((n) & 0xff) << 8) | ||
127 | #define VIDOSD_Wx_ALPHA_B_F(n) (((n) & 0xff) << 0) | ||
128 | |||
129 | /* VIDINTCON0 */ | ||
130 | #define VIDINTCON0_FRAMEDONE (1 << 17) | ||
131 | #define VIDINTCON0_FRAMESEL_BP (0 << 15) | ||
132 | #define VIDINTCON0_FRAMESEL_VS (1 << 15) | ||
133 | #define VIDINTCON0_FRAMESEL_AC (2 << 15) | ||
134 | #define VIDINTCON0_FRAMESEL_FP (3 << 15) | ||
135 | #define VIDINTCON0_INTFRMEN (1 << 12) | ||
136 | #define VIDINTCON0_INTEN (1 << 0) | ||
137 | |||
138 | /* VIDINTCON1 */ | ||
139 | #define VIDINTCON1_INTFRMDONEPEND (1 << 2) | ||
140 | #define VIDINTCON1_INTFRMPEND (1 << 1) | ||
141 | #define VIDINTCON1_INTFIFOPEND (1 << 0) | ||
142 | |||
143 | /* DECON_CMU */ | ||
144 | #define CMU_CLKGAGE_MODE_SFR_F (1 << 1) | ||
145 | #define CMU_CLKGAGE_MODE_MEM_F (1 << 0) | ||
146 | |||
147 | /* DECON_UPDATE */ | ||
148 | #define STANDALONE_UPDATE_F (1 << 0) | ||
149 | |||
150 | /* DECON_VIDCON1 */ | ||
151 | #define VIDCON1_LINECNT_MASK (0x0fff << 16) | ||
152 | #define VIDCON1_I80_ACTIVE (1 << 15) | ||
153 | #define VIDCON1_VSTATUS_MASK (0x3 << 13) | ||
154 | #define VIDCON1_VSTATUS_VS (0 << 13) | ||
155 | #define VIDCON1_VSTATUS_BP (1 << 13) | ||
156 | #define VIDCON1_VSTATUS_AC (2 << 13) | ||
157 | #define VIDCON1_VSTATUS_FP (3 << 13) | ||
158 | #define VIDCON1_VCLK_MASK (0x3 << 9) | ||
159 | #define VIDCON1_VCLK_RUN_VDEN_DISABLE (0x3 << 9) | ||
160 | #define VIDCON1_VCLK_HOLD (0x0 << 9) | ||
161 | #define VIDCON1_VCLK_RUN (0x1 << 9) | ||
162 | |||
163 | |||
164 | /* DECON_VIDTCON00 */ | ||
165 | #define VIDTCON00_VBPD_F(x) (((x) & 0xfff) << 16) | ||
166 | #define VIDTCON00_VFPD_F(x) ((x) & 0xfff) | ||
167 | |||
168 | /* DECON_VIDTCON01 */ | ||
169 | #define VIDTCON01_VSPW_F(x) (((x) & 0xfff) << 16) | ||
170 | |||
171 | /* DECON_VIDTCON10 */ | ||
172 | #define VIDTCON10_HBPD_F(x) (((x) & 0xfff) << 16) | ||
173 | #define VIDTCON10_HFPD_F(x) ((x) & 0xfff) | ||
174 | |||
175 | /* DECON_VIDTCON11 */ | ||
176 | #define VIDTCON11_HSPW_F(x) (((x) & 0xfff) << 16) | ||
177 | |||
178 | /* DECON_VIDTCON2 */ | ||
179 | #define VIDTCON2_LINEVAL(x) (((x) & 0xfff) << 16) | ||
180 | #define VIDTCON2_HOZVAL(x) ((x) & 0xfff) | ||
181 | |||
182 | /* TRIGCON */ | ||
183 | #define TRIGCON_TRIGEN_PER_F (1 << 31) | ||
184 | #define TRIGCON_TRIGEN_F (1 << 30) | ||
185 | #define TRIGCON_TE_AUTO_MASK (1 << 29) | ||
186 | #define TRIGCON_WB_SWTRIGCMD (1 << 28) | ||
187 | #define TRIGCON_SWTRIGCMD_W4BUF (1 << 26) | ||
188 | #define TRIGCON_TRIGMODE_W4BUF (1 << 25) | ||
189 | #define TRIGCON_SWTRIGCMD_W3BUF (1 << 21) | ||
190 | #define TRIGCON_TRIGMODE_W3BUF (1 << 20) | ||
191 | #define TRIGCON_SWTRIGCMD_W2BUF (1 << 16) | ||
192 | #define TRIGCON_TRIGMODE_W2BUF (1 << 15) | ||
193 | #define TRIGCON_SWTRIGCMD_W1BUF (1 << 11) | ||
194 | #define TRIGCON_TRIGMODE_W1BUF (1 << 10) | ||
195 | #define TRIGCON_SWTRIGCMD_W0BUF (1 << 6) | ||
196 | #define TRIGCON_TRIGMODE_W0BUF (1 << 5) | ||
197 | #define TRIGCON_HWTRIGMASK (1 << 4) | ||
198 | #define TRIGCON_HWTRIGEN (1 << 3) | ||
199 | #define TRIGCON_HWTRIG_INV (1 << 2) | ||
200 | #define TRIGCON_SWTRIGCMD (1 << 1) | ||
201 | #define TRIGCON_SWTRIGEN (1 << 0) | ||
202 | |||
203 | /* DECON_CRCCTRL */ | ||
204 | #define CRCCTRL_CRCCLKEN (0x1 << 2) | ||
205 | #define CRCCTRL_CRCSTART_F (0x1 << 1) | ||
206 | #define CRCCTRL_CRCEN (0x1 << 0) | ||
207 | #define CRCCTRL_MASK (0x7) | ||
208 | |||
209 | #endif /* EXYNOS_REGS_DECON5433_H */ | ||
diff --git a/drivers/gpu/drm/exynos/regs-decon7.h b/drivers/gpu/drm/exynos/regs-decon7.h new file mode 100644 index 000000000000..5df7765d2397 --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-decon7.h | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | ||
3 | * Author: Ajay Kumar <ajaykumar.rs@samsung.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2 of the License, or (at your | ||
8 | * option) any later version. | ||
9 | */ | ||
10 | |||
11 | #ifndef EXYNOS_REGS_DECON7_H | ||
12 | #define EXYNOS_REGS_DECON7_H | ||
13 | |||
14 | /* VIDCON0 */ | ||
15 | #define VIDCON0 0x00 | ||
16 | |||
17 | #define VIDCON0_SWRESET (1 << 28) | ||
18 | #define VIDCON0_DECON_STOP_STATUS (1 << 2) | ||
19 | #define VIDCON0_ENVID (1 << 1) | ||
20 | #define VIDCON0_ENVID_F (1 << 0) | ||
21 | |||
22 | /* VIDOUTCON0 */ | ||
23 | #define VIDOUTCON0 0x4 | ||
24 | |||
25 | #define VIDOUTCON0_DUAL_MASK (0x3 << 24) | ||
26 | #define VIDOUTCON0_DUAL_ON (0x3 << 24) | ||
27 | #define VIDOUTCON0_DISP_IF_1_ON (0x2 << 24) | ||
28 | #define VIDOUTCON0_DISP_IF_0_ON (0x1 << 24) | ||
29 | #define VIDOUTCON0_DUAL_OFF (0x0 << 24) | ||
30 | #define VIDOUTCON0_IF_SHIFT 23 | ||
31 | #define VIDOUTCON0_IF_MASK (0x1 << 23) | ||
32 | #define VIDOUTCON0_RGBIF (0x0 << 23) | ||
33 | #define VIDOUTCON0_I80IF (0x1 << 23) | ||
34 | |||
35 | /* VIDCON3 */ | ||
36 | #define VIDCON3 0x8 | ||
37 | |||
38 | /* VIDCON4 */ | ||
39 | #define VIDCON4 0xC | ||
40 | #define VIDCON4_FIFOCNT_START_EN (1 << 0) | ||
41 | |||
42 | /* VCLKCON0 */ | ||
43 | #define VCLKCON0 0x10 | ||
44 | #define VCLKCON0_CLKVALUP (1 << 8) | ||
45 | #define VCLKCON0_VCLKFREE (1 << 0) | ||
46 | |||
47 | /* VCLKCON */ | ||
48 | #define VCLKCON1 0x14 | ||
49 | #define VCLKCON1_CLKVAL_NUM_VCLK(val) (((val) & 0xff) << 0) | ||
50 | #define VCLKCON2 0x18 | ||
51 | |||
52 | /* SHADOWCON */ | ||
53 | #define SHADOWCON 0x30 | ||
54 | |||
55 | #define SHADOWCON_WINx_PROTECT(_win) (1 << (10 + (_win))) | ||
56 | |||
57 | /* WINCONx */ | ||
58 | #define WINCON(_win) (0x50 + ((_win) * 4)) | ||
59 | |||
60 | #define WINCONx_BUFSTATUS (0x3 << 30) | ||
61 | #define WINCONx_BUFSEL_MASK (0x3 << 28) | ||
62 | #define WINCONx_BUFSEL_SHIFT 28 | ||
63 | #define WINCONx_TRIPLE_BUF_MODE (0x1 << 18) | ||
64 | #define WINCONx_DOUBLE_BUF_MODE (0x0 << 18) | ||
65 | #define WINCONx_BURSTLEN_16WORD (0x0 << 11) | ||
66 | #define WINCONx_BURSTLEN_8WORD (0x1 << 11) | ||
67 | #define WINCONx_BURSTLEN_MASK (0x1 << 11) | ||
68 | #define WINCONx_BURSTLEN_SHIFT 11 | ||
69 | #define WINCONx_BLD_PLANE (0 << 8) | ||
70 | #define WINCONx_BLD_PIX (1 << 8) | ||
71 | #define WINCONx_ALPHA_MUL (1 << 7) | ||
72 | |||
73 | #define WINCONx_BPPMODE_MASK (0xf << 2) | ||
74 | #define WINCONx_BPPMODE_SHIFT 2 | ||
75 | #define WINCONx_BPPMODE_16BPP_565 (0x8 << 2) | ||
76 | #define WINCONx_BPPMODE_24BPP_BGRx (0x7 << 2) | ||
77 | #define WINCONx_BPPMODE_24BPP_RGBx (0x6 << 2) | ||
78 | #define WINCONx_BPPMODE_24BPP_xBGR (0x5 << 2) | ||
79 | #define WINCONx_BPPMODE_24BPP_xRGB (0x4 << 2) | ||
80 | #define WINCONx_BPPMODE_32BPP_BGRA (0x3 << 2) | ||
81 | #define WINCONx_BPPMODE_32BPP_RGBA (0x2 << 2) | ||
82 | #define WINCONx_BPPMODE_32BPP_ABGR (0x1 << 2) | ||
83 | #define WINCONx_BPPMODE_32BPP_ARGB (0x0 << 2) | ||
84 | #define WINCONx_ALPHA_SEL (1 << 1) | ||
85 | #define WINCONx_ENWIN (1 << 0) | ||
86 | |||
87 | #define WINCON1_ALPHA_MUL_F (1 << 7) | ||
88 | #define WINCON2_ALPHA_MUL_F (1 << 7) | ||
89 | #define WINCON3_ALPHA_MUL_F (1 << 7) | ||
90 | #define WINCON4_ALPHA_MUL_F (1 << 7) | ||
91 | |||
92 | /* VIDOSDxH: The height for the OSD image(READ ONLY)*/ | ||
93 | #define VIDOSD_H(_x) (0x80 + ((_x) * 4)) | ||
94 | |||
95 | /* Frame buffer start addresses: VIDWxxADD0n */ | ||
96 | #define VIDW_BUF_START(_win) (0x80 + ((_win) * 0x10)) | ||
97 | #define VIDW_BUF_START1(_win) (0x84 + ((_win) * 0x10)) | ||
98 | #define VIDW_BUF_START2(_win) (0x88 + ((_win) * 0x10)) | ||
99 | |||
100 | #define VIDW_WHOLE_X(_win) (0x0130 + ((_win) * 8)) | ||
101 | #define VIDW_WHOLE_Y(_win) (0x0134 + ((_win) * 8)) | ||
102 | #define VIDW_OFFSET_X(_win) (0x0170 + ((_win) * 8)) | ||
103 | #define VIDW_OFFSET_Y(_win) (0x0174 + ((_win) * 8)) | ||
104 | #define VIDW_BLKOFFSET(_win) (0x01B0 + ((_win) * 4)) | ||
105 | #define VIDW_BLKSIZE(win) (0x0200 + ((_win) * 4)) | ||
106 | |||
107 | /* Interrupt controls register */ | ||
108 | #define VIDINTCON2 0x228 | ||
109 | |||
110 | #define VIDINTCON1_INTEXTRA1_EN (1 << 1) | ||
111 | #define VIDINTCON1_INTEXTRA0_EN (1 << 0) | ||
112 | |||
113 | /* Interrupt controls and status register */ | ||
114 | #define VIDINTCON3 0x22C | ||
115 | |||
116 | #define VIDINTCON1_INTEXTRA1_PEND (1 << 1) | ||
117 | #define VIDINTCON1_INTEXTRA0_PEND (1 << 0) | ||
118 | |||
119 | /* VIDOSDxA ~ VIDOSDxE */ | ||
120 | #define VIDOSD_BASE 0x230 | ||
121 | |||
122 | #define OSD_STRIDE 0x20 | ||
123 | |||
124 | #define VIDOSD_A(_win) (VIDOSD_BASE + \ | ||
125 | ((_win) * OSD_STRIDE) + 0x00) | ||
126 | #define VIDOSD_B(_win) (VIDOSD_BASE + \ | ||
127 | ((_win) * OSD_STRIDE) + 0x04) | ||
128 | #define VIDOSD_C(_win) (VIDOSD_BASE + \ | ||
129 | ((_win) * OSD_STRIDE) + 0x08) | ||
130 | #define VIDOSD_D(_win) (VIDOSD_BASE + \ | ||
131 | ((_win) * OSD_STRIDE) + 0x0C) | ||
132 | #define VIDOSD_E(_win) (VIDOSD_BASE + \ | ||
133 | ((_win) * OSD_STRIDE) + 0x10) | ||
134 | |||
135 | #define VIDOSDxA_TOPLEFT_X_MASK (0x1fff << 13) | ||
136 | #define VIDOSDxA_TOPLEFT_X_SHIFT 13 | ||
137 | #define VIDOSDxA_TOPLEFT_X_LIMIT 0x1fff | ||
138 | #define VIDOSDxA_TOPLEFT_X(_x) (((_x) & 0x1fff) << 13) | ||
139 | |||
140 | #define VIDOSDxA_TOPLEFT_Y_MASK (0x1fff << 0) | ||
141 | #define VIDOSDxA_TOPLEFT_Y_SHIFT 0 | ||
142 | #define VIDOSDxA_TOPLEFT_Y_LIMIT 0x1fff | ||
143 | #define VIDOSDxA_TOPLEFT_Y(_x) (((_x) & 0x1fff) << 0) | ||
144 | |||
145 | #define VIDOSDxB_BOTRIGHT_X_MASK (0x1fff << 13) | ||
146 | #define VIDOSDxB_BOTRIGHT_X_SHIFT 13 | ||
147 | #define VIDOSDxB_BOTRIGHT_X_LIMIT 0x1fff | ||
148 | #define VIDOSDxB_BOTRIGHT_X(_x) (((_x) & 0x1fff) << 13) | ||
149 | |||
150 | #define VIDOSDxB_BOTRIGHT_Y_MASK (0x1fff << 0) | ||
151 | #define VIDOSDxB_BOTRIGHT_Y_SHIFT 0 | ||
152 | #define VIDOSDxB_BOTRIGHT_Y_LIMIT 0x1fff | ||
153 | #define VIDOSDxB_BOTRIGHT_Y(_x) (((_x) & 0x1fff) << 0) | ||
154 | |||
155 | #define VIDOSDxC_ALPHA0_R_F(_x) (((_x) & 0xFF) << 16) | ||
156 | #define VIDOSDxC_ALPHA0_G_F(_x) (((_x) & 0xFF) << 8) | ||
157 | #define VIDOSDxC_ALPHA0_B_F(_x) (((_x) & 0xFF) << 0) | ||
158 | |||
159 | #define VIDOSDxD_ALPHA1_R_F(_x) (((_x) & 0xFF) << 16) | ||
160 | #define VIDOSDxD_ALPHA1_G_F(_x) (((_x) & 0xFF) << 8) | ||
161 | #define VIDOSDxD_ALPHA1_B_F(_x) (((_x) & 0xFF) >> 0) | ||
162 | |||
163 | /* Window MAP (Color map) */ | ||
164 | #define WINxMAP(_win) (0x340 + ((_win) * 4)) | ||
165 | |||
166 | #define WINxMAP_MAP (1 << 24) | ||
167 | #define WINxMAP_MAP_COLOUR_MASK (0xffffff << 0) | ||
168 | #define WINxMAP_MAP_COLOUR_SHIFT 0 | ||
169 | #define WINxMAP_MAP_COLOUR_LIMIT 0xffffff | ||
170 | #define WINxMAP_MAP_COLOUR(_x) ((_x) << 0) | ||
171 | |||
172 | /* Window colour-key control registers */ | ||
173 | #define WKEYCON 0x370 | ||
174 | |||
175 | #define WKEYCON0 0x00 | ||
176 | #define WKEYCON1 0x04 | ||
177 | #define WxKEYCON0_KEYBL_EN (1 << 26) | ||
178 | #define WxKEYCON0_KEYEN_F (1 << 25) | ||
179 | #define WxKEYCON0_DIRCON (1 << 24) | ||
180 | #define WxKEYCON0_COMPKEY_MASK (0xffffff << 0) | ||
181 | #define WxKEYCON0_COMPKEY_SHIFT 0 | ||
182 | #define WxKEYCON0_COMPKEY_LIMIT 0xffffff | ||
183 | #define WxKEYCON0_COMPKEY(_x) ((_x) << 0) | ||
184 | #define WxKEYCON1_COLVAL_MASK (0xffffff << 0) | ||
185 | #define WxKEYCON1_COLVAL_SHIFT 0 | ||
186 | #define WxKEYCON1_COLVAL_LIMIT 0xffffff | ||
187 | #define WxKEYCON1_COLVAL(_x) ((_x) << 0) | ||
188 | |||
189 | /* color key control register for hardware window 1 ~ 4. */ | ||
190 | #define WKEYCON0_BASE(x) ((WKEYCON + WKEYCON0) + ((x - 1) * 8)) | ||
191 | /* color key value register for hardware window 1 ~ 4. */ | ||
192 | #define WKEYCON1_BASE(x) ((WKEYCON + WKEYCON1) + ((x - 1) * 8)) | ||
193 | |||
194 | /* Window KEY Alpha value */ | ||
195 | #define WxKEYALPHA(_win) (0x3A0 + (((_win) - 1) * 0x4)) | ||
196 | |||
197 | #define Wx_KEYALPHA_R_F_SHIFT 16 | ||
198 | #define Wx_KEYALPHA_G_F_SHIFT 8 | ||
199 | #define Wx_KEYALPHA_B_F_SHIFT 0 | ||
200 | |||
201 | /* Blending equation */ | ||
202 | #define BLENDE(_win) (0x03C0 + ((_win) * 4)) | ||
203 | #define BLENDE_COEF_ZERO 0x0 | ||
204 | #define BLENDE_COEF_ONE 0x1 | ||
205 | #define BLENDE_COEF_ALPHA_A 0x2 | ||
206 | #define BLENDE_COEF_ONE_MINUS_ALPHA_A 0x3 | ||
207 | #define BLENDE_COEF_ALPHA_B 0x4 | ||
208 | #define BLENDE_COEF_ONE_MINUS_ALPHA_B 0x5 | ||
209 | #define BLENDE_COEF_ALPHA0 0x6 | ||
210 | #define BLENDE_COEF_A 0xA | ||
211 | #define BLENDE_COEF_ONE_MINUS_A 0xB | ||
212 | #define BLENDE_COEF_B 0xC | ||
213 | #define BLENDE_COEF_ONE_MINUS_B 0xD | ||
214 | #define BLENDE_Q_FUNC(_v) ((_v) << 18) | ||
215 | #define BLENDE_P_FUNC(_v) ((_v) << 12) | ||
216 | #define BLENDE_B_FUNC(_v) ((_v) << 6) | ||
217 | #define BLENDE_A_FUNC(_v) ((_v) << 0) | ||
218 | |||
219 | /* Blending equation control */ | ||
220 | #define BLENDCON 0x3D8 | ||
221 | #define BLENDCON_NEW_MASK (1 << 0) | ||
222 | #define BLENDCON_NEW_8BIT_ALPHA_VALUE (1 << 0) | ||
223 | #define BLENDCON_NEW_4BIT_ALPHA_VALUE (0 << 0) | ||
224 | |||
225 | /* Interrupt control register */ | ||
226 | #define VIDINTCON0 0x500 | ||
227 | |||
228 | #define VIDINTCON0_WAKEUP_MASK (0x3f << 26) | ||
229 | #define VIDINTCON0_INTEXTRAEN (1 << 21) | ||
230 | |||
231 | #define VIDINTCON0_FRAMESEL0_SHIFT 15 | ||
232 | #define VIDINTCON0_FRAMESEL0_MASK (0x3 << 15) | ||
233 | #define VIDINTCON0_FRAMESEL0_BACKPORCH (0x0 << 15) | ||
234 | #define VIDINTCON0_FRAMESEL0_VSYNC (0x1 << 15) | ||
235 | #define VIDINTCON0_FRAMESEL0_ACTIVE (0x2 << 15) | ||
236 | #define VIDINTCON0_FRAMESEL0_FRONTPORCH (0x3 << 15) | ||
237 | |||
238 | #define VIDINTCON0_INT_FRAME (1 << 11) | ||
239 | |||
240 | #define VIDINTCON0_FIFOLEVEL_MASK (0x7 << 3) | ||
241 | #define VIDINTCON0_FIFOLEVEL_SHIFT 3 | ||
242 | #define VIDINTCON0_FIFOLEVEL_EMPTY (0x0 << 3) | ||
243 | #define VIDINTCON0_FIFOLEVEL_TO25PC (0x1 << 3) | ||
244 | #define VIDINTCON0_FIFOLEVEL_TO50PC (0x2 << 3) | ||
245 | #define VIDINTCON0_FIFOLEVEL_FULL (0x4 << 3) | ||
246 | |||
247 | #define VIDINTCON0_FIFOSEL_MAIN_EN (1 << 1) | ||
248 | #define VIDINTCON0_INT_FIFO (1 << 1) | ||
249 | |||
250 | #define VIDINTCON0_INT_ENABLE (1 << 0) | ||
251 | |||
252 | /* Interrupt controls and status register */ | ||
253 | #define VIDINTCON1 0x504 | ||
254 | |||
255 | #define VIDINTCON1_INT_EXTRA (1 << 3) | ||
256 | #define VIDINTCON1_INT_I80 (1 << 2) | ||
257 | #define VIDINTCON1_INT_FRAME (1 << 1) | ||
258 | #define VIDINTCON1_INT_FIFO (1 << 0) | ||
259 | |||
260 | /* VIDCON1 */ | ||
261 | #define VIDCON1(_x) (0x0600 + ((_x) * 0x50)) | ||
262 | #define VIDCON1_LINECNT_GET(_v) (((_v) >> 17) & 0x1fff) | ||
263 | #define VIDCON1_VCLK_MASK (0x3 << 9) | ||
264 | #define VIDCON1_VCLK_HOLD (0x0 << 9) | ||
265 | #define VIDCON1_VCLK_RUN (0x1 << 9) | ||
266 | #define VIDCON1_VCLK_RUN_VDEN_DISABLE (0x3 << 9) | ||
267 | #define VIDCON1_RGB_ORDER_O_MASK (0x7 << 4) | ||
268 | #define VIDCON1_RGB_ORDER_O_RGB (0x0 << 4) | ||
269 | #define VIDCON1_RGB_ORDER_O_GBR (0x1 << 4) | ||
270 | #define VIDCON1_RGB_ORDER_O_BRG (0x2 << 4) | ||
271 | #define VIDCON1_RGB_ORDER_O_BGR (0x4 << 4) | ||
272 | #define VIDCON1_RGB_ORDER_O_RBG (0x5 << 4) | ||
273 | #define VIDCON1_RGB_ORDER_O_GRB (0x6 << 4) | ||
274 | |||
275 | /* VIDTCON0 */ | ||
276 | #define VIDTCON0 0x610 | ||
277 | |||
278 | #define VIDTCON0_VBPD_MASK (0xffff << 16) | ||
279 | #define VIDTCON0_VBPD_SHIFT 16 | ||
280 | #define VIDTCON0_VBPD_LIMIT 0xffff | ||
281 | #define VIDTCON0_VBPD(_x) ((_x) << 16) | ||
282 | |||
283 | #define VIDTCON0_VFPD_MASK (0xffff << 0) | ||
284 | #define VIDTCON0_VFPD_SHIFT 0 | ||
285 | #define VIDTCON0_VFPD_LIMIT 0xffff | ||
286 | #define VIDTCON0_VFPD(_x) ((_x) << 0) | ||
287 | |||
288 | /* VIDTCON1 */ | ||
289 | #define VIDTCON1 0x614 | ||
290 | |||
291 | #define VIDTCON1_VSPW_MASK (0xffff << 16) | ||
292 | #define VIDTCON1_VSPW_SHIFT 16 | ||
293 | #define VIDTCON1_VSPW_LIMIT 0xffff | ||
294 | #define VIDTCON1_VSPW(_x) ((_x) << 16) | ||
295 | |||
296 | /* VIDTCON2 */ | ||
297 | #define VIDTCON2 0x618 | ||
298 | |||
299 | #define VIDTCON2_HBPD_MASK (0xffff << 16) | ||
300 | #define VIDTCON2_HBPD_SHIFT 16 | ||
301 | #define VIDTCON2_HBPD_LIMIT 0xffff | ||
302 | #define VIDTCON2_HBPD(_x) ((_x) << 16) | ||
303 | |||
304 | #define VIDTCON2_HFPD_MASK (0xffff << 0) | ||
305 | #define VIDTCON2_HFPD_SHIFT 0 | ||
306 | #define VIDTCON2_HFPD_LIMIT 0xffff | ||
307 | #define VIDTCON2_HFPD(_x) ((_x) << 0) | ||
308 | |||
309 | /* VIDTCON3 */ | ||
310 | #define VIDTCON3 0x61C | ||
311 | |||
312 | #define VIDTCON3_HSPW_MASK (0xffff << 16) | ||
313 | #define VIDTCON3_HSPW_SHIFT 16 | ||
314 | #define VIDTCON3_HSPW_LIMIT 0xffff | ||
315 | #define VIDTCON3_HSPW(_x) ((_x) << 16) | ||
316 | |||
317 | /* VIDTCON4 */ | ||
318 | #define VIDTCON4 0x620 | ||
319 | |||
320 | #define VIDTCON4_LINEVAL_MASK (0xfff << 16) | ||
321 | #define VIDTCON4_LINEVAL_SHIFT 16 | ||
322 | #define VIDTCON4_LINEVAL_LIMIT 0xfff | ||
323 | #define VIDTCON4_LINEVAL(_x) (((_x) & 0xfff) << 16) | ||
324 | |||
325 | #define VIDTCON4_HOZVAL_MASK (0xfff << 0) | ||
326 | #define VIDTCON4_HOZVAL_SHIFT 0 | ||
327 | #define VIDTCON4_HOZVAL_LIMIT 0xfff | ||
328 | #define VIDTCON4_HOZVAL(_x) (((_x) & 0xfff) << 0) | ||
329 | |||
330 | /* LINECNT OP THRSHOLD*/ | ||
331 | #define LINECNT_OP_THRESHOLD 0x630 | ||
332 | |||
333 | /* CRCCTRL */ | ||
334 | #define CRCCTRL 0x6C8 | ||
335 | #define CRCCTRL_CRCCLKEN (0x1 << 2) | ||
336 | #define CRCCTRL_CRCSTART_F (0x1 << 1) | ||
337 | #define CRCCTRL_CRCEN (0x1 << 0) | ||
338 | |||
339 | /* DECON_CMU */ | ||
340 | #define DECON_CMU 0x704 | ||
341 | |||
342 | #define DECON_CMU_ALL_CLKGATE_ENABLE 0x3 | ||
343 | #define DECON_CMU_SE_CLKGATE_ENABLE (0x1 << 2) | ||
344 | #define DECON_CMU_SFR_CLKGATE_ENABLE (0x1 << 1) | ||
345 | #define DECON_CMU_MEM_CLKGATE_ENABLE (0x1 << 0) | ||
346 | |||
347 | /* DECON_UPDATE */ | ||
348 | #define DECON_UPDATE 0x710 | ||
349 | |||
350 | #define DECON_UPDATE_SLAVE_SYNC (1 << 4) | ||
351 | #define DECON_UPDATE_STANDALONE_F (1 << 0) | ||
352 | |||
353 | #endif /* EXYNOS_REGS_DECON7_H */ | ||