blob: c5892dcfd745cc29dc638e00aa55515d95ed209f [file] [log] [blame]
Boris Brezillon1a396782015-01-06 11:13:28 +01001/*
2 * Copyright (C) 2014 Free Electrons
3 * Copyright (C) 2014 Atmel
4 *
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "atmel_hlcdc_dc.h"
21
22#define SUBPIXEL_MASK 0xffff
23
24static uint32_t rgb_formats[] = {
25 DRM_FORMAT_XRGB4444,
26 DRM_FORMAT_ARGB4444,
27 DRM_FORMAT_RGBA4444,
28 DRM_FORMAT_ARGB1555,
29 DRM_FORMAT_RGB565,
30 DRM_FORMAT_RGB888,
31 DRM_FORMAT_XRGB8888,
32 DRM_FORMAT_ARGB8888,
33 DRM_FORMAT_RGBA8888,
34};
35
36struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
37 .formats = rgb_formats,
38 .nformats = ARRAY_SIZE(rgb_formats),
39};
40
41static uint32_t rgb_and_yuv_formats[] = {
42 DRM_FORMAT_XRGB4444,
43 DRM_FORMAT_ARGB4444,
44 DRM_FORMAT_RGBA4444,
45 DRM_FORMAT_ARGB1555,
46 DRM_FORMAT_RGB565,
47 DRM_FORMAT_RGB888,
48 DRM_FORMAT_XRGB8888,
49 DRM_FORMAT_ARGB8888,
50 DRM_FORMAT_RGBA8888,
51 DRM_FORMAT_AYUV,
52 DRM_FORMAT_YUYV,
53 DRM_FORMAT_UYVY,
54 DRM_FORMAT_YVYU,
55 DRM_FORMAT_VYUY,
56 DRM_FORMAT_NV21,
57 DRM_FORMAT_NV61,
58 DRM_FORMAT_YUV422,
59 DRM_FORMAT_YUV420,
60};
61
62struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
63 .formats = rgb_and_yuv_formats,
64 .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
65};
66
67static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
68{
69 switch (format) {
70 case DRM_FORMAT_XRGB4444:
71 *mode = ATMEL_HLCDC_XRGB4444_MODE;
72 break;
73 case DRM_FORMAT_ARGB4444:
74 *mode = ATMEL_HLCDC_ARGB4444_MODE;
75 break;
76 case DRM_FORMAT_RGBA4444:
77 *mode = ATMEL_HLCDC_RGBA4444_MODE;
78 break;
79 case DRM_FORMAT_RGB565:
80 *mode = ATMEL_HLCDC_RGB565_MODE;
81 break;
82 case DRM_FORMAT_RGB888:
83 *mode = ATMEL_HLCDC_RGB888_MODE;
84 break;
85 case DRM_FORMAT_ARGB1555:
86 *mode = ATMEL_HLCDC_ARGB1555_MODE;
87 break;
88 case DRM_FORMAT_XRGB8888:
89 *mode = ATMEL_HLCDC_XRGB8888_MODE;
90 break;
91 case DRM_FORMAT_ARGB8888:
92 *mode = ATMEL_HLCDC_ARGB8888_MODE;
93 break;
94 case DRM_FORMAT_RGBA8888:
95 *mode = ATMEL_HLCDC_RGBA8888_MODE;
96 break;
97 case DRM_FORMAT_AYUV:
98 *mode = ATMEL_HLCDC_AYUV_MODE;
99 break;
100 case DRM_FORMAT_YUYV:
101 *mode = ATMEL_HLCDC_YUYV_MODE;
102 break;
103 case DRM_FORMAT_UYVY:
104 *mode = ATMEL_HLCDC_UYVY_MODE;
105 break;
106 case DRM_FORMAT_YVYU:
107 *mode = ATMEL_HLCDC_YVYU_MODE;
108 break;
109 case DRM_FORMAT_VYUY:
110 *mode = ATMEL_HLCDC_VYUY_MODE;
111 break;
112 case DRM_FORMAT_NV21:
113 *mode = ATMEL_HLCDC_NV21_MODE;
114 break;
115 case DRM_FORMAT_NV61:
116 *mode = ATMEL_HLCDC_NV61_MODE;
117 break;
118 case DRM_FORMAT_YUV420:
119 *mode = ATMEL_HLCDC_YUV420_MODE;
120 break;
121 case DRM_FORMAT_YUV422:
122 *mode = ATMEL_HLCDC_YUV422_MODE;
123 break;
124 default:
125 return -ENOTSUPP;
126 }
127
128 return 0;
129}
130
131static bool atmel_hlcdc_format_embedds_alpha(u32 format)
132{
133 int i;
134
135 for (i = 0; i < sizeof(format); i++) {
136 char tmp = (format >> (8 * i)) & 0xff;
137
138 if (tmp == 'A')
139 return true;
140 }
141
142 return false;
143}
144
145static u32 heo_downscaling_xcoef[] = {
146 0x11343311,
147 0x000000f7,
148 0x1635300c,
149 0x000000f9,
150 0x1b362c08,
151 0x000000fb,
152 0x1f372804,
153 0x000000fe,
154 0x24382400,
155 0x00000000,
156 0x28371ffe,
157 0x00000004,
158 0x2c361bfb,
159 0x00000008,
160 0x303516f9,
161 0x0000000c,
162};
163
164static u32 heo_downscaling_ycoef[] = {
165 0x00123737,
166 0x00173732,
167 0x001b382d,
168 0x001f3928,
169 0x00243824,
170 0x0028391f,
171 0x002d381b,
172 0x00323717,
173};
174
175static u32 heo_upscaling_xcoef[] = {
176 0xf74949f7,
177 0x00000000,
178 0xf55f33fb,
179 0x000000fe,
180 0xf5701efe,
181 0x000000ff,
182 0xf87c0dff,
183 0x00000000,
184 0x00800000,
185 0x00000000,
186 0x0d7cf800,
187 0x000000ff,
188 0x1e70f5ff,
189 0x000000fe,
190 0x335ff5fe,
191 0x000000fb,
192};
193
194static u32 heo_upscaling_ycoef[] = {
195 0x00004040,
196 0x00075920,
197 0x00056f0c,
198 0x00027b03,
199 0x00008000,
200 0x00037b02,
201 0x000c6f05,
202 0x00205907,
203};
204
205static void
206atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
207 struct atmel_hlcdc_plane_update_req *req)
208{
209 const struct atmel_hlcdc_layer_cfg_layout *layout =
210 &plane->layer.desc->layout;
211
212 if (layout->size)
213 atmel_hlcdc_layer_update_cfg(&plane->layer,
214 layout->size,
215 0xffffffff,
216 (req->crtc_w - 1) |
217 ((req->crtc_h - 1) << 16));
218
219 if (layout->memsize)
220 atmel_hlcdc_layer_update_cfg(&plane->layer,
221 layout->memsize,
222 0xffffffff,
223 (req->src_w - 1) |
224 ((req->src_h - 1) << 16));
225
226 if (layout->pos)
227 atmel_hlcdc_layer_update_cfg(&plane->layer,
228 layout->pos,
229 0xffffffff,
230 req->crtc_x |
231 (req->crtc_y << 16));
232
233 /* TODO: rework the rescaling part */
234 if (req->crtc_w != req->src_w || req->crtc_h != req->src_h) {
235 u32 factor_reg = 0;
236
237 if (req->crtc_w != req->src_w) {
238 int i;
239 u32 factor;
240 u32 *coeff_tab = heo_upscaling_xcoef;
241 u32 max_memsize;
242
243 if (req->crtc_w < req->src_w)
244 coeff_tab = heo_downscaling_xcoef;
245 for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
246 atmel_hlcdc_layer_update_cfg(&plane->layer,
247 17 + i,
248 0xffffffff,
249 coeff_tab[i]);
250 factor = ((8 * 256 * req->src_w) - (256 * 4)) /
251 req->crtc_w;
252 factor++;
253 max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
254 2048;
255 if (max_memsize > req->src_w)
256 factor--;
257 factor_reg |= factor | 0x80000000;
258 }
259
260 if (req->crtc_h != req->src_h) {
261 int i;
262 u32 factor;
263 u32 *coeff_tab = heo_upscaling_ycoef;
264 u32 max_memsize;
265
266 if (req->crtc_w < req->src_w)
267 coeff_tab = heo_downscaling_ycoef;
268 for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
269 atmel_hlcdc_layer_update_cfg(&plane->layer,
270 33 + i,
271 0xffffffff,
272 coeff_tab[i]);
273 factor = ((8 * 256 * req->src_w) - (256 * 4)) /
274 req->crtc_w;
275 factor++;
276 max_memsize = ((factor * req->crtc_w) + (256 * 4)) /
277 2048;
278 if (max_memsize > req->src_w)
279 factor--;
280 factor_reg |= (factor << 16) | 0x80000000;
281 }
282
283 atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
284 factor_reg);
285 }
286}
287
288static void
289atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
290 struct atmel_hlcdc_plane_update_req *req)
291{
292 const struct atmel_hlcdc_layer_cfg_layout *layout =
293 &plane->layer.desc->layout;
294 unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
295
296 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
297 cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
298 ATMEL_HLCDC_LAYER_ITER;
299
300 if (atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format))
301 cfg |= ATMEL_HLCDC_LAYER_LAEN;
302 else
303 cfg |= ATMEL_HLCDC_LAYER_GAEN;
304 }
305
306 atmel_hlcdc_layer_update_cfg(&plane->layer,
307 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
308 ATMEL_HLCDC_LAYER_DMA_BLEN_MASK,
309 ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16);
310
311 atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
312 ATMEL_HLCDC_LAYER_ITER2BL |
313 ATMEL_HLCDC_LAYER_ITER |
314 ATMEL_HLCDC_LAYER_GAEN |
315 ATMEL_HLCDC_LAYER_LAEN |
316 ATMEL_HLCDC_LAYER_OVR |
317 ATMEL_HLCDC_LAYER_DMA, cfg);
318}
319
320static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
321 struct atmel_hlcdc_plane_update_req *req)
322{
323 u32 cfg;
324 int ret;
325
326 ret = atmel_hlcdc_format_to_plane_mode(req->fb->pixel_format, &cfg);
327 if (ret)
328 return;
329
330 if ((req->fb->pixel_format == DRM_FORMAT_YUV422 ||
331 req->fb->pixel_format == DRM_FORMAT_NV61) &&
332 (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
333 cfg |= ATMEL_HLCDC_YUV422ROT;
334
335 atmel_hlcdc_layer_update_cfg(&plane->layer,
336 ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
337 0xffffffff,
338 cfg);
339
340 /*
341 * Rotation optimization is not working on RGB888 (rotation is still
342 * working but without any optimization).
343 */
344 if (req->fb->pixel_format == DRM_FORMAT_RGB888)
345 cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
346 else
347 cfg = 0;
348
349 atmel_hlcdc_layer_update_cfg(&plane->layer,
350 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
351 ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
352}
353
354static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
355 struct atmel_hlcdc_plane_update_req *req)
356{
357 struct atmel_hlcdc_layer *layer = &plane->layer;
358 const struct atmel_hlcdc_layer_cfg_layout *layout =
359 &layer->desc->layout;
360 int i;
361
362 atmel_hlcdc_layer_update_set_fb(&plane->layer, req->fb, req->offsets);
363
364 for (i = 0; i < req->nplanes; i++) {
365 if (layout->xstride[i]) {
366 atmel_hlcdc_layer_update_cfg(&plane->layer,
367 layout->xstride[i],
368 0xffffffff,
369 req->xstride[i]);
370 }
371
372 if (layout->pstride[i]) {
373 atmel_hlcdc_layer_update_cfg(&plane->layer,
374 layout->pstride[i],
375 0xffffffff,
376 req->pstride[i]);
377 }
378 }
379}
380
381static int atmel_hlcdc_plane_check_update_req(struct drm_plane *p,
382 struct atmel_hlcdc_plane_update_req *req,
383 const struct drm_display_mode *mode)
384{
385 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
386 const struct atmel_hlcdc_layer_cfg_layout *layout =
387 &plane->layer.desc->layout;
388
389 if (!layout->size &&
390 (mode->hdisplay != req->crtc_w ||
391 mode->vdisplay != req->crtc_h))
392 return -EINVAL;
393
394 if (plane->layer.desc->max_height &&
395 req->crtc_h > plane->layer.desc->max_height)
396 return -EINVAL;
397
398 if (plane->layer.desc->max_width &&
399 req->crtc_w > plane->layer.desc->max_width)
400 return -EINVAL;
401
402 if ((req->crtc_h != req->src_h || req->crtc_w != req->src_w) &&
403 (!layout->memsize ||
404 atmel_hlcdc_format_embedds_alpha(req->fb->pixel_format)))
405 return -EINVAL;
406
407 if (req->crtc_x < 0 || req->crtc_y < 0)
408 return -EINVAL;
409
410 if (req->crtc_w + req->crtc_x > mode->hdisplay ||
411 req->crtc_h + req->crtc_y > mode->vdisplay)
412 return -EINVAL;
413
414 return 0;
415}
416
417int atmel_hlcdc_plane_prepare_update_req(struct drm_plane *p,
418 struct atmel_hlcdc_plane_update_req *req,
419 const struct drm_display_mode *mode)
420{
421 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
422 unsigned int patched_crtc_w;
423 unsigned int patched_crtc_h;
424 unsigned int patched_src_w;
425 unsigned int patched_src_h;
426 unsigned int tmp;
427 int x_offset = 0;
428 int y_offset = 0;
429 int hsub = 1;
430 int vsub = 1;
431 int i;
432
433 if ((req->src_x | req->src_y | req->src_w | req->src_h) &
434 SUBPIXEL_MASK)
435 return -EINVAL;
436
437 req->src_x >>= 16;
438 req->src_y >>= 16;
439 req->src_w >>= 16;
440 req->src_h >>= 16;
441
442 req->nplanes = drm_format_num_planes(req->fb->pixel_format);
443 if (req->nplanes > ATMEL_HLCDC_MAX_PLANES)
444 return -EINVAL;
445
446 /*
447 * Swap width and size in case of 90 or 270 degrees rotation
448 */
449 if (plane->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
450 tmp = req->crtc_w;
451 req->crtc_w = req->crtc_h;
452 req->crtc_h = tmp;
453 tmp = req->src_w;
454 req->src_w = req->src_h;
455 req->src_h = tmp;
456 }
457
458 if (req->crtc_x + req->crtc_w > mode->hdisplay)
459 patched_crtc_w = mode->hdisplay - req->crtc_x;
460 else
461 patched_crtc_w = req->crtc_w;
462
463 if (req->crtc_x < 0) {
464 patched_crtc_w += req->crtc_x;
465 x_offset = -req->crtc_x;
466 req->crtc_x = 0;
467 }
468
469 if (req->crtc_y + req->crtc_h > mode->vdisplay)
470 patched_crtc_h = mode->vdisplay - req->crtc_y;
471 else
472 patched_crtc_h = req->crtc_h;
473
474 if (req->crtc_y < 0) {
475 patched_crtc_h += req->crtc_y;
476 y_offset = -req->crtc_y;
477 req->crtc_y = 0;
478 }
479
480 patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * req->src_w,
481 req->crtc_w);
482 patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * req->src_h,
483 req->crtc_h);
484
485 hsub = drm_format_horz_chroma_subsampling(req->fb->pixel_format);
486 vsub = drm_format_vert_chroma_subsampling(req->fb->pixel_format);
487
488 for (i = 0; i < req->nplanes; i++) {
489 unsigned int offset = 0;
490 int xdiv = i ? hsub : 1;
491 int ydiv = i ? vsub : 1;
492
493 req->bpp[i] = drm_format_plane_cpp(req->fb->pixel_format, i);
494 if (!req->bpp[i])
495 return -EINVAL;
496
497 switch (plane->rotation & 0xf) {
498 case BIT(DRM_ROTATE_90):
499 offset = ((y_offset + req->src_y + patched_src_w - 1) /
500 ydiv) * req->fb->pitches[i];
501 offset += ((x_offset + req->src_x) / xdiv) *
502 req->bpp[i];
503 req->xstride[i] = ((patched_src_w - 1) / ydiv) *
504 req->fb->pitches[i];
505 req->pstride[i] = -req->fb->pitches[i] - req->bpp[i];
506 break;
507 case BIT(DRM_ROTATE_180):
508 offset = ((y_offset + req->src_y + patched_src_h - 1) /
509 ydiv) * req->fb->pitches[i];
510 offset += ((x_offset + req->src_x + patched_src_w - 1) /
511 xdiv) * req->bpp[i];
512 req->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
513 req->bpp[i]) - req->fb->pitches[i];
514 req->pstride[i] = -2 * req->bpp[i];
515 break;
516 case BIT(DRM_ROTATE_270):
517 offset = ((y_offset + req->src_y) / ydiv) *
518 req->fb->pitches[i];
519 offset += ((x_offset + req->src_x + patched_src_h - 1) /
520 xdiv) * req->bpp[i];
521 req->xstride[i] = -(((patched_src_w - 1) / ydiv) *
522 req->fb->pitches[i]) -
523 (2 * req->bpp[i]);
524 req->pstride[i] = req->fb->pitches[i] - req->bpp[i];
525 break;
526 case BIT(DRM_ROTATE_0):
527 default:
528 offset = ((y_offset + req->src_y) / ydiv) *
529 req->fb->pitches[i];
530 offset += ((x_offset + req->src_x) / xdiv) *
531 req->bpp[i];
532 req->xstride[i] = req->fb->pitches[i] -
533 ((patched_src_w / xdiv) *
534 req->bpp[i]);
535 req->pstride[i] = 0;
536 break;
537 }
538
539 req->offsets[i] = offset + req->fb->offsets[i];
540 }
541
542 req->src_w = patched_src_w;
543 req->src_h = patched_src_h;
544 req->crtc_w = patched_crtc_w;
545 req->crtc_h = patched_crtc_h;
546
547 return atmel_hlcdc_plane_check_update_req(p, req, mode);
548}
549
550int atmel_hlcdc_plane_apply_update_req(struct drm_plane *p,
551 struct atmel_hlcdc_plane_update_req *req)
552{
553 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
554 int ret;
555
556 ret = atmel_hlcdc_layer_update_start(&plane->layer);
557 if (ret)
558 return ret;
559
560 atmel_hlcdc_plane_update_pos_and_size(plane, req);
561 atmel_hlcdc_plane_update_general_settings(plane, req);
562 atmel_hlcdc_plane_update_format(plane, req);
563 atmel_hlcdc_plane_update_buffers(plane, req);
564
565 atmel_hlcdc_layer_update_commit(&plane->layer);
566
567 return 0;
568}
569
570int atmel_hlcdc_plane_update_with_mode(struct drm_plane *p,
571 struct drm_crtc *crtc,
572 struct drm_framebuffer *fb,
573 int crtc_x, int crtc_y,
574 unsigned int crtc_w,
575 unsigned int crtc_h,
576 uint32_t src_x, uint32_t src_y,
577 uint32_t src_w, uint32_t src_h,
578 const struct drm_display_mode *mode)
579{
580 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
581 struct atmel_hlcdc_plane_update_req req;
582 int ret = 0;
583
584 memset(&req, 0, sizeof(req));
585 req.crtc_x = crtc_x;
586 req.crtc_y = crtc_y;
587 req.crtc_w = crtc_w;
588 req.crtc_h = crtc_h;
589 req.src_x = src_x;
590 req.src_y = src_y;
591 req.src_w = src_w;
592 req.src_h = src_h;
593 req.fb = fb;
594
595 ret = atmel_hlcdc_plane_prepare_update_req(&plane->base, &req, mode);
596 if (ret)
597 return ret;
598
599 if (!req.crtc_h || !req.crtc_w)
600 return atmel_hlcdc_layer_disable(&plane->layer);
601
602 return atmel_hlcdc_plane_apply_update_req(&plane->base, &req);
603}
604
605static int atmel_hlcdc_plane_update(struct drm_plane *p,
606 struct drm_crtc *crtc,
607 struct drm_framebuffer *fb,
608 int crtc_x, int crtc_y,
609 unsigned int crtc_w, unsigned int crtc_h,
610 uint32_t src_x, uint32_t src_y,
611 uint32_t src_w, uint32_t src_h)
612{
613 return atmel_hlcdc_plane_update_with_mode(p, crtc, fb, crtc_x, crtc_y,
614 crtc_w, crtc_h, src_x, src_y,
615 src_w, src_h, &crtc->hwmode);
616}
617
618static int atmel_hlcdc_plane_disable(struct drm_plane *p)
619{
620 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
621
622 return atmel_hlcdc_layer_disable(&plane->layer);
623}
624
625static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
626{
627 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
628
629 if (plane->base.fb)
630 drm_framebuffer_unreference(plane->base.fb);
631
632 atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
633
634 drm_plane_cleanup(p);
635 devm_kfree(p->dev->dev, plane);
636}
637
638static int atmel_hlcdc_plane_set_alpha(struct atmel_hlcdc_plane *plane,
639 u8 alpha)
640{
641 atmel_hlcdc_layer_update_start(&plane->layer);
642 atmel_hlcdc_layer_update_cfg(&plane->layer,
643 plane->layer.desc->layout.general_config,
644 ATMEL_HLCDC_LAYER_GA_MASK,
645 alpha << ATMEL_HLCDC_LAYER_GA_SHIFT);
646 atmel_hlcdc_layer_update_commit(&plane->layer);
647
648 return 0;
649}
650
651static int atmel_hlcdc_plane_set_rotation(struct atmel_hlcdc_plane *plane,
652 unsigned int rotation)
653{
654 plane->rotation = rotation;
655
656 return 0;
657}
658
659static int atmel_hlcdc_plane_set_property(struct drm_plane *p,
660 struct drm_property *property,
661 uint64_t value)
662{
663 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
664 struct atmel_hlcdc_plane_properties *props = plane->properties;
665
666 if (property == props->alpha)
667 atmel_hlcdc_plane_set_alpha(plane, value);
668 else if (property == props->rotation)
669 atmel_hlcdc_plane_set_rotation(plane, value);
670 else
671 return -EINVAL;
672
673 return 0;
674}
675
676static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
677 const struct atmel_hlcdc_layer_desc *desc,
678 struct atmel_hlcdc_plane_properties *props)
679{
680 struct regmap *regmap = plane->layer.hlcdc->regmap;
681
682 if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
683 desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
684 drm_object_attach_property(&plane->base.base,
685 props->alpha, 255);
686
687 /* Set default alpha value */
688 regmap_update_bits(regmap,
689 desc->regs_offset +
690 ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
691 ATMEL_HLCDC_LAYER_GA_MASK,
692 ATMEL_HLCDC_LAYER_GA_MASK);
693 }
694
695 if (desc->layout.xstride && desc->layout.pstride)
696 drm_object_attach_property(&plane->base.base,
697 props->rotation,
698 BIT(DRM_ROTATE_0));
699
700 if (desc->layout.csc) {
701 /*
702 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
703 * userspace modify these factors (using a BLOB property ?).
704 */
705 regmap_write(regmap,
706 desc->regs_offset +
707 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
708 0x4c900091);
709 regmap_write(regmap,
710 desc->regs_offset +
711 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
712 0x7a5f5090);
713 regmap_write(regmap,
714 desc->regs_offset +
715 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
716 0x40040890);
717 }
718}
719
720static struct drm_plane_funcs layer_plane_funcs = {
721 .update_plane = atmel_hlcdc_plane_update,
722 .disable_plane = atmel_hlcdc_plane_disable,
723 .set_property = atmel_hlcdc_plane_set_property,
724 .destroy = atmel_hlcdc_plane_destroy,
725};
726
727static struct atmel_hlcdc_plane *
728atmel_hlcdc_plane_create(struct drm_device *dev,
729 const struct atmel_hlcdc_layer_desc *desc,
730 struct atmel_hlcdc_plane_properties *props)
731{
732 struct atmel_hlcdc_plane *plane;
733 enum drm_plane_type type;
734 int ret;
735
736 plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
737 if (!plane)
738 return ERR_PTR(-ENOMEM);
739
740 ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
741 if (ret)
742 return ERR_PTR(ret);
743
744 if (desc->type == ATMEL_HLCDC_BASE_LAYER)
745 type = DRM_PLANE_TYPE_PRIMARY;
746 else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
747 type = DRM_PLANE_TYPE_CURSOR;
748 else
749 type = DRM_PLANE_TYPE_OVERLAY;
750
751 ret = drm_universal_plane_init(dev, &plane->base, 0,
752 &layer_plane_funcs,
753 desc->formats->formats,
754 desc->formats->nformats, type);
755 if (ret)
756 return ERR_PTR(ret);
757
758 /* Set default property values*/
759 atmel_hlcdc_plane_init_properties(plane, desc, props);
760
761 return plane;
762}
763
764static struct atmel_hlcdc_plane_properties *
765atmel_hlcdc_plane_create_properties(struct drm_device *dev)
766{
767 struct atmel_hlcdc_plane_properties *props;
768
769 props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
770 if (!props)
771 return ERR_PTR(-ENOMEM);
772
773 props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
774 if (!props->alpha)
775 return ERR_PTR(-ENOMEM);
776
777 props->rotation = drm_mode_create_rotation_property(dev,
778 BIT(DRM_ROTATE_0) |
779 BIT(DRM_ROTATE_90) |
780 BIT(DRM_ROTATE_180) |
781 BIT(DRM_ROTATE_270));
782 if (!props->rotation)
783 return ERR_PTR(-ENOMEM);
784
785 return props;
786}
787
788struct atmel_hlcdc_planes *
789atmel_hlcdc_create_planes(struct drm_device *dev)
790{
791 struct atmel_hlcdc_dc *dc = dev->dev_private;
792 struct atmel_hlcdc_plane_properties *props;
793 struct atmel_hlcdc_planes *planes;
794 const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
795 int nlayers = dc->desc->nlayers;
796 int i;
797
798 planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
799 if (!planes)
800 return ERR_PTR(-ENOMEM);
801
802 for (i = 0; i < nlayers; i++) {
803 if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
804 planes->noverlays++;
805 }
806
807 if (planes->noverlays) {
808 planes->overlays = devm_kzalloc(dev->dev,
809 planes->noverlays *
810 sizeof(*planes->overlays),
811 GFP_KERNEL);
812 if (!planes->overlays)
813 return ERR_PTR(-ENOMEM);
814 }
815
816 props = atmel_hlcdc_plane_create_properties(dev);
817 if (IS_ERR(props))
818 return ERR_CAST(props);
819
820 planes->noverlays = 0;
821 for (i = 0; i < nlayers; i++) {
822 struct atmel_hlcdc_plane *plane;
823
824 if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
825 continue;
826
827 plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
828 if (IS_ERR(plane))
829 return ERR_CAST(plane);
830
831 plane->properties = props;
832
833 switch (descs[i].type) {
834 case ATMEL_HLCDC_BASE_LAYER:
835 if (planes->primary)
836 return ERR_PTR(-EINVAL);
837 planes->primary = plane;
838 break;
839
840 case ATMEL_HLCDC_OVERLAY_LAYER:
841 planes->overlays[planes->noverlays++] = plane;
842 break;
843
844 case ATMEL_HLCDC_CURSOR_LAYER:
845 if (planes->cursor)
846 return ERR_PTR(-EINVAL);
847 planes->cursor = plane;
848 break;
849
850 default:
851 break;
852 }
853 }
854
855 return planes;
856}