2 * Copyright (C) 2012 Samsung Electronics Co.Ltd
4 * YoungJun Cho <yj44.cho@samsung.com>
5 * Eunchul Kim <chulspro.kim@samsung.com>
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
12 #include <linux/kernel.h>
13 #include <linux/component.h>
14 #include <linux/err.h>
15 #include <linux/interrupt.h>
17 #include <linux/platform_device.h>
18 #include <linux/clk.h>
19 #include <linux/of_device.h>
20 #include <linux/pm_runtime.h>
23 #include <drm/exynos_drm.h>
24 #include "regs-rotator.h"
25 #include "exynos_drm_drv.h"
26 #include "exynos_drm_iommu.h"
27 #include "exynos_drm_ipp.h"
30 * Rotator supports image crop/rotator and input/output DMA operations.
31 * input DMA reads image data from the memory.
32 * output DMA writes image data to memory.
35 #define ROTATOR_AUTOSUSPEND_DELAY 2000
37 #define rot_read(offset) readl(rot->regs + (offset))
38 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
41 ROT_IRQ_STATUS_COMPLETE
= 8,
42 ROT_IRQ_STATUS_ILLEGAL
= 9,
46 const struct exynos_drm_ipp_formats
*formats
;
47 unsigned int num_formats
;
51 * A structure of rotator context.
52 * @ippdrv: prepare initialization using ippdrv.
53 * @regs: memory mapped io registers.
54 * @clock: rotator gate clock.
55 * @limit_tbl: limitation of rotator.
59 struct exynos_drm_ipp ipp
;
60 struct drm_device
*drm_dev
;
64 const struct exynos_drm_ipp_formats
*formats
;
65 unsigned int num_formats
;
66 struct exynos_drm_ipp_task
*task
;
69 static void rotator_reg_set_irq(struct rot_context
*rot
, bool enable
)
71 u32 val
= rot_read(ROT_CONFIG
);
74 val
|= ROT_CONFIG_IRQ
;
76 val
&= ~ROT_CONFIG_IRQ
;
78 rot_write(val
, ROT_CONFIG
);
81 static enum rot_irq_status
rotator_reg_get_irq_status(struct rot_context
*rot
)
83 u32 val
= rot_read(ROT_STATUS
);
85 val
= ROT_STATUS_IRQ(val
);
87 if (val
== ROT_STATUS_IRQ_VAL_COMPLETE
)
88 return ROT_IRQ_STATUS_COMPLETE
;
90 return ROT_IRQ_STATUS_ILLEGAL
;
93 static irqreturn_t
rotator_irq_handler(int irq
, void *arg
)
95 struct rot_context
*rot
= arg
;
96 enum rot_irq_status irq_status
;
99 /* Get execution result */
100 irq_status
= rotator_reg_get_irq_status(rot
);
103 val
= rot_read(ROT_STATUS
);
104 val
|= ROT_STATUS_IRQ_PENDING((u32
)irq_status
);
105 rot_write(val
, ROT_STATUS
);
108 struct exynos_drm_ipp_task
*task
= rot
->task
;
111 pm_runtime_mark_last_busy(rot
->dev
);
112 pm_runtime_put_autosuspend(rot
->dev
);
113 exynos_drm_ipp_task_done(task
,
114 irq_status
== ROT_IRQ_STATUS_COMPLETE
? 0 : -EINVAL
);
120 static void rotator_src_set_fmt(struct rot_context
*rot
, u32 fmt
)
124 val
= rot_read(ROT_CONTROL
);
125 val
&= ~ROT_CONTROL_FMT_MASK
;
128 case DRM_FORMAT_NV12
:
129 val
|= ROT_CONTROL_FMT_YCBCR420_2P
;
131 case DRM_FORMAT_XRGB8888
:
132 val
|= ROT_CONTROL_FMT_RGB888
;
136 rot_write(val
, ROT_CONTROL
);
139 static void rotator_src_set_buf(struct rot_context
*rot
,
140 struct exynos_drm_ipp_buffer
*buf
)
144 /* Set buffer size configuration */
145 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
146 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
147 rot_write(val
, ROT_SRC_BUF_SIZE
);
149 /* Set crop image position configuration */
150 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
151 rot_write(val
, ROT_SRC_CROP_POS
);
152 val
= ROT_SRC_CROP_SIZE_H(buf
->rect
.h
) |
153 ROT_SRC_CROP_SIZE_W(buf
->rect
.w
);
154 rot_write(val
, ROT_SRC_CROP_SIZE
);
156 /* Set buffer DMA address */
157 rot_write(buf
->dma_addr
[0], ROT_SRC_BUF_ADDR(0));
158 rot_write(buf
->dma_addr
[1], ROT_SRC_BUF_ADDR(1));
161 static void rotator_dst_set_transf(struct rot_context
*rot
,
162 unsigned int rotation
)
166 /* Set transform configuration */
167 val
= rot_read(ROT_CONTROL
);
168 val
&= ~ROT_CONTROL_FLIP_MASK
;
170 if (rotation
& DRM_MODE_REFLECT_X
)
171 val
|= ROT_CONTROL_FLIP_VERTICAL
;
172 if (rotation
& DRM_MODE_REFLECT_Y
)
173 val
|= ROT_CONTROL_FLIP_HORIZONTAL
;
175 val
&= ~ROT_CONTROL_ROT_MASK
;
177 if (rotation
& DRM_MODE_ROTATE_90
)
178 val
|= ROT_CONTROL_ROT_90
;
179 else if (rotation
& DRM_MODE_ROTATE_180
)
180 val
|= ROT_CONTROL_ROT_180
;
181 else if (rotation
& DRM_MODE_ROTATE_270
)
182 val
|= ROT_CONTROL_ROT_270
;
184 rot_write(val
, ROT_CONTROL
);
187 static void rotator_dst_set_buf(struct rot_context
*rot
,
188 struct exynos_drm_ipp_buffer
*buf
)
192 /* Set buffer size configuration */
193 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
194 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
195 rot_write(val
, ROT_DST_BUF_SIZE
);
197 /* Set crop image position configuration */
198 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
199 rot_write(val
, ROT_DST_CROP_POS
);
201 /* Set buffer DMA address */
202 rot_write(buf
->dma_addr
[0], ROT_DST_BUF_ADDR(0));
203 rot_write(buf
->dma_addr
[1], ROT_DST_BUF_ADDR(1));
206 static void rotator_start(struct rot_context
*rot
)
210 /* Set interrupt enable */
211 rotator_reg_set_irq(rot
, true);
213 val
= rot_read(ROT_CONTROL
);
214 val
|= ROT_CONTROL_START
;
215 rot_write(val
, ROT_CONTROL
);
218 static int rotator_commit(struct exynos_drm_ipp
*ipp
,
219 struct exynos_drm_ipp_task
*task
)
221 struct rot_context
*rot
=
222 container_of(ipp
, struct rot_context
, ipp
);
224 pm_runtime_get_sync(rot
->dev
);
227 rotator_src_set_fmt(rot
, task
->src
.buf
.fourcc
);
228 rotator_src_set_buf(rot
, &task
->src
);
229 rotator_dst_set_transf(rot
, task
->transform
.rotation
);
230 rotator_dst_set_buf(rot
, &task
->dst
);
236 static const struct exynos_drm_ipp_funcs ipp_funcs
= {
237 .commit
= rotator_commit
,
240 static int rotator_bind(struct device
*dev
, struct device
*master
, void *data
)
242 struct rot_context
*rot
= dev_get_drvdata(dev
);
243 struct drm_device
*drm_dev
= data
;
244 struct exynos_drm_ipp
*ipp
= &rot
->ipp
;
246 rot
->drm_dev
= drm_dev
;
247 drm_iommu_attach_device(drm_dev
, dev
);
249 exynos_drm_ipp_register(drm_dev
, ipp
, &ipp_funcs
,
250 DRM_EXYNOS_IPP_CAP_CROP
| DRM_EXYNOS_IPP_CAP_ROTATE
,
251 rot
->formats
, rot
->num_formats
, "rotator");
253 dev_info(dev
, "The exynos rotator has been probed successfully\n");
258 static void rotator_unbind(struct device
*dev
, struct device
*master
,
261 struct rot_context
*rot
= dev_get_drvdata(dev
);
262 struct drm_device
*drm_dev
= data
;
263 struct exynos_drm_ipp
*ipp
= &rot
->ipp
;
265 exynos_drm_ipp_unregister(drm_dev
, ipp
);
266 drm_iommu_detach_device(rot
->drm_dev
, rot
->dev
);
269 static const struct component_ops rotator_component_ops
= {
270 .bind
= rotator_bind
,
271 .unbind
= rotator_unbind
,
274 static int rotator_probe(struct platform_device
*pdev
)
276 struct device
*dev
= &pdev
->dev
;
277 struct resource
*regs_res
;
278 struct rot_context
*rot
;
279 const struct rot_variant
*variant
;
283 rot
= devm_kzalloc(dev
, sizeof(*rot
), GFP_KERNEL
);
287 variant
= of_device_get_match_data(dev
);
288 rot
->formats
= variant
->formats
;
289 rot
->num_formats
= variant
->num_formats
;
291 regs_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
292 rot
->regs
= devm_ioremap_resource(dev
, regs_res
);
293 if (IS_ERR(rot
->regs
))
294 return PTR_ERR(rot
->regs
);
296 irq
= platform_get_irq(pdev
, 0);
298 dev_err(dev
, "failed to get irq\n");
302 ret
= devm_request_irq(dev
, irq
, rotator_irq_handler
, 0, dev_name(dev
),
305 dev_err(dev
, "failed to request irq\n");
309 rot
->clock
= devm_clk_get(dev
, "rotator");
310 if (IS_ERR(rot
->clock
)) {
311 dev_err(dev
, "failed to get clock\n");
312 return PTR_ERR(rot
->clock
);
315 pm_runtime_use_autosuspend(dev
);
316 pm_runtime_set_autosuspend_delay(dev
, ROTATOR_AUTOSUSPEND_DELAY
);
317 pm_runtime_enable(dev
);
318 platform_set_drvdata(pdev
, rot
);
320 ret
= component_add(dev
, &rotator_component_ops
);
327 pm_runtime_dont_use_autosuspend(dev
);
328 pm_runtime_disable(dev
);
332 static int rotator_remove(struct platform_device
*pdev
)
334 struct device
*dev
= &pdev
->dev
;
336 component_del(dev
, &rotator_component_ops
);
337 pm_runtime_dont_use_autosuspend(dev
);
338 pm_runtime_disable(dev
);
344 static int rotator_runtime_suspend(struct device
*dev
)
346 struct rot_context
*rot
= dev_get_drvdata(dev
);
348 clk_disable_unprepare(rot
->clock
);
352 static int rotator_runtime_resume(struct device
*dev
)
354 struct rot_context
*rot
= dev_get_drvdata(dev
);
356 return clk_prepare_enable(rot
->clock
);
360 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits
[] = {
361 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_16K
}, .v
= { 8, SZ_16K
}) },
362 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
365 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits
[] = {
366 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
367 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
370 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits
[] = {
371 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
372 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 2, .v
.align
= 2) },
375 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits
[] = {
376 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_64K
}, .v
= { 32, SZ_64K
}) },
377 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
380 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits
[] = {
381 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_32K
}, .v
= { 32, SZ_32K
}) },
382 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
385 static const struct exynos_drm_ipp_formats rotator_4210_formats
[] = {
386 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4210_rbg888_limits
) },
387 { IPP_SRCDST_FORMAT(NV12
, rotator_4210_yuv_limits
) },
390 static const struct exynos_drm_ipp_formats rotator_4412_formats
[] = {
391 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4412_rbg888_limits
) },
392 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
395 static const struct exynos_drm_ipp_formats rotator_5250_formats
[] = {
396 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_5250_rbg888_limits
) },
397 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
400 static const struct rot_variant rotator_4210_data
= {
401 .formats
= rotator_4210_formats
,
402 .num_formats
= ARRAY_SIZE(rotator_4210_formats
),
405 static const struct rot_variant rotator_4412_data
= {
406 .formats
= rotator_4412_formats
,
407 .num_formats
= ARRAY_SIZE(rotator_4412_formats
),
410 static const struct rot_variant rotator_5250_data
= {
411 .formats
= rotator_5250_formats
,
412 .num_formats
= ARRAY_SIZE(rotator_5250_formats
),
415 static const struct of_device_id exynos_rotator_match
[] = {
417 .compatible
= "samsung,exynos4210-rotator",
418 .data
= &rotator_4210_data
,
420 .compatible
= "samsung,exynos4212-rotator",
421 .data
= &rotator_4412_data
,
423 .compatible
= "samsung,exynos5250-rotator",
424 .data
= &rotator_5250_data
,
428 MODULE_DEVICE_TABLE(of
, exynos_rotator_match
);
430 static const struct dev_pm_ops rotator_pm_ops
= {
431 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
432 pm_runtime_force_resume
)
433 SET_RUNTIME_PM_OPS(rotator_runtime_suspend
, rotator_runtime_resume
,
437 struct platform_driver rotator_driver
= {
438 .probe
= rotator_probe
,
439 .remove
= rotator_remove
,
441 .name
= "exynos-rotator",
442 .owner
= THIS_MODULE
,
443 .pm
= &rotator_pm_ops
,
444 .of_match_table
= exynos_rotator_match
,