Loading...
1/*
2 * Copyright (C) 2012 Samsung Electronics Co.Ltd
3 * Authors:
4 * YoungJun Cho <yj44.cho@samsung.com>
5 * Eunchul Kim <chulspro.kim@samsung.com>
6 *
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
10 */
11
12#include <linux/kernel.h>
13#include <linux/err.h>
14#include <linux/interrupt.h>
15#include <linux/io.h>
16#include <linux/platform_device.h>
17#include <linux/clk.h>
18#include <linux/pm_runtime.h>
19
20#include <drm/drmP.h>
21#include <drm/exynos_drm.h>
22#include "regs-rotator.h"
23#include "exynos_drm_drv.h"
24#include "exynos_drm_ipp.h"
25
26/*
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.
30 *
31 * M2M operation : supports crop/scale/rotation/csc so on.
32 * Memory ----> Rotator H/W ----> Memory.
33 */
34
35/*
36 * TODO
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.
41 */
42
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))
48
49enum rot_irq_status {
50 ROT_IRQ_STATUS_COMPLETE = 8,
51 ROT_IRQ_STATUS_ILLEGAL = 9,
52};
53
54/*
55 * A structure of limitation.
56 *
57 * @min_w: minimum width.
58 * @min_h: minimum height.
59 * @max_w: maximum width.
60 * @max_h: maximum height.
61 * @align: align size.
62 */
63struct rot_limit {
64 u32 min_w;
65 u32 min_h;
66 u32 max_w;
67 u32 max_h;
68 u32 align;
69};
70
71/*
72 * A structure of limitation table.
73 *
74 * @ycbcr420_2p: case of YUV.
75 * @rgb888: case of RGB.
76 */
77struct rot_limit_table {
78 struct rot_limit ycbcr420_2p;
79 struct rot_limit rgb888;
80};
81
82/*
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.
89 * @irq: irq number.
90 * @cur_buf_id: current operation buffer id.
91 * @suspended: suspended state.
92 */
93struct rot_context {
94 struct exynos_drm_ippdrv ippdrv;
95 struct resource *regs_res;
96 void __iomem *regs;
97 struct clk *clock;
98 struct rot_limit_table *limit_tbl;
99 int irq;
100 int cur_buf_id[EXYNOS_DRM_OPS_MAX];
101 bool suspended;
102};
103
104static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
105{
106 u32 val = rot_read(ROT_CONFIG);
107
108 if (enable == true)
109 val |= ROT_CONFIG_IRQ;
110 else
111 val &= ~ROT_CONFIG_IRQ;
112
113 rot_write(val, ROT_CONFIG);
114}
115
116static u32 rotator_reg_get_fmt(struct rot_context *rot)
117{
118 u32 val = rot_read(ROT_CONTROL);
119
120 val &= ROT_CONTROL_FMT_MASK;
121
122 return val;
123}
124
125static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
126{
127 u32 val = rot_read(ROT_STATUS);
128
129 val = ROT_STATUS_IRQ(val);
130
131 if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
132 return ROT_IRQ_STATUS_COMPLETE;
133
134 return ROT_IRQ_STATUS_ILLEGAL;
135}
136
137static irqreturn_t rotator_irq_handler(int irq, void *arg)
138{
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;
144 u32 val;
145
146 /* Get execution result */
147 irq_status = rotator_reg_get_irq_status(rot);
148
149 /* clear status */
150 val = rot_read(ROT_STATUS);
151 val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
152 rot_write(val, ROT_STATUS);
153
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);
159 } else {
160 DRM_ERROR("the SFR is set illegally\n");
161 }
162
163 return IRQ_HANDLED;
164}
165
166static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize,
167 u32 *vsize)
168{
169 struct rot_limit_table *limit_tbl = rot->limit_tbl;
170 struct rot_limit *limit;
171 u32 mask, val;
172
173 /* Get size limit */
174 if (fmt == ROT_CONTROL_FMT_RGB888)
175 limit = &limit_tbl->rgb888;
176 else
177 limit = &limit_tbl->ycbcr420_2p;
178
179 /* Get mask for rounding to nearest aligned val */
180 mask = ~((1 << limit->align) - 1);
181
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);
188 else
189 *hsize = val;
190
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);
197 else
198 *vsize = val;
199}
200
201static int rotator_src_set_fmt(struct device *dev, u32 fmt)
202{
203 struct rot_context *rot = dev_get_drvdata(dev);
204 u32 val;
205
206 val = rot_read(ROT_CONTROL);
207 val &= ~ROT_CONTROL_FMT_MASK;
208
209 switch (fmt) {
210 case DRM_FORMAT_NV12:
211 val |= ROT_CONTROL_FMT_YCBCR420_2P;
212 break;
213 case DRM_FORMAT_XRGB8888:
214 val |= ROT_CONTROL_FMT_RGB888;
215 break;
216 default:
217 DRM_ERROR("invalid image format\n");
218 return -EINVAL;
219 }
220
221 rot_write(val, ROT_CONTROL);
222
223 return 0;
224}
225
226static inline bool rotator_check_reg_fmt(u32 fmt)
227{
228 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) ||
229 (fmt == ROT_CONTROL_FMT_RGB888))
230 return true;
231
232 return false;
233}
234
235static int rotator_src_set_size(struct device *dev, int swap,
236 struct drm_exynos_pos *pos,
237 struct drm_exynos_sz *sz)
238{
239 struct rot_context *rot = dev_get_drvdata(dev);
240 u32 fmt, hsize, vsize;
241 u32 val;
242
243 /* Get format */
244 fmt = rotator_reg_get_fmt(rot);
245 if (!rotator_check_reg_fmt(fmt)) {
246 DRM_ERROR("invalid format.\n");
247 return -EINVAL;
248 }
249
250 /* Align buffer size */
251 hsize = sz->hsize;
252 vsize = sz->vsize;
253 rotator_align_size(rot, fmt, &hsize, &vsize);
254
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);
258
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);
264
265 return 0;
266}
267
268static 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)
271{
272 struct rot_context *rot = dev_get_drvdata(dev);
273 dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
274 u32 val, fmt, hsize, vsize;
275 int i;
276
277 /* Set current buf_id */
278 rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id;
279
280 switch (buf_type) {
281 case IPP_BUF_ENQUEUE:
282 /* Set address configuration */
283 for_each_ipp_planar(i)
284 addr[i] = buf_info->base[i];
285
286 /* Get format */
287 fmt = rotator_reg_get_fmt(rot);
288 if (!rotator_check_reg_fmt(fmt)) {
289 DRM_ERROR("invalid format.\n");
290 return -EINVAL;
291 }
292
293 /* Re-set cb planar for NV12 format */
294 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
295 !addr[EXYNOS_DRM_PLANAR_CB]) {
296
297 val = rot_read(ROT_SRC_BUF_SIZE);
298 hsize = ROT_GET_BUF_SIZE_W(val);
299 vsize = ROT_GET_BUF_SIZE_H(val);
300
301 /* Set cb planar */
302 addr[EXYNOS_DRM_PLANAR_CB] =
303 addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
304 }
305
306 for_each_ipp_planar(i)
307 rot_write(addr[i], ROT_SRC_BUF_ADDR(i));
308 break;
309 case IPP_BUF_DEQUEUE:
310 for_each_ipp_planar(i)
311 rot_write(0x0, ROT_SRC_BUF_ADDR(i));
312 break;
313 default:
314 /* Nothing to do */
315 break;
316 }
317
318 return 0;
319}
320
321static int rotator_dst_set_transf(struct device *dev,
322 enum drm_exynos_degree degree,
323 enum drm_exynos_flip flip, bool *swap)
324{
325 struct rot_context *rot = dev_get_drvdata(dev);
326 u32 val;
327
328 /* Set transform configuration */
329 val = rot_read(ROT_CONTROL);
330 val &= ~ROT_CONTROL_FLIP_MASK;
331
332 switch (flip) {
333 case EXYNOS_DRM_FLIP_VERTICAL:
334 val |= ROT_CONTROL_FLIP_VERTICAL;
335 break;
336 case EXYNOS_DRM_FLIP_HORIZONTAL:
337 val |= ROT_CONTROL_FLIP_HORIZONTAL;
338 break;
339 default:
340 /* Flip None */
341 break;
342 }
343
344 val &= ~ROT_CONTROL_ROT_MASK;
345
346 switch (degree) {
347 case EXYNOS_DRM_DEGREE_90:
348 val |= ROT_CONTROL_ROT_90;
349 break;
350 case EXYNOS_DRM_DEGREE_180:
351 val |= ROT_CONTROL_ROT_180;
352 break;
353 case EXYNOS_DRM_DEGREE_270:
354 val |= ROT_CONTROL_ROT_270;
355 break;
356 default:
357 /* Rotation 0 Degree */
358 break;
359 }
360
361 rot_write(val, ROT_CONTROL);
362
363 /* Check degree for setting buffer size swap */
364 if ((degree == EXYNOS_DRM_DEGREE_90) ||
365 (degree == EXYNOS_DRM_DEGREE_270))
366 *swap = true;
367 else
368 *swap = false;
369
370 return 0;
371}
372
373static int rotator_dst_set_size(struct device *dev, int swap,
374 struct drm_exynos_pos *pos,
375 struct drm_exynos_sz *sz)
376{
377 struct rot_context *rot = dev_get_drvdata(dev);
378 u32 val, fmt, hsize, vsize;
379
380 /* Get format */
381 fmt = rotator_reg_get_fmt(rot);
382 if (!rotator_check_reg_fmt(fmt)) {
383 DRM_ERROR("invalid format.\n");
384 return -EINVAL;
385 }
386
387 /* Align buffer size */
388 hsize = sz->hsize;
389 vsize = sz->vsize;
390 rotator_align_size(rot, fmt, &hsize, &vsize);
391
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);
395
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);
399
400 return 0;
401}
402
403static 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)
406{
407 struct rot_context *rot = dev_get_drvdata(dev);
408 dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX];
409 u32 val, fmt, hsize, vsize;
410 int i;
411
412 /* Set current buf_id */
413 rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id;
414
415 switch (buf_type) {
416 case IPP_BUF_ENQUEUE:
417 /* Set address configuration */
418 for_each_ipp_planar(i)
419 addr[i] = buf_info->base[i];
420
421 /* Get format */
422 fmt = rotator_reg_get_fmt(rot);
423 if (!rotator_check_reg_fmt(fmt)) {
424 DRM_ERROR("invalid format.\n");
425 return -EINVAL;
426 }
427
428 /* Re-set cb planar for NV12 format */
429 if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) &&
430 !addr[EXYNOS_DRM_PLANAR_CB]) {
431 /* Get buf size */
432 val = rot_read(ROT_DST_BUF_SIZE);
433
434 hsize = ROT_GET_BUF_SIZE_W(val);
435 vsize = ROT_GET_BUF_SIZE_H(val);
436
437 /* Set cb planar */
438 addr[EXYNOS_DRM_PLANAR_CB] =
439 addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize;
440 }
441
442 for_each_ipp_planar(i)
443 rot_write(addr[i], ROT_DST_BUF_ADDR(i));
444 break;
445 case IPP_BUF_DEQUEUE:
446 for_each_ipp_planar(i)
447 rot_write(0x0, ROT_DST_BUF_ADDR(i));
448 break;
449 default:
450 /* Nothing to do */
451 break;
452 }
453
454 return 0;
455}
456
457static 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,
461};
462
463static 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,
467};
468
469static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv)
470{
471 struct drm_exynos_ipp_prop_list *prop_list = &ippdrv->prop_list;
472
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);
480 prop_list->csc = 0;
481 prop_list->crop = 0;
482 prop_list->scale = 0;
483
484 return 0;
485}
486
487static inline bool rotator_check_drm_fmt(u32 fmt)
488{
489 switch (fmt) {
490 case DRM_FORMAT_XRGB8888:
491 case DRM_FORMAT_NV12:
492 return true;
493 default:
494 DRM_DEBUG_KMS("not support format\n");
495 return false;
496 }
497}
498
499static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip)
500{
501 switch (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:
506 return true;
507 default:
508 DRM_DEBUG_KMS("invalid flip\n");
509 return false;
510 }
511}
512
513static int rotator_ippdrv_check_property(struct device *dev,
514 struct drm_exynos_ipp_property *property)
515{
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;
524 bool swap = false;
525
526 /* Check format configuration */
527 if (src_config->fmt != dst_config->fmt) {
528 DRM_DEBUG_KMS("not support csc feature\n");
529 return -EINVAL;
530 }
531
532 if (!rotator_check_drm_fmt(dst_config->fmt)) {
533 DRM_DEBUG_KMS("invalid format\n");
534 return -EINVAL;
535 }
536
537 /* Check transform configuration */
538 if (src_config->degree != EXYNOS_DRM_DEGREE_0) {
539 DRM_DEBUG_KMS("not support source-side rotation\n");
540 return -EINVAL;
541 }
542
543 switch (dst_config->degree) {
544 case EXYNOS_DRM_DEGREE_90:
545 case EXYNOS_DRM_DEGREE_270:
546 swap = true;
547 case EXYNOS_DRM_DEGREE_0:
548 case EXYNOS_DRM_DEGREE_180:
549 /* No problem */
550 break;
551 default:
552 DRM_DEBUG_KMS("invalid degree\n");
553 return -EINVAL;
554 }
555
556 if (src_config->flip != EXYNOS_DRM_FLIP_NONE) {
557 DRM_DEBUG_KMS("not support source-side flip\n");
558 return -EINVAL;
559 }
560
561 if (!rotator_check_drm_flip(dst_config->flip)) {
562 DRM_DEBUG_KMS("invalid flip\n");
563 return -EINVAL;
564 }
565
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");
570 return -EINVAL;
571 }
572
573 if (swap) {
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");
577 return -EINVAL;
578 }
579
580 if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) {
581 DRM_DEBUG_KMS("not support scale feature\n");
582 return -EINVAL;
583 }
584 } else {
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");
588 return -EINVAL;
589 }
590
591 if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) {
592 DRM_DEBUG_KMS("not support scale feature\n");
593 return -EINVAL;
594 }
595 }
596
597 return 0;
598}
599
600static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd)
601{
602 struct rot_context *rot = dev_get_drvdata(dev);
603 u32 val;
604
605 if (rot->suspended) {
606 DRM_ERROR("suspended state\n");
607 return -EPERM;
608 }
609
610 if (cmd != IPP_CMD_M2M) {
611 DRM_ERROR("not support cmd: %d\n", cmd);
612 return -EINVAL;
613 }
614
615 /* Set interrupt enable */
616 rotator_reg_set_irq(rot, true);
617
618 val = rot_read(ROT_CONTROL);
619 val |= ROT_CONTROL_START;
620
621 rot_write(val, ROT_CONTROL);
622
623 return 0;
624}
625
626static struct rot_limit_table rot_limit_tbl_4210 = {
627 .ycbcr420_2p = {
628 .min_w = 32,
629 .min_h = 32,
630 .max_w = SZ_64K,
631 .max_h = SZ_64K,
632 .align = 3,
633 },
634 .rgb888 = {
635 .min_w = 8,
636 .min_h = 8,
637 .max_w = SZ_16K,
638 .max_h = SZ_16K,
639 .align = 2,
640 },
641};
642
643static struct rot_limit_table rot_limit_tbl_4x12 = {
644 .ycbcr420_2p = {
645 .min_w = 32,
646 .min_h = 32,
647 .max_w = SZ_32K,
648 .max_h = SZ_32K,
649 .align = 3,
650 },
651 .rgb888 = {
652 .min_w = 8,
653 .min_h = 8,
654 .max_w = SZ_8K,
655 .max_h = SZ_8K,
656 .align = 2,
657 },
658};
659
660static struct rot_limit_table rot_limit_tbl_5250 = {
661 .ycbcr420_2p = {
662 .min_w = 32,
663 .min_h = 32,
664 .max_w = SZ_32K,
665 .max_h = SZ_32K,
666 .align = 3,
667 },
668 .rgb888 = {
669 .min_w = 8,
670 .min_h = 8,
671 .max_w = SZ_8K,
672 .max_h = SZ_8K,
673 .align = 1,
674 },
675};
676
677static const struct of_device_id exynos_rotator_match[] = {
678 {
679 .compatible = "samsung,exynos4210-rotator",
680 .data = &rot_limit_tbl_4210,
681 },
682 {
683 .compatible = "samsung,exynos4212-rotator",
684 .data = &rot_limit_tbl_4x12,
685 },
686 {
687 .compatible = "samsung,exynos5250-rotator",
688 .data = &rot_limit_tbl_5250,
689 },
690 {},
691};
692MODULE_DEVICE_TABLE(of, exynos_rotator_match);
693
694static int rotator_probe(struct platform_device *pdev)
695{
696 struct device *dev = &pdev->dev;
697 struct rot_context *rot;
698 struct exynos_drm_ippdrv *ippdrv;
699 const struct of_device_id *match;
700 int ret;
701
702 if (!dev->of_node) {
703 dev_err(dev, "cannot find of_node.\n");
704 return -ENODEV;
705 }
706
707 rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
708 if (!rot)
709 return -ENOMEM;
710
711 match = of_match_node(exynos_rotator_match, dev->of_node);
712 if (!match) {
713 dev_err(dev, "failed to match node\n");
714 return -ENODEV;
715 }
716 rot->limit_tbl = (struct rot_limit_table *)match->data;
717
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);
722
723 rot->irq = platform_get_irq(pdev, 0);
724 if (rot->irq < 0) {
725 dev_err(dev, "failed to get irq\n");
726 return rot->irq;
727 }
728
729 ret = devm_request_threaded_irq(dev, rot->irq, NULL,
730 rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot);
731 if (ret < 0) {
732 dev_err(dev, "failed to request irq\n");
733 return ret;
734 }
735
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);
740 }
741
742 pm_runtime_enable(dev);
743
744 ippdrv = &rot->ippdrv;
745 ippdrv->dev = dev;
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);
751 if (ret < 0) {
752 dev_err(dev, "failed to init property list.\n");
753 goto err_ippdrv_register;
754 }
755
756 DRM_DEBUG_KMS("ippdrv[%p]\n", ippdrv);
757
758 platform_set_drvdata(pdev, rot);
759
760 ret = exynos_drm_ippdrv_register(ippdrv);
761 if (ret < 0) {
762 dev_err(dev, "failed to register drm rotator device\n");
763 goto err_ippdrv_register;
764 }
765
766 dev_info(dev, "The exynos rotator is probed successfully\n");
767
768 return 0;
769
770err_ippdrv_register:
771 pm_runtime_disable(dev);
772 return ret;
773}
774
775static int rotator_remove(struct platform_device *pdev)
776{
777 struct device *dev = &pdev->dev;
778 struct rot_context *rot = dev_get_drvdata(dev);
779 struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv;
780
781 exynos_drm_ippdrv_unregister(ippdrv);
782
783 pm_runtime_disable(dev);
784
785 return 0;
786}
787
788#ifdef CONFIG_PM
789static int rotator_clk_crtl(struct rot_context *rot, bool enable)
790{
791 if (enable) {
792 clk_prepare_enable(rot->clock);
793 rot->suspended = false;
794 } else {
795 clk_disable_unprepare(rot->clock);
796 rot->suspended = true;
797 }
798
799 return 0;
800}
801
802
803#ifdef CONFIG_PM_SLEEP
804static int rotator_suspend(struct device *dev)
805{
806 struct rot_context *rot = dev_get_drvdata(dev);
807
808 if (pm_runtime_suspended(dev))
809 return 0;
810
811 return rotator_clk_crtl(rot, false);
812}
813
814static int rotator_resume(struct device *dev)
815{
816 struct rot_context *rot = dev_get_drvdata(dev);
817
818 if (!pm_runtime_suspended(dev))
819 return rotator_clk_crtl(rot, true);
820
821 return 0;
822}
823#endif
824
825static int rotator_runtime_suspend(struct device *dev)
826{
827 struct rot_context *rot = dev_get_drvdata(dev);
828
829 return rotator_clk_crtl(rot, false);
830}
831
832static int rotator_runtime_resume(struct device *dev)
833{
834 struct rot_context *rot = dev_get_drvdata(dev);
835
836 return rotator_clk_crtl(rot, true);
837}
838#endif
839
840static 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,
843 NULL)
844};
845
846struct platform_driver rotator_driver = {
847 .probe = rotator_probe,
848 .remove = rotator_remove,
849 .driver = {
850 .name = "exynos-rot",
851 .owner = THIS_MODULE,
852 .pm = &rotator_pm_ops,
853 .of_match_table = exynos_rotator_match,
854 },
855};
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2012 Samsung Electronics Co.Ltd
4 * Authors:
5 * YoungJun Cho <yj44.cho@samsung.com>
6 * Eunchul Kim <chulspro.kim@samsung.com>
7 */
8
9#include <linux/clk.h>
10#include <linux/component.h>
11#include <linux/err.h>
12#include <linux/interrupt.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/of.h>
16#include <linux/platform_device.h>
17#include <linux/pm_runtime.h>
18#include <linux/sizes.h>
19
20#include <drm/drm_fourcc.h>
21#include <drm/exynos_drm.h>
22
23#include "exynos_drm_drv.h"
24#include "exynos_drm_ipp.h"
25#include "regs-rotator.h"
26
27/*
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.
31 */
32
33#define ROTATOR_AUTOSUSPEND_DELAY 2000
34
35#define rot_read(offset) readl(rot->regs + (offset))
36#define rot_write(cfg, offset) writel(cfg, rot->regs + (offset))
37
38enum rot_irq_status {
39 ROT_IRQ_STATUS_COMPLETE = 8,
40 ROT_IRQ_STATUS_ILLEGAL = 9,
41};
42
43struct rot_variant {
44 const struct exynos_drm_ipp_formats *formats;
45 unsigned int num_formats;
46};
47
48/*
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.
54 * @irq: irq number.
55 */
56struct rot_context {
57 struct exynos_drm_ipp ipp;
58 struct drm_device *drm_dev;
59 void *dma_priv;
60 struct device *dev;
61 void __iomem *regs;
62 struct clk *clock;
63 const struct exynos_drm_ipp_formats *formats;
64 unsigned int num_formats;
65 struct exynos_drm_ipp_task *task;
66};
67
68static void rotator_reg_set_irq(struct rot_context *rot, bool enable)
69{
70 u32 val = rot_read(ROT_CONFIG);
71
72 if (enable == true)
73 val |= ROT_CONFIG_IRQ;
74 else
75 val &= ~ROT_CONFIG_IRQ;
76
77 rot_write(val, ROT_CONFIG);
78}
79
80static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot)
81{
82 u32 val = rot_read(ROT_STATUS);
83
84 val = ROT_STATUS_IRQ(val);
85
86 if (val == ROT_STATUS_IRQ_VAL_COMPLETE)
87 return ROT_IRQ_STATUS_COMPLETE;
88
89 return ROT_IRQ_STATUS_ILLEGAL;
90}
91
92static irqreturn_t rotator_irq_handler(int irq, void *arg)
93{
94 struct rot_context *rot = arg;
95 enum rot_irq_status irq_status;
96 u32 val;
97
98 /* Get execution result */
99 irq_status = rotator_reg_get_irq_status(rot);
100
101 /* clear status */
102 val = rot_read(ROT_STATUS);
103 val |= ROT_STATUS_IRQ_PENDING((u32)irq_status);
104 rot_write(val, ROT_STATUS);
105
106 if (rot->task) {
107 struct exynos_drm_ipp_task *task = rot->task;
108
109 rot->task = NULL;
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);
114 }
115
116 return IRQ_HANDLED;
117}
118
119static void rotator_src_set_fmt(struct rot_context *rot, u32 fmt)
120{
121 u32 val;
122
123 val = rot_read(ROT_CONTROL);
124 val &= ~ROT_CONTROL_FMT_MASK;
125
126 switch (fmt) {
127 case DRM_FORMAT_NV12:
128 val |= ROT_CONTROL_FMT_YCBCR420_2P;
129 break;
130 case DRM_FORMAT_XRGB8888:
131 val |= ROT_CONTROL_FMT_RGB888;
132 break;
133 }
134
135 rot_write(val, ROT_CONTROL);
136}
137
138static void rotator_src_set_buf(struct rot_context *rot,
139 struct exynos_drm_ipp_buffer *buf)
140{
141 u32 val;
142
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);
147
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);
154
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));
158}
159
160static void rotator_dst_set_transf(struct rot_context *rot,
161 unsigned int rotation)
162{
163 u32 val;
164
165 /* Set transform configuration */
166 val = rot_read(ROT_CONTROL);
167 val &= ~ROT_CONTROL_FLIP_MASK;
168
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;
173
174 val &= ~ROT_CONTROL_ROT_MASK;
175
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;
182
183 rot_write(val, ROT_CONTROL);
184}
185
186static void rotator_dst_set_buf(struct rot_context *rot,
187 struct exynos_drm_ipp_buffer *buf)
188{
189 u32 val;
190
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);
195
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);
199
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));
203}
204
205static void rotator_start(struct rot_context *rot)
206{
207 u32 val;
208
209 /* Set interrupt enable */
210 rotator_reg_set_irq(rot, true);
211
212 val = rot_read(ROT_CONTROL);
213 val |= ROT_CONTROL_START;
214 rot_write(val, ROT_CONTROL);
215}
216
217static int rotator_commit(struct exynos_drm_ipp *ipp,
218 struct exynos_drm_ipp_task *task)
219{
220 struct rot_context *rot =
221 container_of(ipp, struct rot_context, ipp);
222 int ret;
223
224 ret = pm_runtime_resume_and_get(rot->dev);
225 if (ret < 0) {
226 dev_err(rot->dev, "failed to enable ROTATOR device.\n");
227 return ret;
228 }
229 rot->task = task;
230
231 rotator_src_set_fmt(rot, task->src.buf.fourcc);
232 rotator_src_set_buf(rot, &task->src);
233 rotator_dst_set_transf(rot, task->transform.rotation);
234 rotator_dst_set_buf(rot, &task->dst);
235 rotator_start(rot);
236
237 return 0;
238}
239
240static const struct exynos_drm_ipp_funcs ipp_funcs = {
241 .commit = rotator_commit,
242};
243
244static int rotator_bind(struct device *dev, struct device *master, void *data)
245{
246 struct rot_context *rot = dev_get_drvdata(dev);
247 struct drm_device *drm_dev = data;
248 struct exynos_drm_ipp *ipp = &rot->ipp;
249
250 rot->drm_dev = drm_dev;
251 ipp->drm_dev = drm_dev;
252 exynos_drm_register_dma(drm_dev, dev, &rot->dma_priv);
253
254 exynos_drm_ipp_register(dev, ipp, &ipp_funcs,
255 DRM_EXYNOS_IPP_CAP_CROP | DRM_EXYNOS_IPP_CAP_ROTATE,
256 rot->formats, rot->num_formats, "rotator");
257
258 dev_info(dev, "The exynos rotator has been probed successfully\n");
259
260 return 0;
261}
262
263static void rotator_unbind(struct device *dev, struct device *master,
264 void *data)
265{
266 struct rot_context *rot = dev_get_drvdata(dev);
267 struct exynos_drm_ipp *ipp = &rot->ipp;
268
269 exynos_drm_ipp_unregister(dev, ipp);
270 exynos_drm_unregister_dma(rot->drm_dev, rot->dev, &rot->dma_priv);
271}
272
273static const struct component_ops rotator_component_ops = {
274 .bind = rotator_bind,
275 .unbind = rotator_unbind,
276};
277
278static int rotator_probe(struct platform_device *pdev)
279{
280 struct device *dev = &pdev->dev;
281 struct rot_context *rot;
282 const struct rot_variant *variant;
283 int irq;
284 int ret;
285
286 rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL);
287 if (!rot)
288 return -ENOMEM;
289
290 variant = of_device_get_match_data(dev);
291 rot->formats = variant->formats;
292 rot->num_formats = variant->num_formats;
293 rot->dev = dev;
294 rot->regs = devm_platform_ioremap_resource(pdev, 0);
295 if (IS_ERR(rot->regs))
296 return PTR_ERR(rot->regs);
297
298 irq = platform_get_irq(pdev, 0);
299 if (irq < 0)
300 return irq;
301
302 ret = devm_request_irq(dev, irq, rotator_irq_handler, 0, dev_name(dev),
303 rot);
304 if (ret < 0) {
305 dev_err(dev, "failed to request irq\n");
306 return ret;
307 }
308
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);
313 }
314
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);
319
320 ret = component_add(dev, &rotator_component_ops);
321 if (ret)
322 goto err_component;
323
324 return 0;
325
326err_component:
327 pm_runtime_dont_use_autosuspend(dev);
328 pm_runtime_disable(dev);
329 return ret;
330}
331
332static void rotator_remove(struct platform_device *pdev)
333{
334 struct device *dev = &pdev->dev;
335
336 component_del(dev, &rotator_component_ops);
337 pm_runtime_dont_use_autosuspend(dev);
338 pm_runtime_disable(dev);
339}
340
341static int rotator_runtime_suspend(struct device *dev)
342{
343 struct rot_context *rot = dev_get_drvdata(dev);
344
345 clk_disable_unprepare(rot->clock);
346 return 0;
347}
348
349static int rotator_runtime_resume(struct device *dev)
350{
351 struct rot_context *rot = dev_get_drvdata(dev);
352
353 return clk_prepare_enable(rot->clock);
354}
355
356static 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) },
359};
360
361static 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) },
364};
365
366static 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) },
369};
370
371static 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) },
374};
375
376static 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) },
379};
380
381static 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) },
384};
385
386static 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) },
389};
390
391static 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) },
394};
395
396static 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) },
399};
400
401static 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) },
404};
405
406static 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) },
409};
410
411static const struct rot_variant rotator_s5pv210_data = {
412 .formats = rotator_s5pv210_formats,
413 .num_formats = ARRAY_SIZE(rotator_s5pv210_formats),
414};
415
416static const struct rot_variant rotator_4210_data = {
417 .formats = rotator_4210_formats,
418 .num_formats = ARRAY_SIZE(rotator_4210_formats),
419};
420
421static const struct rot_variant rotator_4412_data = {
422 .formats = rotator_4412_formats,
423 .num_formats = ARRAY_SIZE(rotator_4412_formats),
424};
425
426static const struct rot_variant rotator_5250_data = {
427 .formats = rotator_5250_formats,
428 .num_formats = ARRAY_SIZE(rotator_5250_formats),
429};
430
431static const struct of_device_id exynos_rotator_match[] = {
432 {
433 .compatible = "samsung,s5pv210-rotator",
434 .data = &rotator_s5pv210_data,
435 }, {
436 .compatible = "samsung,exynos4210-rotator",
437 .data = &rotator_4210_data,
438 }, {
439 .compatible = "samsung,exynos4212-rotator",
440 .data = &rotator_4412_data,
441 }, {
442 .compatible = "samsung,exynos5250-rotator",
443 .data = &rotator_5250_data,
444 }, {
445 },
446};
447MODULE_DEVICE_TABLE(of, exynos_rotator_match);
448
449static DEFINE_RUNTIME_DEV_PM_OPS(rotator_pm_ops, rotator_runtime_suspend,
450 rotator_runtime_resume, NULL);
451
452struct platform_driver rotator_driver = {
453 .probe = rotator_probe,
454 .remove_new = rotator_remove,
455 .driver = {
456 .name = "exynos-rotator",
457 .owner = THIS_MODULE,
458 .pm = pm_ptr(&rotator_pm_ops),
459 .of_match_table = exynos_rotator_match,
460 },
461};