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/err.h>
14 #include <linux/interrupt.h>
16 #include <linux/platform_device.h>
17 #include <linux/clk.h>
18 #include <linux/pm_runtime.h>
21 #include <drm/exynos_drm.h>
22 #include "regs-rotator.h"
23 #include "exynos_drm_drv.h"
24 #include "exynos_drm_ipp.h"
27 * Rotator supports image crop/rotator and input/output DMA operations.
28 * input DMA reads image data from the memory.
29 * output DMA writes image data to memory.
31 * M2M operation : supports crop/scale/rotation/csc so on.
32 * Memory ----> Rotator H/W ----> Memory.
37 * 1. check suspend/resume api if needed.
38 * 2. need to check use case platform_device_id.
39 * 3. check src/dst size with, height.
40 * 4. need to add supported list in prop_list.
43 #define get_rot_context(dev) platform_get_drvdata(to_platform_device(dev))
44 #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\
45 struct rot_context, ippdrv);
46 #define rot_read(offset) readl(rot->regs + (offset))
47 #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
50 ROT_IRQ_STATUS_COMPLETE
= 8,
51 ROT_IRQ_STATUS_ILLEGAL
= 9,
55 * A structure of limitation.
57 * @min_w: minimum width.
58 * @min_h: minimum height.
59 * @max_w: maximum width.
60 * @max_h: maximum height.
72 * A structure of limitation table.
74 * @ycbcr420_2p: case of YUV.
75 * @rgb888: case of RGB.
77 struct rot_limit_table
{
78 struct rot_limit ycbcr420_2p
;
79 struct rot_limit rgb888
;
83 * A structure of rotator context.
84 * @ippdrv: prepare initialization using ippdrv.
85 * @regs_res: register resources.
86 * @regs: memory mapped io registers.
87 * @clock: rotator gate clock.
88 * @limit_tbl: limitation of rotator.
90 * @cur_buf_id: current operation buffer id.
91 * @suspended: suspended state.
94 struct exynos_drm_ippdrv ippdrv
;
95 struct resource
*regs_res
;
98 struct rot_limit_table
*limit_tbl
;
100 int cur_buf_id
[EXYNOS_DRM_OPS_MAX
];
104 static void rotator_reg_set_irq(struct rot_context
*rot
, bool enable
)
106 u32 val
= rot_read(ROT_CONFIG
);
109 val
|= ROT_CONFIG_IRQ
;
111 val
&= ~ROT_CONFIG_IRQ
;
113 rot_write(val
, ROT_CONFIG
);
116 static u32
rotator_reg_get_fmt(struct rot_context
*rot
)
118 u32 val
= rot_read(ROT_CONTROL
);
120 val
&= ROT_CONTROL_FMT_MASK
;
125 static enum rot_irq_status
rotator_reg_get_irq_status(struct rot_context
*rot
)
127 u32 val
= rot_read(ROT_STATUS
);
129 val
= ROT_STATUS_IRQ(val
);
131 if (val
== ROT_STATUS_IRQ_VAL_COMPLETE
)
132 return ROT_IRQ_STATUS_COMPLETE
;
134 return ROT_IRQ_STATUS_ILLEGAL
;
137 static irqreturn_t
rotator_irq_handler(int irq
, void *arg
)
139 struct rot_context
*rot
= arg
;
140 struct exynos_drm_ippdrv
*ippdrv
= &rot
->ippdrv
;
141 struct drm_exynos_ipp_cmd_node
*c_node
= ippdrv
->c_node
;
142 struct drm_exynos_ipp_event_work
*event_work
= c_node
->event_work
;
143 enum rot_irq_status irq_status
;
146 /* Get execution result */
147 irq_status
= rotator_reg_get_irq_status(rot
);
150 val
= rot_read(ROT_STATUS
);
151 val
|= ROT_STATUS_IRQ_PENDING((u32
)irq_status
);
152 rot_write(val
, ROT_STATUS
);
154 if (irq_status
== ROT_IRQ_STATUS_COMPLETE
) {
155 event_work
->ippdrv
= ippdrv
;
156 event_work
->buf_id
[EXYNOS_DRM_OPS_DST
] =
157 rot
->cur_buf_id
[EXYNOS_DRM_OPS_DST
];
158 queue_work(ippdrv
->event_workq
, &event_work
->work
);
160 DRM_ERROR("the SFR is set illegally\n");
166 static void rotator_align_size(struct rot_context
*rot
, u32 fmt
, u32
*hsize
,
169 struct rot_limit_table
*limit_tbl
= rot
->limit_tbl
;
170 struct rot_limit
*limit
;
174 if (fmt
== ROT_CONTROL_FMT_RGB888
)
175 limit
= &limit_tbl
->rgb888
;
177 limit
= &limit_tbl
->ycbcr420_2p
;
179 /* Get mask for rounding to nearest aligned val */
180 mask
= ~((1 << limit
->align
) - 1);
182 /* Set aligned width */
183 val
= ROT_ALIGN(*hsize
, limit
->align
, mask
);
184 if (val
< limit
->min_w
)
185 *hsize
= ROT_MIN(limit
->min_w
, mask
);
186 else if (val
> limit
->max_w
)
187 *hsize
= ROT_MAX(limit
->max_w
, mask
);
191 /* Set aligned height */
192 val
= ROT_ALIGN(*vsize
, limit
->align
, mask
);
193 if (val
< limit
->min_h
)
194 *vsize
= ROT_MIN(limit
->min_h
, mask
);
195 else if (val
> limit
->max_h
)
196 *vsize
= ROT_MAX(limit
->max_h
, mask
);
201 static int rotator_src_set_fmt(struct device
*dev
, u32 fmt
)
203 struct rot_context
*rot
= dev_get_drvdata(dev
);
206 val
= rot_read(ROT_CONTROL
);
207 val
&= ~ROT_CONTROL_FMT_MASK
;
210 case DRM_FORMAT_NV12
:
211 val
|= ROT_CONTROL_FMT_YCBCR420_2P
;
213 case DRM_FORMAT_XRGB8888
:
214 val
|= ROT_CONTROL_FMT_RGB888
;
217 DRM_ERROR("invalid image format\n");
221 rot_write(val
, ROT_CONTROL
);
226 static inline bool rotator_check_reg_fmt(u32 fmt
)
228 if ((fmt
== ROT_CONTROL_FMT_YCBCR420_2P
) ||
229 (fmt
== ROT_CONTROL_FMT_RGB888
))
235 static int rotator_src_set_size(struct device
*dev
, int swap
,
236 struct drm_exynos_pos
*pos
,
237 struct drm_exynos_sz
*sz
)
239 struct rot_context
*rot
= dev_get_drvdata(dev
);
240 u32 fmt
, hsize
, vsize
;
244 fmt
= rotator_reg_get_fmt(rot
);
245 if (!rotator_check_reg_fmt(fmt
)) {
246 DRM_ERROR("invalid format.\n");
250 /* Align buffer size */
253 rotator_align_size(rot
, fmt
, &hsize
, &vsize
);
255 /* Set buffer size configuration */
256 val
= ROT_SET_BUF_SIZE_H(vsize
) | ROT_SET_BUF_SIZE_W(hsize
);
257 rot_write(val
, ROT_SRC_BUF_SIZE
);
259 /* Set crop image position configuration */
260 val
= ROT_CROP_POS_Y(pos
->y
) | ROT_CROP_POS_X(pos
->x
);
261 rot_write(val
, ROT_SRC_CROP_POS
);
262 val
= ROT_SRC_CROP_SIZE_H(pos
->h
) | ROT_SRC_CROP_SIZE_W(pos
->w
);
263 rot_write(val
, ROT_SRC_CROP_SIZE
);
268 static int rotator_src_set_addr(struct device
*dev
,
269 struct drm_exynos_ipp_buf_info
*buf_info
,
270 u32 buf_id
, enum drm_exynos_ipp_buf_type buf_type
)
272 struct rot_context
*rot
= dev_get_drvdata(dev
);
273 dma_addr_t addr
[EXYNOS_DRM_PLANAR_MAX
];
274 u32 val
, fmt
, hsize
, vsize
;
277 /* Set current buf_id */
278 rot
->cur_buf_id
[EXYNOS_DRM_OPS_SRC
] = buf_id
;
281 case IPP_BUF_ENQUEUE
:
282 /* Set address configuration */
283 for_each_ipp_planar(i
)
284 addr
[i
] = buf_info
->base
[i
];
287 fmt
= rotator_reg_get_fmt(rot
);
288 if (!rotator_check_reg_fmt(fmt
)) {
289 DRM_ERROR("invalid format.\n");
293 /* Re-set cb planar for NV12 format */
294 if ((fmt
== ROT_CONTROL_FMT_YCBCR420_2P
) &&
295 !addr
[EXYNOS_DRM_PLANAR_CB
]) {
297 val
= rot_read(ROT_SRC_BUF_SIZE
);
298 hsize
= ROT_GET_BUF_SIZE_W(val
);
299 vsize
= ROT_GET_BUF_SIZE_H(val
);
302 addr
[EXYNOS_DRM_PLANAR_CB
] =
303 addr
[EXYNOS_DRM_PLANAR_Y
] + hsize
* vsize
;
306 for_each_ipp_planar(i
)
307 rot_write(addr
[i
], ROT_SRC_BUF_ADDR(i
));
309 case IPP_BUF_DEQUEUE
:
310 for_each_ipp_planar(i
)
311 rot_write(0x0, ROT_SRC_BUF_ADDR(i
));
321 static int rotator_dst_set_transf(struct device
*dev
,
322 enum drm_exynos_degree degree
,
323 enum drm_exynos_flip flip
, bool *swap
)
325 struct rot_context
*rot
= dev_get_drvdata(dev
);
328 /* Set transform configuration */
329 val
= rot_read(ROT_CONTROL
);
330 val
&= ~ROT_CONTROL_FLIP_MASK
;
333 case EXYNOS_DRM_FLIP_VERTICAL
:
334 val
|= ROT_CONTROL_FLIP_VERTICAL
;
336 case EXYNOS_DRM_FLIP_HORIZONTAL
:
337 val
|= ROT_CONTROL_FLIP_HORIZONTAL
;
344 val
&= ~ROT_CONTROL_ROT_MASK
;
347 case EXYNOS_DRM_DEGREE_90
:
348 val
|= ROT_CONTROL_ROT_90
;
350 case EXYNOS_DRM_DEGREE_180
:
351 val
|= ROT_CONTROL_ROT_180
;
353 case EXYNOS_DRM_DEGREE_270
:
354 val
|= ROT_CONTROL_ROT_270
;
357 /* Rotation 0 Degree */
361 rot_write(val
, ROT_CONTROL
);
363 /* Check degree for setting buffer size swap */
364 if ((degree
== EXYNOS_DRM_DEGREE_90
) ||
365 (degree
== EXYNOS_DRM_DEGREE_270
))
373 static int rotator_dst_set_size(struct device
*dev
, int swap
,
374 struct drm_exynos_pos
*pos
,
375 struct drm_exynos_sz
*sz
)
377 struct rot_context
*rot
= dev_get_drvdata(dev
);
378 u32 val
, fmt
, hsize
, vsize
;
381 fmt
= rotator_reg_get_fmt(rot
);
382 if (!rotator_check_reg_fmt(fmt
)) {
383 DRM_ERROR("invalid format.\n");
387 /* Align buffer size */
390 rotator_align_size(rot
, fmt
, &hsize
, &vsize
);
392 /* Set buffer size configuration */
393 val
= ROT_SET_BUF_SIZE_H(vsize
) | ROT_SET_BUF_SIZE_W(hsize
);
394 rot_write(val
, ROT_DST_BUF_SIZE
);
396 /* Set crop image position configuration */
397 val
= ROT_CROP_POS_Y(pos
->y
) | ROT_CROP_POS_X(pos
->x
);
398 rot_write(val
, ROT_DST_CROP_POS
);
403 static int rotator_dst_set_addr(struct device
*dev
,
404 struct drm_exynos_ipp_buf_info
*buf_info
,
405 u32 buf_id
, enum drm_exynos_ipp_buf_type buf_type
)
407 struct rot_context
*rot
= dev_get_drvdata(dev
);
408 dma_addr_t addr
[EXYNOS_DRM_PLANAR_MAX
];
409 u32 val
, fmt
, hsize
, vsize
;
412 /* Set current buf_id */
413 rot
->cur_buf_id
[EXYNOS_DRM_OPS_DST
] = buf_id
;
416 case IPP_BUF_ENQUEUE
:
417 /* Set address configuration */
418 for_each_ipp_planar(i
)
419 addr
[i
] = buf_info
->base
[i
];
422 fmt
= rotator_reg_get_fmt(rot
);
423 if (!rotator_check_reg_fmt(fmt
)) {
424 DRM_ERROR("invalid format.\n");
428 /* Re-set cb planar for NV12 format */
429 if ((fmt
== ROT_CONTROL_FMT_YCBCR420_2P
) &&
430 !addr
[EXYNOS_DRM_PLANAR_CB
]) {
432 val
= rot_read(ROT_DST_BUF_SIZE
);
434 hsize
= ROT_GET_BUF_SIZE_W(val
);
435 vsize
= ROT_GET_BUF_SIZE_H(val
);
438 addr
[EXYNOS_DRM_PLANAR_CB
] =
439 addr
[EXYNOS_DRM_PLANAR_Y
] + hsize
* vsize
;
442 for_each_ipp_planar(i
)
443 rot_write(addr
[i
], ROT_DST_BUF_ADDR(i
));
445 case IPP_BUF_DEQUEUE
:
446 for_each_ipp_planar(i
)
447 rot_write(0x0, ROT_DST_BUF_ADDR(i
));
457 static struct exynos_drm_ipp_ops rot_src_ops
= {
458 .set_fmt
= rotator_src_set_fmt
,
459 .set_size
= rotator_src_set_size
,
460 .set_addr
= rotator_src_set_addr
,
463 static struct exynos_drm_ipp_ops rot_dst_ops
= {
464 .set_transf
= rotator_dst_set_transf
,
465 .set_size
= rotator_dst_set_size
,
466 .set_addr
= rotator_dst_set_addr
,
469 static int rotator_init_prop_list(struct exynos_drm_ippdrv
*ippdrv
)
471 struct drm_exynos_ipp_prop_list
*prop_list
= &ippdrv
->prop_list
;
473 prop_list
->version
= 1;
474 prop_list
->flip
= (1 << EXYNOS_DRM_FLIP_VERTICAL
) |
475 (1 << EXYNOS_DRM_FLIP_HORIZONTAL
);
476 prop_list
->degree
= (1 << EXYNOS_DRM_DEGREE_0
) |
477 (1 << EXYNOS_DRM_DEGREE_90
) |
478 (1 << EXYNOS_DRM_DEGREE_180
) |
479 (1 << EXYNOS_DRM_DEGREE_270
);
482 prop_list
->scale
= 0;
487 static inline bool rotator_check_drm_fmt(u32 fmt
)
490 case DRM_FORMAT_XRGB8888
:
491 case DRM_FORMAT_NV12
:
494 DRM_DEBUG_KMS("not support format\n");
499 static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip
)
502 case EXYNOS_DRM_FLIP_NONE
:
503 case EXYNOS_DRM_FLIP_VERTICAL
:
504 case EXYNOS_DRM_FLIP_HORIZONTAL
:
505 case EXYNOS_DRM_FLIP_BOTH
:
508 DRM_DEBUG_KMS("invalid flip\n");
513 static int rotator_ippdrv_check_property(struct device
*dev
,
514 struct drm_exynos_ipp_property
*property
)
516 struct drm_exynos_ipp_config
*src_config
=
517 &property
->config
[EXYNOS_DRM_OPS_SRC
];
518 struct drm_exynos_ipp_config
*dst_config
=
519 &property
->config
[EXYNOS_DRM_OPS_DST
];
520 struct drm_exynos_pos
*src_pos
= &src_config
->pos
;
521 struct drm_exynos_pos
*dst_pos
= &dst_config
->pos
;
522 struct drm_exynos_sz
*src_sz
= &src_config
->sz
;
523 struct drm_exynos_sz
*dst_sz
= &dst_config
->sz
;
526 /* Check format configuration */
527 if (src_config
->fmt
!= dst_config
->fmt
) {
528 DRM_DEBUG_KMS("not support csc feature\n");
532 if (!rotator_check_drm_fmt(dst_config
->fmt
)) {
533 DRM_DEBUG_KMS("invalid format\n");
537 /* Check transform configuration */
538 if (src_config
->degree
!= EXYNOS_DRM_DEGREE_0
) {
539 DRM_DEBUG_KMS("not support source-side rotation\n");
543 switch (dst_config
->degree
) {
544 case EXYNOS_DRM_DEGREE_90
:
545 case EXYNOS_DRM_DEGREE_270
:
547 case EXYNOS_DRM_DEGREE_0
:
548 case EXYNOS_DRM_DEGREE_180
:
552 DRM_DEBUG_KMS("invalid degree\n");
556 if (src_config
->flip
!= EXYNOS_DRM_FLIP_NONE
) {
557 DRM_DEBUG_KMS("not support source-side flip\n");
561 if (!rotator_check_drm_flip(dst_config
->flip
)) {
562 DRM_DEBUG_KMS("invalid flip\n");
566 /* Check size configuration */
567 if ((src_pos
->x
+ src_pos
->w
> src_sz
->hsize
) ||
568 (src_pos
->y
+ src_pos
->h
> src_sz
->vsize
)) {
569 DRM_DEBUG_KMS("out of source buffer bound\n");
574 if ((dst_pos
->x
+ dst_pos
->h
> dst_sz
->vsize
) ||
575 (dst_pos
->y
+ dst_pos
->w
> dst_sz
->hsize
)) {
576 DRM_DEBUG_KMS("out of destination buffer bound\n");
580 if ((src_pos
->w
!= dst_pos
->h
) || (src_pos
->h
!= dst_pos
->w
)) {
581 DRM_DEBUG_KMS("not support scale feature\n");
585 if ((dst_pos
->x
+ dst_pos
->w
> dst_sz
->hsize
) ||
586 (dst_pos
->y
+ dst_pos
->h
> dst_sz
->vsize
)) {
587 DRM_DEBUG_KMS("out of destination buffer bound\n");
591 if ((src_pos
->w
!= dst_pos
->w
) || (src_pos
->h
!= dst_pos
->h
)) {
592 DRM_DEBUG_KMS("not support scale feature\n");
600 static int rotator_ippdrv_start(struct device
*dev
, enum drm_exynos_ipp_cmd cmd
)
602 struct rot_context
*rot
= dev_get_drvdata(dev
);
605 if (rot
->suspended
) {
606 DRM_ERROR("suspended state\n");
610 if (cmd
!= IPP_CMD_M2M
) {
611 DRM_ERROR("not support cmd: %d\n", cmd
);
615 /* Set interrupt enable */
616 rotator_reg_set_irq(rot
, true);
618 val
= rot_read(ROT_CONTROL
);
619 val
|= ROT_CONTROL_START
;
621 rot_write(val
, ROT_CONTROL
);
626 static struct rot_limit_table rot_limit_tbl_4210
= {
643 static struct rot_limit_table rot_limit_tbl_4x12
= {
660 static struct rot_limit_table rot_limit_tbl_5250
= {
677 static const struct of_device_id exynos_rotator_match
[] = {
679 .compatible
= "samsung,exynos4210-rotator",
680 .data
= &rot_limit_tbl_4210
,
683 .compatible
= "samsung,exynos4212-rotator",
684 .data
= &rot_limit_tbl_4x12
,
687 .compatible
= "samsung,exynos5250-rotator",
688 .data
= &rot_limit_tbl_5250
,
692 MODULE_DEVICE_TABLE(of
, exynos_rotator_match
);
694 static int rotator_probe(struct platform_device
*pdev
)
696 struct device
*dev
= &pdev
->dev
;
697 struct rot_context
*rot
;
698 struct exynos_drm_ippdrv
*ippdrv
;
699 const struct of_device_id
*match
;
703 dev_err(dev
, "cannot find of_node.\n");
707 rot
= devm_kzalloc(dev
, sizeof(*rot
), GFP_KERNEL
);
711 match
= of_match_node(exynos_rotator_match
, dev
->of_node
);
713 dev_err(dev
, "failed to match node\n");
716 rot
->limit_tbl
= (struct rot_limit_table
*)match
->data
;
718 rot
->regs_res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
719 rot
->regs
= devm_ioremap_resource(dev
, rot
->regs_res
);
720 if (IS_ERR(rot
->regs
))
721 return PTR_ERR(rot
->regs
);
723 rot
->irq
= platform_get_irq(pdev
, 0);
725 dev_err(dev
, "failed to get irq\n");
729 ret
= devm_request_threaded_irq(dev
, rot
->irq
, NULL
,
730 rotator_irq_handler
, IRQF_ONESHOT
, "drm_rotator", rot
);
732 dev_err(dev
, "failed to request irq\n");
736 rot
->clock
= devm_clk_get(dev
, "rotator");
737 if (IS_ERR(rot
->clock
)) {
738 dev_err(dev
, "failed to get clock\n");
739 return PTR_ERR(rot
->clock
);
742 pm_runtime_enable(dev
);
744 ippdrv
= &rot
->ippdrv
;
746 ippdrv
->ops
[EXYNOS_DRM_OPS_SRC
] = &rot_src_ops
;
747 ippdrv
->ops
[EXYNOS_DRM_OPS_DST
] = &rot_dst_ops
;
748 ippdrv
->check_property
= rotator_ippdrv_check_property
;
749 ippdrv
->start
= rotator_ippdrv_start
;
750 ret
= rotator_init_prop_list(ippdrv
);
752 dev_err(dev
, "failed to init property list.\n");
753 goto err_ippdrv_register
;
756 DRM_DEBUG_KMS("ippdrv[%p]\n", ippdrv
);
758 platform_set_drvdata(pdev
, rot
);
760 ret
= exynos_drm_ippdrv_register(ippdrv
);
762 dev_err(dev
, "failed to register drm rotator device\n");
763 goto err_ippdrv_register
;
766 dev_info(dev
, "The exynos rotator is probed successfully\n");
771 pm_runtime_disable(dev
);
775 static int rotator_remove(struct platform_device
*pdev
)
777 struct device
*dev
= &pdev
->dev
;
778 struct rot_context
*rot
= dev_get_drvdata(dev
);
779 struct exynos_drm_ippdrv
*ippdrv
= &rot
->ippdrv
;
781 exynos_drm_ippdrv_unregister(ippdrv
);
783 pm_runtime_disable(dev
);
789 static int rotator_clk_crtl(struct rot_context
*rot
, bool enable
)
792 clk_prepare_enable(rot
->clock
);
793 rot
->suspended
= false;
795 clk_disable_unprepare(rot
->clock
);
796 rot
->suspended
= true;
803 #ifdef CONFIG_PM_SLEEP
804 static int rotator_suspend(struct device
*dev
)
806 struct rot_context
*rot
= dev_get_drvdata(dev
);
808 if (pm_runtime_suspended(dev
))
811 return rotator_clk_crtl(rot
, false);
814 static int rotator_resume(struct device
*dev
)
816 struct rot_context
*rot
= dev_get_drvdata(dev
);
818 if (!pm_runtime_suspended(dev
))
819 return rotator_clk_crtl(rot
, true);
825 static int rotator_runtime_suspend(struct device
*dev
)
827 struct rot_context
*rot
= dev_get_drvdata(dev
);
829 return rotator_clk_crtl(rot
, false);
832 static int rotator_runtime_resume(struct device
*dev
)
834 struct rot_context
*rot
= dev_get_drvdata(dev
);
836 return rotator_clk_crtl(rot
, true);
840 static 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
,
846 struct platform_driver rotator_driver
= {
847 .probe
= rotator_probe
,
848 .remove
= rotator_remove
,
850 .name
= "exynos-rot",
851 .owner
= THIS_MODULE
,
852 .pm
= &rotator_pm_ops
,
853 .of_match_table
= exynos_rotator_match
,