1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2012 Samsung Electronics Co.Ltd
5 * YoungJun Cho <yj44.cho@samsung.com>
6 * Eunchul Kim <chulspro.kim@samsung.com>
10 #include <linux/component.h>
11 #include <linux/err.h>
12 #include <linux/interrupt.h>
14 #include <linux/kernel.h>
15 #include <linux/of_device.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm_runtime.h>
18 #include <linux/sizes.h>
20 #include <drm/drm_fourcc.h>
21 #include <drm/exynos_drm.h>
23 #include "exynos_drm_drv.h"
24 #include "exynos_drm_ipp.h"
25 #include "regs-rotator.h"
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.
33 #define ROTATOR_AUTOSUSPEND_DELAY 2000
35 #define rot_read(offset) readl(rot->regs + (offset))
36 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
39 ROT_IRQ_STATUS_COMPLETE
= 8,
40 ROT_IRQ_STATUS_ILLEGAL
= 9,
44 const struct exynos_drm_ipp_formats
*formats
;
45 unsigned int num_formats
;
49 * A structure of rotator context.
50 * @ippdrv: prepare initialization using ippdrv.
51 * @regs: memory mapped io registers.
52 * @clock: rotator gate clock.
53 * @limit_tbl: limitation of rotator.
57 struct exynos_drm_ipp ipp
;
58 struct drm_device
*drm_dev
;
62 const struct exynos_drm_ipp_formats
*formats
;
63 unsigned int num_formats
;
64 struct exynos_drm_ipp_task
*task
;
67 static void rotator_reg_set_irq(struct rot_context
*rot
, bool enable
)
69 u32 val
= rot_read(ROT_CONFIG
);
72 val
|= ROT_CONFIG_IRQ
;
74 val
&= ~ROT_CONFIG_IRQ
;
76 rot_write(val
, ROT_CONFIG
);
79 static enum rot_irq_status
rotator_reg_get_irq_status(struct rot_context
*rot
)
81 u32 val
= rot_read(ROT_STATUS
);
83 val
= ROT_STATUS_IRQ(val
);
85 if (val
== ROT_STATUS_IRQ_VAL_COMPLETE
)
86 return ROT_IRQ_STATUS_COMPLETE
;
88 return ROT_IRQ_STATUS_ILLEGAL
;
91 static irqreturn_t
rotator_irq_handler(int irq
, void *arg
)
93 struct rot_context
*rot
= arg
;
94 enum rot_irq_status irq_status
;
97 /* Get execution result */
98 irq_status
= rotator_reg_get_irq_status(rot
);
101 val
= rot_read(ROT_STATUS
);
102 val
|= ROT_STATUS_IRQ_PENDING((u32
)irq_status
);
103 rot_write(val
, ROT_STATUS
);
106 struct exynos_drm_ipp_task
*task
= rot
->task
;
109 pm_runtime_mark_last_busy(rot
->dev
);
110 pm_runtime_put_autosuspend(rot
->dev
);
111 exynos_drm_ipp_task_done(task
,
112 irq_status
== ROT_IRQ_STATUS_COMPLETE
? 0 : -EINVAL
);
118 static void rotator_src_set_fmt(struct rot_context
*rot
, u32 fmt
)
122 val
= rot_read(ROT_CONTROL
);
123 val
&= ~ROT_CONTROL_FMT_MASK
;
126 case DRM_FORMAT_NV12
:
127 val
|= ROT_CONTROL_FMT_YCBCR420_2P
;
129 case DRM_FORMAT_XRGB8888
:
130 val
|= ROT_CONTROL_FMT_RGB888
;
134 rot_write(val
, ROT_CONTROL
);
137 static void rotator_src_set_buf(struct rot_context
*rot
,
138 struct exynos_drm_ipp_buffer
*buf
)
142 /* Set buffer size configuration */
143 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
144 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
145 rot_write(val
, ROT_SRC_BUF_SIZE
);
147 /* Set crop image position configuration */
148 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
149 rot_write(val
, ROT_SRC_CROP_POS
);
150 val
= ROT_SRC_CROP_SIZE_H(buf
->rect
.h
) |
151 ROT_SRC_CROP_SIZE_W(buf
->rect
.w
);
152 rot_write(val
, ROT_SRC_CROP_SIZE
);
154 /* Set buffer DMA address */
155 rot_write(buf
->dma_addr
[0], ROT_SRC_BUF_ADDR(0));
156 rot_write(buf
->dma_addr
[1], ROT_SRC_BUF_ADDR(1));
159 static void rotator_dst_set_transf(struct rot_context
*rot
,
160 unsigned int rotation
)
164 /* Set transform configuration */
165 val
= rot_read(ROT_CONTROL
);
166 val
&= ~ROT_CONTROL_FLIP_MASK
;
168 if (rotation
& DRM_MODE_REFLECT_X
)
169 val
|= ROT_CONTROL_FLIP_VERTICAL
;
170 if (rotation
& DRM_MODE_REFLECT_Y
)
171 val
|= ROT_CONTROL_FLIP_HORIZONTAL
;
173 val
&= ~ROT_CONTROL_ROT_MASK
;
175 if (rotation
& DRM_MODE_ROTATE_90
)
176 val
|= ROT_CONTROL_ROT_90
;
177 else if (rotation
& DRM_MODE_ROTATE_180
)
178 val
|= ROT_CONTROL_ROT_180
;
179 else if (rotation
& DRM_MODE_ROTATE_270
)
180 val
|= ROT_CONTROL_ROT_270
;
182 rot_write(val
, ROT_CONTROL
);
185 static void rotator_dst_set_buf(struct rot_context
*rot
,
186 struct exynos_drm_ipp_buffer
*buf
)
190 /* Set buffer size configuration */
191 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
192 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
193 rot_write(val
, ROT_DST_BUF_SIZE
);
195 /* Set crop image position configuration */
196 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
197 rot_write(val
, ROT_DST_CROP_POS
);
199 /* Set buffer DMA address */
200 rot_write(buf
->dma_addr
[0], ROT_DST_BUF_ADDR(0));
201 rot_write(buf
->dma_addr
[1], ROT_DST_BUF_ADDR(1));
204 static void rotator_start(struct rot_context
*rot
)
208 /* Set interrupt enable */
209 rotator_reg_set_irq(rot
, true);
211 val
= rot_read(ROT_CONTROL
);
212 val
|= ROT_CONTROL_START
;
213 rot_write(val
, ROT_CONTROL
);
216 static int rotator_commit(struct exynos_drm_ipp
*ipp
,
217 struct exynos_drm_ipp_task
*task
)
219 struct rot_context
*rot
=
220 container_of(ipp
, struct rot_context
, ipp
);
222 pm_runtime_get_sync(rot
->dev
);
225 rotator_src_set_fmt(rot
, task
->src
.buf
.fourcc
);
226 rotator_src_set_buf(rot
, &task
->src
);
227 rotator_dst_set_transf(rot
, task
->transform
.rotation
);
228 rotator_dst_set_buf(rot
, &task
->dst
);
234 static const struct exynos_drm_ipp_funcs ipp_funcs
= {
235 .commit
= rotator_commit
,
238 static int rotator_bind(struct device
*dev
, struct device
*master
, void *data
)
240 struct rot_context
*rot
= dev_get_drvdata(dev
);
241 struct drm_device
*drm_dev
= data
;
242 struct exynos_drm_ipp
*ipp
= &rot
->ipp
;
244 rot
->drm_dev
= drm_dev
;
245 ipp
->drm_dev
= drm_dev
;
246 exynos_drm_register_dma(drm_dev
, dev
);
248 exynos_drm_ipp_register(dev
, ipp
, &ipp_funcs
,
249 DRM_EXYNOS_IPP_CAP_CROP
| DRM_EXYNOS_IPP_CAP_ROTATE
,
250 rot
->formats
, rot
->num_formats
, "rotator");
252 dev_info(dev
, "The exynos rotator has been probed successfully\n");
257 static void rotator_unbind(struct device
*dev
, struct device
*master
,
260 struct rot_context
*rot
= dev_get_drvdata(dev
);
261 struct exynos_drm_ipp
*ipp
= &rot
->ipp
;
263 exynos_drm_ipp_unregister(dev
, ipp
);
264 exynos_drm_unregister_dma(rot
->drm_dev
, rot
->dev
);
267 static const struct component_ops rotator_component_ops
= {
268 .bind
= rotator_bind
,
269 .unbind
= rotator_unbind
,
272 static int rotator_probe(struct platform_device
*pdev
)
274 struct device
*dev
= &pdev
->dev
;
275 struct resource
*regs_res
;
276 struct rot_context
*rot
;
277 const struct rot_variant
*variant
;
281 rot
= devm_kzalloc(dev
, sizeof(*rot
), GFP_KERNEL
);
285 variant
= of_device_get_match_data(dev
);
286 rot
->formats
= variant
->formats
;
287 rot
->num_formats
= variant
->num_formats
;
289 regs_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
290 rot
->regs
= devm_ioremap_resource(dev
, regs_res
);
291 if (IS_ERR(rot
->regs
))
292 return PTR_ERR(rot
->regs
);
294 irq
= platform_get_irq(pdev
, 0);
296 dev_err(dev
, "failed to get irq\n");
300 ret
= devm_request_irq(dev
, irq
, rotator_irq_handler
, 0, dev_name(dev
),
303 dev_err(dev
, "failed to request irq\n");
307 rot
->clock
= devm_clk_get(dev
, "rotator");
308 if (IS_ERR(rot
->clock
)) {
309 dev_err(dev
, "failed to get clock\n");
310 return PTR_ERR(rot
->clock
);
313 pm_runtime_use_autosuspend(dev
);
314 pm_runtime_set_autosuspend_delay(dev
, ROTATOR_AUTOSUSPEND_DELAY
);
315 pm_runtime_enable(dev
);
316 platform_set_drvdata(pdev
, rot
);
318 ret
= component_add(dev
, &rotator_component_ops
);
325 pm_runtime_dont_use_autosuspend(dev
);
326 pm_runtime_disable(dev
);
330 static int rotator_remove(struct platform_device
*pdev
)
332 struct device
*dev
= &pdev
->dev
;
334 component_del(dev
, &rotator_component_ops
);
335 pm_runtime_dont_use_autosuspend(dev
);
336 pm_runtime_disable(dev
);
342 static int rotator_runtime_suspend(struct device
*dev
)
344 struct rot_context
*rot
= dev_get_drvdata(dev
);
346 clk_disable_unprepare(rot
->clock
);
350 static int rotator_runtime_resume(struct device
*dev
)
352 struct rot_context
*rot
= dev_get_drvdata(dev
);
354 return clk_prepare_enable(rot
->clock
);
358 static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits
[] = {
359 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_16K
}, .v
= { 8, SZ_16K
}) },
360 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 2, .v
.align
= 2) },
363 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits
[] = {
364 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_16K
}, .v
= { 8, SZ_16K
}) },
365 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
368 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits
[] = {
369 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
370 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
373 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits
[] = {
374 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
375 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 2, .v
.align
= 2) },
378 static const struct drm_exynos_ipp_limit rotator_s5pv210_yuv_limits
[] = {
379 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_64K
}, .v
= { 32, SZ_64K
}) },
380 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
383 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits
[] = {
384 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_64K
}, .v
= { 32, SZ_64K
}) },
385 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
388 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits
[] = {
389 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_32K
}, .v
= { 32, SZ_32K
}) },
390 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
393 static const struct exynos_drm_ipp_formats rotator_s5pv210_formats
[] = {
394 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_s5pv210_rbg888_limits
) },
395 { IPP_SRCDST_FORMAT(NV12
, rotator_s5pv210_yuv_limits
) },
398 static const struct exynos_drm_ipp_formats rotator_4210_formats
[] = {
399 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4210_rbg888_limits
) },
400 { IPP_SRCDST_FORMAT(NV12
, rotator_4210_yuv_limits
) },
403 static const struct exynos_drm_ipp_formats rotator_4412_formats
[] = {
404 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4412_rbg888_limits
) },
405 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
408 static const struct exynos_drm_ipp_formats rotator_5250_formats
[] = {
409 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_5250_rbg888_limits
) },
410 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
413 static const struct rot_variant rotator_s5pv210_data
= {
414 .formats
= rotator_s5pv210_formats
,
415 .num_formats
= ARRAY_SIZE(rotator_s5pv210_formats
),
418 static const struct rot_variant rotator_4210_data
= {
419 .formats
= rotator_4210_formats
,
420 .num_formats
= ARRAY_SIZE(rotator_4210_formats
),
423 static const struct rot_variant rotator_4412_data
= {
424 .formats
= rotator_4412_formats
,
425 .num_formats
= ARRAY_SIZE(rotator_4412_formats
),
428 static const struct rot_variant rotator_5250_data
= {
429 .formats
= rotator_5250_formats
,
430 .num_formats
= ARRAY_SIZE(rotator_5250_formats
),
433 static const struct of_device_id exynos_rotator_match
[] = {
435 .compatible
= "samsung,s5pv210-rotator",
436 .data
= &rotator_s5pv210_data
,
438 .compatible
= "samsung,exynos4210-rotator",
439 .data
= &rotator_4210_data
,
441 .compatible
= "samsung,exynos4212-rotator",
442 .data
= &rotator_4412_data
,
444 .compatible
= "samsung,exynos5250-rotator",
445 .data
= &rotator_5250_data
,
449 MODULE_DEVICE_TABLE(of
, exynos_rotator_match
);
451 static const struct dev_pm_ops rotator_pm_ops
= {
452 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
453 pm_runtime_force_resume
)
454 SET_RUNTIME_PM_OPS(rotator_runtime_suspend
, rotator_runtime_resume
,
458 struct platform_driver rotator_driver
= {
459 .probe
= rotator_probe
,
460 .remove
= rotator_remove
,
462 .name
= "exynos-rotator",
463 .owner
= THIS_MODULE
,
464 .pm
= &rotator_pm_ops
,
465 .of_match_table
= exynos_rotator_match
,