xref: /linux/drivers/gpu/drm/exynos/exynos_drm_rotator.c (revision 110e6f26af80dfd90b6e5c645b1aed7228aa580d)
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 
49 enum 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  */
63 struct 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  */
77 struct 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  */
93 struct 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 
104 static 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 
116 static 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 
125 static 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 
137 static 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 
166 static 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 
201 static 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 
226 static 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 
235 static 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 
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)
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 
321 static 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 
373 static 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 
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)
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 
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,
461 };
462 
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,
467 };
468 
469 static 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 
487 static 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 
499 static 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 
513 static 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 
600 static 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 
626 static 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 
643 static 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 
660 static 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 
677 static 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 };
692 MODULE_DEVICE_TABLE(of, exynos_rotator_match);
693 
694 static 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 
770 err_ippdrv_register:
771 	pm_runtime_disable(dev);
772 	return ret;
773 }
774 
775 static 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
789 static 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
804 static 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 
814 static 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 
825 static 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 
832 static 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 
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,
843 									NULL)
844 };
845 
846 struct 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 };
856