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>
19 #include <drm/exynos_drm.h>
21 #include "exynos_drm_drv.h"
22 #include "exynos_drm_ipp.h"
23 #include "regs-rotator.h"
26 * Rotator supports image crop/rotator and input/output DMA operations.
27 * input DMA reads image data from the memory.
28 * output DMA writes image data to memory.
31 #define ROTATOR_AUTOSUSPEND_DELAY 2000
33 #define rot_read(offset) readl(rot->regs + (offset))
34 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
37 ROT_IRQ_STATUS_COMPLETE = 8,
38 ROT_IRQ_STATUS_ILLEGAL = 9,
42 const struct exynos_drm_ipp_formats *formats;
43 unsigned int num_formats;
47 * A structure of rotator context.
48 * @ippdrv: prepare initialization using ippdrv.
49 * @regs: memory mapped io registers.
50 * @clock: rotator gate clock.
51 * @limit_tbl: limitation of rotator.
55 struct exynos_drm_ipp ipp;
56 struct drm_device *drm_dev;
60 const struct exynos_drm_ipp_formats *formats;
61 unsigned int num_formats;
62 struct exynos_drm_ipp_task *task;
65 static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
67 u32 val = rot_read(ROT_CONFIG);
70 val |= ROT_CONFIG_IRQ;
72 val &= ~ROT_CONFIG_IRQ;
74 rot_write(val, ROT_CONFIG);
77 static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
79 u32 val = rot_read(ROT_STATUS);
81 val = ROT_STATUS_IRQ(val);
83 if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
84 return ROT_IRQ_STATUS_COMPLETE;
86 return ROT_IRQ_STATUS_ILLEGAL;
89 static irqreturn_t rotator_irq_handler(int irq, void *arg)
91 struct rot_context *rot = arg;
92 enum rot_irq_status irq_status;
95 /* Get execution result */
96 irq_status = rotator_reg_get_irq_status(rot);
99 val = rot_read(ROT_STATUS);
100 val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
101 rot_write(val, ROT_STATUS);
104 struct exynos_drm_ipp_task *task = rot->task;
107 pm_runtime_mark_last_busy(rot->dev);
108 pm_runtime_put_autosuspend(rot->dev);
109 exynos_drm_ipp_task_done(task,
110 irq_status == ROT_IRQ_STATUS_COMPLETE ? 0 : -EINVAL);
116 static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt)
120 val = rot_read(ROT_CONTROL);
121 val &= ~ROT_CONTROL_FMT_MASK;
124 case DRM_FORMAT_NV12:
125 val |= ROT_CONTROL_FMT_YCBCR420_2P;
127 case DRM_FORMAT_XRGB8888:
128 val |= ROT_CONTROL_FMT_RGB888;
132 rot_write(val, ROT_CONTROL);
135 static void rotator_src_set_buf(struct rot_context *rot,
136 struct exynos_drm_ipp_buffer *buf)
140 /* Set buffer size configuration */
141 val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
142 ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
143 rot_write(val, ROT_SRC_BUF_SIZE);
145 /* Set crop image position configuration */
146 val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
147 rot_write(val, ROT_SRC_CROP_POS);
148 val = ROT_SRC_CROP_SIZE_H(buf->rect.h) |
149 ROT_SRC_CROP_SIZE_W(buf->rect.w);
150 rot_write(val, ROT_SRC_CROP_SIZE);
152 /* Set buffer DMA address */
153 rot_write(buf->dma_addr[0], ROT_SRC_BUF_ADDR(0));
154 rot_write(buf->dma_addr[1], ROT_SRC_BUF_ADDR(1));
157 static void rotator_dst_set_transf(struct rot_context *rot,
158 unsigned int rotation)
162 /* Set transform configuration */
163 val = rot_read(ROT_CONTROL);
164 val &= ~ROT_CONTROL_FLIP_MASK;
166 if (rotation & DRM_MODE_REFLECT_X)
167 val |= ROT_CONTROL_FLIP_VERTICAL;
168 if (rotation & DRM_MODE_REFLECT_Y)
169 val |= ROT_CONTROL_FLIP_HORIZONTAL;
171 val &= ~ROT_CONTROL_ROT_MASK;
173 if (rotation & DRM_MODE_ROTATE_90)
174 val |= ROT_CONTROL_ROT_90;
175 else if (rotation & DRM_MODE_ROTATE_180)
176 val |= ROT_CONTROL_ROT_180;
177 else if (rotation & DRM_MODE_ROTATE_270)
178 val |= ROT_CONTROL_ROT_270;
180 rot_write(val, ROT_CONTROL);
183 static void rotator_dst_set_buf(struct rot_context *rot,
184 struct exynos_drm_ipp_buffer *buf)
188 /* Set buffer size configuration */
189 val = ROT_SET_BUF_SIZE_H(buf->buf.height) |
190 ROT_SET_BUF_SIZE_W(buf->buf.pitch[0] / buf->format->cpp[0]);
191 rot_write(val, ROT_DST_BUF_SIZE);
193 /* Set crop image position configuration */
194 val = ROT_CROP_POS_Y(buf->rect.y) | ROT_CROP_POS_X(buf->rect.x);
195 rot_write(val, ROT_DST_CROP_POS);
197 /* Set buffer DMA address */
198 rot_write(buf->dma_addr[0], ROT_DST_BUF_ADDR(0));
199 rot_write(buf->dma_addr[1], ROT_DST_BUF_ADDR(1));
202 static void rotator_start(struct rot_context *rot)
206 /* Set interrupt enable */
207 rotator_reg_set_irq(rot, true);
209 val = rot_read(ROT_CONTROL);
210 val |= ROT_CONTROL_START;
211 rot_write(val, ROT_CONTROL);
214 static int rotator_commit(struct exynos_drm_ipp *ipp,
215 struct exynos_drm_ipp_task *task)
217 struct rot_context *rot =
218 container_of(ipp, struct rot_context, ipp);
220 pm_runtime_get_sync(rot->dev);
223 rotator_src_set_fmt(rot, task->src.buf.fourcc);
224 rotator_src_set_buf(rot, &task->src);
225 rotator_dst_set_transf(rot, task->transform.rotation);
226 rotator_dst_set_buf(rot, &task->dst);
232 static const struct exynos_drm_ipp_funcs ipp_funcs = {
233 .commit = rotator_commit,
236 static int rotator_bind(struct device *dev, struct device *master, void *data)
238 struct rot_context *rot = dev_get_drvdata(dev);
239 struct drm_device *drm_dev = data;
240 struct exynos_drm_ipp *ipp = &rot->ipp;
242 rot->drm_dev = drm_dev;
243 ipp->drm_dev = drm_dev;
244 exynos_drm_register_dma(drm_dev, dev);
246 exynos_drm_ipp_register(dev, ipp, &ipp_funcs,
247 DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE,
248 rot->formats, rot->num_formats, "rotator");
250 dev_info(dev, "The exynos rotator has been probed successfully\n");
255 static void rotator_unbind(struct device *dev, struct device *master,
258 struct rot_context *rot = dev_get_drvdata(dev);
259 struct exynos_drm_ipp *ipp = &rot->ipp;
261 exynos_drm_ipp_unregister(dev, ipp);
262 exynos_drm_unregister_dma(rot->drm_dev, rot->dev);
265 static const struct component_ops rotator_component_ops = {
266 .bind = rotator_bind,
267 .unbind = rotator_unbind,
270 static int rotator_probe(struct platform_device *pdev)
272 struct device *dev = &pdev->dev;
273 struct resource *regs_res;
274 struct rot_context *rot;
275 const struct rot_variant *variant;
279 rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
283 variant = of_device_get_match_data(dev);
284 rot->formats = variant->formats;
285 rot->num_formats = variant->num_formats;
287 regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
288 rot->regs = devm_ioremap_resource(dev, regs_res);
289 if (IS_ERR(rot->regs))
290 return PTR_ERR(rot->regs);
292 irq = platform_get_irq(pdev, 0);
294 dev_err(dev, "failed to get irq\n");
298 ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev),
301 dev_err(dev, "failed to request irq\n");
305 rot->clock = devm_clk_get(dev, "rotator");
306 if (IS_ERR(rot->clock)) {
307 dev_err(dev, "failed to get clock\n");
308 return PTR_ERR(rot->clock);
311 pm_runtime_use_autosuspend(dev);
312 pm_runtime_set_autosuspend_delay(dev, ROTATOR_AUTOSUSPEND_DELAY);
313 pm_runtime_enable(dev);
314 platform_set_drvdata(pdev, rot);
316 ret = component_add(dev, &rotator_component_ops);
323 pm_runtime_dont_use_autosuspend(dev);
324 pm_runtime_disable(dev);
328 static int rotator_remove(struct platform_device *pdev)
330 struct device *dev = &pdev->dev;
332 component_del(dev, &rotator_component_ops);
333 pm_runtime_dont_use_autosuspend(dev);
334 pm_runtime_disable(dev);
340 static int rotator_runtime_suspend(struct device *dev)
342 struct rot_context *rot = dev_get_drvdata(dev);
344 clk_disable_unprepare(rot->clock);
348 static int rotator_runtime_resume(struct device *dev)
350 struct rot_context *rot = dev_get_drvdata(dev);
352 return clk_prepare_enable(rot->clock);
356 static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = {
357 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
358 { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
361 static const struct drm_exynos_ipp_limit rotator_4210_rbg888_limits[] = {
362 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) },
363 { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
366 static const struct drm_exynos_ipp_limit rotator_4412_rbg888_limits[] = {
367 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
368 { IPP_SIZE_LIMIT(AREA, .h.align = 4, .v.align = 4) },
371 static const struct drm_exynos_ipp_limit rotator_5250_rbg888_limits[] = {
372 { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_8K }, .v = { 8, SZ_8K }) },
373 { IPP_SIZE_LIMIT(AREA, .h.align = 2, .v.align = 2) },
376 static const struct drm_exynos_ipp_limit rotator_s5pv210_yuv_limits[] = {
377 { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
378 { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
381 static const struct drm_exynos_ipp_limit rotator_4210_yuv_limits[] = {
382 { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_64K }, .v = { 32, SZ_64K }) },
383 { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
386 static const struct drm_exynos_ipp_limit rotator_4412_yuv_limits[] = {
387 { IPP_SIZE_LIMIT(BUFFER, .h = { 32, SZ_32K }, .v = { 32, SZ_32K }) },
388 { IPP_SIZE_LIMIT(AREA, .h.align = 8, .v.align = 8) },
391 static const struct exynos_drm_ipp_formats rotator_s5pv210_formats[] = {
392 { IPP_SRCDST_FORMAT(XRGB8888, rotator_s5pv210_rbg888_limits) },
393 { IPP_SRCDST_FORMAT(NV12, rotator_s5pv210_yuv_limits) },
396 static const struct exynos_drm_ipp_formats rotator_4210_formats[] = {
397 { IPP_SRCDST_FORMAT(XRGB8888, rotator_4210_rbg888_limits) },
398 { IPP_SRCDST_FORMAT(NV12, rotator_4210_yuv_limits) },
401 static const struct exynos_drm_ipp_formats rotator_4412_formats[] = {
402 { IPP_SRCDST_FORMAT(XRGB8888, rotator_4412_rbg888_limits) },
403 { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
406 static const struct exynos_drm_ipp_formats rotator_5250_formats[] = {
407 { IPP_SRCDST_FORMAT(XRGB8888, rotator_5250_rbg888_limits) },
408 { IPP_SRCDST_FORMAT(NV12, rotator_4412_yuv_limits) },
411 static const struct rot_variant rotator_s5pv210_data = {
412 .formats = rotator_s5pv210_formats,
413 .num_formats = ARRAY_SIZE(rotator_s5pv210_formats),
416 static const struct rot_variant rotator_4210_data = {
417 .formats = rotator_4210_formats,
418 .num_formats = ARRAY_SIZE(rotator_4210_formats),
421 static const struct rot_variant rotator_4412_data = {
422 .formats = rotator_4412_formats,
423 .num_formats = ARRAY_SIZE(rotator_4412_formats),
426 static const struct rot_variant rotator_5250_data = {
427 .formats = rotator_5250_formats,
428 .num_formats = ARRAY_SIZE(rotator_5250_formats),
431 static const struct of_device_id exynos_rotator_match[] = {
433 .compatible = "samsung,s5pv210-rotator",
434 .data = &rotator_s5pv210_data,
436 .compatible = "samsung,exynos4210-rotator",
437 .data = &rotator_4210_data,
439 .compatible = "samsung,exynos4212-rotator",
440 .data = &rotator_4412_data,
442 .compatible = "samsung,exynos5250-rotator",
443 .data = &rotator_5250_data,
447 MODULE_DEVICE_TABLE(of, exynos_rotator_match);
449 static const struct dev_pm_ops rotator_pm_ops = {
450 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
451 pm_runtime_force_resume)
452 SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume,
456 struct platform_driver rotator_driver = {
457 .probe = rotator_probe,
458 .remove = rotator_remove,
460 .name = "exynos-rotator",
461 .owner = THIS_MODULE,
462 .pm = &rotator_pm_ops,
463 .of_match_table = exynos_rotator_match,