diff options
-rw-r--r-- | drivers/gpu/drm/exynos/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_drv.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_rotator.c | 855 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_drm_rotator.h | 33 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/regs-rotator.h | 73 |
7 files changed, 985 insertions, 0 deletions
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 66c02df360e7..e0056a3ab756 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig | |||
@@ -57,3 +57,10 @@ config DRM_EXYNOS_FIMC | |||
57 | depends on DRM_EXYNOS_IPP | 57 | depends on DRM_EXYNOS_IPP |
58 | help | 58 | help |
59 | Choose this option if you want to use Exynos FIMC for DRM. | 59 | Choose this option if you want to use Exynos FIMC for DRM. |
60 | |||
61 | config DRM_EXYNOS_ROTATOR | ||
62 | bool "Exynos DRM Rotator" | ||
63 | depends on DRM_EXYNOS_IPP | ||
64 | help | ||
65 | Choose this option if you want to use Exynos Rotator for DRM. | ||
66 | |||
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 9710024ed000..3b706682b4ab 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile | |||
@@ -18,5 +18,6 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o | |||
18 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o | 18 | exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o |
19 | exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o | 19 | exynosdrm-$(CONFIG_DRM_EXYNOS_IPP) += exynos_drm_ipp.o |
20 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o | 20 | exynosdrm-$(CONFIG_DRM_EXYNOS_FIMC) += exynos_drm_fimc.o |
21 | exynosdrm-$(CONFIG_DRM_EXYNOS_ROTATOR) += exynos_drm_rotator.o | ||
21 | 22 | ||
22 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o | 23 | obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 73f02ac53bad..09d884ba9e2e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c | |||
@@ -378,6 +378,12 @@ static int __init exynos_drm_init(void) | |||
378 | goto out_fimc; | 378 | goto out_fimc; |
379 | #endif | 379 | #endif |
380 | 380 | ||
381 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
382 | ret = platform_driver_register(&rotator_driver); | ||
383 | if (ret < 0) | ||
384 | goto out_rotator; | ||
385 | #endif | ||
386 | |||
381 | #ifdef CONFIG_DRM_EXYNOS_IPP | 387 | #ifdef CONFIG_DRM_EXYNOS_IPP |
382 | ret = platform_driver_register(&ipp_driver); | 388 | ret = platform_driver_register(&ipp_driver); |
383 | if (ret < 0) | 389 | if (ret < 0) |
@@ -406,6 +412,11 @@ out_drm: | |||
406 | out_ipp: | 412 | out_ipp: |
407 | #endif | 413 | #endif |
408 | 414 | ||
415 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
416 | platform_driver_unregister(&rotator_driver); | ||
417 | out_rotator: | ||
418 | #endif | ||
419 | |||
409 | #ifdef CONFIG_DRM_EXYNOS_FIMC | 420 | #ifdef CONFIG_DRM_EXYNOS_FIMC |
410 | platform_driver_unregister(&fimc_driver); | 421 | platform_driver_unregister(&fimc_driver); |
411 | out_fimc: | 422 | out_fimc: |
@@ -451,6 +462,10 @@ static void __exit exynos_drm_exit(void) | |||
451 | platform_driver_unregister(&ipp_driver); | 462 | platform_driver_unregister(&ipp_driver); |
452 | #endif | 463 | #endif |
453 | 464 | ||
465 | #ifdef CONFIG_DRM_EXYNOS_ROTATOR | ||
466 | platform_driver_unregister(&rotator_driver); | ||
467 | #endif | ||
468 | |||
454 | #ifdef CONFIG_DRM_EXYNOS_FIMC | 469 | #ifdef CONFIG_DRM_EXYNOS_FIMC |
455 | platform_driver_unregister(&fimc_driver); | 470 | platform_driver_unregister(&fimc_driver); |
456 | #endif | 471 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 63803508e4df..de49956539e7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h | |||
@@ -350,5 +350,6 @@ extern struct platform_driver exynos_drm_common_hdmi_driver; | |||
350 | extern struct platform_driver vidi_driver; | 350 | extern struct platform_driver vidi_driver; |
351 | extern struct platform_driver g2d_driver; | 351 | extern struct platform_driver g2d_driver; |
352 | extern struct platform_driver fimc_driver; | 352 | extern struct platform_driver fimc_driver; |
353 | extern struct platform_driver rotator_driver; | ||
353 | extern struct platform_driver ipp_driver; | 354 | extern struct platform_driver ipp_driver; |
354 | #endif | 355 | #endif |
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c new file mode 100644 index 000000000000..1c2366083c70 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c | |||
@@ -0,0 +1,855 @@ | |||
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/module.h> | ||
14 | #include <linux/err.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/io.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/pm_runtime.h> | ||
20 | |||
21 | #include <drm/drmP.h> | ||
22 | #include <drm/exynos_drm.h> | ||
23 | #include "regs-rotator.h" | ||
24 | #include "exynos_drm.h" | ||
25 | #include "exynos_drm_ipp.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 | * M2M operation : supports crop/scale/rotation/csc so on. | ||
33 | * Memory ----> Rotator H/W ----> Memory. | ||
34 | */ | ||
35 | |||
36 | /* | ||
37 | * TODO | ||
38 | * 1. check suspend/resume api if needed. | ||
39 | * 2. need to check use case platform_device_id. | ||
40 | * 3. check src/dst size with, height. | ||
41 | * 4. need to add supported list in prop_list. | ||
42 | */ | ||
43 | |||
44 | #define get_rot_context(dev) platform_get_drvdata(to_platform_device(dev)) | ||
45 | #define get_ctx_from_ippdrv(ippdrv) container_of(ippdrv,\ | ||
46 | struct rot_context, ippdrv); | ||
47 | #define rot_read(offset) readl(rot->regs + (offset)) | ||
48 | #define rot_write(cfg, offset) writel(cfg, rot->regs + (offset)) | ||
49 | |||
50 | enum rot_irq_status { | ||
51 | ROT_IRQ_STATUS_COMPLETE = 8, | ||
52 | ROT_IRQ_STATUS_ILLEGAL = 9, | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * A structure of limitation. | ||
57 | * | ||
58 | * @min_w: minimum width. | ||
59 | * @min_h: minimum height. | ||
60 | * @max_w: maximum width. | ||
61 | * @max_h: maximum height. | ||
62 | * @align: align size. | ||
63 | */ | ||
64 | struct rot_limit { | ||
65 | u32 min_w; | ||
66 | u32 min_h; | ||
67 | u32 max_w; | ||
68 | u32 max_h; | ||
69 | u32 align; | ||
70 | }; | ||
71 | |||
72 | /* | ||
73 | * A structure of limitation table. | ||
74 | * | ||
75 | * @ycbcr420_2p: case of YUV. | ||
76 | * @rgb888: case of RGB. | ||
77 | */ | ||
78 | struct rot_limit_table { | ||
79 | struct rot_limit ycbcr420_2p; | ||
80 | struct rot_limit rgb888; | ||
81 | }; | ||
82 | |||
83 | /* | ||
84 | * A structure of rotator context. | ||
85 | * @ippdrv: prepare initialization using ippdrv. | ||
86 | * @regs_res: register resources. | ||
87 | * @regs: memory mapped io registers. | ||
88 | * @clock: rotator gate clock. | ||
89 | * @limit_tbl: limitation of rotator. | ||
90 | * @irq: irq number. | ||
91 | * @cur_buf_id: current operation buffer id. | ||
92 | * @suspended: suspended state. | ||
93 | */ | ||
94 | struct rot_context { | ||
95 | struct exynos_drm_ippdrv ippdrv; | ||
96 | struct resource *regs_res; | ||
97 | void __iomem *regs; | ||
98 | struct clk *clock; | ||
99 | struct rot_limit_table *limit_tbl; | ||
100 | int irq; | ||
101 | int cur_buf_id[EXYNOS_DRM_OPS_MAX]; | ||
102 | bool suspended; | ||
103 | }; | ||
104 | |||
105 | static void rotator_reg_set_irq(struct rot_context *rot, bool enable) | ||
106 | { | ||
107 | u32 val = rot_read(ROT_CONFIG); | ||
108 | |||
109 | if (enable == true) | ||
110 | val |= ROT_CONFIG_IRQ; | ||
111 | else | ||
112 | val &= ~ROT_CONFIG_IRQ; | ||
113 | |||
114 | rot_write(val, ROT_CONFIG); | ||
115 | } | ||
116 | |||
117 | static u32 rotator_reg_get_fmt(struct rot_context *rot) | ||
118 | { | ||
119 | u32 val = rot_read(ROT_CONTROL); | ||
120 | |||
121 | val &= ROT_CONTROL_FMT_MASK; | ||
122 | |||
123 | return val; | ||
124 | } | ||
125 | |||
126 | static enum rot_irq_status rotator_reg_get_irq_status(struct rot_context *rot) | ||
127 | { | ||
128 | u32 val = rot_read(ROT_STATUS); | ||
129 | |||
130 | val = ROT_STATUS_IRQ(val); | ||
131 | |||
132 | if (val == ROT_STATUS_IRQ_VAL_COMPLETE) | ||
133 | return ROT_IRQ_STATUS_COMPLETE; | ||
134 | |||
135 | return ROT_IRQ_STATUS_ILLEGAL; | ||
136 | } | ||
137 | |||
138 | static irqreturn_t rotator_irq_handler(int irq, void *arg) | ||
139 | { | ||
140 | struct rot_context *rot = arg; | ||
141 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
142 | struct drm_exynos_ipp_cmd_node *c_node = ippdrv->cmd; | ||
143 | struct drm_exynos_ipp_event_work *event_work = c_node->event_work; | ||
144 | enum rot_irq_status irq_status; | ||
145 | u32 val; | ||
146 | |||
147 | /* Get execution result */ | ||
148 | irq_status = rotator_reg_get_irq_status(rot); | ||
149 | |||
150 | /* clear status */ | ||
151 | val = rot_read(ROT_STATUS); | ||
152 | val |= ROT_STATUS_IRQ_PENDING((u32)irq_status); | ||
153 | rot_write(val, ROT_STATUS); | ||
154 | |||
155 | if (irq_status == ROT_IRQ_STATUS_COMPLETE) { | ||
156 | event_work->ippdrv = ippdrv; | ||
157 | event_work->buf_id[EXYNOS_DRM_OPS_DST] = | ||
158 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST]; | ||
159 | queue_work(ippdrv->event_workq, | ||
160 | (struct work_struct *)event_work); | ||
161 | } else | ||
162 | DRM_ERROR("the SFR is set illegally\n"); | ||
163 | |||
164 | return IRQ_HANDLED; | ||
165 | } | ||
166 | |||
167 | static void rotator_align_size(struct rot_context *rot, u32 fmt, u32 *hsize, | ||
168 | u32 *vsize) | ||
169 | { | ||
170 | struct rot_limit_table *limit_tbl = rot->limit_tbl; | ||
171 | struct rot_limit *limit; | ||
172 | u32 mask, val; | ||
173 | |||
174 | /* Get size limit */ | ||
175 | if (fmt == ROT_CONTROL_FMT_RGB888) | ||
176 | limit = &limit_tbl->rgb888; | ||
177 | else | ||
178 | limit = &limit_tbl->ycbcr420_2p; | ||
179 | |||
180 | /* Get mask for rounding to nearest aligned val */ | ||
181 | mask = ~((1 << limit->align) - 1); | ||
182 | |||
183 | /* Set aligned width */ | ||
184 | val = ROT_ALIGN(*hsize, limit->align, mask); | ||
185 | if (val < limit->min_w) | ||
186 | *hsize = ROT_MIN(limit->min_w, mask); | ||
187 | else if (val > limit->max_w) | ||
188 | *hsize = ROT_MAX(limit->max_w, mask); | ||
189 | else | ||
190 | *hsize = val; | ||
191 | |||
192 | /* Set aligned height */ | ||
193 | val = ROT_ALIGN(*vsize, limit->align, mask); | ||
194 | if (val < limit->min_h) | ||
195 | *vsize = ROT_MIN(limit->min_h, mask); | ||
196 | else if (val > limit->max_h) | ||
197 | *vsize = ROT_MAX(limit->max_h, mask); | ||
198 | else | ||
199 | *vsize = val; | ||
200 | } | ||
201 | |||
202 | static int rotator_src_set_fmt(struct device *dev, u32 fmt) | ||
203 | { | ||
204 | struct rot_context *rot = dev_get_drvdata(dev); | ||
205 | u32 val; | ||
206 | |||
207 | val = rot_read(ROT_CONTROL); | ||
208 | val &= ~ROT_CONTROL_FMT_MASK; | ||
209 | |||
210 | switch (fmt) { | ||
211 | case DRM_FORMAT_NV12: | ||
212 | val |= ROT_CONTROL_FMT_YCBCR420_2P; | ||
213 | break; | ||
214 | case DRM_FORMAT_XRGB8888: | ||
215 | val |= ROT_CONTROL_FMT_RGB888; | ||
216 | break; | ||
217 | default: | ||
218 | DRM_ERROR("invalid image format\n"); | ||
219 | return -EINVAL; | ||
220 | } | ||
221 | |||
222 | rot_write(val, ROT_CONTROL); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static inline bool rotator_check_reg_fmt(u32 fmt) | ||
228 | { | ||
229 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) || | ||
230 | (fmt == ROT_CONTROL_FMT_RGB888)) | ||
231 | return true; | ||
232 | |||
233 | return false; | ||
234 | } | ||
235 | |||
236 | static int rotator_src_set_size(struct device *dev, int swap, | ||
237 | struct drm_exynos_pos *pos, | ||
238 | struct drm_exynos_sz *sz) | ||
239 | { | ||
240 | struct rot_context *rot = dev_get_drvdata(dev); | ||
241 | u32 fmt, hsize, vsize; | ||
242 | u32 val; | ||
243 | |||
244 | /* Get format */ | ||
245 | fmt = rotator_reg_get_fmt(rot); | ||
246 | if (!rotator_check_reg_fmt(fmt)) { | ||
247 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | |||
251 | /* Align buffer size */ | ||
252 | hsize = sz->hsize; | ||
253 | vsize = sz->vsize; | ||
254 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
255 | |||
256 | /* Set buffer size configuration */ | ||
257 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | ||
258 | rot_write(val, ROT_SRC_BUF_SIZE); | ||
259 | |||
260 | /* Set crop image position configuration */ | ||
261 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | ||
262 | rot_write(val, ROT_SRC_CROP_POS); | ||
263 | val = ROT_SRC_CROP_SIZE_H(pos->h) | ROT_SRC_CROP_SIZE_W(pos->w); | ||
264 | rot_write(val, ROT_SRC_CROP_SIZE); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static int rotator_src_set_addr(struct device *dev, | ||
270 | struct drm_exynos_ipp_buf_info *buf_info, | ||
271 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
272 | { | ||
273 | struct rot_context *rot = dev_get_drvdata(dev); | ||
274 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
275 | u32 val, fmt, hsize, vsize; | ||
276 | int i; | ||
277 | |||
278 | /* Set current buf_id */ | ||
279 | rot->cur_buf_id[EXYNOS_DRM_OPS_SRC] = buf_id; | ||
280 | |||
281 | switch (buf_type) { | ||
282 | case IPP_BUF_ENQUEUE: | ||
283 | /* Set address configuration */ | ||
284 | for_each_ipp_planar(i) | ||
285 | addr[i] = buf_info->base[i]; | ||
286 | |||
287 | /* Get format */ | ||
288 | fmt = rotator_reg_get_fmt(rot); | ||
289 | if (!rotator_check_reg_fmt(fmt)) { | ||
290 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
291 | return -EINVAL; | ||
292 | } | ||
293 | |||
294 | /* Re-set cb planar for NV12 format */ | ||
295 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
296 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
297 | |||
298 | val = rot_read(ROT_SRC_BUF_SIZE); | ||
299 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
300 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
301 | |||
302 | /* Set cb planar */ | ||
303 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
304 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
305 | } | ||
306 | |||
307 | for_each_ipp_planar(i) | ||
308 | rot_write(addr[i], ROT_SRC_BUF_ADDR(i)); | ||
309 | break; | ||
310 | case IPP_BUF_DEQUEUE: | ||
311 | for_each_ipp_planar(i) | ||
312 | rot_write(0x0, ROT_SRC_BUF_ADDR(i)); | ||
313 | break; | ||
314 | default: | ||
315 | /* Nothing to do */ | ||
316 | break; | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static int rotator_dst_set_transf(struct device *dev, | ||
323 | enum drm_exynos_degree degree, | ||
324 | enum drm_exynos_flip flip, bool *swap) | ||
325 | { | ||
326 | struct rot_context *rot = dev_get_drvdata(dev); | ||
327 | u32 val; | ||
328 | |||
329 | /* Set transform configuration */ | ||
330 | val = rot_read(ROT_CONTROL); | ||
331 | val &= ~ROT_CONTROL_FLIP_MASK; | ||
332 | |||
333 | switch (flip) { | ||
334 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
335 | val |= ROT_CONTROL_FLIP_VERTICAL; | ||
336 | break; | ||
337 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
338 | val |= ROT_CONTROL_FLIP_HORIZONTAL; | ||
339 | break; | ||
340 | default: | ||
341 | /* Flip None */ | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | val &= ~ROT_CONTROL_ROT_MASK; | ||
346 | |||
347 | switch (degree) { | ||
348 | case EXYNOS_DRM_DEGREE_90: | ||
349 | val |= ROT_CONTROL_ROT_90; | ||
350 | break; | ||
351 | case EXYNOS_DRM_DEGREE_180: | ||
352 | val |= ROT_CONTROL_ROT_180; | ||
353 | break; | ||
354 | case EXYNOS_DRM_DEGREE_270: | ||
355 | val |= ROT_CONTROL_ROT_270; | ||
356 | break; | ||
357 | default: | ||
358 | /* Rotation 0 Degree */ | ||
359 | break; | ||
360 | } | ||
361 | |||
362 | rot_write(val, ROT_CONTROL); | ||
363 | |||
364 | /* Check degree for setting buffer size swap */ | ||
365 | if ((degree == EXYNOS_DRM_DEGREE_90) || | ||
366 | (degree == EXYNOS_DRM_DEGREE_270)) | ||
367 | *swap = true; | ||
368 | else | ||
369 | *swap = false; | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int rotator_dst_set_size(struct device *dev, int swap, | ||
375 | struct drm_exynos_pos *pos, | ||
376 | struct drm_exynos_sz *sz) | ||
377 | { | ||
378 | struct rot_context *rot = dev_get_drvdata(dev); | ||
379 | u32 val, fmt, hsize, vsize; | ||
380 | |||
381 | /* Get format */ | ||
382 | fmt = rotator_reg_get_fmt(rot); | ||
383 | if (!rotator_check_reg_fmt(fmt)) { | ||
384 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
385 | return -EINVAL; | ||
386 | } | ||
387 | |||
388 | /* Align buffer size */ | ||
389 | hsize = sz->hsize; | ||
390 | vsize = sz->vsize; | ||
391 | rotator_align_size(rot, fmt, &hsize, &vsize); | ||
392 | |||
393 | /* Set buffer size configuration */ | ||
394 | val = ROT_SET_BUF_SIZE_H(vsize) | ROT_SET_BUF_SIZE_W(hsize); | ||
395 | rot_write(val, ROT_DST_BUF_SIZE); | ||
396 | |||
397 | /* Set crop image position configuration */ | ||
398 | val = ROT_CROP_POS_Y(pos->y) | ROT_CROP_POS_X(pos->x); | ||
399 | rot_write(val, ROT_DST_CROP_POS); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int rotator_dst_set_addr(struct device *dev, | ||
405 | struct drm_exynos_ipp_buf_info *buf_info, | ||
406 | u32 buf_id, enum drm_exynos_ipp_buf_type buf_type) | ||
407 | { | ||
408 | struct rot_context *rot = dev_get_drvdata(dev); | ||
409 | dma_addr_t addr[EXYNOS_DRM_PLANAR_MAX]; | ||
410 | u32 val, fmt, hsize, vsize; | ||
411 | int i; | ||
412 | |||
413 | /* Set current buf_id */ | ||
414 | rot->cur_buf_id[EXYNOS_DRM_OPS_DST] = buf_id; | ||
415 | |||
416 | switch (buf_type) { | ||
417 | case IPP_BUF_ENQUEUE: | ||
418 | /* Set address configuration */ | ||
419 | for_each_ipp_planar(i) | ||
420 | addr[i] = buf_info->base[i]; | ||
421 | |||
422 | /* Get format */ | ||
423 | fmt = rotator_reg_get_fmt(rot); | ||
424 | if (!rotator_check_reg_fmt(fmt)) { | ||
425 | DRM_ERROR("%s:invalid format.\n", __func__); | ||
426 | return -EINVAL; | ||
427 | } | ||
428 | |||
429 | /* Re-set cb planar for NV12 format */ | ||
430 | if ((fmt == ROT_CONTROL_FMT_YCBCR420_2P) && | ||
431 | !addr[EXYNOS_DRM_PLANAR_CB]) { | ||
432 | /* Get buf size */ | ||
433 | val = rot_read(ROT_DST_BUF_SIZE); | ||
434 | |||
435 | hsize = ROT_GET_BUF_SIZE_W(val); | ||
436 | vsize = ROT_GET_BUF_SIZE_H(val); | ||
437 | |||
438 | /* Set cb planar */ | ||
439 | addr[EXYNOS_DRM_PLANAR_CB] = | ||
440 | addr[EXYNOS_DRM_PLANAR_Y] + hsize * vsize; | ||
441 | } | ||
442 | |||
443 | for_each_ipp_planar(i) | ||
444 | rot_write(addr[i], ROT_DST_BUF_ADDR(i)); | ||
445 | break; | ||
446 | case IPP_BUF_DEQUEUE: | ||
447 | for_each_ipp_planar(i) | ||
448 | rot_write(0x0, ROT_DST_BUF_ADDR(i)); | ||
449 | break; | ||
450 | default: | ||
451 | /* Nothing to do */ | ||
452 | break; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static struct exynos_drm_ipp_ops rot_src_ops = { | ||
459 | .set_fmt = rotator_src_set_fmt, | ||
460 | .set_size = rotator_src_set_size, | ||
461 | .set_addr = rotator_src_set_addr, | ||
462 | }; | ||
463 | |||
464 | static struct exynos_drm_ipp_ops rot_dst_ops = { | ||
465 | .set_transf = rotator_dst_set_transf, | ||
466 | .set_size = rotator_dst_set_size, | ||
467 | .set_addr = rotator_dst_set_addr, | ||
468 | }; | ||
469 | |||
470 | static int rotator_init_prop_list(struct exynos_drm_ippdrv *ippdrv) | ||
471 | { | ||
472 | struct drm_exynos_ipp_prop_list *prop_list; | ||
473 | |||
474 | DRM_DEBUG_KMS("%s\n", __func__); | ||
475 | |||
476 | prop_list = devm_kzalloc(ippdrv->dev, sizeof(*prop_list), GFP_KERNEL); | ||
477 | if (!prop_list) { | ||
478 | DRM_ERROR("failed to alloc property list.\n"); | ||
479 | return -ENOMEM; | ||
480 | } | ||
481 | |||
482 | prop_list->version = 1; | ||
483 | prop_list->flip = (1 << EXYNOS_DRM_FLIP_VERTICAL) | | ||
484 | (1 << EXYNOS_DRM_FLIP_HORIZONTAL); | ||
485 | prop_list->degree = (1 << EXYNOS_DRM_DEGREE_0) | | ||
486 | (1 << EXYNOS_DRM_DEGREE_90) | | ||
487 | (1 << EXYNOS_DRM_DEGREE_180) | | ||
488 | (1 << EXYNOS_DRM_DEGREE_270); | ||
489 | prop_list->csc = 0; | ||
490 | prop_list->crop = 0; | ||
491 | prop_list->scale = 0; | ||
492 | |||
493 | ippdrv->prop_list = prop_list; | ||
494 | |||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static inline bool rotator_check_drm_fmt(u32 fmt) | ||
499 | { | ||
500 | switch (fmt) { | ||
501 | case DRM_FORMAT_XRGB8888: | ||
502 | case DRM_FORMAT_NV12: | ||
503 | return true; | ||
504 | default: | ||
505 | DRM_DEBUG_KMS("%s:not support format\n", __func__); | ||
506 | return false; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | static inline bool rotator_check_drm_flip(enum drm_exynos_flip flip) | ||
511 | { | ||
512 | switch (flip) { | ||
513 | case EXYNOS_DRM_FLIP_NONE: | ||
514 | case EXYNOS_DRM_FLIP_VERTICAL: | ||
515 | case EXYNOS_DRM_FLIP_HORIZONTAL: | ||
516 | return true; | ||
517 | default: | ||
518 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
519 | return false; | ||
520 | } | ||
521 | } | ||
522 | |||
523 | static int rotator_ippdrv_check_property(struct device *dev, | ||
524 | struct drm_exynos_ipp_property *property) | ||
525 | { | ||
526 | struct drm_exynos_ipp_config *src_config = | ||
527 | &property->config[EXYNOS_DRM_OPS_SRC]; | ||
528 | struct drm_exynos_ipp_config *dst_config = | ||
529 | &property->config[EXYNOS_DRM_OPS_DST]; | ||
530 | struct drm_exynos_pos *src_pos = &src_config->pos; | ||
531 | struct drm_exynos_pos *dst_pos = &dst_config->pos; | ||
532 | struct drm_exynos_sz *src_sz = &src_config->sz; | ||
533 | struct drm_exynos_sz *dst_sz = &dst_config->sz; | ||
534 | bool swap = false; | ||
535 | |||
536 | /* Check format configuration */ | ||
537 | if (src_config->fmt != dst_config->fmt) { | ||
538 | DRM_DEBUG_KMS("%s:not support csc feature\n", __func__); | ||
539 | return -EINVAL; | ||
540 | } | ||
541 | |||
542 | if (!rotator_check_drm_fmt(dst_config->fmt)) { | ||
543 | DRM_DEBUG_KMS("%s:invalid format\n", __func__); | ||
544 | return -EINVAL; | ||
545 | } | ||
546 | |||
547 | /* Check transform configuration */ | ||
548 | if (src_config->degree != EXYNOS_DRM_DEGREE_0) { | ||
549 | DRM_DEBUG_KMS("%s:not support source-side rotation\n", | ||
550 | __func__); | ||
551 | return -EINVAL; | ||
552 | } | ||
553 | |||
554 | switch (dst_config->degree) { | ||
555 | case EXYNOS_DRM_DEGREE_90: | ||
556 | case EXYNOS_DRM_DEGREE_270: | ||
557 | swap = true; | ||
558 | case EXYNOS_DRM_DEGREE_0: | ||
559 | case EXYNOS_DRM_DEGREE_180: | ||
560 | /* No problem */ | ||
561 | break; | ||
562 | default: | ||
563 | DRM_DEBUG_KMS("%s:invalid degree\n", __func__); | ||
564 | return -EINVAL; | ||
565 | } | ||
566 | |||
567 | if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { | ||
568 | DRM_DEBUG_KMS("%s:not support source-side flip\n", __func__); | ||
569 | return -EINVAL; | ||
570 | } | ||
571 | |||
572 | if (!rotator_check_drm_flip(dst_config->flip)) { | ||
573 | DRM_DEBUG_KMS("%s:invalid flip\n", __func__); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | /* Check size configuration */ | ||
578 | if ((src_pos->x + src_pos->w > src_sz->hsize) || | ||
579 | (src_pos->y + src_pos->h > src_sz->vsize)) { | ||
580 | DRM_DEBUG_KMS("%s:out of source buffer bound\n", __func__); | ||
581 | return -EINVAL; | ||
582 | } | ||
583 | |||
584 | if (swap) { | ||
585 | if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || | ||
586 | (dst_pos->y + dst_pos->w > dst_sz->hsize)) { | ||
587 | DRM_DEBUG_KMS("%s:out of destination buffer bound\n", | ||
588 | __func__); | ||
589 | return -EINVAL; | ||
590 | } | ||
591 | |||
592 | if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { | ||
593 | DRM_DEBUG_KMS("%s:not support scale feature\n", | ||
594 | __func__); | ||
595 | return -EINVAL; | ||
596 | } | ||
597 | } else { | ||
598 | if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || | ||
599 | (dst_pos->y + dst_pos->h > dst_sz->vsize)) { | ||
600 | DRM_DEBUG_KMS("%s:out of destination buffer bound\n", | ||
601 | __func__); | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { | ||
606 | DRM_DEBUG_KMS("%s:not support scale feature\n", | ||
607 | __func__); | ||
608 | return -EINVAL; | ||
609 | } | ||
610 | } | ||
611 | |||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static int rotator_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) | ||
616 | { | ||
617 | struct rot_context *rot = dev_get_drvdata(dev); | ||
618 | u32 val; | ||
619 | |||
620 | if (rot->suspended) { | ||
621 | DRM_ERROR("suspended state\n"); | ||
622 | return -EPERM; | ||
623 | } | ||
624 | |||
625 | if (cmd != IPP_CMD_M2M) { | ||
626 | DRM_ERROR("not support cmd: %d\n", cmd); | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | |||
630 | /* Set interrupt enable */ | ||
631 | rotator_reg_set_irq(rot, true); | ||
632 | |||
633 | val = rot_read(ROT_CONTROL); | ||
634 | val |= ROT_CONTROL_START; | ||
635 | |||
636 | rot_write(val, ROT_CONTROL); | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | static int __devinit rotator_probe(struct platform_device *pdev) | ||
642 | { | ||
643 | struct device *dev = &pdev->dev; | ||
644 | struct rot_context *rot; | ||
645 | struct exynos_drm_ippdrv *ippdrv; | ||
646 | int ret; | ||
647 | |||
648 | rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); | ||
649 | if (!rot) { | ||
650 | dev_err(dev, "failed to allocate rot\n"); | ||
651 | return -ENOMEM; | ||
652 | } | ||
653 | |||
654 | rot->limit_tbl = (struct rot_limit_table *) | ||
655 | platform_get_device_id(pdev)->driver_data; | ||
656 | |||
657 | rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
658 | if (!rot->regs_res) { | ||
659 | dev_err(dev, "failed to find registers\n"); | ||
660 | ret = -ENOENT; | ||
661 | goto err_get_resource; | ||
662 | } | ||
663 | |||
664 | rot->regs = devm_request_and_ioremap(dev, rot->regs_res); | ||
665 | if (!rot->regs) { | ||
666 | dev_err(dev, "failed to map register\n"); | ||
667 | ret = -ENXIO; | ||
668 | goto err_get_resource; | ||
669 | } | ||
670 | |||
671 | rot->irq = platform_get_irq(pdev, 0); | ||
672 | if (rot->irq < 0) { | ||
673 | dev_err(dev, "failed to get irq\n"); | ||
674 | ret = rot->irq; | ||
675 | goto err_get_irq; | ||
676 | } | ||
677 | |||
678 | ret = request_threaded_irq(rot->irq, NULL, rotator_irq_handler, | ||
679 | IRQF_ONESHOT, "drm_rotator", rot); | ||
680 | if (ret < 0) { | ||
681 | dev_err(dev, "failed to request irq\n"); | ||
682 | goto err_get_irq; | ||
683 | } | ||
684 | |||
685 | rot->clock = clk_get(dev, "rotator"); | ||
686 | if (IS_ERR_OR_NULL(rot->clock)) { | ||
687 | dev_err(dev, "failed to get clock\n"); | ||
688 | ret = PTR_ERR(rot->clock); | ||
689 | goto err_clk_get; | ||
690 | } | ||
691 | |||
692 | pm_runtime_enable(dev); | ||
693 | |||
694 | ippdrv = &rot->ippdrv; | ||
695 | ippdrv->dev = dev; | ||
696 | ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; | ||
697 | ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; | ||
698 | ippdrv->check_property = rotator_ippdrv_check_property; | ||
699 | ippdrv->start = rotator_ippdrv_start; | ||
700 | ret = rotator_init_prop_list(ippdrv); | ||
701 | if (ret < 0) { | ||
702 | dev_err(dev, "failed to init property list.\n"); | ||
703 | goto err_ippdrv_register; | ||
704 | } | ||
705 | |||
706 | DRM_DEBUG_KMS("%s:ippdrv[0x%x]\n", __func__, (int)ippdrv); | ||
707 | |||
708 | platform_set_drvdata(pdev, rot); | ||
709 | |||
710 | ret = exynos_drm_ippdrv_register(ippdrv); | ||
711 | if (ret < 0) { | ||
712 | dev_err(dev, "failed to register drm rotator device\n"); | ||
713 | goto err_ippdrv_register; | ||
714 | } | ||
715 | |||
716 | dev_info(dev, "The exynos rotator is probed successfully\n"); | ||
717 | |||
718 | return 0; | ||
719 | |||
720 | err_ippdrv_register: | ||
721 | devm_kfree(dev, ippdrv->prop_list); | ||
722 | pm_runtime_disable(dev); | ||
723 | clk_put(rot->clock); | ||
724 | err_clk_get: | ||
725 | free_irq(rot->irq, rot); | ||
726 | err_get_irq: | ||
727 | devm_iounmap(dev, rot->regs); | ||
728 | err_get_resource: | ||
729 | devm_kfree(dev, rot); | ||
730 | return ret; | ||
731 | } | ||
732 | |||
733 | static int __devexit rotator_remove(struct platform_device *pdev) | ||
734 | { | ||
735 | struct device *dev = &pdev->dev; | ||
736 | struct rot_context *rot = dev_get_drvdata(dev); | ||
737 | struct exynos_drm_ippdrv *ippdrv = &rot->ippdrv; | ||
738 | |||
739 | devm_kfree(dev, ippdrv->prop_list); | ||
740 | exynos_drm_ippdrv_unregister(ippdrv); | ||
741 | |||
742 | pm_runtime_disable(dev); | ||
743 | clk_put(rot->clock); | ||
744 | |||
745 | free_irq(rot->irq, rot); | ||
746 | devm_iounmap(dev, rot->regs); | ||
747 | |||
748 | devm_kfree(dev, rot); | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | struct rot_limit_table rot_limit_tbl = { | ||
754 | .ycbcr420_2p = { | ||
755 | .min_w = 32, | ||
756 | .min_h = 32, | ||
757 | .max_w = SZ_32K, | ||
758 | .max_h = SZ_32K, | ||
759 | .align = 3, | ||
760 | }, | ||
761 | .rgb888 = { | ||
762 | .min_w = 8, | ||
763 | .min_h = 8, | ||
764 | .max_w = SZ_8K, | ||
765 | .max_h = SZ_8K, | ||
766 | .align = 2, | ||
767 | }, | ||
768 | }; | ||
769 | |||
770 | struct platform_device_id rotator_driver_ids[] = { | ||
771 | { | ||
772 | .name = "exynos-rot", | ||
773 | .driver_data = (unsigned long)&rot_limit_tbl, | ||
774 | }, | ||
775 | {}, | ||
776 | }; | ||
777 | |||
778 | static int rotator_clk_crtl(struct rot_context *rot, bool enable) | ||
779 | { | ||
780 | DRM_DEBUG_KMS("%s\n", __func__); | ||
781 | |||
782 | if (enable) { | ||
783 | clk_enable(rot->clock); | ||
784 | rot->suspended = false; | ||
785 | } else { | ||
786 | clk_disable(rot->clock); | ||
787 | rot->suspended = true; | ||
788 | } | ||
789 | |||
790 | return 0; | ||
791 | } | ||
792 | |||
793 | |||
794 | #ifdef CONFIG_PM_SLEEP | ||
795 | static int rotator_suspend(struct device *dev) | ||
796 | { | ||
797 | struct rot_context *rot = dev_get_drvdata(dev); | ||
798 | |||
799 | DRM_DEBUG_KMS("%s\n", __func__); | ||
800 | |||
801 | if (pm_runtime_suspended(dev)) | ||
802 | return 0; | ||
803 | |||
804 | return rotator_clk_crtl(rot, false); | ||
805 | } | ||
806 | |||
807 | static int rotator_resume(struct device *dev) | ||
808 | { | ||
809 | struct rot_context *rot = dev_get_drvdata(dev); | ||
810 | |||
811 | DRM_DEBUG_KMS("%s\n", __func__); | ||
812 | |||
813 | if (!pm_runtime_suspended(dev)) | ||
814 | return rotator_clk_crtl(rot, true); | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | #endif | ||
819 | |||
820 | #ifdef CONFIG_PM_RUNTIME | ||
821 | static int rotator_runtime_suspend(struct device *dev) | ||
822 | { | ||
823 | struct rot_context *rot = dev_get_drvdata(dev); | ||
824 | |||
825 | DRM_DEBUG_KMS("%s\n", __func__); | ||
826 | |||
827 | return rotator_clk_crtl(rot, false); | ||
828 | } | ||
829 | |||
830 | static int rotator_runtime_resume(struct device *dev) | ||
831 | { | ||
832 | struct rot_context *rot = dev_get_drvdata(dev); | ||
833 | |||
834 | DRM_DEBUG_KMS("%s\n", __func__); | ||
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 = __devexit_p(rotator_remove), | ||
849 | .id_table = rotator_driver_ids, | ||
850 | .driver = { | ||
851 | .name = "exynos-rot", | ||
852 | .owner = THIS_MODULE, | ||
853 | .pm = &rotator_pm_ops, | ||
854 | }, | ||
855 | }; | ||
diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.h b/drivers/gpu/drm/exynos/exynos_drm_rotator.h new file mode 100644 index 000000000000..a2d7a14a52b6 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
3 | * | ||
4 | * Authors: | ||
5 | * YoungJun Cho <yj44.cho@samsung.com> | ||
6 | * Eunchul Kim <chulspro.kim@samsung.com> | ||
7 | * | ||
8 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
9 | * copy of this software and associated documentation files (the "Software"), | ||
10 | * to deal in the Software without restriction, including without limitation | ||
11 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
12 | * and/or sell copies of the Software, and to permit persons to whom the | ||
13 | * Software is furnished to do so, subject to the following conditions: | ||
14 | * | ||
15 | * The above copyright notice and this permission notice (including the next | ||
16 | * paragraph) shall be included in all copies or substantial portions of the | ||
17 | * Software. | ||
18 | * | ||
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
22 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
23 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
24 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||
25 | * OTHER DEALINGS IN THE SOFTWARE. | ||
26 | */ | ||
27 | |||
28 | #ifndef _EXYNOS_DRM_ROTATOR_H_ | ||
29 | #define _EXYNOS_DRM_ROTATOR_H_ | ||
30 | |||
31 | /* TODO */ | ||
32 | |||
33 | #endif | ||
diff --git a/drivers/gpu/drm/exynos/regs-rotator.h b/drivers/gpu/drm/exynos/regs-rotator.h new file mode 100644 index 000000000000..a09ac6e180da --- /dev/null +++ b/drivers/gpu/drm/exynos/regs-rotator.h | |||
@@ -0,0 +1,73 @@ | |||
1 | /* drivers/gpu/drm/exynos/regs-rotator.h | ||
2 | * | ||
3 | * Copyright (c) 2012 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * Register definition file for Samsung Rotator Interface (Rotator) driver | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #ifndef EXYNOS_REGS_ROTATOR_H | ||
14 | #define EXYNOS_REGS_ROTATOR_H | ||
15 | |||
16 | /* Configuration */ | ||
17 | #define ROT_CONFIG 0x00 | ||
18 | #define ROT_CONFIG_IRQ (3 << 8) | ||
19 | |||
20 | /* Image Control */ | ||
21 | #define ROT_CONTROL 0x10 | ||
22 | #define ROT_CONTROL_PATTERN_WRITE (1 << 16) | ||
23 | #define ROT_CONTROL_FMT_YCBCR420_2P (1 << 8) | ||
24 | #define ROT_CONTROL_FMT_RGB888 (6 << 8) | ||
25 | #define ROT_CONTROL_FMT_MASK (7 << 8) | ||
26 | #define ROT_CONTROL_FLIP_VERTICAL (2 << 6) | ||
27 | #define ROT_CONTROL_FLIP_HORIZONTAL (3 << 6) | ||
28 | #define ROT_CONTROL_FLIP_MASK (3 << 6) | ||
29 | #define ROT_CONTROL_ROT_90 (1 << 4) | ||
30 | #define ROT_CONTROL_ROT_180 (2 << 4) | ||
31 | #define ROT_CONTROL_ROT_270 (3 << 4) | ||
32 | #define ROT_CONTROL_ROT_MASK (3 << 4) | ||
33 | #define ROT_CONTROL_START (1 << 0) | ||
34 | |||
35 | /* Status */ | ||
36 | #define ROT_STATUS 0x20 | ||
37 | #define ROT_STATUS_IRQ_PENDING(x) (1 << (x)) | ||
38 | #define ROT_STATUS_IRQ(x) (((x) >> 8) & 0x3) | ||
39 | #define ROT_STATUS_IRQ_VAL_COMPLETE 1 | ||
40 | #define ROT_STATUS_IRQ_VAL_ILLEGAL 2 | ||
41 | |||
42 | /* Buffer Address */ | ||
43 | #define ROT_SRC_BUF_ADDR(n) (0x30 + ((n) << 2)) | ||
44 | #define ROT_DST_BUF_ADDR(n) (0x50 + ((n) << 2)) | ||
45 | |||
46 | /* Buffer Size */ | ||
47 | #define ROT_SRC_BUF_SIZE 0x3c | ||
48 | #define ROT_DST_BUF_SIZE 0x5c | ||
49 | #define ROT_SET_BUF_SIZE_H(x) ((x) << 16) | ||
50 | #define ROT_SET_BUF_SIZE_W(x) ((x) << 0) | ||
51 | #define ROT_GET_BUF_SIZE_H(x) ((x) >> 16) | ||
52 | #define ROT_GET_BUF_SIZE_W(x) ((x) & 0xffff) | ||
53 | |||
54 | /* Crop Position */ | ||
55 | #define ROT_SRC_CROP_POS 0x40 | ||
56 | #define ROT_DST_CROP_POS 0x60 | ||
57 | #define ROT_CROP_POS_Y(x) ((x) << 16) | ||
58 | #define ROT_CROP_POS_X(x) ((x) << 0) | ||
59 | |||
60 | /* Source Crop Size */ | ||
61 | #define ROT_SRC_CROP_SIZE 0x44 | ||
62 | #define ROT_SRC_CROP_SIZE_H(x) ((x) << 16) | ||
63 | #define ROT_SRC_CROP_SIZE_W(x) ((x) << 0) | ||
64 | |||
65 | /* Round to nearest aligned value */ | ||
66 | #define ROT_ALIGN(x, align, mask) (((x) + (1 << ((align) - 1))) & (mask)) | ||
67 | /* Minimum limit value */ | ||
68 | #define ROT_MIN(min, mask) (((min) + ~(mask)) & (mask)) | ||
69 | /* Maximum limit value */ | ||
70 | #define ROT_MAX(max, mask) ((max) & (mask)) | ||
71 | |||
72 | #endif /* EXYNOS_REGS_ROTATOR_H */ | ||
73 | |||