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
;
63 const struct exynos_drm_ipp_formats
*formats
;
64 unsigned int num_formats
;
65 struct exynos_drm_ipp_task
*task
;
68 static void rotator_reg_set_irq(struct rot_context
*rot
, bool enable
)
70 u32 val
= rot_read(ROT_CONFIG
);
73 val
|= ROT_CONFIG_IRQ
;
75 val
&= ~ROT_CONFIG_IRQ
;
77 rot_write(val
, ROT_CONFIG
);
80 static enum rot_irq_status
rotator_reg_get_irq_status(struct rot_context
*rot
)
82 u32 val
= rot_read(ROT_STATUS
);
84 val
= ROT_STATUS_IRQ(val
);
86 if (val
== ROT_STATUS_IRQ_VAL_COMPLETE
)
87 return ROT_IRQ_STATUS_COMPLETE
;
89 return ROT_IRQ_STATUS_ILLEGAL
;
92 static irqreturn_t
rotator_irq_handler(int irq
, void *arg
)
94 struct rot_context
*rot
= arg
;
95 enum rot_irq_status irq_status
;
98 /* Get execution result */
99 irq_status
= rotator_reg_get_irq_status(rot
);
102 val
= rot_read(ROT_STATUS
);
103 val
|= ROT_STATUS_IRQ_PENDING((u32
)irq_status
);
104 rot_write(val
, ROT_STATUS
);
107 struct exynos_drm_ipp_task
*task
= rot
->task
;
110 pm_runtime_mark_last_busy(rot
->dev
);
111 pm_runtime_put_autosuspend(rot
->dev
);
112 exynos_drm_ipp_task_done(task
,
113 irq_status
== ROT_IRQ_STATUS_COMPLETE
? 0 : -EINVAL
);
119 static void rotator_src_set_fmt(struct rot_context
*rot
, u32 fmt
)
123 val
= rot_read(ROT_CONTROL
);
124 val
&= ~ROT_CONTROL_FMT_MASK
;
127 case DRM_FORMAT_NV12
:
128 val
|= ROT_CONTROL_FMT_YCBCR420_2P
;
130 case DRM_FORMAT_XRGB8888
:
131 val
|= ROT_CONTROL_FMT_RGB888
;
135 rot_write(val
, ROT_CONTROL
);
138 static void rotator_src_set_buf(struct rot_context
*rot
,
139 struct exynos_drm_ipp_buffer
*buf
)
143 /* Set buffer size configuration */
144 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
145 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
146 rot_write(val
, ROT_SRC_BUF_SIZE
);
148 /* Set crop image position configuration */
149 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
150 rot_write(val
, ROT_SRC_CROP_POS
);
151 val
= ROT_SRC_CROP_SIZE_H(buf
->rect
.h
) |
152 ROT_SRC_CROP_SIZE_W(buf
->rect
.w
);
153 rot_write(val
, ROT_SRC_CROP_SIZE
);
155 /* Set buffer DMA address */
156 rot_write(buf
->dma_addr
[0], ROT_SRC_BUF_ADDR(0));
157 rot_write(buf
->dma_addr
[1], ROT_SRC_BUF_ADDR(1));
160 static void rotator_dst_set_transf(struct rot_context
*rot
,
161 unsigned int rotation
)
165 /* Set transform configuration */
166 val
= rot_read(ROT_CONTROL
);
167 val
&= ~ROT_CONTROL_FLIP_MASK
;
169 if (rotation
& DRM_MODE_REFLECT_X
)
170 val
|= ROT_CONTROL_FLIP_VERTICAL
;
171 if (rotation
& DRM_MODE_REFLECT_Y
)
172 val
|= ROT_CONTROL_FLIP_HORIZONTAL
;
174 val
&= ~ROT_CONTROL_ROT_MASK
;
176 if (rotation
& DRM_MODE_ROTATE_90
)
177 val
|= ROT_CONTROL_ROT_90
;
178 else if (rotation
& DRM_MODE_ROTATE_180
)
179 val
|= ROT_CONTROL_ROT_180
;
180 else if (rotation
& DRM_MODE_ROTATE_270
)
181 val
|= ROT_CONTROL_ROT_270
;
183 rot_write(val
, ROT_CONTROL
);
186 static void rotator_dst_set_buf(struct rot_context
*rot
,
187 struct exynos_drm_ipp_buffer
*buf
)
191 /* Set buffer size configuration */
192 val
= ROT_SET_BUF_SIZE_H(buf
->buf
.height
) |
193 ROT_SET_BUF_SIZE_W(buf
->buf
.pitch
[0] / buf
->format
->cpp
[0]);
194 rot_write(val
, ROT_DST_BUF_SIZE
);
196 /* Set crop image position configuration */
197 val
= ROT_CROP_POS_Y(buf
->rect
.y
) | ROT_CROP_POS_X(buf
->rect
.x
);
198 rot_write(val
, ROT_DST_CROP_POS
);
200 /* Set buffer DMA address */
201 rot_write(buf
->dma_addr
[0], ROT_DST_BUF_ADDR(0));
202 rot_write(buf
->dma_addr
[1], ROT_DST_BUF_ADDR(1));
205 static void rotator_start(struct rot_context
*rot
)
209 /* Set interrupt enable */
210 rotator_reg_set_irq(rot
, true);
212 val
= rot_read(ROT_CONTROL
);
213 val
|= ROT_CONTROL_START
;
214 rot_write(val
, ROT_CONTROL
);
217 static int rotator_commit(struct exynos_drm_ipp
*ipp
,
218 struct exynos_drm_ipp_task
*task
)
220 struct rot_context
*rot
=
221 container_of(ipp
, struct rot_context
, ipp
);
223 pm_runtime_get_sync(rot
->dev
);
226 rotator_src_set_fmt(rot
, task
->src
.buf
.fourcc
);
227 rotator_src_set_buf(rot
, &task
->src
);
228 rotator_dst_set_transf(rot
, task
->transform
.rotation
);
229 rotator_dst_set_buf(rot
, &task
->dst
);
235 static const struct exynos_drm_ipp_funcs ipp_funcs
= {
236 .commit
= rotator_commit
,
239 static int rotator_bind(struct device
*dev
, struct device
*master
, void *data
)
241 struct rot_context
*rot
= dev_get_drvdata(dev
);
242 struct drm_device
*drm_dev
= data
;
243 struct exynos_drm_ipp
*ipp
= &rot
->ipp
;
245 rot
->drm_dev
= drm_dev
;
246 ipp
->drm_dev
= drm_dev
;
247 exynos_drm_register_dma(drm_dev
, dev
, &rot
->dma_priv
);
249 exynos_drm_ipp_register(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 exynos_drm_ipp
*ipp
= &rot
->ipp
;
264 exynos_drm_ipp_unregister(dev
, ipp
);
265 exynos_drm_unregister_dma(rot
->drm_dev
, rot
->dev
, &rot
->dma_priv
);
268 static const struct component_ops rotator_component_ops
= {
269 .bind
= rotator_bind
,
270 .unbind
= rotator_unbind
,
273 static int rotator_probe(struct platform_device
*pdev
)
275 struct device
*dev
= &pdev
->dev
;
276 struct resource
*regs_res
;
277 struct rot_context
*rot
;
278 const struct rot_variant
*variant
;
282 rot
= devm_kzalloc(dev
, sizeof(*rot
), GFP_KERNEL
);
286 variant
= of_device_get_match_data(dev
);
287 rot
->formats
= variant
->formats
;
288 rot
->num_formats
= variant
->num_formats
;
290 regs_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
291 rot
->regs
= devm_ioremap_resource(dev
, regs_res
);
292 if (IS_ERR(rot
->regs
))
293 return PTR_ERR(rot
->regs
);
295 irq
= platform_get_irq(pdev
, 0);
299 ret
= devm_request_irq(dev
, irq
, rotator_irq_handler
, 0, dev_name(dev
),
302 dev_err(dev
, "failed to request irq\n");
306 rot
->clock
= devm_clk_get(dev
, "rotator");
307 if (IS_ERR(rot
->clock
)) {
308 dev_err(dev
, "failed to get clock\n");
309 return PTR_ERR(rot
->clock
);
312 pm_runtime_use_autosuspend(dev
);
313 pm_runtime_set_autosuspend_delay(dev
, ROTATOR_AUTOSUSPEND_DELAY
);
314 pm_runtime_enable(dev
);
315 platform_set_drvdata(pdev
, rot
);
317 ret
= component_add(dev
, &rotator_component_ops
);
324 pm_runtime_dont_use_autosuspend(dev
);
325 pm_runtime_disable(dev
);
329 static int rotator_remove(struct platform_device
*pdev
)
331 struct device
*dev
= &pdev
->dev
;
333 component_del(dev
, &rotator_component_ops
);
334 pm_runtime_dont_use_autosuspend(dev
);
335 pm_runtime_disable(dev
);
341 static int rotator_runtime_suspend(struct device
*dev
)
343 struct rot_context
*rot
= dev_get_drvdata(dev
);
345 clk_disable_unprepare(rot
->clock
);
349 static int rotator_runtime_resume(struct device
*dev
)
351 struct rot_context
*rot
= dev_get_drvdata(dev
);
353 return clk_prepare_enable(rot
->clock
);
357 static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits
[] = {
358 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_16K
}, .v
= { 8, SZ_16K
}) },
359 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 2, .v
.align
= 2) },
362 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits
[] = {
363 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_16K
}, .v
= { 8, SZ_16K
}) },
364 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
367 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits
[] = {
368 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
369 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 4, .v
.align
= 4) },
372 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits
[] = {
373 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 8, SZ_8K
}, .v
= { 8, SZ_8K
}) },
374 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 2, .v
.align
= 2) },
377 static const struct drm_exynos_ipp_limit rotator_s5pv210_yuv_limits
[] = {
378 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_64K
}, .v
= { 32, SZ_64K
}) },
379 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
382 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits
[] = {
383 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_64K
}, .v
= { 32, SZ_64K
}) },
384 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
387 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits
[] = {
388 { IPP_SIZE_LIMIT(BUFFER
, .h
= { 32, SZ_32K
}, .v
= { 32, SZ_32K
}) },
389 { IPP_SIZE_LIMIT(AREA
, .h
.align
= 8, .v
.align
= 8) },
392 static const struct exynos_drm_ipp_formats rotator_s5pv210_formats
[] = {
393 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_s5pv210_rbg888_limits
) },
394 { IPP_SRCDST_FORMAT(NV12
, rotator_s5pv210_yuv_limits
) },
397 static const struct exynos_drm_ipp_formats rotator_4210_formats
[] = {
398 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4210_rbg888_limits
) },
399 { IPP_SRCDST_FORMAT(NV12
, rotator_4210_yuv_limits
) },
402 static const struct exynos_drm_ipp_formats rotator_4412_formats
[] = {
403 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_4412_rbg888_limits
) },
404 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
407 static const struct exynos_drm_ipp_formats rotator_5250_formats
[] = {
408 { IPP_SRCDST_FORMAT(XRGB8888
, rotator_5250_rbg888_limits
) },
409 { IPP_SRCDST_FORMAT(NV12
, rotator_4412_yuv_limits
) },
412 static const struct rot_variant rotator_s5pv210_data
= {
413 .formats
= rotator_s5pv210_formats
,
414 .num_formats
= ARRAY_SIZE(rotator_s5pv210_formats
),
417 static const struct rot_variant rotator_4210_data
= {
418 .formats
= rotator_4210_formats
,
419 .num_formats
= ARRAY_SIZE(rotator_4210_formats
),
422 static const struct rot_variant rotator_4412_data
= {
423 .formats
= rotator_4412_formats
,
424 .num_formats
= ARRAY_SIZE(rotator_4412_formats
),
427 static const struct rot_variant rotator_5250_data
= {
428 .formats
= rotator_5250_formats
,
429 .num_formats
= ARRAY_SIZE(rotator_5250_formats
),
432 static const struct of_device_id exynos_rotator_match
[] = {
434 .compatible
= "samsung,s5pv210-rotator",
435 .data
= &rotator_s5pv210_data
,
437 .compatible
= "samsung,exynos4210-rotator",
438 .data
= &rotator_4210_data
,
440 .compatible
= "samsung,exynos4212-rotator",
441 .data
= &rotator_4412_data
,
443 .compatible
= "samsung,exynos5250-rotator",
444 .data
= &rotator_5250_data
,
448 MODULE_DEVICE_TABLE(of
, exynos_rotator_match
);
450 static const struct dev_pm_ops rotator_pm_ops
= {
451 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
452 pm_runtime_force_resume
)
453 SET_RUNTIME_PM_OPS(rotator_runtime_suspend
, rotator_runtime_resume
,
457 struct platform_driver rotator_driver
= {
458 .probe
= rotator_probe
,
459 .remove
= rotator_remove
,
461 .name
= "exynos-rotator",
462 .owner
= THIS_MODULE
,
463 .pm
= &rotator_pm_ops
,
464 .of_match_table
= exynos_rotator_match
,