aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorEunchul Kim <chulspro.kim@samsung.com>2012-12-14 03:58:56 -0500
committerInki Dae <daeinki@gmail.com>2012-12-14 12:39:41 -0500
commitbea8a429d91a1fd9a88c87df28062c632a1081f9 (patch)
treecba3e81be4c5dd75b2715f195f1ace57d18ff9da /drivers/gpu
parent16102edb49b6cc7fbb68b10c04a42b78fbceb3ed (diff)
drm/exynos: add rotator ipp driver
This patch adds IPP subsystem-based rotator driver. And Rotator supports the following features. - Image crop operation support. - Rotate operation support to 90, 180 or 270 degree. - Flip operation support to vertical, horizontal or both. . as limitaions, the pixel format to source buffer should be same as the one to destination buffer and no scaler. This driver is registered to IPP subsystem framework to be used by user side and user can control the Rotator hardware through some interfaces of IPP subsystem framework. Changelog v6: - fix build warning. Changelog v1 ~ v5: - added comments, code fixups and cleanups. Signed-off-by: Eunchul Kim <chulspro.kim@samsung.com> Signed-off-by: Youngjun Cho <yj44.cho@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/exynos/Kconfig7
-rw-r--r--drivers/gpu/drm/exynos/Makefile1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c15
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.c855
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_rotator.h33
-rw-r--r--drivers/gpu/drm/exynos/regs-rotator.h73
7 files changed, 985 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 66c02df360e..e0056a3ab75 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC
57 depends on DRM_EXYNOS_IPP 57 depends on DRM_EXYNOS_IPP
58 help 58 help
59 Choose this option if you want to use Exynos FIMC for DRM. 59 Choose this option if you want to use Exynos FIMC for DRM.
60
61config DRM_EXYNOS_ROTATOR
62 bool "Exynos DRM Rotator"
63 depends on DRM_EXYNOS_IPP
64 help
65 Choose this option if you want to use Exynos Rotator for DRM.
66
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 9710024ed00..3b706682b4a 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
18exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o 18exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
19exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o 19exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o
20exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o 20exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o
21exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o
21 22
22obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o 23obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 73f02ac53ba..09d884ba9e2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -378,6 +378,12 @@ static int __init exynos_drm_init(void)
378 goto out_fimc; 378 goto out_fimc;
379#endif 379#endif
380 380
381#ifdef CONFIG_DRM_EXYNOS_ROTATOR
382 ret = platform_driver_register(&rotator_driver);
383 if (ret < 0)
384 goto out_rotator;
385#endif
386
381#ifdef CONFIG_DRM_EXYNOS_IPP 387#ifdef CONFIG_DRM_EXYNOS_IPP
382 ret = platform_driver_register(&ipp_driver); 388 ret = platform_driver_register(&ipp_driver);
383 if (ret < 0) 389 if (ret < 0)
@@ -406,6 +412,11 @@ out_drm:
406out_ipp: 412out_ipp:
407#endif 413#endif
408 414
415#ifdef CONFIG_DRM_EXYNOS_ROTATOR
416 platform_driver_unregister(&rotator_driver);
417out_rotator:
418#endif
419
409#ifdef CONFIG_DRM_EXYNOS_FIMC 420#ifdef CONFIG_DRM_EXYNOS_FIMC
410 platform_driver_unregister(&fimc_driver); 421 platform_driver_unregister(&fimc_driver);
411out_fimc: 422out_fimc:
@@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void)
451 platform_driver_unregister(&ipp_driver); 462 platform_driver_unregister(&ipp_driver);
452#endif 463#endif
453 464
465#ifdef CONFIG_DRM_EXYNOS_ROTATOR
466 platform_driver_unregister(&rotator_driver);
467#endif
468
454#ifdef CONFIG_DRM_EXYNOS_FIMC 469#ifdef CONFIG_DRM_EXYNOS_FIMC
455 platform_driver_unregister(&fimc_driver); 470 platform_driver_unregister(&fimc_driver);
456#endif 471#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 63803508e4d..de49956539e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -350,5 +350,6 @@ extern struct platform_driver exynos_drm_common_hdmi_driver;
350extern struct platform_driver vidi_driver; 350extern struct platform_driver vidi_driver;
351extern struct platform_driver g2d_driver; 351extern struct platform_driver g2d_driver;
352extern struct platform_driver fimc_driver; 352extern struct platform_driver fimc_driver;
353extern struct platform_driver rotator_driver;
353extern struct platform_driver ipp_driver; 354extern struct platform_driver ipp_driver;
354#endif 355#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
new file mode 100644
index 00000000000..1c2366083c7
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c
@@ -0,0 +1,855 @@
1/*
2 * Copyright (C) 2012 Samsung Electronics Co.Ltd
3 * Authors:
4 * YoungJun Cho <yj44.cho@samsung.com>
5 * Eunchul Kim <chulspro.kim@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundationr
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/err.h>
15#include <linux/interrupt.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <linux/clk.h>
19#include <linux/pm_runtime.h>
20
21#include <drm/drmP.h>
22#include <drm/exynos_drm.h>
23#include "regs-rotator.h"
24#include "exynos_drm.h"
25#include "exynos_drm_ipp.h"
26
27/*
28 * Rotator supports image crop/rotator and input/output DMA operations.
29 * input DMA reads image data from the memory.
30 * output DMA writes image data to memory.
31 *
32 * M2M operation : supports crop/scale/rotation/csc so on.
33 * Memory ----> Rotator H/W ----> Memory.
34 */
35
36/*
37 * TODO
38 * 1. check suspend/resume api if needed.
39 * 2. need to check use case platform_device_id.
40 * 3. check src/dst size with, height.
41 * 4. need to add supported list in prop_list.
42 */
43
44#define get_rot_context(dev) platform_get_drvdata(to_platform_device(dev))
45#define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\
46 struct rot_context, ippdrv);
47#define rot_read(offset) readl(rot->regs + (offset))
48#define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
49
50enum rot_irq_status {
51 ROT_IRQ_STATUS_COMPLETE = 8,
52 ROT_IRQ_STATUS_ILLEGAL = 9,
53};
54
55/*
56 * A structure of limitation.
57 *
58 * @min_w: minimum width.
59 * @min_h: minimum height.
60 * @max_w: maximum width.
61 * @max_h: maximum height.
62 * @align: align size.
63 */
64struct rot_limit {
65 u32 min_w;
66 u32 min_h;
67 u32 max_w;
68 u32 max_h;
69 u32 align;
70};
71
72/*
73 * A structure of limitation table.
74 *
75 * @ycbcr420_2p: case of YUV.
76 * @rgb888: case of RGB.
77 */
78struct rot_limit_table {
79 struct rot_limit ycbcr420_2p;
80 struct rot_limit rgb888;
81};
82
83/*
84 * A structure of rotator context.
85 * @ippdrv: prepare initialization using ippdrv.
86 * @regs_res: register resources.
87 * @regs: memory mapped io registers.
88 * @clock: rotator gate clock.
89 * @limit_tbl: limitation of rotator.
90 * @irq: irq number.
91 * @cur_buf_id: current operation buffer id.
92 * @suspended: suspended state.
93 */
94struct rot_context {
95 struct exynos_drm_ippdrv ippdrv;
96 struct resource *regs_res;
97 void __iomem *regs;
98 struct clk *clock;
99 struct rot_limit_table *limit_tbl;
100 int irq;
101 int cur_buf_id[EXYNOS_DRM_OPS_MAX];
102 bool suspended;
103};
104
105static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
106{
107 u32 val = rot_read(ROT_CONFIG);
108
109 if (enable == true)
110 val |= ROT_CONFIG_IRQ;
111 else
112 val &= ~ROT_CONFIG_IRQ;
113
114 rot_write(val, ROT_CONFIG);
115}
116
117static u32 rotator_reg_get_fmt(struct rot_context *rot)
118{
119 u32 val = rot_read(ROT_CONTROL);
120
121 val &= ROT_CONTROL_FMT_MASK;
122
123 return val;
124}
125
126static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
127{
128 u32 val = rot_read(ROT_STATUS);
129
130 val = ROT_STATUS_IRQ(val);
131
132 if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
133 return ROT_IRQ_STATUS_COMPLETE;
134
135 return ROT_IRQ_STATUS_ILLEGAL;
136}
137
138static irqreturn_t rotator_irq_handler(int irq, void *arg)
139{
140 struct rot_context *rot = arg;
141 struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
142 struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd;
143 struct drm_exynos_ipp_event_work *event_work = c_node->event_work;
144 enum rot_irq_status irq_status;
145 u32 val;
146
147 /* Get execution result */
148 irq_status = rotator_reg_get_irq_status(rot);
149
150 /* clear status */
151 val = rot_read(ROT_STATUS);
152 val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
153 rot_write(val, ROT_STATUS);
154
155 if (irq_status == ROT_IRQ_STATUS_COMPLETE) {
156 event_work->ippdrv = ippdrv;
157 event_work->buf_id[EXYNOS_DRM_OPS_DST] =
158 rot->cur_buf_id[EXYNOS_DRM_OPS_DST];
159 queue_work(ippdrv->event_workq,
160 (struct work_struct *)event_work);
161 } else
162 DRM_ERROR("the SFR is set illegally\n");
163
164 return IRQ_HANDLED;
165}
166
167static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize,
168 u32 *vsize)
169{
170 struct rot_limit_table *limit_tbl = rot->limit_tbl;
171 struct rot_limit *limit;
172 u32 mask, val;
173
174 /* Get size limit */
175 if (fmt == ROT_CONTROL_FMT_RGB888)
176 limit = &limit_tbl->rgb888;
177 else
178 limit = &limit_tbl->ycbcr420_2p;
179
180 /* Get mask for rounding to nearest aligned val */
181 mask = ~((1 << limit->align) - 1);
182
183 /* Set aligned width */
184 val = ROT_ALIGN(*hsize, limit->align, mask);
185 if (val < limit->min_w)
186 *hsize = ROT_MIN(limit->min_w, mask);
187 else if (val > limit->max_w)
188 *hsize = ROT_MAX(limit->max_w, mask);
189 else
190 *hsize = val;
191
192 /* Set aligned height */
193 val = ROT_ALIGN(*vsize, limit->align, mask);
194 if (val < limit->min_h)
195 *vsize = ROT_MIN(limit->min_h, mask);
196 else if (val > limit->max_h)
197 *vsize = ROT_MAX(limit->max_h, mask);
198 else
199 *vsize = val;
200}
201
202static int rotator_src_set_fmt(struct device *dev, u32 fmt)
203{
204 struct rot_context *rot = dev_get_drvdata(dev);
205 u32 val;
206
207 val = rot_read(ROT_CONTROL);
208 val &= ~ROT_CONTROL_FMT_MASK;
209
210 switch (fmt) {
211 case DRM_FORMAT_NV12:
212 val |= ROT_CONTROL_FMT_YCBCR420_2P;
213 break;
214 case DRM_FORMAT_XRGB8888:
215 val |= ROT_CONTROL_FMT_RGB888;
216 break;
217 default:
218 DRM_ERROR("invalid image format\n");
219 return -EINVAL;
220 }
221
222 rot_write(val, ROT_CONTROL);
223
224 return 0;
225}
226
227static inline bool rotator_check_reg_fmt(u32 fmt)
228{
229 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) ||
230 (fmt == ROT_CONTROL_FMT_RGB888))
231 return true;
232
233 return false;
234}
235
236static int rotator_src_set_size(struct device *dev, int swap,
237 struct drm_exynos_pos *pos,
238 struct drm_exynos_sz *sz)
239{
240 struct rot_context *rot = dev_get_drvdata(dev);
241 u32 fmt, hsize, vsize;
242 u32 val;
243
244 /* Get format */
245 fmt = rotator_reg_get_fmt(rot);
246 if (!rotator_check_reg_fmt(fmt)) {
247 DRM_ERROR("%s:invalid format.\n", __func__);
248 return -EINVAL;
249 }
250
251 /* Align buffer size */
252 hsize = sz->hsize;
253 vsize = sz->vsize;
254 rotator_align_size(rot, fmt, &hsize, &vsize);
255
256 /* Set buffer size configuration */
257 val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
258 rot_write(val, ROT_SRC_BUF_SIZE);
259
260 /* Set crop image position configuration */
261 val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
262 rot_write(val, ROT_SRC_CROP_POS);
263 val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w);
264 rot_write(val, ROT_SRC_CROP_SIZE);
265
266 return 0;
267}
268
269static int rotator_src_set_addr(struct device *dev,
270 struct drm_exynos_ipp_buf_info *buf_info,
271 u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
272{
273 struct rot_context *rot = dev_get_drvdata(dev);
274 dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
275 u32 val, fmt, hsize, vsize;
276 int i;
277
278 /* Set current buf_id */
279 rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
280
281 switch (buf_type) {
282 case IPP_BUF_ENQUEUE:
283 /* Set address configuration */
284 for_each_ipp_planar(i)
285 addr[i] = buf_info->base[i];
286
287 /* Get format */
288 fmt = rotator_reg_get_fmt(rot);
289 if (!rotator_check_reg_fmt(fmt)) {
290 DRM_ERROR("%s:invalid format.\n", __func__);
291 return -EINVAL;
292 }
293
294 /* Re-set cb planar for NV12 format */
295 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
296 !addr[EXYNOS_DRM_PLANAR_CB]) {
297
298 val = rot_read(ROT_SRC_BUF_SIZE);
299 hsize = ROT_GET_BUF_SIZE_W(val);
300 vsize = ROT_GET_BUF_SIZE_H(val);
301
302 /* Set cb planar */
303 addr[EXYNOS_DRM_PLANAR_CB] =
304 addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
305 }
306
307 for_each_ipp_planar(i)
308 rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
309 break;
310 case IPP_BUF_DEQUEUE:
311 for_each_ipp_planar(i)
312 rot_write(0x0, ROT_SRC_BUF_ADDR(i));
313 break;
314 default:
315 /* Nothing to do */
316 break;
317 }
318
319 return 0;
320}
321
322static int rotator_dst_set_transf(struct device *dev,
323 enum drm_exynos_degree degree,
324 enum drm_exynos_flip flip, bool *swap)
325{
326 struct rot_context *rot = dev_get_drvdata(dev);
327 u32 val;
328
329 /* Set transform configuration */
330 val = rot_read(ROT_CONTROL);
331 val &= ~ROT_CONTROL_FLIP_MASK;
332
333 switch (flip) {
334 case EXYNOS_DRM_FLIP_VERTICAL:
335 val |= ROT_CONTROL_FLIP_VERTICAL;
336 break;
337 case EXYNOS_DRM_FLIP_HORIZONTAL:
338 val |= ROT_CONTROL_FLIP_HORIZONTAL;
339 break;
340 default:
341 /* Flip None */
342 break;
343 }
344
345 val &= ~ROT_CONTROL_ROT_MASK;
346
347 switch (degree) {
348 case EXYNOS_DRM_DEGREE_90:
349 val |= ROT_CONTROL_ROT_90;
350 break;
351 case EXYNOS_DRM_DEGREE_180:
352 val |= ROT_CONTROL_ROT_180;
353 break;
354 case EXYNOS_DRM_DEGREE_270:
355 val |= ROT_CONTROL_ROT_270;
356 break;
357 default:
358 /* Rotation 0 Degree */
359 break;
360 }
361
362 rot_write(val, ROT_CONTROL);
363
364 /* Check degree for setting buffer size swap */
365 if ((degree == EXYNOS_DRM_DEGREE_90) ||
366 (degree == EXYNOS_DRM_DEGREE_270))
367 *swap = true;
368 else
369 *swap = false;
370
371 return 0;
372}
373
374static int rotator_dst_set_size(struct device *dev, int swap,
375 struct drm_exynos_pos *pos,
376 struct drm_exynos_sz *sz)
377{
378 struct rot_context *rot = dev_get_drvdata(dev);
379 u32 val, fmt, hsize, vsize;
380
381 /* Get format */
382 fmt = rotator_reg_get_fmt(rot);
383 if (!rotator_check_reg_fmt(fmt)) {
384 DRM_ERROR("%s:invalid format.\n", __func__);
385 return -EINVAL;
386 }
387
388 /* Align buffer size */
389 hsize = sz->hsize;
390 vsize = sz->vsize;
391 rotator_align_size(rot, fmt, &hsize, &vsize);
392
393 /* Set buffer size configuration */
394 val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize);
395 rot_write(val, ROT_DST_BUF_SIZE);
396
397 /* Set crop image position configuration */
398 val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x);
399 rot_write(val, ROT_DST_CROP_POS);
400
401 return 0;
402}
403
404static int rotator_dst_set_addr(struct device *dev,
405 struct drm_exynos_ipp_buf_info *buf_info,
406 u32 buf_id, enum drm_exynos_ipp_buf_type buf_type)
407{
408 struct rot_context *rot = dev_get_drvdata(dev);
409 dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
410 u32 val, fmt, hsize, vsize;
411 int i;
412
413 /* Set current buf_id */
414 rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
415
416 switch (buf_type) {
417 case IPP_BUF_ENQUEUE:
418 /* Set address configuration */
419 for_each_ipp_planar(i)
420 addr[i] = buf_info->base[i];
421
422 /* Get format */
423 fmt = rotator_reg_get_fmt(rot);
424 if (!rotator_check_reg_fmt(fmt)) {
425 DRM_ERROR("%s:invalid format.\n", __func__);
426 return -EINVAL;
427 }
428
429 /* Re-set cb planar for NV12 format */
430 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
431 !addr[EXYNOS_DRM_PLANAR_CB]) {
432 /* Get buf size */
433 val = rot_read(ROT_DST_BUF_SIZE);
434
435 hsize = ROT_GET_BUF_SIZE_W(val);
436 vsize = ROT_GET_BUF_SIZE_H(val);
437
438 /* Set cb planar */
439 addr[EXYNOS_DRM_PLANAR_CB] =
440 addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
441 }
442
443 for_each_ipp_planar(i)
444 rot_write(addr[i], ROT_DST_BUF_ADDR(i));
445 break;
446 case IPP_BUF_DEQUEUE:
447 for_each_ipp_planar(i)
448 rot_write(0x0, ROT_DST_BUF_ADDR(i));
449 break;
450 default:
451 /* Nothing to do */
452 break;
453 }
454
455 return 0;
456}
457
458static struct exynos_drm_ipp_ops rot_src_ops = {
459 .set_fmt = rotator_src_set_fmt,
460 .set_size = rotator_src_set_size,
461 .set_addr = rotator_src_set_addr,
462};
463
464static struct exynos_drm_ipp_ops rot_dst_ops = {
465 .set_transf = rotator_dst_set_transf,
466 .set_size = rotator_dst_set_size,
467 .set_addr = rotator_dst_set_addr,
468};
469
470static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
471{
472 struct drm_exynos_ipp_prop_list *prop_list;
473
474 DRM_DEBUG_KMS("%s\n", __func__);
475
476 prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL);
477 if (!prop_list) {
478 DRM_ERROR("failed to alloc property list.\n");
479 return -ENOMEM;
480 }
481
482 prop_list->version = 1;
483 prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) |
484 (1 << EXYNOS_DRM_FLIP_HORIZONTAL);
485 prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) |
486 (1 << EXYNOS_DRM_DEGREE_90) |
487 (1 << EXYNOS_DRM_DEGREE_180) |
488 (1 << EXYNOS_DRM_DEGREE_270);
489 prop_list->csc = 0;
490 prop_list->crop = 0;
491 prop_list->scale = 0;
492
493 ippdrv->prop_list = prop_list;
494
495 return 0;
496}
497
498static inline bool rotator_check_drm_fmt(u32 fmt)
499{
500 switch (fmt) {
501 case DRM_FORMAT_XRGB8888:
502 case DRM_FORMAT_NV12:
503 return true;
504 default:
505 DRM_DEBUG_KMS("%s:not support format\n", __func__);
506 return false;
507 }
508}
509
510static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip)
511{
512 switch (flip) {
513 case EXYNOS_DRM_FLIP_NONE:
514 case EXYNOS_DRM_FLIP_VERTICAL:
515 case EXYNOS_DRM_FLIP_HORIZONTAL:
516 return true;
517 default:
518 DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
519 return false;
520 }
521}
522
523static int rotator_ippdrv_check_property(struct device *dev,
524 struct drm_exynos_ipp_property *property)
525{
526 struct drm_exynos_ipp_config *src_config =
527 &property->config[EXYNOS_DRM_OPS_SRC];
528 struct drm_exynos_ipp_config *dst_config =
529 &property->config[EXYNOS_DRM_OPS_DST];
530 struct drm_exynos_pos *src_pos = &src_config->pos;
531 struct drm_exynos_pos *dst_pos = &dst_config->pos;
532 struct drm_exynos_sz *src_sz = &src_config->sz;
533 struct drm_exynos_sz *dst_sz = &dst_config->sz;
534 bool swap = false;
535
536 /* Check format configuration */
537 if (src_config->fmt != dst_config->fmt) {
538 DRM_DEBUG_KMS("%s:not support csc feature\n", __func__);
539 return -EINVAL;
540 }
541
542 if (!rotator_check_drm_fmt(dst_config->fmt)) {
543 DRM_DEBUG_KMS("%s:invalid format\n", __func__);
544 return -EINVAL;
545 }
546
547 /* Check transform configuration */
548 if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
549 DRM_DEBUG_KMS("%s:not support source-side rotation\n",
550 __func__);
551 return -EINVAL;
552 }
553
554 switch (dst_config->degree) {
555 case EXYNOS_DRM_DEGREE_90:
556 case EXYNOS_DRM_DEGREE_270:
557 swap = true;
558 case EXYNOS_DRM_DEGREE_0:
559 case EXYNOS_DRM_DEGREE_180:
560 /* No problem */
561 break;
562 default:
563 DRM_DEBUG_KMS("%s:invalid degree\n", __func__);
564 return -EINVAL;
565 }
566
567 if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
568 DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__);
569 return -EINVAL;
570 }
571
572 if (!rotator_check_drm_flip(dst_config->flip)) {
573 DRM_DEBUG_KMS("%s:invalid flip\n", __func__);
574 return -EINVAL;
575 }
576
577 /* Check size configuration */
578 if ((src_pos->x + src_pos->w > src_sz->hsize) ||
579 (src_pos->y + src_pos->h > src_sz->vsize)) {
580 DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__);
581 return -EINVAL;
582 }
583
584 if (swap) {
585 if ((dst_pos->x + dst_pos->h > dst_sz->vsize) ||
586 (dst_pos->y + dst_pos->w > dst_sz->hsize)) {
587 DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
588 __func__);
589 return -EINVAL;
590 }
591
592 if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) {
593 DRM_DEBUG_KMS("%s:not support scale feature\n",
594 __func__);
595 return -EINVAL;
596 }
597 } else {
598 if ((dst_pos->x + dst_pos->w > dst_sz->hsize) ||
599 (dst_pos->y + dst_pos->h > dst_sz->vsize)) {
600 DRM_DEBUG_KMS("%s:out of destination buffer bound\n",
601 __func__);
602 return -EINVAL;
603 }
604
605 if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) {
606 DRM_DEBUG_KMS("%s:not support scale feature\n",
607 __func__);
608 return -EINVAL;
609 }
610 }
611
612 return 0;
613}
614
615static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
616{
617 struct rot_context *rot = dev_get_drvdata(dev);
618 u32 val;
619
620 if (rot->suspended) {
621 DRM_ERROR("suspended state\n");
622 return -EPERM;
623 }
624
625 if (cmd != IPP_CMD_M2M) {
626 DRM_ERROR("not support cmd: %d\n", cmd);
627 return -EINVAL;
628 }
629
630 /* Set interrupt enable */
631 rotator_reg_set_irq(rot, true);
632
633 val = rot_read(ROT_CONTROL);
634 val |= ROT_CONTROL_START;
635
636 rot_write(val, ROT_CONTROL);
637
638 return 0;
639}
640
641static int __devinit rotator_probe(struct platform_device *pdev)
642{
643 struct device *dev = &pdev->dev;
644 struct rot_context *rot;
645 struct exynos_drm_ippdrv *ippdrv;
646 int ret;
647
648 rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
649 if (!rot) {
650 dev_err(dev, "failed to allocate rot\n");
651 return -ENOMEM;
652 }
653
654 rot->limit_tbl = (struct rot_limit_table *)
655 platform_get_device_id(pdev)->driver_data;
656
657 rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
658 if (!rot->regs_res) {
659 dev_err(dev, "failed to find registers\n");
660 ret = -ENOENT;
661 goto err_get_resource;
662 }
663
664 rot->regs = devm_request_and_ioremap(dev, rot->regs_res);
665 if (!rot->regs) {
666 dev_err(dev, "failed to map register\n");
667 ret = -ENXIO;
668 goto err_get_resource;
669 }
670
671 rot->irq = platform_get_irq(pdev, 0);
672 if (rot->irq < 0) {
673 dev_err(dev, "failed to get irq\n");
674 ret = rot->irq;
675 goto err_get_irq;
676 }
677
678 ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler,
679 IRQF_ONESHOT, "drm_rotator", rot);
680 if (ret < 0) {
681 dev_err(dev, "failed to request irq\n");
682 goto err_get_irq;
683 }
684
685 rot->clock = clk_get(dev, "rotator");
686 if (IS_ERR_OR_NULL(rot->clock)) {
687 dev_err(dev, "failed to get clock\n");
688 ret = PTR_ERR(rot->clock);
689 goto err_clk_get;
690 }
691
692 pm_runtime_enable(dev);
693
694 ippdrv = &rot->ippdrv;
695 ippdrv->dev = dev;
696 ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops;
697 ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops;
698 ippdrv->check_property = rotator_ippdrv_check_property;
699 ippdrv->start = rotator_ippdrv_start;
700 ret = rotator_init_prop_list(ippdrv);
701 if (ret < 0) {
702 dev_err(dev, "failed to init property list.\n");
703 goto err_ippdrv_register;
704 }
705
706 DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv);
707
708 platform_set_drvdata(pdev, rot);
709
710 ret = exynos_drm_ippdrv_register(ippdrv);
711 if (ret < 0) {
712 dev_err(dev, "failed to register drm rotator device\n");
713 goto err_ippdrv_register;
714 }
715
716 dev_info(dev, "The exynos rotator is probed successfully\n");
717
718 return 0;
719
720err_ippdrv_register:
721 devm_kfree(dev, ippdrv->prop_list);
722 pm_runtime_disable(dev);
723 clk_put(rot->clock);
724err_clk_get:
725 free_irq(rot->irq, rot);
726err_get_irq:
727 devm_iounmap(dev, rot->regs);
728err_get_resource:
729 devm_kfree(dev, rot);
730 return ret;
731}
732
733static int __devexit rotator_remove(struct platform_device *pdev)
734{
735 struct device *dev = &pdev->dev;
736 struct rot_context *rot = dev_get_drvdata(dev);
737 struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
738
739 devm_kfree(dev, ippdrv->prop_list);
740 exynos_drm_ippdrv_unregister(ippdrv);
741
742 pm_runtime_disable(dev);
743 clk_put(rot->clock);
744
745 free_irq(rot->irq, rot);
746 devm_iounmap(dev, rot->regs);
747
748 devm_kfree(dev, rot);
749
750 return 0;
751}
752
753struct rot_limit_table rot_limit_tbl = {
754 .ycbcr420_2p = {
755 .min_w = 32,
756 .min_h = 32,
757 .max_w = SZ_32K,
758 .max_h = SZ_32K,
759 .align = 3,
760 },
761 .rgb888 = {
762 .min_w = 8,
763 .min_h = 8,
764 .max_w = SZ_8K,
765 .max_h = SZ_8K,
766 .align = 2,
767 },
768};
769
770struct platform_device_id rotator_driver_ids[] = {
771 {
772 .name = "exynos-rot",
773 .driver_data = (unsigned long)&rot_limit_tbl,
774 },
775 {},
776};
777
778static int rotator_clk_crtl(struct rot_context *rot, bool enable)
779{
780 DRM_DEBUG_KMS("%s\n", __func__);
781
782 if (enable) {
783 clk_enable(rot->clock);
784 rot->suspended = false;
785 } else {
786 clk_disable(rot->clock);
787 rot->suspended = true;
788 }
789
790 return 0;
791}
792
793
794#ifdef CONFIG_PM_SLEEP
795static int rotator_suspend(struct device *dev)
796{
797 struct rot_context *rot = dev_get_drvdata(dev);
798
799 DRM_DEBUG_KMS("%s\n", __func__);
800
801 if (pm_runtime_suspended(dev))
802 return 0;
803
804 return rotator_clk_crtl(rot, false);
805}
806
807static int rotator_resume(struct device *dev)
808{
809 struct rot_context *rot = dev_get_drvdata(dev);
810
811 DRM_DEBUG_KMS("%s\n", __func__);
812
813 if (!pm_runtime_suspended(dev))
814 return rotator_clk_crtl(rot, true);
815
816 return 0;
817}
818#endif
819
820#ifdef CONFIG_PM_RUNTIME
821static int rotator_runtime_suspend(struct device *dev)
822{
823 struct rot_context *rot = dev_get_drvdata(dev);
824
825 DRM_DEBUG_KMS("%s\n", __func__);
826
827 return rotator_clk_crtl(rot, false);
828}
829
830static int rotator_runtime_resume(struct device *dev)
831{
832 struct rot_context *rot = dev_get_drvdata(dev);
833
834 DRM_DEBUG_KMS("%s\n", __func__);
835
836 return rotator_clk_crtl(rot, true);
837}
838#endif
839
840static const struct dev_pm_ops rotator_pm_ops = {
841 SET_SYSTEM_SLEEP_PM_OPS(rotator_suspend, rotator_resume)
842 SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
843 NULL)
844};
845
846struct platform_driver rotator_driver = {
847 .probe = rotator_probe,
848 .remove = __devexit_p(rotator_remove),
849 .id_table = rotator_driver_ids,
850 .driver = {
851 .name = "exynos-rot",
852 .owner = THIS_MODULE,
853 .pm = &rotator_pm_ops,
854 },
855};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
new file mode 100644
index 00000000000..a2d7a14a52b
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h
@@ -0,0 +1,33 @@
1/*
2 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 *
4 * Authors:
5 * YoungJun Cho <yj44.cho@samsung.com>
6 * Eunchul Kim <chulspro.kim@samsung.com>
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 (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 * OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28#ifndef _EXYNOS_DRM_ROTATOR_H_
29#define _EXYNOS_DRM_ROTATOR_H_
30
31/* TODO */
32
33#endif
diff --git a/drivers/gpu/drm/exynos/regs-rotator.h b/drivers/gpu/drm/exynos/regs-rotator.h
new file mode 100644
index 00000000000..a09ac6e180d
--- /dev/null
+++ b/drivers/gpu/drm/exynos/regs-rotator.h
@@ -0,0 +1,73 @@
1/* drivers/gpu/drm/exynos/regs-rotator.h
2 *
3 * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * Register definition file for Samsung Rotator Interface (Rotator) driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11*/
12
13#ifndef EXYNOS_REGS_ROTATOR_H
14#define EXYNOS_REGS_ROTATOR_H
15
16/* Configuration */
17#define ROT_CONFIG 0x00
18#define ROT_CONFIG_IRQ (3 << 8)
19
20/* Image Control */
21#define ROT_CONTROL 0x10
22#define ROT_CONTROL_PATTERN_WRITE (1 << 16)
23#define ROT_CONTROL_FMT_YCBCR420_2P (1 << 8)
24#define ROT_CONTROL_FMT_RGB888 (6 << 8)
25#define ROT_CONTROL_FMT_MASK (7 << 8)
26#define ROT_CONTROL_FLIP_VERTICAL (2 << 6)
27#define ROT_CONTROL_FLIP_HORIZONTAL (3 << 6)
28#define ROT_CONTROL_FLIP_MASK (3 << 6)
29#define ROT_CONTROL_ROT_90 (1 << 4)
30#define ROT_CONTROL_ROT_180 (2 << 4)
31#define ROT_CONTROL_ROT_270 (3 << 4)
32#define ROT_CONTROL_ROT_MASK (3 << 4)
33#define ROT_CONTROL_START (1 << 0)
34
35/* Status */
36#define ROT_STATUS 0x20
37#define ROT_STATUS_IRQ_PENDING(x) (1 << (x))
38#define ROT_STATUS_IRQ(x) (((x) >> 8) & 0x3)
39#define ROT_STATUS_IRQ_VAL_COMPLETE 1
40#define ROT_STATUS_IRQ_VAL_ILLEGAL 2
41
42/* Buffer Address */
43#define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2))
44#define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2))
45
46/* Buffer Size */
47#define ROT_SRC_BUF_SIZE 0x3c
48#define ROT_DST_BUF_SIZE 0x5c
49#define ROT_SET_BUF_SIZE_H(x) ((x) << 16)
50#define ROT_SET_BUF_SIZE_W(x) ((x) << 0)
51#define ROT_GET_BUF_SIZE_H(x) ((x) >> 16)
52#define ROT_GET_BUF_SIZE_W(x) ((x) & 0xffff)
53
54/* Crop Position */
55#define ROT_SRC_CROP_POS 0x40
56#define ROT_DST_CROP_POS 0x60
57#define ROT_CROP_POS_Y(x) ((x) << 16)
58#define ROT_CROP_POS_X(x) ((x) << 0)
59
60/* Source Crop Size */
61#define ROT_SRC_CROP_SIZE 0x44
62#define ROT_SRC_CROP_SIZE_H(x) ((x) << 16)
63#define ROT_SRC_CROP_SIZE_W(x) ((x) << 0)
64
65/* Round to nearest aligned value */
66#define ROT_ALIGN(x, align, mask) (((x) + (1 << ((align) - 1))) & (mask))
67/* Minimum limit value */
68#define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask))
69/* Maximum limit value */
70#define ROT_MAX(max, mask) ((max) & (mask))
71
72#endif /* EXYNOS_REGS_ROTATOR_H */
73