aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2018-05-09 19:46:36 -0400
committerInki Dae <inki.dae@samsung.com>2018-05-09 19:48:53 -0400
commit9913f74fe15705acd5163551ddf449568cf0048d (patch)
treec93d3cc7faef96a38af3399f1ae1b64eee895541
parent5fae288d8ddd5b75d38d323cb4aa51ed2190ce17 (diff)
drm/exynos: ipp: Add IPP v2 framework
This patch adds Exynos IPP v2 subsystem and userspace API. New userspace API is focused ONLY on memory-to-memory image processing. The two remainging operation modes of obsolete IPP v1 API (framebuffer writeback and local-path output with image processing) can be implemented using standard DRM features: writeback connectors and additional DRM planes with scaling features. V2 IPP userspace API is based on stateless approach, which much better fits to memory-to-memory image processing model. It also provides support for all image formats, which are both already defined in DRM API and supported by the existing IPP hardware modules. The API consists of the following ioctls: - DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES: to enumerate all available image processing modules, - DRM_IOCTL_EXYNOS_IPP_GET_CAPS: to query capabilities and supported image formats of given IPP module, - DRM_IOCTL_EXYNOS_IPP_GET_LIMITS: to query hardware limitiations for selected image format of given IPP module, - DRM_IOCTL_EXYNOS_IPP_COMMIT: to perform operation described by the provided structures (source and destination buffers, operation rectangle, transformation, etc). The proposed userspace API is extensible. In the future more advanced image processing operations can be defined to support for example blending. Userspace API is fully functional also on DRM render nodes, so it is not limited to the root/privileged client. Internal driver API also has been completely rewritten. New IPP core performs all possible input validation, checks and object life-time control. The drivers can focus only on writing configuration to hardware registers. Stateless nature of DRM_IOCTL_EXYNOS_IPP_COMMIT ioctl simplifies the driver API. Minimal driver needs to provide a single callback for starting processing and an array with supported image formats. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Tested-by: Hoegeun Kwon <hoegeun.kwon@samsung.com> Merge conflict so merged manually. Signed-off-by: Inki Dae <inki.dae@samsung.com>
-rw-r--r--drivers/gpu/drm/exynos/Kconfig3
-rw-r--r--drivers/gpu/drm/exynos/Makefile1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c22
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c916
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.h175
-rw-r--r--include/uapi/drm/exynos_drm.h240
6 files changed, 1355 insertions, 2 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 1548a784ef71..9e914655c430 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -95,6 +95,9 @@ 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
98config DRM_EXYNOS_IPP
99 bool
100
98config DRM_EXYNOS_FIMC 101config DRM_EXYNOS_FIMC
99 bool "FIMC" 102 bool "FIMC"
100 depends on BROKEN && MFD_SYSCON 103 depends on BROKEN && MFD_SYSCON
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index a51c5459bb13..bdf4212dde7b 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -18,6 +18,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER) += exynos_mixer.o
18exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o 18exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o
19exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o 19exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
20exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o 20exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
21exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
21exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o 22exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o
22exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o 23exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
23exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o 24exynosdrm-$(CONFIG_DRM_EXYNOS_GSC) += exynos_drm_gsc.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index a518e9c6d6cc..37c0db759674 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -27,15 +27,23 @@
27#include "exynos_drm_fb.h" 27#include "exynos_drm_fb.h"
28#include "exynos_drm_gem.h" 28#include "exynos_drm_gem.h"
29#include "exynos_drm_plane.h" 29#include "exynos_drm_plane.h"
30#include "exynos_drm_ipp.h"
30#include "exynos_drm_vidi.h" 31#include "exynos_drm_vidi.h"
31#include "exynos_drm_g2d.h" 32#include "exynos_drm_g2d.h"
32#include "exynos_drm_iommu.h" 33#include "exynos_drm_iommu.h"
33 34
34#define DRIVER_NAME "exynos" 35#define DRIVER_NAME "exynos"
35#define DRIVER_DESC "Samsung SoC DRM" 36#define DRIVER_DESC "Samsung SoC DRM"
36#define DRIVER_DATE "20110530" 37#define DRIVER_DATE "20180330"
38
39/*
40 * Interface history:
41 *
42 * 1.0 - Original version
43 * 1.1 - Upgrade IPP driver to version 2.0
44 */
37#define DRIVER_MAJOR 1 45#define DRIVER_MAJOR 1
38#define DRIVER_MINOR 0 46#define DRIVER_MINOR 1
39 47
40int exynos_atomic_check(struct drm_device *dev, 48int exynos_atomic_check(struct drm_device *dev,
41 struct drm_atomic_state *state) 49 struct drm_atomic_state *state)
@@ -108,6 +116,16 @@ static const struct drm_ioctl_desc exynos_ioctls[] = {
108 DRM_AUTH | DRM_RENDER_ALLOW), 116 DRM_AUTH | DRM_RENDER_ALLOW),
109 DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl, 117 DRM_IOCTL_DEF_DRV(EXYNOS_G2D_EXEC, exynos_g2d_exec_ioctl,
110 DRM_AUTH | DRM_RENDER_ALLOW), 118 DRM_AUTH | DRM_RENDER_ALLOW),
119 DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_RESOURCES,
120 exynos_drm_ipp_get_res_ioctl,
121 DRM_AUTH | DRM_RENDER_ALLOW),
122 DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_CAPS, exynos_drm_ipp_get_caps_ioctl,
123 DRM_AUTH | DRM_RENDER_ALLOW),
124 DRM_IOCTL_DEF_DRV(EXYNOS_IPP_GET_LIMITS,
125 exynos_drm_ipp_get_limits_ioctl,
126 DRM_AUTH | DRM_RENDER_ALLOW),
127 DRM_IOCTL_DEF_DRV(EXYNOS_IPP_COMMIT, exynos_drm_ipp_commit_ioctl,
128 DRM_AUTH | DRM_RENDER_ALLOW),
111}; 129};
112 130
113static const struct file_operations exynos_drm_driver_fops = { 131static const struct file_operations exynos_drm_driver_fops = {
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
new file mode 100644
index 000000000000..26374e58c557
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -0,0 +1,916 @@
1/*
2 * Copyright (C) 2017 Samsung Electronics Co.Ltd
3 * Authors:
4 * Marek Szyprowski <m.szyprowski@samsung.com>
5 *
6 * Exynos DRM Image Post Processing (IPP) related functions
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 */
18
19
20#include <drm/drmP.h>
21#include <drm/drm_mode.h>
22#include <uapi/drm/exynos_drm.h>
23
24#include "exynos_drm_drv.h"
25#include "exynos_drm_gem.h"
26#include "exynos_drm_ipp.h"
27
28static int num_ipp;
29static LIST_HEAD(ipp_list);
30
31/**
32 * exynos_drm_ipp_register - Register a new picture processor hardware module
33 * @dev: DRM device
34 * @ipp: ipp module to init
35 * @funcs: callbacks for the new ipp object
36 * @caps: bitmask of ipp capabilities (%DRM_EXYNOS_IPP_CAP_*)
37 * @formats: array of supported formats
38 * @num_formats: size of the supported formats array
39 * @name: name (for debugging purposes)
40 *
41 * Initializes a ipp module.
42 *
43 * Returns:
44 * Zero on success, error code on failure.
45 */
46int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp,
47 const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
48 const struct exynos_drm_ipp_formats *formats,
49 unsigned int num_formats, const char *name)
50{
51 WARN_ON(!ipp);
52 WARN_ON(!funcs);
53 WARN_ON(!formats);
54 WARN_ON(!num_formats);
55
56 spin_lock_init(&ipp->lock);
57 INIT_LIST_HEAD(&ipp->todo_list);
58 init_waitqueue_head(&ipp->done_wq);
59 ipp->dev = dev;
60 ipp->funcs = funcs;
61 ipp->capabilities = caps;
62 ipp->name = name;
63 ipp->formats = formats;
64 ipp->num_formats = num_formats;
65
66 /* ipp_list modification is serialized by component framework */
67 list_add_tail(&ipp->head, &ipp_list);
68 ipp->id = num_ipp++;
69
70 DRM_DEBUG_DRIVER("Registered ipp %d\n", ipp->id);
71
72 return 0;
73}
74
75/**
76 * exynos_drm_ipp_unregister - Unregister the picture processor module
77 * @dev: DRM device
78 * @ipp: ipp module
79 */
80void exynos_drm_ipp_unregister(struct drm_device *dev,
81 struct exynos_drm_ipp *ipp)
82{
83 WARN_ON(ipp->task);
84 WARN_ON(!list_empty(&ipp->todo_list));
85 list_del(&ipp->head);
86}
87
88/**
89 * exynos_drm_ipp_ioctl_get_res_ioctl - enumerate all ipp modules
90 * @dev: DRM device
91 * @data: ioctl data
92 * @file_priv: DRM file info
93 *
94 * Construct a list of ipp ids.
95 *
96 * Called by the user via ioctl.
97 *
98 * Returns:
99 * Zero on success, negative errno on failure.
100 */
101int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
102 struct drm_file *file_priv)
103{
104 struct drm_exynos_ioctl_ipp_get_res *resp = data;
105 struct exynos_drm_ipp *ipp;
106 uint32_t __user *ipp_ptr = (uint32_t __user *)
107 (unsigned long)resp->ipp_id_ptr;
108 unsigned int count = num_ipp, copied = 0;
109
110 /*
111 * This ioctl is called twice, once to determine how much space is
112 * needed, and the 2nd time to fill it.
113 */
114 if (count && resp->count_ipps >= count) {
115 list_for_each_entry(ipp, &ipp_list, head) {
116 if (put_user(ipp->id, ipp_ptr + copied))
117 return -EFAULT;
118 copied++;
119 }
120 }
121 resp->count_ipps = count;
122
123 return 0;
124}
125
126static inline struct exynos_drm_ipp *__ipp_get(uint32_t id)
127{
128 struct exynos_drm_ipp *ipp;
129
130 list_for_each_entry(ipp, &ipp_list, head)
131 if (ipp->id == id)
132 return ipp;
133 return NULL;
134}
135
136/**
137 * exynos_drm_ipp_ioctl_get_caps - get ipp module capabilities and formats
138 * @dev: DRM device
139 * @data: ioctl data
140 * @file_priv: DRM file info
141 *
142 * Construct a structure describing ipp module capabilities.
143 *
144 * Called by the user via ioctl.
145 *
146 * Returns:
147 * Zero on success, negative errno on failure.
148 */
149int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
150 struct drm_file *file_priv)
151{
152 struct drm_exynos_ioctl_ipp_get_caps *resp = data;
153 void __user *ptr = (void __user *)(unsigned long)resp->formats_ptr;
154 struct exynos_drm_ipp *ipp;
155 int i;
156
157 ipp = __ipp_get(resp->ipp_id);
158 if (!ipp)
159 return -ENOENT;
160
161 resp->ipp_id = ipp->id;
162 resp->capabilities = ipp->capabilities;
163
164 /*
165 * This ioctl is called twice, once to determine how much space is
166 * needed, and the 2nd time to fill it.
167 */
168 if (resp->formats_count >= ipp->num_formats) {
169 for (i = 0; i < ipp->num_formats; i++) {
170 struct drm_exynos_ipp_format tmp = {
171 .fourcc = ipp->formats[i].fourcc,
172 .type = ipp->formats[i].type,
173 .modifier = ipp->formats[i].modifier,
174 };
175
176 if (copy_to_user(ptr, &tmp, sizeof(tmp)))
177 return -EFAULT;
178 ptr += sizeof(tmp);
179 }
180 }
181 resp->formats_count = ipp->num_formats;
182
183 return 0;
184}
185
186static inline const struct exynos_drm_ipp_formats *__ipp_format_get(
187 struct exynos_drm_ipp *ipp, uint32_t fourcc,
188 uint64_t mod, unsigned int type)
189{
190 int i;
191
192 for (i = 0; i < ipp->num_formats; i++) {
193 if ((ipp->formats[i].type & type) &&
194 ipp->formats[i].fourcc == fourcc &&
195 ipp->formats[i].modifier == mod)
196 return &ipp->formats[i];
197 }
198 return NULL;
199}
200
201/**
202 * exynos_drm_ipp_get_limits_ioctl - get ipp module limits
203 * @dev: DRM device
204 * @data: ioctl data
205 * @file_priv: DRM file info
206 *
207 * Construct a structure describing ipp module limitations for provided
208 * picture format.
209 *
210 * Called by the user via ioctl.
211 *
212 * Returns:
213 * Zero on success, negative errno on failure.
214 */
215int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
216 struct drm_file *file_priv)
217{
218 struct drm_exynos_ioctl_ipp_get_limits *resp = data;
219 void __user *ptr = (void __user *)(unsigned long)resp->limits_ptr;
220 const struct exynos_drm_ipp_formats *format;
221 struct exynos_drm_ipp *ipp;
222
223 if (resp->type != DRM_EXYNOS_IPP_FORMAT_SOURCE &&
224 resp->type != DRM_EXYNOS_IPP_FORMAT_DESTINATION)
225 return -EINVAL;
226
227 ipp = __ipp_get(resp->ipp_id);
228 if (!ipp)
229 return -ENOENT;
230
231 format = __ipp_format_get(ipp, resp->fourcc, resp->modifier,
232 resp->type);
233 if (!format)
234 return -EINVAL;
235
236 /*
237 * This ioctl is called twice, once to determine how much space is
238 * needed, and the 2nd time to fill it.
239 */
240 if (format->num_limits && resp->limits_count >= format->num_limits)
241 if (copy_to_user((void __user *)ptr, format->limits,
242 sizeof(*format->limits) * format->num_limits))
243 return -EFAULT;
244 resp->limits_count = format->num_limits;
245
246 return 0;
247}
248
249struct drm_pending_exynos_ipp_event {
250 struct drm_pending_event base;
251 struct drm_exynos_ipp_event event;
252};
253
254static inline struct exynos_drm_ipp_task *
255 exynos_drm_ipp_task_alloc(struct exynos_drm_ipp *ipp)
256{
257 struct exynos_drm_ipp_task *task;
258
259 task = kzalloc(sizeof(*task), GFP_KERNEL);
260 if (!task)
261 return NULL;
262
263 task->dev = ipp->dev;
264 task->ipp = ipp;
265
266 /* some defaults */
267 task->src.rect.w = task->dst.rect.w = UINT_MAX;
268 task->src.rect.h = task->dst.rect.h = UINT_MAX;
269 task->transform.rotation = DRM_MODE_ROTATE_0;
270
271 DRM_DEBUG_DRIVER("Allocated task %pK\n", task);
272
273 return task;
274}
275
276static const struct exynos_drm_param_map {
277 unsigned int id;
278 unsigned int size;
279 unsigned int offset;
280} exynos_drm_ipp_params_maps[] = {
281 {
282 DRM_EXYNOS_IPP_TASK_BUFFER | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
283 sizeof(struct drm_exynos_ipp_task_buffer),
284 offsetof(struct exynos_drm_ipp_task, src.buf),
285 }, {
286 DRM_EXYNOS_IPP_TASK_BUFFER |
287 DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
288 sizeof(struct drm_exynos_ipp_task_buffer),
289 offsetof(struct exynos_drm_ipp_task, dst.buf),
290 }, {
291 DRM_EXYNOS_IPP_TASK_RECTANGLE | DRM_EXYNOS_IPP_TASK_TYPE_SOURCE,
292 sizeof(struct drm_exynos_ipp_task_rect),
293 offsetof(struct exynos_drm_ipp_task, src.rect),
294 }, {
295 DRM_EXYNOS_IPP_TASK_RECTANGLE |
296 DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION,
297 sizeof(struct drm_exynos_ipp_task_rect),
298 offsetof(struct exynos_drm_ipp_task, dst.rect),
299 }, {
300 DRM_EXYNOS_IPP_TASK_TRANSFORM,
301 sizeof(struct drm_exynos_ipp_task_transform),
302 offsetof(struct exynos_drm_ipp_task, transform),
303 }, {
304 DRM_EXYNOS_IPP_TASK_ALPHA,
305 sizeof(struct drm_exynos_ipp_task_alpha),
306 offsetof(struct exynos_drm_ipp_task, alpha),
307 },
308};
309
310static int exynos_drm_ipp_task_set(struct exynos_drm_ipp_task *task,
311 struct drm_exynos_ioctl_ipp_commit *arg)
312{
313 const struct exynos_drm_param_map *map = exynos_drm_ipp_params_maps;
314 void __user *params = (void __user *)(unsigned long)arg->params_ptr;
315 unsigned int size = arg->params_size;
316 uint32_t id;
317 int i;
318
319 while (size) {
320 if (get_user(id, (uint32_t __user *)params))
321 return -EFAULT;
322
323 for (i = 0; i < ARRAY_SIZE(exynos_drm_ipp_params_maps); i++)
324 if (map[i].id == id)
325 break;
326 if (i == ARRAY_SIZE(exynos_drm_ipp_params_maps) ||
327 map[i].size > size)
328 return -EINVAL;
329
330 if (copy_from_user((void *)task + map[i].offset, params,
331 map[i].size))
332 return -EFAULT;
333
334 params += map[i].size;
335 size -= map[i].size;
336 }
337
338 DRM_DEBUG_DRIVER("Got task %pK configuration from userspace\n", task);
339 return 0;
340}
341
342static int exynos_drm_ipp_task_setup_buffer(struct exynos_drm_ipp_buffer *buf,
343 struct drm_file *filp)
344{
345 int ret = 0;
346 int i;
347
348 /* basic checks */
349 if (buf->buf.width == 0 || buf->buf.height == 0)
350 return -EINVAL;
351 buf->format = drm_format_info(buf->buf.fourcc);
352 for (i = 0; i < buf->format->num_planes; i++) {
353 unsigned int width = (i == 0) ? buf->buf.width :
354 DIV_ROUND_UP(buf->buf.width, buf->format->hsub);
355
356 if (buf->buf.pitch[i] == 0)
357 buf->buf.pitch[i] = width * buf->format->cpp[i];
358 if (buf->buf.pitch[i] < width * buf->format->cpp[i])
359 return -EINVAL;
360 if (!buf->buf.gem_id[i])
361 return -ENOENT;
362 }
363
364 /* pitch for additional planes must match */
365 if (buf->format->num_planes > 2 &&
366 buf->buf.pitch[1] != buf->buf.pitch[2])
367 return -EINVAL;
368
369 /* get GEM buffers and check their size */
370 for (i = 0; i < buf->format->num_planes; i++) {
371 unsigned int height = (i == 0) ? buf->buf.height :
372 DIV_ROUND_UP(buf->buf.height, buf->format->vsub);
373 unsigned long size = height * buf->buf.pitch[i];
374 struct drm_gem_object *obj = drm_gem_object_lookup(filp,
375 buf->buf.gem_id[i]);
376 if (!obj) {
377 ret = -ENOENT;
378 goto gem_free;
379 }
380 buf->exynos_gem[i] = to_exynos_gem(obj);
381
382 if (size + buf->buf.offset[i] > buf->exynos_gem[i]->size) {
383 i++;
384 ret = -EINVAL;
385 goto gem_free;
386 }
387 buf->dma_addr[i] = buf->exynos_gem[i]->dma_addr +
388 buf->buf.offset[i];
389 }
390
391 return 0;
392gem_free:
393 while (i--) {
394 drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base);
395 buf->exynos_gem[i] = NULL;
396 }
397 return ret;
398}
399
400static void exynos_drm_ipp_task_release_buf(struct exynos_drm_ipp_buffer *buf)
401{
402 int i;
403
404 if (!buf->exynos_gem[0])
405 return;
406 for (i = 0; i < buf->format->num_planes; i++)
407 drm_gem_object_put_unlocked(&buf->exynos_gem[i]->base);
408}
409
410static void exynos_drm_ipp_task_free(struct exynos_drm_ipp *ipp,
411 struct exynos_drm_ipp_task *task)
412{
413 DRM_DEBUG_DRIVER("Freeing task %pK\n", task);
414
415 exynos_drm_ipp_task_release_buf(&task->src);
416 exynos_drm_ipp_task_release_buf(&task->dst);
417 if (task->event)
418 drm_event_cancel_free(ipp->dev, &task->event->base);
419 kfree(task);
420}
421
422struct drm_ipp_limit {
423 struct drm_exynos_ipp_limit_val h;
424 struct drm_exynos_ipp_limit_val v;
425};
426
427enum drm_ipp_size_id {
428 IPP_LIMIT_BUFFER, IPP_LIMIT_AREA, IPP_LIMIT_ROTATED, IPP_LIMIT_MAX
429};
430
431static const enum drm_ipp_size_id limit_id_fallback[IPP_LIMIT_MAX][4] = {
432 [IPP_LIMIT_BUFFER] = { DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
433 [IPP_LIMIT_AREA] = { DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
434 DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
435 [IPP_LIMIT_ROTATED] = { DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED,
436 DRM_EXYNOS_IPP_LIMIT_SIZE_AREA,
437 DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER },
438};
439
440static inline void __limit_set_val(unsigned int *ptr, unsigned int val)
441{
442 if (!*ptr)
443 *ptr = val;
444}
445
446static void __get_size_limit(const struct drm_exynos_ipp_limit *limits,
447 unsigned int num_limits, enum drm_ipp_size_id id,
448 struct drm_ipp_limit *res)
449{
450 const struct drm_exynos_ipp_limit *l = limits;
451 int i = 0;
452
453 memset(res, 0, sizeof(*res));
454 for (i = 0; limit_id_fallback[id][i]; i++)
455 for (l = limits; l - limits < num_limits; l++) {
456 if (((l->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) !=
457 DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE) ||
458 ((l->type & DRM_EXYNOS_IPP_LIMIT_SIZE_MASK) !=
459 limit_id_fallback[id][i]))
460 continue;
461 __limit_set_val(&res->h.min, l->h.min);
462 __limit_set_val(&res->h.max, l->h.max);
463 __limit_set_val(&res->h.align, l->h.align);
464 __limit_set_val(&res->v.min, l->v.min);
465 __limit_set_val(&res->v.max, l->v.max);
466 __limit_set_val(&res->v.align, l->v.align);
467 }
468}
469
470static inline bool __align_check(unsigned int val, unsigned int align)
471{
472 if (align && (val & (align - 1))) {
473 DRM_DEBUG_DRIVER("Value %d exceeds HW limits (align %d)\n",
474 val, align);
475 return false;
476 }
477 return true;
478}
479
480static inline bool __size_limit_check(unsigned int val,
481 struct drm_exynos_ipp_limit_val *l)
482{
483 if ((l->min && val < l->min) || (l->max && val > l->max)) {
484 DRM_DEBUG_DRIVER("Value %d exceeds HW limits (min %d, max %d)\n",
485 val, l->min, l->max);
486 return false;
487 }
488 return __align_check(val, l->align);
489}
490
491static int exynos_drm_ipp_check_size_limits(struct exynos_drm_ipp_buffer *buf,
492 const struct drm_exynos_ipp_limit *limits, unsigned int num_limits,
493 bool rotate, bool swap)
494{
495 enum drm_ipp_size_id id = rotate ? IPP_LIMIT_ROTATED : IPP_LIMIT_AREA;
496 struct drm_ipp_limit l;
497 struct drm_exynos_ipp_limit_val *lh = &l.h, *lv = &l.v;
498
499 if (!limits)
500 return 0;
501
502 __get_size_limit(limits, num_limits, IPP_LIMIT_BUFFER, &l);
503 if (!__size_limit_check(buf->buf.width, &l.h) ||
504 !__size_limit_check(buf->buf.height, &l.v))
505 return -EINVAL;
506
507 if (swap) {
508 lv = &l.h;
509 lh = &l.v;
510 }
511 __get_size_limit(limits, num_limits, id, &l);
512 if (!__size_limit_check(buf->rect.w, lh) ||
513 !__align_check(buf->rect.x, lh->align) ||
514 !__size_limit_check(buf->rect.h, lv) ||
515 !__align_check(buf->rect.y, lv->align))
516 return -EINVAL;
517
518 return 0;
519}
520
521static inline bool __scale_limit_check(unsigned int src, unsigned int dst,
522 unsigned int min, unsigned int max)
523{
524 if ((max && (dst << 16) > src * max) ||
525 (min && (dst << 16) < src * min)) {
526 DRM_DEBUG_DRIVER("Scale from %d to %d exceeds HW limits (ratio min %d.%05d, max %d.%05d)\n",
527 src, dst,
528 min >> 16, 100000 * (min & 0xffff) / (1 << 16),
529 max >> 16, 100000 * (max & 0xffff) / (1 << 16));
530 return false;
531 }
532 return true;
533}
534
535static int exynos_drm_ipp_check_scale_limits(
536 struct drm_exynos_ipp_task_rect *src,
537 struct drm_exynos_ipp_task_rect *dst,
538 const struct drm_exynos_ipp_limit *limits,
539 unsigned int num_limits, bool swap)
540{
541 const struct drm_exynos_ipp_limit_val *lh, *lv;
542 int dw, dh;
543
544 for (; num_limits; limits++, num_limits--)
545 if ((limits->type & DRM_EXYNOS_IPP_LIMIT_TYPE_MASK) ==
546 DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE)
547 break;
548 if (!num_limits)
549 return 0;
550
551 lh = (!swap) ? &limits->h : &limits->v;
552 lv = (!swap) ? &limits->v : &limits->h;
553 dw = (!swap) ? dst->w : dst->h;
554 dh = (!swap) ? dst->h : dst->w;
555
556 if (!__scale_limit_check(src->w, dw, lh->min, lh->max) ||
557 !__scale_limit_check(src->h, dh, lv->min, lv->max))
558 return -EINVAL;
559
560 return 0;
561}
562
563static int exynos_drm_ipp_task_check(struct exynos_drm_ipp_task *task)
564{
565 struct exynos_drm_ipp *ipp = task->ipp;
566 const struct exynos_drm_ipp_formats *src_fmt, *dst_fmt;
567 struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
568 unsigned int rotation = task->transform.rotation;
569 int ret = 0;
570 bool swap = drm_rotation_90_or_270(rotation);
571 bool rotate = (rotation != DRM_MODE_ROTATE_0);
572 bool scale = false;
573
574 DRM_DEBUG_DRIVER("Checking task %pK\n", task);
575
576 if (src->rect.w == UINT_MAX)
577 src->rect.w = src->buf.width;
578 if (src->rect.h == UINT_MAX)
579 src->rect.h = src->buf.height;
580 if (dst->rect.w == UINT_MAX)
581 dst->rect.w = dst->buf.width;
582 if (dst->rect.h == UINT_MAX)
583 dst->rect.h = dst->buf.height;
584
585 if (src->rect.x + src->rect.w > (src->buf.width) ||
586 src->rect.y + src->rect.h > (src->buf.height) ||
587 dst->rect.x + dst->rect.w > (dst->buf.width) ||
588 dst->rect.y + dst->rect.h > (dst->buf.height)) {
589 DRM_DEBUG_DRIVER("Task %pK: defined area is outside provided buffers\n",
590 task);
591 return -EINVAL;
592 }
593
594 if ((!swap && (src->rect.w != dst->rect.w ||
595 src->rect.h != dst->rect.h)) ||
596 (swap && (src->rect.w != dst->rect.h ||
597 src->rect.h != dst->rect.w)))
598 scale = true;
599
600 if ((!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CROP) &&
601 (src->rect.x || src->rect.y || dst->rect.x || dst->rect.y)) ||
602 (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_ROTATE) && rotate) ||
603 (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_SCALE) && scale) ||
604 (!(ipp->capabilities & DRM_EXYNOS_IPP_CAP_CONVERT) &&
605 src->buf.fourcc != dst->buf.fourcc)) {
606 DRM_DEBUG_DRIVER("Task %pK: hw capabilities exceeded\n", task);
607 return -EINVAL;
608 }
609
610 src_fmt = __ipp_format_get(ipp, src->buf.fourcc, src->buf.modifier,
611 DRM_EXYNOS_IPP_FORMAT_SOURCE);
612 if (!src_fmt) {
613 DRM_DEBUG_DRIVER("Task %pK: src format not supported\n", task);
614 return -EINVAL;
615 }
616 ret = exynos_drm_ipp_check_size_limits(src, src_fmt->limits,
617 src_fmt->num_limits,
618 rotate, false);
619 if (ret)
620 return ret;
621 ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
622 src_fmt->limits,
623 src_fmt->num_limits, swap);
624 if (ret)
625 return ret;
626
627 dst_fmt = __ipp_format_get(ipp, dst->buf.fourcc, dst->buf.modifier,
628 DRM_EXYNOS_IPP_FORMAT_DESTINATION);
629 if (!dst_fmt) {
630 DRM_DEBUG_DRIVER("Task %pK: dst format not supported\n", task);
631 return -EINVAL;
632 }
633 ret = exynos_drm_ipp_check_size_limits(dst, dst_fmt->limits,
634 dst_fmt->num_limits,
635 false, swap);
636 if (ret)
637 return ret;
638 ret = exynos_drm_ipp_check_scale_limits(&src->rect, &dst->rect,
639 dst_fmt->limits,
640 dst_fmt->num_limits, swap);
641 if (ret)
642 return ret;
643
644 DRM_DEBUG_DRIVER("Task %pK: all checks done.\n", task);
645
646 return ret;
647}
648
649static int exynos_drm_ipp_task_setup_buffers(struct exynos_drm_ipp_task *task,
650 struct drm_file *filp)
651{
652 struct exynos_drm_ipp_buffer *src = &task->src, *dst = &task->dst;
653 int ret = 0;
654
655 DRM_DEBUG_DRIVER("Setting buffer for task %pK\n", task);
656
657 ret = exynos_drm_ipp_task_setup_buffer(src, filp);
658 if (ret) {
659 DRM_DEBUG_DRIVER("Task %pK: src buffer setup failed\n", task);
660 return ret;
661 }
662 ret = exynos_drm_ipp_task_setup_buffer(dst, filp);
663 if (ret) {
664 DRM_DEBUG_DRIVER("Task %pK: dst buffer setup failed\n", task);
665 return ret;
666 }
667
668 DRM_DEBUG_DRIVER("Task %pK: buffers prepared.\n", task);
669
670 return ret;
671}
672
673
674static int exynos_drm_ipp_event_create(struct exynos_drm_ipp_task *task,
675 struct drm_file *file_priv, uint64_t user_data)
676{
677 struct drm_pending_exynos_ipp_event *e = NULL;
678 int ret;
679
680 e = kzalloc(sizeof(*e), GFP_KERNEL);
681 if (!e)
682 return -ENOMEM;
683
684 e->event.base.type = DRM_EXYNOS_IPP_EVENT;
685 e->event.base.length = sizeof(e->event);
686 e->event.user_data = user_data;
687
688 ret = drm_event_reserve_init(task->dev, file_priv, &e->base,
689 &e->event.base);
690 if (ret)
691 goto free;
692
693 task->event = e;
694 return 0;
695free:
696 kfree(e);
697 return ret;
698}
699
700static void exynos_drm_ipp_event_send(struct exynos_drm_ipp_task *task)
701{
702 struct timespec64 now;
703
704 ktime_get_ts64(&now);
705 task->event->event.tv_sec = now.tv_sec;
706 task->event->event.tv_usec = now.tv_nsec / NSEC_PER_USEC;
707 task->event->event.sequence = atomic_inc_return(&task->ipp->sequence);
708
709 drm_send_event(task->dev, &task->event->base);
710}
711
712static int exynos_drm_ipp_task_cleanup(struct exynos_drm_ipp_task *task)
713{
714 int ret = task->ret;
715
716 if (ret == 0 && task->event) {
717 exynos_drm_ipp_event_send(task);
718 /* ensure event won't be canceled on task free */
719 task->event = NULL;
720 }
721
722 exynos_drm_ipp_task_free(task->ipp, task);
723 return ret;
724}
725
726static void exynos_drm_ipp_cleanup_work(struct work_struct *work)
727{
728 struct exynos_drm_ipp_task *task = container_of(work,
729 struct exynos_drm_ipp_task, cleanup_work);
730
731 exynos_drm_ipp_task_cleanup(task);
732}
733
734static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp);
735
736/**
737 * exynos_drm_ipp_task_done - finish given task and set return code
738 * @task: ipp task to finish
739 * @ret: error code or 0 if operation has been performed successfully
740 */
741void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret)
742{
743 struct exynos_drm_ipp *ipp = task->ipp;
744 unsigned long flags;
745
746 DRM_DEBUG_DRIVER("ipp: %d, task %pK done: %d\n", ipp->id, task, ret);
747
748 spin_lock_irqsave(&ipp->lock, flags);
749 if (ipp->task == task)
750 ipp->task = NULL;
751 task->flags |= DRM_EXYNOS_IPP_TASK_DONE;
752 task->ret = ret;
753 spin_unlock_irqrestore(&ipp->lock, flags);
754
755 exynos_drm_ipp_next_task(ipp);
756 wake_up(&ipp->done_wq);
757
758 if (task->flags & DRM_EXYNOS_IPP_TASK_ASYNC) {
759 INIT_WORK(&task->cleanup_work, exynos_drm_ipp_cleanup_work);
760 schedule_work(&task->cleanup_work);
761 }
762}
763
764static void exynos_drm_ipp_next_task(struct exynos_drm_ipp *ipp)
765{
766 struct exynos_drm_ipp_task *task;
767 unsigned long flags;
768 int ret;
769
770 DRM_DEBUG_DRIVER("ipp: %d, try to run new task\n", ipp->id);
771
772 spin_lock_irqsave(&ipp->lock, flags);
773
774 if (ipp->task || list_empty(&ipp->todo_list)) {
775 spin_unlock_irqrestore(&ipp->lock, flags);
776 return;
777 }
778
779 task = list_first_entry(&ipp->todo_list, struct exynos_drm_ipp_task,
780 head);
781 list_del_init(&task->head);
782 ipp->task = task;
783
784 spin_unlock_irqrestore(&ipp->lock, flags);
785
786 DRM_DEBUG_DRIVER("ipp: %d, selected task %pK to run\n", ipp->id, task);
787
788 ret = ipp->funcs->commit(ipp, task);
789 if (ret)
790 exynos_drm_ipp_task_done(task, ret);
791}
792
793static void exynos_drm_ipp_schedule_task(struct exynos_drm_ipp *ipp,
794 struct exynos_drm_ipp_task *task)
795{
796 unsigned long flags;
797
798 spin_lock_irqsave(&ipp->lock, flags);
799 list_add(&task->head, &ipp->todo_list);
800 spin_unlock_irqrestore(&ipp->lock, flags);
801
802 exynos_drm_ipp_next_task(ipp);
803}
804
805static void exynos_drm_ipp_task_abort(struct exynos_drm_ipp *ipp,
806 struct exynos_drm_ipp_task *task)
807{
808 unsigned long flags;
809
810 spin_lock_irqsave(&ipp->lock, flags);
811 if (task->flags & DRM_EXYNOS_IPP_TASK_DONE) {
812 /* already completed task */
813 exynos_drm_ipp_task_cleanup(task);
814 } else if (ipp->task != task) {
815 /* task has not been scheduled for execution yet */
816 list_del_init(&task->head);
817 exynos_drm_ipp_task_cleanup(task);
818 } else {
819 /*
820 * currently processed task, call abort() and perform
821 * cleanup with async worker
822 */
823 task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
824 spin_unlock_irqrestore(&ipp->lock, flags);
825 if (ipp->funcs->abort)
826 ipp->funcs->abort(ipp, task);
827 return;
828 }
829 spin_unlock_irqrestore(&ipp->lock, flags);
830}
831
832/**
833 * exynos_drm_ipp_commit_ioctl - perform image processing operation
834 * @dev: DRM device
835 * @data: ioctl data
836 * @file_priv: DRM file info
837 *
838 * Construct a ipp task from the set of properties provided from the user
839 * and try to schedule it to framebuffer processor hardware.
840 *
841 * Called by the user via ioctl.
842 *
843 * Returns:
844 * Zero on success, negative errno on failure.
845 */
846int exynos_drm_ipp_commit_ioctl(struct drm_device *dev, void *data,
847 struct drm_file *file_priv)
848{
849 struct drm_exynos_ioctl_ipp_commit *arg = data;
850 struct exynos_drm_ipp *ipp;
851 struct exynos_drm_ipp_task *task;
852 int ret = 0;
853
854 if ((arg->flags & ~DRM_EXYNOS_IPP_FLAGS) || arg->reserved)
855 return -EINVAL;
856
857 /* can't test and expect an event at the same time */
858 if ((arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY) &&
859 (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT))
860 return -EINVAL;
861
862 ipp = __ipp_get(arg->ipp_id);
863 if (!ipp)
864 return -ENOENT;
865
866 task = exynos_drm_ipp_task_alloc(ipp);
867 if (!task)
868 return -ENOMEM;
869
870 ret = exynos_drm_ipp_task_set(task, arg);
871 if (ret)
872 goto free;
873
874 ret = exynos_drm_ipp_task_check(task);
875 if (ret)
876 goto free;
877
878 ret = exynos_drm_ipp_task_setup_buffers(task, file_priv);
879 if (ret || arg->flags & DRM_EXYNOS_IPP_FLAG_TEST_ONLY)
880 goto free;
881
882 if (arg->flags & DRM_EXYNOS_IPP_FLAG_EVENT) {
883 ret = exynos_drm_ipp_event_create(task, file_priv,
884 arg->user_data);
885 if (ret)
886 goto free;
887 }
888
889 /*
890 * Queue task for processing on the hardware. task object will be
891 * then freed after exynos_drm_ipp_task_done()
892 */
893 if (arg->flags & DRM_EXYNOS_IPP_FLAG_NONBLOCK) {
894 DRM_DEBUG_DRIVER("ipp: %d, nonblocking processing task %pK\n",
895 ipp->id, task);
896
897 task->flags |= DRM_EXYNOS_IPP_TASK_ASYNC;
898 exynos_drm_ipp_schedule_task(task->ipp, task);
899 ret = 0;
900 } else {
901 DRM_DEBUG_DRIVER("ipp: %d, processing task %pK\n", ipp->id,
902 task);
903 exynos_drm_ipp_schedule_task(ipp, task);
904 ret = wait_event_interruptible(ipp->done_wq,
905 task->flags & DRM_EXYNOS_IPP_TASK_DONE);
906 if (ret)
907 exynos_drm_ipp_task_abort(ipp, task);
908 else
909 ret = exynos_drm_ipp_task_cleanup(task);
910 }
911 return ret;
912free:
913 exynos_drm_ipp_task_free(ipp, task);
914
915 return ret;
916}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.h b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
new file mode 100644
index 000000000000..0b27d4a9bf94
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.h
@@ -0,0 +1,175 @@
1/*
2 * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#ifndef _EXYNOS_DRM_IPP_H_
11#define _EXYNOS_DRM_IPP_H_
12
13#include <drm/drmP.h>
14
15struct exynos_drm_ipp;
16struct exynos_drm_ipp_task;
17
18/**
19 * struct exynos_drm_ipp_funcs - exynos_drm_ipp control functions
20 */
21struct exynos_drm_ipp_funcs {
22 /**
23 * @commit:
24 *
25 * This is the main entry point to start framebuffer processing
26 * in the hardware. The exynos_drm_ipp_task has been already validated.
27 * This function must not wait until the device finishes processing.
28 * When the driver finishes processing, it has to call
29 * exynos_exynos_drm_ipp_task_done() function.
30 *
31 * RETURNS:
32 *
33 * 0 on success or negative error codes in case of failure.
34 */
35 int (*commit)(struct exynos_drm_ipp *ipp,
36 struct exynos_drm_ipp_task *task);
37
38 /**
39 * @abort:
40 *
41 * Informs the driver that it has to abort the currently running
42 * task as soon as possible (i.e. as soon as it can stop the device
43 * safely), even if the task would not have been finished by then.
44 * After the driver performs the necessary steps, it has to call
45 * exynos_drm_ipp_task_done() (as if the task ended normally).
46 * This function does not have to (and will usually not) wait
47 * until the device enters a state when it can be stopped.
48 */
49 void (*abort)(struct exynos_drm_ipp *ipp,
50 struct exynos_drm_ipp_task *task);
51};
52
53/**
54 * struct exynos_drm_ipp - central picture processor module structure
55 */
56struct exynos_drm_ipp {
57 struct drm_device *dev;
58 struct list_head head;
59 unsigned int id;
60
61 const char *name;
62 const struct exynos_drm_ipp_funcs *funcs;
63 unsigned int capabilities;
64 const struct exynos_drm_ipp_formats *formats;
65 unsigned int num_formats;
66 atomic_t sequence;
67
68 spinlock_t lock;
69 struct exynos_drm_ipp_task *task;
70 struct list_head todo_list;
71 wait_queue_head_t done_wq;
72};
73
74struct exynos_drm_ipp_buffer {
75 struct drm_exynos_ipp_task_buffer buf;
76 struct drm_exynos_ipp_task_rect rect;
77
78 struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER];
79 const struct drm_format_info *format;
80 dma_addr_t dma_addr[MAX_FB_BUFFER];
81};
82
83/**
84 * struct exynos_drm_ipp_task - a structure describing transformation that
85 * has to be performed by the picture processor hardware module
86 */
87struct exynos_drm_ipp_task {
88 struct drm_device *dev;
89 struct exynos_drm_ipp *ipp;
90 struct list_head head;
91
92 struct exynos_drm_ipp_buffer src;
93 struct exynos_drm_ipp_buffer dst;
94
95 struct drm_exynos_ipp_task_transform transform;
96 struct drm_exynos_ipp_task_alpha alpha;
97
98 struct work_struct cleanup_work;
99 unsigned int flags;
100 int ret;
101
102 struct drm_pending_exynos_ipp_event *event;
103};
104
105#define DRM_EXYNOS_IPP_TASK_DONE (1 << 0)
106#define DRM_EXYNOS_IPP_TASK_ASYNC (1 << 1)
107
108struct exynos_drm_ipp_formats {
109 uint32_t fourcc;
110 uint32_t type;
111 uint64_t modifier;
112 const struct drm_exynos_ipp_limit *limits;
113 unsigned int num_limits;
114};
115
116/* helper macros to set exynos_drm_ipp_formats structure and limits*/
117#define IPP_SRCDST_MFORMAT(f, m, l) \
118 .fourcc = DRM_FORMAT_##f, .modifier = m, .limits = l, \
119 .num_limits = ARRAY_SIZE(l), \
120 .type = (DRM_EXYNOS_IPP_FORMAT_SOURCE | \
121 DRM_EXYNOS_IPP_FORMAT_DESTINATION)
122
123#define IPP_SRCDST_FORMAT(f, l) IPP_SRCDST_MFORMAT(f, 0, l)
124
125#define IPP_SIZE_LIMIT(l, val...) \
126 .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE | \
127 DRM_EXYNOS_IPP_LIMIT_SIZE_##l), val
128
129#define IPP_SCALE_LIMIT(val...) \
130 .type = (DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE), val
131
132int exynos_drm_ipp_register(struct drm_device *dev, struct exynos_drm_ipp *ipp,
133 const struct exynos_drm_ipp_funcs *funcs, unsigned int caps,
134 const struct exynos_drm_ipp_formats *formats,
135 unsigned int num_formats, const char *name);
136void exynos_drm_ipp_unregister(struct drm_device *dev,
137 struct exynos_drm_ipp *ipp);
138
139void exynos_drm_ipp_task_done(struct exynos_drm_ipp_task *task, int ret);
140
141#ifdef CONFIG_DRM_EXYNOS_IPP
142int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev, void *data,
143 struct drm_file *file_priv);
144int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev, void *data,
145 struct drm_file *file_priv);
146int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev, void *data,
147 struct drm_file *file_priv);
148int exynos_drm_ipp_commit_ioctl(struct drm_device *dev,
149 void *data, struct drm_file *file_priv);
150#else
151static inline int exynos_drm_ipp_get_res_ioctl(struct drm_device *dev,
152 void *data, struct drm_file *file_priv)
153{
154 struct drm_exynos_ioctl_ipp_get_res *resp = data;
155
156 resp->count_ipps = 0;
157 return 0;
158}
159static inline int exynos_drm_ipp_get_caps_ioctl(struct drm_device *dev,
160 void *data, struct drm_file *file_priv)
161{
162 return -ENODEV;
163}
164static inline int exynos_drm_ipp_get_limits_ioctl(struct drm_device *dev,
165 void *data, struct drm_file *file_priv)
166{
167 return -ENODEV;
168}
169static inline int exynos_drm_ipp_commit_ioctl(struct drm_device *dev,
170 void *data, struct drm_file *file_priv)
171{
172 return -ENODEV;
173}
174#endif
175#endif
diff --git a/include/uapi/drm/exynos_drm.h b/include/uapi/drm/exynos_drm.h
index 4a54305120e0..3e59b8382dd8 100644
--- a/include/uapi/drm/exynos_drm.h
+++ b/include/uapi/drm/exynos_drm.h
@@ -135,6 +135,219 @@ struct drm_exynos_g2d_exec {
135 __u64 async; 135 __u64 async;
136}; 136};
137 137
138/* Exynos DRM IPP v2 API */
139
140/**
141 * Enumerate available IPP hardware modules.
142 *
143 * @count_ipps: size of ipp_id array / number of ipp modules (set by driver)
144 * @reserved: padding
145 * @ipp_id_ptr: pointer to ipp_id array or NULL
146 */
147struct drm_exynos_ioctl_ipp_get_res {
148 __u32 count_ipps;
149 __u32 reserved;
150 __u64 ipp_id_ptr;
151};
152
153enum drm_exynos_ipp_format_type {
154 DRM_EXYNOS_IPP_FORMAT_SOURCE = 0x01,
155 DRM_EXYNOS_IPP_FORMAT_DESTINATION = 0x02,
156};
157
158struct drm_exynos_ipp_format {
159 __u32 fourcc;
160 __u32 type;
161 __u64 modifier;
162};
163
164enum drm_exynos_ipp_capability {
165 DRM_EXYNOS_IPP_CAP_CROP = 0x01,
166 DRM_EXYNOS_IPP_CAP_ROTATE = 0x02,
167 DRM_EXYNOS_IPP_CAP_SCALE = 0x04,
168 DRM_EXYNOS_IPP_CAP_CONVERT = 0x08,
169};
170
171/**
172 * Get IPP hardware capabilities and supported image formats.
173 *
174 * @ipp_id: id of IPP module to query
175 * @capabilities: bitmask of drm_exynos_ipp_capability (set by driver)
176 * @reserved: padding
177 * @formats_count: size of formats array (in entries) / number of filled
178 * formats (set by driver)
179 * @formats_ptr: pointer to formats array or NULL
180 */
181struct drm_exynos_ioctl_ipp_get_caps {
182 __u32 ipp_id;
183 __u32 capabilities;
184 __u32 reserved;
185 __u32 formats_count;
186 __u64 formats_ptr;
187};
188
189enum drm_exynos_ipp_limit_type {
190 /* size (horizontal/vertial) limits, in pixels (min, max, alignment) */
191 DRM_EXYNOS_IPP_LIMIT_TYPE_SIZE = 0x0001,
192 /* scale ratio (horizonta/vertial), 16.16 fixed point (min, max) */
193 DRM_EXYNOS_IPP_LIMIT_TYPE_SCALE = 0x0002,
194
195 /* image buffer area */
196 DRM_EXYNOS_IPP_LIMIT_SIZE_BUFFER = 0x0001 << 16,
197 /* src/dst rectangle area */
198 DRM_EXYNOS_IPP_LIMIT_SIZE_AREA = 0x0002 << 16,
199 /* src/dst rectangle area when rotation enabled */
200 DRM_EXYNOS_IPP_LIMIT_SIZE_ROTATED = 0x0003 << 16,
201
202 DRM_EXYNOS_IPP_LIMIT_TYPE_MASK = 0x000f,
203 DRM_EXYNOS_IPP_LIMIT_SIZE_MASK = 0x000f << 16,
204};
205
206struct drm_exynos_ipp_limit_val {
207 __u32 min;
208 __u32 max;
209 __u32 align;
210 __u32 reserved;
211};
212
213/**
214 * IPP module limitation.
215 *
216 * @type: limit type (see drm_exynos_ipp_limit_type enum)
217 * @reserved: padding
218 * @h: horizontal limits
219 * @v: vertical limits
220 */
221struct drm_exynos_ipp_limit {
222 __u32 type;
223 __u32 reserved;
224 struct drm_exynos_ipp_limit_val h;
225 struct drm_exynos_ipp_limit_val v;
226};
227
228/**
229 * Get IPP limits for given image format.
230 *
231 * @ipp_id: id of IPP module to query
232 * @fourcc: image format code (see DRM_FORMAT_* in drm_fourcc.h)
233 * @modifier: image format modifier (see DRM_FORMAT_MOD_* in drm_fourcc.h)
234 * @type: source/destination identifier (drm_exynos_ipp_format_flag enum)
235 * @limits_count: size of limits array (in entries) / number of filled entries
236 * (set by driver)
237 * @limits_ptr: pointer to limits array or NULL
238 */
239struct drm_exynos_ioctl_ipp_get_limits {
240 __u32 ipp_id;
241 __u32 fourcc;
242 __u64 modifier;
243 __u32 type;
244 __u32 limits_count;
245 __u64 limits_ptr;
246};
247
248enum drm_exynos_ipp_task_id {
249 /* buffer described by struct drm_exynos_ipp_task_buffer */
250 DRM_EXYNOS_IPP_TASK_BUFFER = 0x0001,
251 /* rectangle described by struct drm_exynos_ipp_task_rect */
252 DRM_EXYNOS_IPP_TASK_RECTANGLE = 0x0002,
253 /* transformation described by struct drm_exynos_ipp_task_transform */
254 DRM_EXYNOS_IPP_TASK_TRANSFORM = 0x0003,
255 /* alpha configuration described by struct drm_exynos_ipp_task_alpha */
256 DRM_EXYNOS_IPP_TASK_ALPHA = 0x0004,
257
258 /* source image data (for buffer and rectangle chunks) */
259 DRM_EXYNOS_IPP_TASK_TYPE_SOURCE = 0x0001 << 16,
260 /* destination image data (for buffer and rectangle chunks) */
261 DRM_EXYNOS_IPP_TASK_TYPE_DESTINATION = 0x0002 << 16,
262};
263
264/**
265 * Memory buffer with image data.
266 *
267 * @id: must be DRM_EXYNOS_IPP_TASK_BUFFER
268 * other parameters are same as for AddFB2 generic DRM ioctl
269 */
270struct drm_exynos_ipp_task_buffer {
271 __u32 id;
272 __u32 fourcc;
273 __u32 width, height;
274 __u32 gem_id[4];
275 __u32 offset[4];
276 __u32 pitch[4];
277 __u64 modifier;
278};
279
280/**
281 * Rectangle for processing.
282 *
283 * @id: must be DRM_EXYNOS_IPP_TASK_RECTANGLE
284 * @reserved: padding
285 * @x,@y: left corner in pixels
286 * @w,@h: width/height in pixels
287 */
288struct drm_exynos_ipp_task_rect {
289 __u32 id;
290 __u32 reserved;
291 __u32 x;
292 __u32 y;
293 __u32 w;
294 __u32 h;
295};
296
297/**
298 * Image tranformation description.
299 *
300 * @id: must be DRM_EXYNOS_IPP_TASK_TRANSFORM
301 * @rotation: DRM_MODE_ROTATE_* and DRM_MODE_REFLECT_* values
302 */
303struct drm_exynos_ipp_task_transform {
304 __u32 id;
305 __u32 rotation;
306};
307
308/**
309 * Image global alpha configuration for formats without alpha values.
310 *
311 * @id: must be DRM_EXYNOS_IPP_TASK_ALPHA
312 * @value: global alpha value (0-255)
313 */
314struct drm_exynos_ipp_task_alpha {
315 __u32 id;
316 __u32 value;
317};
318
319enum drm_exynos_ipp_flag {
320 /* generate DRM event after processing */
321 DRM_EXYNOS_IPP_FLAG_EVENT = 0x01,
322 /* dry run, only check task parameters */
323 DRM_EXYNOS_IPP_FLAG_TEST_ONLY = 0x02,
324 /* non-blocking processing */
325 DRM_EXYNOS_IPP_FLAG_NONBLOCK = 0x04,
326};
327
328#define DRM_EXYNOS_IPP_FLAGS (DRM_EXYNOS_IPP_FLAG_EVENT |\
329 DRM_EXYNOS_IPP_FLAG_TEST_ONLY | DRM_EXYNOS_IPP_FLAG_NONBLOCK)
330
331/**
332 * Perform image processing described by array of drm_exynos_ipp_task_*
333 * structures (parameters array).
334 *
335 * @ipp_id: id of IPP module to run the task
336 * @flags: bitmask of drm_exynos_ipp_flag values
337 * @reserved: padding
338 * @params_size: size of parameters array (in bytes)
339 * @params_ptr: pointer to parameters array or NULL
340 * @user_data: (optional) data for drm event
341 */
342struct drm_exynos_ioctl_ipp_commit {
343 __u32 ipp_id;
344 __u32 flags;
345 __u32 reserved;
346 __u32 params_size;
347 __u64 params_ptr;
348 __u64 user_data;
349};
350
138#define DRM_EXYNOS_GEM_CREATE 0x00 351#define DRM_EXYNOS_GEM_CREATE 0x00
139#define DRM_EXYNOS_GEM_MAP 0x01 352#define DRM_EXYNOS_GEM_MAP 0x01
140/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */ 353/* Reserved 0x03 ~ 0x05 for exynos specific gem ioctl */
@@ -147,6 +360,11 @@ struct drm_exynos_g2d_exec {
147#define DRM_EXYNOS_G2D_EXEC 0x22 360#define DRM_EXYNOS_G2D_EXEC 0x22
148 361
149/* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */ 362/* Reserved 0x30 ~ 0x33 for obsolete Exynos IPP ioctls */
363/* IPP - Image Post Processing */
364#define DRM_EXYNOS_IPP_GET_RESOURCES 0x40
365#define DRM_EXYNOS_IPP_GET_CAPS 0x41
366#define DRM_EXYNOS_IPP_GET_LIMITS 0x42
367#define DRM_EXYNOS_IPP_COMMIT 0x43
150 368
151#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ 369#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \
152 DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) 370 DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create)
@@ -165,8 +383,20 @@ struct drm_exynos_g2d_exec {
165#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \ 383#define DRM_IOCTL_EXYNOS_G2D_EXEC DRM_IOWR(DRM_COMMAND_BASE + \
166 DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec) 384 DRM_EXYNOS_G2D_EXEC, struct drm_exynos_g2d_exec)
167 385
386#define DRM_IOCTL_EXYNOS_IPP_GET_RESOURCES DRM_IOWR(DRM_COMMAND_BASE + \
387 DRM_EXYNOS_IPP_GET_RESOURCES, \
388 struct drm_exynos_ioctl_ipp_get_res)
389#define DRM_IOCTL_EXYNOS_IPP_GET_CAPS DRM_IOWR(DRM_COMMAND_BASE + \
390 DRM_EXYNOS_IPP_GET_CAPS, struct drm_exynos_ioctl_ipp_get_caps)
391#define DRM_IOCTL_EXYNOS_IPP_GET_LIMITS DRM_IOWR(DRM_COMMAND_BASE + \
392 DRM_EXYNOS_IPP_GET_LIMITS, \
393 struct drm_exynos_ioctl_ipp_get_limits)
394#define DRM_IOCTL_EXYNOS_IPP_COMMIT DRM_IOWR(DRM_COMMAND_BASE + \
395 DRM_EXYNOS_IPP_COMMIT, struct drm_exynos_ioctl_ipp_commit)
396
168/* EXYNOS specific events */ 397/* EXYNOS specific events */
169#define DRM_EXYNOS_G2D_EVENT 0x80000000 398#define DRM_EXYNOS_G2D_EVENT 0x80000000
399#define DRM_EXYNOS_IPP_EVENT 0x80000002
170 400
171struct drm_exynos_g2d_event { 401struct drm_exynos_g2d_event {
172 struct drm_event base; 402 struct drm_event base;
@@ -177,6 +407,16 @@ struct drm_exynos_g2d_event {
177 __u32 reserved; 407 __u32 reserved;
178}; 408};
179 409
410struct drm_exynos_ipp_event {
411 struct drm_event base;
412 __u64 user_data;
413 __u32 tv_sec;
414 __u32 tv_usec;
415 __u32 ipp_id;
416 __u32 sequence;
417 __u64 reserved;
418};
419
180#if defined(__cplusplus) 420#if defined(__cplusplus)
181} 421}
182#endif 422#endif