Loading...
1/*
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/**
23 * Atmel HLCDC Plane state structure.
24 *
25 * @base: DRM plane state
26 * @crtc_x: x position of the plane relative to the CRTC
27 * @crtc_y: y position of the plane relative to the CRTC
28 * @crtc_w: visible width of the plane
29 * @crtc_h: visible height of the plane
30 * @src_x: x buffer position
31 * @src_y: y buffer position
32 * @src_w: buffer width
33 * @src_h: buffer height
34 * @alpha: alpha blending of the plane
35 * @bpp: bytes per pixel deduced from pixel_format
36 * @offsets: offsets to apply to the GEM buffers
37 * @xstride: value to add to the pixel pointer between each line
38 * @pstride: value to add to the pixel pointer between each pixel
39 * @nplanes: number of planes (deduced from pixel_format)
40 */
41struct atmel_hlcdc_plane_state {
42 struct drm_plane_state base;
43 int crtc_x;
44 int crtc_y;
45 unsigned int crtc_w;
46 unsigned int crtc_h;
47 uint32_t src_x;
48 uint32_t src_y;
49 uint32_t src_w;
50 uint32_t src_h;
51
52 u8 alpha;
53
54 bool disc_updated;
55
56 int disc_x;
57 int disc_y;
58 int disc_w;
59 int disc_h;
60
61 /* These fields are private and should not be touched */
62 int bpp[ATMEL_HLCDC_MAX_PLANES];
63 unsigned int offsets[ATMEL_HLCDC_MAX_PLANES];
64 int xstride[ATMEL_HLCDC_MAX_PLANES];
65 int pstride[ATMEL_HLCDC_MAX_PLANES];
66 int nplanes;
67};
68
69static inline struct atmel_hlcdc_plane_state *
70drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
71{
72 return container_of(s, struct atmel_hlcdc_plane_state, base);
73}
74
75#define SUBPIXEL_MASK 0xffff
76
77static uint32_t rgb_formats[] = {
78 DRM_FORMAT_XRGB4444,
79 DRM_FORMAT_ARGB4444,
80 DRM_FORMAT_RGBA4444,
81 DRM_FORMAT_ARGB1555,
82 DRM_FORMAT_RGB565,
83 DRM_FORMAT_RGB888,
84 DRM_FORMAT_XRGB8888,
85 DRM_FORMAT_ARGB8888,
86 DRM_FORMAT_RGBA8888,
87};
88
89struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
90 .formats = rgb_formats,
91 .nformats = ARRAY_SIZE(rgb_formats),
92};
93
94static uint32_t rgb_and_yuv_formats[] = {
95 DRM_FORMAT_XRGB4444,
96 DRM_FORMAT_ARGB4444,
97 DRM_FORMAT_RGBA4444,
98 DRM_FORMAT_ARGB1555,
99 DRM_FORMAT_RGB565,
100 DRM_FORMAT_RGB888,
101 DRM_FORMAT_XRGB8888,
102 DRM_FORMAT_ARGB8888,
103 DRM_FORMAT_RGBA8888,
104 DRM_FORMAT_AYUV,
105 DRM_FORMAT_YUYV,
106 DRM_FORMAT_UYVY,
107 DRM_FORMAT_YVYU,
108 DRM_FORMAT_VYUY,
109 DRM_FORMAT_NV21,
110 DRM_FORMAT_NV61,
111 DRM_FORMAT_YUV422,
112 DRM_FORMAT_YUV420,
113};
114
115struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
116 .formats = rgb_and_yuv_formats,
117 .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
118};
119
120static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
121{
122 switch (format) {
123 case DRM_FORMAT_XRGB4444:
124 *mode = ATMEL_HLCDC_XRGB4444_MODE;
125 break;
126 case DRM_FORMAT_ARGB4444:
127 *mode = ATMEL_HLCDC_ARGB4444_MODE;
128 break;
129 case DRM_FORMAT_RGBA4444:
130 *mode = ATMEL_HLCDC_RGBA4444_MODE;
131 break;
132 case DRM_FORMAT_RGB565:
133 *mode = ATMEL_HLCDC_RGB565_MODE;
134 break;
135 case DRM_FORMAT_RGB888:
136 *mode = ATMEL_HLCDC_RGB888_MODE;
137 break;
138 case DRM_FORMAT_ARGB1555:
139 *mode = ATMEL_HLCDC_ARGB1555_MODE;
140 break;
141 case DRM_FORMAT_XRGB8888:
142 *mode = ATMEL_HLCDC_XRGB8888_MODE;
143 break;
144 case DRM_FORMAT_ARGB8888:
145 *mode = ATMEL_HLCDC_ARGB8888_MODE;
146 break;
147 case DRM_FORMAT_RGBA8888:
148 *mode = ATMEL_HLCDC_RGBA8888_MODE;
149 break;
150 case DRM_FORMAT_AYUV:
151 *mode = ATMEL_HLCDC_AYUV_MODE;
152 break;
153 case DRM_FORMAT_YUYV:
154 *mode = ATMEL_HLCDC_YUYV_MODE;
155 break;
156 case DRM_FORMAT_UYVY:
157 *mode = ATMEL_HLCDC_UYVY_MODE;
158 break;
159 case DRM_FORMAT_YVYU:
160 *mode = ATMEL_HLCDC_YVYU_MODE;
161 break;
162 case DRM_FORMAT_VYUY:
163 *mode = ATMEL_HLCDC_VYUY_MODE;
164 break;
165 case DRM_FORMAT_NV21:
166 *mode = ATMEL_HLCDC_NV21_MODE;
167 break;
168 case DRM_FORMAT_NV61:
169 *mode = ATMEL_HLCDC_NV61_MODE;
170 break;
171 case DRM_FORMAT_YUV420:
172 *mode = ATMEL_HLCDC_YUV420_MODE;
173 break;
174 case DRM_FORMAT_YUV422:
175 *mode = ATMEL_HLCDC_YUV422_MODE;
176 break;
177 default:
178 return -ENOTSUPP;
179 }
180
181 return 0;
182}
183
184static bool atmel_hlcdc_format_embeds_alpha(u32 format)
185{
186 int i;
187
188 for (i = 0; i < sizeof(format); i++) {
189 char tmp = (format >> (8 * i)) & 0xff;
190
191 if (tmp == 'A')
192 return true;
193 }
194
195 return false;
196}
197
198static u32 heo_downscaling_xcoef[] = {
199 0x11343311,
200 0x000000f7,
201 0x1635300c,
202 0x000000f9,
203 0x1b362c08,
204 0x000000fb,
205 0x1f372804,
206 0x000000fe,
207 0x24382400,
208 0x00000000,
209 0x28371ffe,
210 0x00000004,
211 0x2c361bfb,
212 0x00000008,
213 0x303516f9,
214 0x0000000c,
215};
216
217static u32 heo_downscaling_ycoef[] = {
218 0x00123737,
219 0x00173732,
220 0x001b382d,
221 0x001f3928,
222 0x00243824,
223 0x0028391f,
224 0x002d381b,
225 0x00323717,
226};
227
228static u32 heo_upscaling_xcoef[] = {
229 0xf74949f7,
230 0x00000000,
231 0xf55f33fb,
232 0x000000fe,
233 0xf5701efe,
234 0x000000ff,
235 0xf87c0dff,
236 0x00000000,
237 0x00800000,
238 0x00000000,
239 0x0d7cf800,
240 0x000000ff,
241 0x1e70f5ff,
242 0x000000fe,
243 0x335ff5fe,
244 0x000000fb,
245};
246
247static u32 heo_upscaling_ycoef[] = {
248 0x00004040,
249 0x00075920,
250 0x00056f0c,
251 0x00027b03,
252 0x00008000,
253 0x00037b02,
254 0x000c6f05,
255 0x00205907,
256};
257
258static void
259atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
260 struct atmel_hlcdc_plane_state *state)
261{
262 const struct atmel_hlcdc_layer_cfg_layout *layout =
263 &plane->layer.desc->layout;
264
265 if (layout->size)
266 atmel_hlcdc_layer_update_cfg(&plane->layer,
267 layout->size,
268 0xffffffff,
269 (state->crtc_w - 1) |
270 ((state->crtc_h - 1) << 16));
271
272 if (layout->memsize)
273 atmel_hlcdc_layer_update_cfg(&plane->layer,
274 layout->memsize,
275 0xffffffff,
276 (state->src_w - 1) |
277 ((state->src_h - 1) << 16));
278
279 if (layout->pos)
280 atmel_hlcdc_layer_update_cfg(&plane->layer,
281 layout->pos,
282 0xffffffff,
283 state->crtc_x |
284 (state->crtc_y << 16));
285
286 /* TODO: rework the rescaling part */
287 if (state->crtc_w != state->src_w || state->crtc_h != state->src_h) {
288 u32 factor_reg = 0;
289
290 if (state->crtc_w != state->src_w) {
291 int i;
292 u32 factor;
293 u32 *coeff_tab = heo_upscaling_xcoef;
294 u32 max_memsize;
295
296 if (state->crtc_w < state->src_w)
297 coeff_tab = heo_downscaling_xcoef;
298 for (i = 0; i < ARRAY_SIZE(heo_upscaling_xcoef); i++)
299 atmel_hlcdc_layer_update_cfg(&plane->layer,
300 17 + i,
301 0xffffffff,
302 coeff_tab[i]);
303 factor = ((8 * 256 * state->src_w) - (256 * 4)) /
304 state->crtc_w;
305 factor++;
306 max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
307 2048;
308 if (max_memsize > state->src_w)
309 factor--;
310 factor_reg |= factor | 0x80000000;
311 }
312
313 if (state->crtc_h != state->src_h) {
314 int i;
315 u32 factor;
316 u32 *coeff_tab = heo_upscaling_ycoef;
317 u32 max_memsize;
318
319 if (state->crtc_w < state->src_w)
320 coeff_tab = heo_downscaling_ycoef;
321 for (i = 0; i < ARRAY_SIZE(heo_upscaling_ycoef); i++)
322 atmel_hlcdc_layer_update_cfg(&plane->layer,
323 33 + i,
324 0xffffffff,
325 coeff_tab[i]);
326 factor = ((8 * 256 * state->src_w) - (256 * 4)) /
327 state->crtc_w;
328 factor++;
329 max_memsize = ((factor * state->crtc_w) + (256 * 4)) /
330 2048;
331 if (max_memsize > state->src_w)
332 factor--;
333 factor_reg |= (factor << 16) | 0x80000000;
334 }
335
336 atmel_hlcdc_layer_update_cfg(&plane->layer, 13, 0xffffffff,
337 factor_reg);
338 }
339}
340
341static void
342atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
343 struct atmel_hlcdc_plane_state *state)
344{
345 const struct atmel_hlcdc_layer_cfg_layout *layout =
346 &plane->layer.desc->layout;
347 unsigned int cfg = ATMEL_HLCDC_LAYER_DMA;
348
349 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
350 cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
351 ATMEL_HLCDC_LAYER_ITER;
352
353 if (atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format))
354 cfg |= ATMEL_HLCDC_LAYER_LAEN;
355 else
356 cfg |= ATMEL_HLCDC_LAYER_GAEN |
357 ATMEL_HLCDC_LAYER_GA(state->alpha);
358 }
359
360 atmel_hlcdc_layer_update_cfg(&plane->layer,
361 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
362 ATMEL_HLCDC_LAYER_DMA_BLEN_MASK,
363 ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16);
364
365 atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
366 ATMEL_HLCDC_LAYER_ITER2BL |
367 ATMEL_HLCDC_LAYER_ITER |
368 ATMEL_HLCDC_LAYER_GAEN |
369 ATMEL_HLCDC_LAYER_GA_MASK |
370 ATMEL_HLCDC_LAYER_LAEN |
371 ATMEL_HLCDC_LAYER_OVR |
372 ATMEL_HLCDC_LAYER_DMA, cfg);
373}
374
375static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
376 struct atmel_hlcdc_plane_state *state)
377{
378 u32 cfg;
379 int ret;
380
381 ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->pixel_format,
382 &cfg);
383 if (ret)
384 return;
385
386 if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 ||
387 state->base.fb->pixel_format == DRM_FORMAT_NV61) &&
388 (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
389 cfg |= ATMEL_HLCDC_YUV422ROT;
390
391 atmel_hlcdc_layer_update_cfg(&plane->layer,
392 ATMEL_HLCDC_LAYER_FORMAT_CFG_ID,
393 0xffffffff,
394 cfg);
395
396 /*
397 * Rotation optimization is not working on RGB888 (rotation is still
398 * working but without any optimization).
399 */
400 if (state->base.fb->pixel_format == DRM_FORMAT_RGB888)
401 cfg = ATMEL_HLCDC_LAYER_DMA_ROTDIS;
402 else
403 cfg = 0;
404
405 atmel_hlcdc_layer_update_cfg(&plane->layer,
406 ATMEL_HLCDC_LAYER_DMA_CFG_ID,
407 ATMEL_HLCDC_LAYER_DMA_ROTDIS, cfg);
408}
409
410static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
411 struct atmel_hlcdc_plane_state *state)
412{
413 struct atmel_hlcdc_layer *layer = &plane->layer;
414 const struct atmel_hlcdc_layer_cfg_layout *layout =
415 &layer->desc->layout;
416 int i;
417
418 atmel_hlcdc_layer_update_set_fb(&plane->layer, state->base.fb,
419 state->offsets);
420
421 for (i = 0; i < state->nplanes; i++) {
422 if (layout->xstride[i]) {
423 atmel_hlcdc_layer_update_cfg(&plane->layer,
424 layout->xstride[i],
425 0xffffffff,
426 state->xstride[i]);
427 }
428
429 if (layout->pstride[i]) {
430 atmel_hlcdc_layer_update_cfg(&plane->layer,
431 layout->pstride[i],
432 0xffffffff,
433 state->pstride[i]);
434 }
435 }
436}
437
438int
439atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
440{
441 int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
442 const struct atmel_hlcdc_layer_cfg_layout *layout;
443 struct atmel_hlcdc_plane_state *primary_state;
444 struct drm_plane_state *primary_s;
445 struct atmel_hlcdc_plane *primary;
446 struct drm_plane *ovl;
447
448 primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
449 layout = &primary->layer.desc->layout;
450 if (!layout->disc_pos || !layout->disc_size)
451 return 0;
452
453 primary_s = drm_atomic_get_plane_state(c_state->state,
454 &primary->base);
455 if (IS_ERR(primary_s))
456 return PTR_ERR(primary_s);
457
458 primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
459
460 drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
461 struct atmel_hlcdc_plane_state *ovl_state;
462 struct drm_plane_state *ovl_s;
463
464 if (ovl == c_state->crtc->primary)
465 continue;
466
467 ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
468 if (IS_ERR(ovl_s))
469 return PTR_ERR(ovl_s);
470
471 ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
472
473 if (!ovl_s->fb ||
474 atmel_hlcdc_format_embeds_alpha(ovl_s->fb->pixel_format) ||
475 ovl_state->alpha != 255)
476 continue;
477
478 /* TODO: implement a smarter hidden area detection */
479 if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
480 continue;
481
482 disc_x = ovl_state->crtc_x;
483 disc_y = ovl_state->crtc_y;
484 disc_h = ovl_state->crtc_h;
485 disc_w = ovl_state->crtc_w;
486 }
487
488 if (disc_x == primary_state->disc_x &&
489 disc_y == primary_state->disc_y &&
490 disc_w == primary_state->disc_w &&
491 disc_h == primary_state->disc_h)
492 return 0;
493
494
495 primary_state->disc_x = disc_x;
496 primary_state->disc_y = disc_y;
497 primary_state->disc_w = disc_w;
498 primary_state->disc_h = disc_h;
499 primary_state->disc_updated = true;
500
501 return 0;
502}
503
504static void
505atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
506 struct atmel_hlcdc_plane_state *state)
507{
508 const struct atmel_hlcdc_layer_cfg_layout *layout =
509 &plane->layer.desc->layout;
510 int disc_surface = 0;
511
512 if (!state->disc_updated)
513 return;
514
515 disc_surface = state->disc_h * state->disc_w;
516
517 atmel_hlcdc_layer_update_cfg(&plane->layer, layout->general_config,
518 ATMEL_HLCDC_LAYER_DISCEN,
519 disc_surface ? ATMEL_HLCDC_LAYER_DISCEN : 0);
520
521 if (!disc_surface)
522 return;
523
524 atmel_hlcdc_layer_update_cfg(&plane->layer,
525 layout->disc_pos,
526 0xffffffff,
527 state->disc_x | (state->disc_y << 16));
528
529 atmel_hlcdc_layer_update_cfg(&plane->layer,
530 layout->disc_size,
531 0xffffffff,
532 (state->disc_w - 1) |
533 ((state->disc_h - 1) << 16));
534}
535
536static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
537 struct drm_plane_state *s)
538{
539 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
540 struct atmel_hlcdc_plane_state *state =
541 drm_plane_state_to_atmel_hlcdc_plane_state(s);
542 const struct atmel_hlcdc_layer_cfg_layout *layout =
543 &plane->layer.desc->layout;
544 struct drm_framebuffer *fb = state->base.fb;
545 const struct drm_display_mode *mode;
546 struct drm_crtc_state *crtc_state;
547 unsigned int patched_crtc_w;
548 unsigned int patched_crtc_h;
549 unsigned int patched_src_w;
550 unsigned int patched_src_h;
551 unsigned int tmp;
552 int x_offset = 0;
553 int y_offset = 0;
554 int hsub = 1;
555 int vsub = 1;
556 int i;
557
558 if (!state->base.crtc || !fb)
559 return 0;
560
561 crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc);
562 mode = &crtc_state->adjusted_mode;
563
564 state->src_x = s->src_x;
565 state->src_y = s->src_y;
566 state->src_h = s->src_h;
567 state->src_w = s->src_w;
568 state->crtc_x = s->crtc_x;
569 state->crtc_y = s->crtc_y;
570 state->crtc_h = s->crtc_h;
571 state->crtc_w = s->crtc_w;
572 if ((state->src_x | state->src_y | state->src_w | state->src_h) &
573 SUBPIXEL_MASK)
574 return -EINVAL;
575
576 state->src_x >>= 16;
577 state->src_y >>= 16;
578 state->src_w >>= 16;
579 state->src_h >>= 16;
580
581 state->nplanes = drm_format_num_planes(fb->pixel_format);
582 if (state->nplanes > ATMEL_HLCDC_MAX_PLANES)
583 return -EINVAL;
584
585 /*
586 * Swap width and size in case of 90 or 270 degrees rotation
587 */
588 if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
589 tmp = state->crtc_w;
590 state->crtc_w = state->crtc_h;
591 state->crtc_h = tmp;
592 tmp = state->src_w;
593 state->src_w = state->src_h;
594 state->src_h = tmp;
595 }
596
597 if (state->crtc_x + state->crtc_w > mode->hdisplay)
598 patched_crtc_w = mode->hdisplay - state->crtc_x;
599 else
600 patched_crtc_w = state->crtc_w;
601
602 if (state->crtc_x < 0) {
603 patched_crtc_w += state->crtc_x;
604 x_offset = -state->crtc_x;
605 state->crtc_x = 0;
606 }
607
608 if (state->crtc_y + state->crtc_h > mode->vdisplay)
609 patched_crtc_h = mode->vdisplay - state->crtc_y;
610 else
611 patched_crtc_h = state->crtc_h;
612
613 if (state->crtc_y < 0) {
614 patched_crtc_h += state->crtc_y;
615 y_offset = -state->crtc_y;
616 state->crtc_y = 0;
617 }
618
619 patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w,
620 state->crtc_w);
621 patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h,
622 state->crtc_h);
623
624 hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
625 vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
626
627 for (i = 0; i < state->nplanes; i++) {
628 unsigned int offset = 0;
629 int xdiv = i ? hsub : 1;
630 int ydiv = i ? vsub : 1;
631
632 state->bpp[i] = drm_format_plane_cpp(fb->pixel_format, i);
633 if (!state->bpp[i])
634 return -EINVAL;
635
636 switch (state->base.rotation & DRM_ROTATE_MASK) {
637 case BIT(DRM_ROTATE_90):
638 offset = ((y_offset + state->src_y + patched_src_w - 1) /
639 ydiv) * fb->pitches[i];
640 offset += ((x_offset + state->src_x) / xdiv) *
641 state->bpp[i];
642 state->xstride[i] = ((patched_src_w - 1) / ydiv) *
643 fb->pitches[i];
644 state->pstride[i] = -fb->pitches[i] - state->bpp[i];
645 break;
646 case BIT(DRM_ROTATE_180):
647 offset = ((y_offset + state->src_y + patched_src_h - 1) /
648 ydiv) * fb->pitches[i];
649 offset += ((x_offset + state->src_x + patched_src_w - 1) /
650 xdiv) * state->bpp[i];
651 state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
652 state->bpp[i]) - fb->pitches[i];
653 state->pstride[i] = -2 * state->bpp[i];
654 break;
655 case BIT(DRM_ROTATE_270):
656 offset = ((y_offset + state->src_y) / ydiv) *
657 fb->pitches[i];
658 offset += ((x_offset + state->src_x + patched_src_h - 1) /
659 xdiv) * state->bpp[i];
660 state->xstride[i] = -(((patched_src_w - 1) / ydiv) *
661 fb->pitches[i]) -
662 (2 * state->bpp[i]);
663 state->pstride[i] = fb->pitches[i] - state->bpp[i];
664 break;
665 case BIT(DRM_ROTATE_0):
666 default:
667 offset = ((y_offset + state->src_y) / ydiv) *
668 fb->pitches[i];
669 offset += ((x_offset + state->src_x) / xdiv) *
670 state->bpp[i];
671 state->xstride[i] = fb->pitches[i] -
672 ((patched_src_w / xdiv) *
673 state->bpp[i]);
674 state->pstride[i] = 0;
675 break;
676 }
677
678 state->offsets[i] = offset + fb->offsets[i];
679 }
680
681 state->src_w = patched_src_w;
682 state->src_h = patched_src_h;
683 state->crtc_w = patched_crtc_w;
684 state->crtc_h = patched_crtc_h;
685
686 if (!layout->size &&
687 (mode->hdisplay != state->crtc_w ||
688 mode->vdisplay != state->crtc_h))
689 return -EINVAL;
690
691 if (plane->layer.desc->max_height &&
692 state->crtc_h > plane->layer.desc->max_height)
693 return -EINVAL;
694
695 if (plane->layer.desc->max_width &&
696 state->crtc_w > plane->layer.desc->max_width)
697 return -EINVAL;
698
699 if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
700 (!layout->memsize ||
701 atmel_hlcdc_format_embeds_alpha(state->base.fb->pixel_format)))
702 return -EINVAL;
703
704 if (state->crtc_x < 0 || state->crtc_y < 0)
705 return -EINVAL;
706
707 if (state->crtc_w + state->crtc_x > mode->hdisplay ||
708 state->crtc_h + state->crtc_y > mode->vdisplay)
709 return -EINVAL;
710
711 return 0;
712}
713
714static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
715 const struct drm_plane_state *new_state)
716{
717 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
718
719 if (!new_state->fb)
720 return 0;
721
722 return atmel_hlcdc_layer_update_start(&plane->layer);
723}
724
725static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
726 struct drm_plane_state *old_s)
727{
728 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
729 struct atmel_hlcdc_plane_state *state =
730 drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
731
732 if (!p->state->crtc || !p->state->fb)
733 return;
734
735 atmel_hlcdc_plane_update_pos_and_size(plane, state);
736 atmel_hlcdc_plane_update_general_settings(plane, state);
737 atmel_hlcdc_plane_update_format(plane, state);
738 atmel_hlcdc_plane_update_buffers(plane, state);
739 atmel_hlcdc_plane_update_disc_area(plane, state);
740
741 atmel_hlcdc_layer_update_commit(&plane->layer);
742}
743
744static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
745 struct drm_plane_state *old_state)
746{
747 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
748
749 atmel_hlcdc_layer_disable(&plane->layer);
750}
751
752static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
753{
754 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
755
756 if (plane->base.fb)
757 drm_framebuffer_unreference(plane->base.fb);
758
759 atmel_hlcdc_layer_cleanup(p->dev, &plane->layer);
760
761 drm_plane_cleanup(p);
762 devm_kfree(p->dev->dev, plane);
763}
764
765static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
766 struct drm_plane_state *s,
767 struct drm_property *property,
768 uint64_t val)
769{
770 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
771 struct atmel_hlcdc_plane_properties *props = plane->properties;
772 struct atmel_hlcdc_plane_state *state =
773 drm_plane_state_to_atmel_hlcdc_plane_state(s);
774
775 if (property == props->alpha)
776 state->alpha = val;
777 else
778 return -EINVAL;
779
780 return 0;
781}
782
783static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
784 const struct drm_plane_state *s,
785 struct drm_property *property,
786 uint64_t *val)
787{
788 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
789 struct atmel_hlcdc_plane_properties *props = plane->properties;
790 const struct atmel_hlcdc_plane_state *state =
791 container_of(s, const struct atmel_hlcdc_plane_state, base);
792
793 if (property == props->alpha)
794 *val = state->alpha;
795 else
796 return -EINVAL;
797
798 return 0;
799}
800
801static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
802 const struct atmel_hlcdc_layer_desc *desc,
803 struct atmel_hlcdc_plane_properties *props)
804{
805 struct regmap *regmap = plane->layer.hlcdc->regmap;
806
807 if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
808 desc->type == ATMEL_HLCDC_CURSOR_LAYER) {
809 drm_object_attach_property(&plane->base.base,
810 props->alpha, 255);
811
812 /* Set default alpha value */
813 regmap_update_bits(regmap,
814 desc->regs_offset +
815 ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane->layer),
816 ATMEL_HLCDC_LAYER_GA_MASK,
817 ATMEL_HLCDC_LAYER_GA_MASK);
818 }
819
820 if (desc->layout.xstride && desc->layout.pstride)
821 drm_object_attach_property(&plane->base.base,
822 plane->base.dev->mode_config.rotation_property,
823 BIT(DRM_ROTATE_0));
824
825 if (desc->layout.csc) {
826 /*
827 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
828 * userspace modify these factors (using a BLOB property ?).
829 */
830 regmap_write(regmap,
831 desc->regs_offset +
832 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 0),
833 0x4c900091);
834 regmap_write(regmap,
835 desc->regs_offset +
836 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 1),
837 0x7a5f5090);
838 regmap_write(regmap,
839 desc->regs_offset +
840 ATMEL_HLCDC_LAYER_CSC_CFG(&plane->layer, 2),
841 0x40040890);
842 }
843}
844
845static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
846 .prepare_fb = atmel_hlcdc_plane_prepare_fb,
847 .atomic_check = atmel_hlcdc_plane_atomic_check,
848 .atomic_update = atmel_hlcdc_plane_atomic_update,
849 .atomic_disable = atmel_hlcdc_plane_atomic_disable,
850};
851
852static void atmel_hlcdc_plane_reset(struct drm_plane *p)
853{
854 struct atmel_hlcdc_plane_state *state;
855
856 if (p->state) {
857 state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
858
859 if (state->base.fb)
860 drm_framebuffer_unreference(state->base.fb);
861
862 kfree(state);
863 p->state = NULL;
864 }
865
866 state = kzalloc(sizeof(*state), GFP_KERNEL);
867 if (state) {
868 state->alpha = 255;
869 p->state = &state->base;
870 p->state->plane = p;
871 }
872}
873
874static struct drm_plane_state *
875atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
876{
877 struct atmel_hlcdc_plane_state *state =
878 drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
879 struct atmel_hlcdc_plane_state *copy;
880
881 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
882 if (!copy)
883 return NULL;
884
885 copy->disc_updated = false;
886
887 if (copy->base.fb)
888 drm_framebuffer_reference(copy->base.fb);
889
890 return ©->base;
891}
892
893static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *plane,
894 struct drm_plane_state *s)
895{
896 struct atmel_hlcdc_plane_state *state =
897 drm_plane_state_to_atmel_hlcdc_plane_state(s);
898
899 if (s->fb)
900 drm_framebuffer_unreference(s->fb);
901
902 kfree(state);
903}
904
905static struct drm_plane_funcs layer_plane_funcs = {
906 .update_plane = drm_atomic_helper_update_plane,
907 .disable_plane = drm_atomic_helper_disable_plane,
908 .set_property = drm_atomic_helper_plane_set_property,
909 .destroy = atmel_hlcdc_plane_destroy,
910 .reset = atmel_hlcdc_plane_reset,
911 .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
912 .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
913 .atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
914 .atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
915};
916
917static struct atmel_hlcdc_plane *
918atmel_hlcdc_plane_create(struct drm_device *dev,
919 const struct atmel_hlcdc_layer_desc *desc,
920 struct atmel_hlcdc_plane_properties *props)
921{
922 struct atmel_hlcdc_plane *plane;
923 enum drm_plane_type type;
924 int ret;
925
926 plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
927 if (!plane)
928 return ERR_PTR(-ENOMEM);
929
930 ret = atmel_hlcdc_layer_init(dev, &plane->layer, desc);
931 if (ret)
932 return ERR_PTR(ret);
933
934 if (desc->type == ATMEL_HLCDC_BASE_LAYER)
935 type = DRM_PLANE_TYPE_PRIMARY;
936 else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
937 type = DRM_PLANE_TYPE_CURSOR;
938 else
939 type = DRM_PLANE_TYPE_OVERLAY;
940
941 ret = drm_universal_plane_init(dev, &plane->base, 0,
942 &layer_plane_funcs,
943 desc->formats->formats,
944 desc->formats->nformats, type, NULL);
945 if (ret)
946 return ERR_PTR(ret);
947
948 drm_plane_helper_add(&plane->base,
949 &atmel_hlcdc_layer_plane_helper_funcs);
950
951 /* Set default property values*/
952 atmel_hlcdc_plane_init_properties(plane, desc, props);
953
954 return plane;
955}
956
957static struct atmel_hlcdc_plane_properties *
958atmel_hlcdc_plane_create_properties(struct drm_device *dev)
959{
960 struct atmel_hlcdc_plane_properties *props;
961
962 props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
963 if (!props)
964 return ERR_PTR(-ENOMEM);
965
966 props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
967 if (!props->alpha)
968 return ERR_PTR(-ENOMEM);
969
970 dev->mode_config.rotation_property =
971 drm_mode_create_rotation_property(dev,
972 BIT(DRM_ROTATE_0) |
973 BIT(DRM_ROTATE_90) |
974 BIT(DRM_ROTATE_180) |
975 BIT(DRM_ROTATE_270));
976 if (!dev->mode_config.rotation_property)
977 return ERR_PTR(-ENOMEM);
978
979 return props;
980}
981
982struct atmel_hlcdc_planes *
983atmel_hlcdc_create_planes(struct drm_device *dev)
984{
985 struct atmel_hlcdc_dc *dc = dev->dev_private;
986 struct atmel_hlcdc_plane_properties *props;
987 struct atmel_hlcdc_planes *planes;
988 const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
989 int nlayers = dc->desc->nlayers;
990 int i;
991
992 planes = devm_kzalloc(dev->dev, sizeof(*planes), GFP_KERNEL);
993 if (!planes)
994 return ERR_PTR(-ENOMEM);
995
996 for (i = 0; i < nlayers; i++) {
997 if (descs[i].type == ATMEL_HLCDC_OVERLAY_LAYER)
998 planes->noverlays++;
999 }
1000
1001 if (planes->noverlays) {
1002 planes->overlays = devm_kzalloc(dev->dev,
1003 planes->noverlays *
1004 sizeof(*planes->overlays),
1005 GFP_KERNEL);
1006 if (!planes->overlays)
1007 return ERR_PTR(-ENOMEM);
1008 }
1009
1010 props = atmel_hlcdc_plane_create_properties(dev);
1011 if (IS_ERR(props))
1012 return ERR_CAST(props);
1013
1014 planes->noverlays = 0;
1015 for (i = 0; i < nlayers; i++) {
1016 struct atmel_hlcdc_plane *plane;
1017
1018 if (descs[i].type == ATMEL_HLCDC_PP_LAYER)
1019 continue;
1020
1021 plane = atmel_hlcdc_plane_create(dev, &descs[i], props);
1022 if (IS_ERR(plane))
1023 return ERR_CAST(plane);
1024
1025 plane->properties = props;
1026
1027 switch (descs[i].type) {
1028 case ATMEL_HLCDC_BASE_LAYER:
1029 if (planes->primary)
1030 return ERR_PTR(-EINVAL);
1031 planes->primary = plane;
1032 break;
1033
1034 case ATMEL_HLCDC_OVERLAY_LAYER:
1035 planes->overlays[planes->noverlays++] = plane;
1036 break;
1037
1038 case ATMEL_HLCDC_CURSOR_LAYER:
1039 if (planes->cursor)
1040 return ERR_PTR(-EINVAL);
1041 planes->cursor = plane;
1042 break;
1043
1044 default:
1045 break;
1046 }
1047 }
1048
1049 return planes;
1050}
1/*
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/**
23 * Atmel HLCDC Plane state structure.
24 *
25 * @base: DRM plane state
26 * @crtc_x: x position of the plane relative to the CRTC
27 * @crtc_y: y position of the plane relative to the CRTC
28 * @crtc_w: visible width of the plane
29 * @crtc_h: visible height of the plane
30 * @src_x: x buffer position
31 * @src_y: y buffer position
32 * @src_w: buffer width
33 * @src_h: buffer height
34 * @alpha: alpha blending of the plane
35 * @disc_x: x discard position
36 * @disc_y: y discard position
37 * @disc_w: discard width
38 * @disc_h: discard height
39 * @bpp: bytes per pixel deduced from pixel_format
40 * @offsets: offsets to apply to the GEM buffers
41 * @xstride: value to add to the pixel pointer between each line
42 * @pstride: value to add to the pixel pointer between each pixel
43 * @nplanes: number of planes (deduced from pixel_format)
44 * @dscrs: DMA descriptors
45 */
46struct atmel_hlcdc_plane_state {
47 struct drm_plane_state base;
48 int crtc_x;
49 int crtc_y;
50 unsigned int crtc_w;
51 unsigned int crtc_h;
52 uint32_t src_x;
53 uint32_t src_y;
54 uint32_t src_w;
55 uint32_t src_h;
56
57 u8 alpha;
58
59 int disc_x;
60 int disc_y;
61 int disc_w;
62 int disc_h;
63
64 int ahb_id;
65
66 /* These fields are private and should not be touched */
67 int bpp[ATMEL_HLCDC_LAYER_MAX_PLANES];
68 unsigned int offsets[ATMEL_HLCDC_LAYER_MAX_PLANES];
69 int xstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
70 int pstride[ATMEL_HLCDC_LAYER_MAX_PLANES];
71 int nplanes;
72
73 /* DMA descriptors. */
74 struct atmel_hlcdc_dma_channel_dscr *dscrs[ATMEL_HLCDC_LAYER_MAX_PLANES];
75};
76
77static inline struct atmel_hlcdc_plane_state *
78drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state *s)
79{
80 return container_of(s, struct atmel_hlcdc_plane_state, base);
81}
82
83#define SUBPIXEL_MASK 0xffff
84
85static uint32_t rgb_formats[] = {
86 DRM_FORMAT_C8,
87 DRM_FORMAT_XRGB4444,
88 DRM_FORMAT_ARGB4444,
89 DRM_FORMAT_RGBA4444,
90 DRM_FORMAT_ARGB1555,
91 DRM_FORMAT_RGB565,
92 DRM_FORMAT_RGB888,
93 DRM_FORMAT_XRGB8888,
94 DRM_FORMAT_ARGB8888,
95 DRM_FORMAT_RGBA8888,
96};
97
98struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats = {
99 .formats = rgb_formats,
100 .nformats = ARRAY_SIZE(rgb_formats),
101};
102
103static uint32_t rgb_and_yuv_formats[] = {
104 DRM_FORMAT_C8,
105 DRM_FORMAT_XRGB4444,
106 DRM_FORMAT_ARGB4444,
107 DRM_FORMAT_RGBA4444,
108 DRM_FORMAT_ARGB1555,
109 DRM_FORMAT_RGB565,
110 DRM_FORMAT_RGB888,
111 DRM_FORMAT_XRGB8888,
112 DRM_FORMAT_ARGB8888,
113 DRM_FORMAT_RGBA8888,
114 DRM_FORMAT_AYUV,
115 DRM_FORMAT_YUYV,
116 DRM_FORMAT_UYVY,
117 DRM_FORMAT_YVYU,
118 DRM_FORMAT_VYUY,
119 DRM_FORMAT_NV21,
120 DRM_FORMAT_NV61,
121 DRM_FORMAT_YUV422,
122 DRM_FORMAT_YUV420,
123};
124
125struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats = {
126 .formats = rgb_and_yuv_formats,
127 .nformats = ARRAY_SIZE(rgb_and_yuv_formats),
128};
129
130static int atmel_hlcdc_format_to_plane_mode(u32 format, u32 *mode)
131{
132 switch (format) {
133 case DRM_FORMAT_C8:
134 *mode = ATMEL_HLCDC_C8_MODE;
135 break;
136 case DRM_FORMAT_XRGB4444:
137 *mode = ATMEL_HLCDC_XRGB4444_MODE;
138 break;
139 case DRM_FORMAT_ARGB4444:
140 *mode = ATMEL_HLCDC_ARGB4444_MODE;
141 break;
142 case DRM_FORMAT_RGBA4444:
143 *mode = ATMEL_HLCDC_RGBA4444_MODE;
144 break;
145 case DRM_FORMAT_RGB565:
146 *mode = ATMEL_HLCDC_RGB565_MODE;
147 break;
148 case DRM_FORMAT_RGB888:
149 *mode = ATMEL_HLCDC_RGB888_MODE;
150 break;
151 case DRM_FORMAT_ARGB1555:
152 *mode = ATMEL_HLCDC_ARGB1555_MODE;
153 break;
154 case DRM_FORMAT_XRGB8888:
155 *mode = ATMEL_HLCDC_XRGB8888_MODE;
156 break;
157 case DRM_FORMAT_ARGB8888:
158 *mode = ATMEL_HLCDC_ARGB8888_MODE;
159 break;
160 case DRM_FORMAT_RGBA8888:
161 *mode = ATMEL_HLCDC_RGBA8888_MODE;
162 break;
163 case DRM_FORMAT_AYUV:
164 *mode = ATMEL_HLCDC_AYUV_MODE;
165 break;
166 case DRM_FORMAT_YUYV:
167 *mode = ATMEL_HLCDC_YUYV_MODE;
168 break;
169 case DRM_FORMAT_UYVY:
170 *mode = ATMEL_HLCDC_UYVY_MODE;
171 break;
172 case DRM_FORMAT_YVYU:
173 *mode = ATMEL_HLCDC_YVYU_MODE;
174 break;
175 case DRM_FORMAT_VYUY:
176 *mode = ATMEL_HLCDC_VYUY_MODE;
177 break;
178 case DRM_FORMAT_NV21:
179 *mode = ATMEL_HLCDC_NV21_MODE;
180 break;
181 case DRM_FORMAT_NV61:
182 *mode = ATMEL_HLCDC_NV61_MODE;
183 break;
184 case DRM_FORMAT_YUV420:
185 *mode = ATMEL_HLCDC_YUV420_MODE;
186 break;
187 case DRM_FORMAT_YUV422:
188 *mode = ATMEL_HLCDC_YUV422_MODE;
189 break;
190 default:
191 return -ENOTSUPP;
192 }
193
194 return 0;
195}
196
197static u32 heo_downscaling_xcoef[] = {
198 0x11343311,
199 0x000000f7,
200 0x1635300c,
201 0x000000f9,
202 0x1b362c08,
203 0x000000fb,
204 0x1f372804,
205 0x000000fe,
206 0x24382400,
207 0x00000000,
208 0x28371ffe,
209 0x00000004,
210 0x2c361bfb,
211 0x00000008,
212 0x303516f9,
213 0x0000000c,
214};
215
216static u32 heo_downscaling_ycoef[] = {
217 0x00123737,
218 0x00173732,
219 0x001b382d,
220 0x001f3928,
221 0x00243824,
222 0x0028391f,
223 0x002d381b,
224 0x00323717,
225};
226
227static u32 heo_upscaling_xcoef[] = {
228 0xf74949f7,
229 0x00000000,
230 0xf55f33fb,
231 0x000000fe,
232 0xf5701efe,
233 0x000000ff,
234 0xf87c0dff,
235 0x00000000,
236 0x00800000,
237 0x00000000,
238 0x0d7cf800,
239 0x000000ff,
240 0x1e70f5ff,
241 0x000000fe,
242 0x335ff5fe,
243 0x000000fb,
244};
245
246static u32 heo_upscaling_ycoef[] = {
247 0x00004040,
248 0x00075920,
249 0x00056f0c,
250 0x00027b03,
251 0x00008000,
252 0x00037b02,
253 0x000c6f05,
254 0x00205907,
255};
256
257#define ATMEL_HLCDC_XPHIDEF 4
258#define ATMEL_HLCDC_YPHIDEF 4
259
260static u32 atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize,
261 u32 dstsize,
262 u32 phidef)
263{
264 u32 factor, max_memsize;
265
266 factor = (256 * ((8 * (srcsize - 1)) - phidef)) / (dstsize - 1);
267 max_memsize = ((factor * (dstsize - 1)) + (256 * phidef)) / 2048;
268
269 if (max_memsize > srcsize - 1)
270 factor--;
271
272 return factor;
273}
274
275static void
276atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane *plane,
277 const u32 *coeff_tab, int size,
278 unsigned int cfg_offs)
279{
280 int i;
281
282 for (i = 0; i < size; i++)
283 atmel_hlcdc_layer_write_cfg(&plane->layer, cfg_offs + i,
284 coeff_tab[i]);
285}
286
287void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
288 struct atmel_hlcdc_plane_state *state)
289{
290 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
291 u32 xfactor, yfactor;
292
293 if (!desc->layout.scaler_config)
294 return;
295
296 if (state->crtc_w == state->src_w && state->crtc_h == state->src_h) {
297 atmel_hlcdc_layer_write_cfg(&plane->layer,
298 desc->layout.scaler_config, 0);
299 return;
300 }
301
302 if (desc->layout.phicoeffs.x) {
303 xfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_w,
304 state->crtc_w,
305 ATMEL_HLCDC_XPHIDEF);
306
307 yfactor = atmel_hlcdc_plane_phiscaler_get_factor(state->src_h,
308 state->crtc_h,
309 ATMEL_HLCDC_YPHIDEF);
310
311 atmel_hlcdc_plane_scaler_set_phicoeff(plane,
312 state->crtc_w < state->src_w ?
313 heo_downscaling_xcoef :
314 heo_upscaling_xcoef,
315 ARRAY_SIZE(heo_upscaling_xcoef),
316 desc->layout.phicoeffs.x);
317
318 atmel_hlcdc_plane_scaler_set_phicoeff(plane,
319 state->crtc_h < state->src_h ?
320 heo_downscaling_ycoef :
321 heo_upscaling_ycoef,
322 ARRAY_SIZE(heo_upscaling_ycoef),
323 desc->layout.phicoeffs.y);
324 } else {
325 xfactor = (1024 * state->src_w) / state->crtc_w;
326 yfactor = (1024 * state->src_h) / state->crtc_h;
327 }
328
329 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config,
330 ATMEL_HLCDC_LAYER_SCALER_ENABLE |
331 ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor,
332 yfactor));
333}
334
335static void
336atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane *plane,
337 struct atmel_hlcdc_plane_state *state)
338{
339 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
340
341 if (desc->layout.size)
342 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.size,
343 ATMEL_HLCDC_LAYER_SIZE(state->crtc_w,
344 state->crtc_h));
345
346 if (desc->layout.memsize)
347 atmel_hlcdc_layer_write_cfg(&plane->layer,
348 desc->layout.memsize,
349 ATMEL_HLCDC_LAYER_SIZE(state->src_w,
350 state->src_h));
351
352 if (desc->layout.pos)
353 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.pos,
354 ATMEL_HLCDC_LAYER_POS(state->crtc_x,
355 state->crtc_y));
356
357 atmel_hlcdc_plane_setup_scaler(plane, state);
358}
359
360static void
361atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane *plane,
362 struct atmel_hlcdc_plane_state *state)
363{
364 unsigned int cfg = ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16 | state->ahb_id;
365 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
366 const struct drm_format_info *format = state->base.fb->format;
367
368 /*
369 * Rotation optimization is not working on RGB888 (rotation is still
370 * working but without any optimization).
371 */
372 if (format->format == DRM_FORMAT_RGB888)
373 cfg |= ATMEL_HLCDC_LAYER_DMA_ROTDIS;
374
375 atmel_hlcdc_layer_write_cfg(&plane->layer, ATMEL_HLCDC_LAYER_DMA_CFG,
376 cfg);
377
378 cfg = ATMEL_HLCDC_LAYER_DMA;
379
380 if (plane->base.type != DRM_PLANE_TYPE_PRIMARY) {
381 cfg |= ATMEL_HLCDC_LAYER_OVR | ATMEL_HLCDC_LAYER_ITER2BL |
382 ATMEL_HLCDC_LAYER_ITER;
383
384 if (format->has_alpha)
385 cfg |= ATMEL_HLCDC_LAYER_LAEN;
386 else
387 cfg |= ATMEL_HLCDC_LAYER_GAEN |
388 ATMEL_HLCDC_LAYER_GA(state->alpha);
389 }
390
391 if (state->disc_h && state->disc_w)
392 cfg |= ATMEL_HLCDC_LAYER_DISCEN;
393
394 atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.general_config,
395 cfg);
396}
397
398static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
399 struct atmel_hlcdc_plane_state *state)
400{
401 u32 cfg;
402 int ret;
403
404 ret = atmel_hlcdc_format_to_plane_mode(state->base.fb->format->format,
405 &cfg);
406 if (ret)
407 return;
408
409 if ((state->base.fb->format->format == DRM_FORMAT_YUV422 ||
410 state->base.fb->format->format == DRM_FORMAT_NV61) &&
411 drm_rotation_90_or_270(state->base.rotation))
412 cfg |= ATMEL_HLCDC_YUV422ROT;
413
414 atmel_hlcdc_layer_write_cfg(&plane->layer,
415 ATMEL_HLCDC_LAYER_FORMAT_CFG, cfg);
416}
417
418static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane *plane)
419{
420 struct drm_crtc *crtc = plane->base.crtc;
421 struct drm_color_lut *lut;
422 int idx;
423
424 if (!crtc || !crtc->state)
425 return;
426
427 if (!crtc->state->color_mgmt_changed || !crtc->state->gamma_lut)
428 return;
429
430 lut = (struct drm_color_lut *)crtc->state->gamma_lut->data;
431
432 for (idx = 0; idx < ATMEL_HLCDC_CLUT_SIZE; idx++, lut++) {
433 u32 val = ((lut->red << 8) & 0xff0000) |
434 (lut->green & 0xff00) |
435 (lut->blue >> 8);
436
437 atmel_hlcdc_layer_write_clut(&plane->layer, idx, val);
438 }
439}
440
441static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane *plane,
442 struct atmel_hlcdc_plane_state *state)
443{
444 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
445 struct drm_framebuffer *fb = state->base.fb;
446 u32 sr;
447 int i;
448
449 sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
450
451 for (i = 0; i < state->nplanes; i++) {
452 struct drm_gem_cma_object *gem = drm_fb_cma_get_gem_obj(fb, i);
453
454 state->dscrs[i]->addr = gem->paddr + state->offsets[i];
455
456 atmel_hlcdc_layer_write_reg(&plane->layer,
457 ATMEL_HLCDC_LAYER_PLANE_HEAD(i),
458 state->dscrs[i]->self);
459
460 if (!(sr & ATMEL_HLCDC_LAYER_EN)) {
461 atmel_hlcdc_layer_write_reg(&plane->layer,
462 ATMEL_HLCDC_LAYER_PLANE_ADDR(i),
463 state->dscrs[i]->addr);
464 atmel_hlcdc_layer_write_reg(&plane->layer,
465 ATMEL_HLCDC_LAYER_PLANE_CTRL(i),
466 state->dscrs[i]->ctrl);
467 atmel_hlcdc_layer_write_reg(&plane->layer,
468 ATMEL_HLCDC_LAYER_PLANE_NEXT(i),
469 state->dscrs[i]->self);
470 }
471
472 if (desc->layout.xstride[i])
473 atmel_hlcdc_layer_write_cfg(&plane->layer,
474 desc->layout.xstride[i],
475 state->xstride[i]);
476
477 if (desc->layout.pstride[i])
478 atmel_hlcdc_layer_write_cfg(&plane->layer,
479 desc->layout.pstride[i],
480 state->pstride[i]);
481 }
482}
483
484int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state *c_state)
485{
486 unsigned int ahb_load[2] = { };
487 struct drm_plane *plane;
488
489 drm_atomic_crtc_state_for_each_plane(plane, c_state) {
490 struct atmel_hlcdc_plane_state *plane_state;
491 struct drm_plane_state *plane_s;
492 unsigned int pixels, load = 0;
493 int i;
494
495 plane_s = drm_atomic_get_plane_state(c_state->state, plane);
496 if (IS_ERR(plane_s))
497 return PTR_ERR(plane_s);
498
499 plane_state =
500 drm_plane_state_to_atmel_hlcdc_plane_state(plane_s);
501
502 pixels = (plane_state->src_w * plane_state->src_h) -
503 (plane_state->disc_w * plane_state->disc_h);
504
505 for (i = 0; i < plane_state->nplanes; i++)
506 load += pixels * plane_state->bpp[i];
507
508 if (ahb_load[0] <= ahb_load[1])
509 plane_state->ahb_id = 0;
510 else
511 plane_state->ahb_id = 1;
512
513 ahb_load[plane_state->ahb_id] += load;
514 }
515
516 return 0;
517}
518
519int
520atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state *c_state)
521{
522 int disc_x = 0, disc_y = 0, disc_w = 0, disc_h = 0;
523 const struct atmel_hlcdc_layer_cfg_layout *layout;
524 struct atmel_hlcdc_plane_state *primary_state;
525 struct drm_plane_state *primary_s;
526 struct atmel_hlcdc_plane *primary;
527 struct drm_plane *ovl;
528
529 primary = drm_plane_to_atmel_hlcdc_plane(c_state->crtc->primary);
530 layout = &primary->layer.desc->layout;
531 if (!layout->disc_pos || !layout->disc_size)
532 return 0;
533
534 primary_s = drm_atomic_get_plane_state(c_state->state,
535 &primary->base);
536 if (IS_ERR(primary_s))
537 return PTR_ERR(primary_s);
538
539 primary_state = drm_plane_state_to_atmel_hlcdc_plane_state(primary_s);
540
541 drm_atomic_crtc_state_for_each_plane(ovl, c_state) {
542 struct atmel_hlcdc_plane_state *ovl_state;
543 struct drm_plane_state *ovl_s;
544
545 if (ovl == c_state->crtc->primary)
546 continue;
547
548 ovl_s = drm_atomic_get_plane_state(c_state->state, ovl);
549 if (IS_ERR(ovl_s))
550 return PTR_ERR(ovl_s);
551
552 ovl_state = drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s);
553
554 if (!ovl_s->fb ||
555 ovl_s->fb->format->has_alpha ||
556 ovl_state->alpha != 255)
557 continue;
558
559 /* TODO: implement a smarter hidden area detection */
560 if (ovl_state->crtc_h * ovl_state->crtc_w < disc_h * disc_w)
561 continue;
562
563 disc_x = ovl_state->crtc_x;
564 disc_y = ovl_state->crtc_y;
565 disc_h = ovl_state->crtc_h;
566 disc_w = ovl_state->crtc_w;
567 }
568
569 primary_state->disc_x = disc_x;
570 primary_state->disc_y = disc_y;
571 primary_state->disc_w = disc_w;
572 primary_state->disc_h = disc_h;
573
574 return 0;
575}
576
577static void
578atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane *plane,
579 struct atmel_hlcdc_plane_state *state)
580{
581 const struct atmel_hlcdc_layer_cfg_layout *layout;
582
583 layout = &plane->layer.desc->layout;
584 if (!layout->disc_pos || !layout->disc_size)
585 return;
586
587 atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_pos,
588 ATMEL_HLCDC_LAYER_DISC_POS(state->disc_x,
589 state->disc_y));
590
591 atmel_hlcdc_layer_write_cfg(&plane->layer, layout->disc_size,
592 ATMEL_HLCDC_LAYER_DISC_SIZE(state->disc_w,
593 state->disc_h));
594}
595
596static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
597 struct drm_plane_state *s)
598{
599 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
600 struct atmel_hlcdc_plane_state *state =
601 drm_plane_state_to_atmel_hlcdc_plane_state(s);
602 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
603 struct drm_framebuffer *fb = state->base.fb;
604 const struct drm_display_mode *mode;
605 struct drm_crtc_state *crtc_state;
606 unsigned int patched_crtc_w;
607 unsigned int patched_crtc_h;
608 unsigned int patched_src_w;
609 unsigned int patched_src_h;
610 unsigned int tmp;
611 int x_offset = 0;
612 int y_offset = 0;
613 int hsub = 1;
614 int vsub = 1;
615 int i;
616
617 if (!state->base.crtc || !fb)
618 return 0;
619
620 crtc_state = drm_atomic_get_existing_crtc_state(s->state, s->crtc);
621 mode = &crtc_state->adjusted_mode;
622
623 state->src_x = s->src_x;
624 state->src_y = s->src_y;
625 state->src_h = s->src_h;
626 state->src_w = s->src_w;
627 state->crtc_x = s->crtc_x;
628 state->crtc_y = s->crtc_y;
629 state->crtc_h = s->crtc_h;
630 state->crtc_w = s->crtc_w;
631 if ((state->src_x | state->src_y | state->src_w | state->src_h) &
632 SUBPIXEL_MASK)
633 return -EINVAL;
634
635 state->src_x >>= 16;
636 state->src_y >>= 16;
637 state->src_w >>= 16;
638 state->src_h >>= 16;
639
640 state->nplanes = fb->format->num_planes;
641 if (state->nplanes > ATMEL_HLCDC_LAYER_MAX_PLANES)
642 return -EINVAL;
643
644 /*
645 * Swap width and size in case of 90 or 270 degrees rotation
646 */
647 if (drm_rotation_90_or_270(state->base.rotation)) {
648 tmp = state->crtc_w;
649 state->crtc_w = state->crtc_h;
650 state->crtc_h = tmp;
651 tmp = state->src_w;
652 state->src_w = state->src_h;
653 state->src_h = tmp;
654 }
655
656 if (state->crtc_x + state->crtc_w > mode->hdisplay)
657 patched_crtc_w = mode->hdisplay - state->crtc_x;
658 else
659 patched_crtc_w = state->crtc_w;
660
661 if (state->crtc_x < 0) {
662 patched_crtc_w += state->crtc_x;
663 x_offset = -state->crtc_x;
664 state->crtc_x = 0;
665 }
666
667 if (state->crtc_y + state->crtc_h > mode->vdisplay)
668 patched_crtc_h = mode->vdisplay - state->crtc_y;
669 else
670 patched_crtc_h = state->crtc_h;
671
672 if (state->crtc_y < 0) {
673 patched_crtc_h += state->crtc_y;
674 y_offset = -state->crtc_y;
675 state->crtc_y = 0;
676 }
677
678 patched_src_w = DIV_ROUND_CLOSEST(patched_crtc_w * state->src_w,
679 state->crtc_w);
680 patched_src_h = DIV_ROUND_CLOSEST(patched_crtc_h * state->src_h,
681 state->crtc_h);
682
683 hsub = drm_format_horz_chroma_subsampling(fb->format->format);
684 vsub = drm_format_vert_chroma_subsampling(fb->format->format);
685
686 for (i = 0; i < state->nplanes; i++) {
687 unsigned int offset = 0;
688 int xdiv = i ? hsub : 1;
689 int ydiv = i ? vsub : 1;
690
691 state->bpp[i] = fb->format->cpp[i];
692 if (!state->bpp[i])
693 return -EINVAL;
694
695 switch (state->base.rotation & DRM_MODE_ROTATE_MASK) {
696 case DRM_MODE_ROTATE_90:
697 offset = ((y_offset + state->src_y + patched_src_w - 1) /
698 ydiv) * fb->pitches[i];
699 offset += ((x_offset + state->src_x) / xdiv) *
700 state->bpp[i];
701 state->xstride[i] = ((patched_src_w - 1) / ydiv) *
702 fb->pitches[i];
703 state->pstride[i] = -fb->pitches[i] - state->bpp[i];
704 break;
705 case DRM_MODE_ROTATE_180:
706 offset = ((y_offset + state->src_y + patched_src_h - 1) /
707 ydiv) * fb->pitches[i];
708 offset += ((x_offset + state->src_x + patched_src_w - 1) /
709 xdiv) * state->bpp[i];
710 state->xstride[i] = ((((patched_src_w - 1) / xdiv) - 1) *
711 state->bpp[i]) - fb->pitches[i];
712 state->pstride[i] = -2 * state->bpp[i];
713 break;
714 case DRM_MODE_ROTATE_270:
715 offset = ((y_offset + state->src_y) / ydiv) *
716 fb->pitches[i];
717 offset += ((x_offset + state->src_x + patched_src_h - 1) /
718 xdiv) * state->bpp[i];
719 state->xstride[i] = -(((patched_src_w - 1) / ydiv) *
720 fb->pitches[i]) -
721 (2 * state->bpp[i]);
722 state->pstride[i] = fb->pitches[i] - state->bpp[i];
723 break;
724 case DRM_MODE_ROTATE_0:
725 default:
726 offset = ((y_offset + state->src_y) / ydiv) *
727 fb->pitches[i];
728 offset += ((x_offset + state->src_x) / xdiv) *
729 state->bpp[i];
730 state->xstride[i] = fb->pitches[i] -
731 ((patched_src_w / xdiv) *
732 state->bpp[i]);
733 state->pstride[i] = 0;
734 break;
735 }
736
737 state->offsets[i] = offset + fb->offsets[i];
738 }
739
740 state->src_w = patched_src_w;
741 state->src_h = patched_src_h;
742 state->crtc_w = patched_crtc_w;
743 state->crtc_h = patched_crtc_h;
744
745 if (!desc->layout.size &&
746 (mode->hdisplay != state->crtc_w ||
747 mode->vdisplay != state->crtc_h))
748 return -EINVAL;
749
750 if (desc->max_height && state->crtc_h > desc->max_height)
751 return -EINVAL;
752
753 if (desc->max_width && state->crtc_w > desc->max_width)
754 return -EINVAL;
755
756 if ((state->crtc_h != state->src_h || state->crtc_w != state->src_w) &&
757 (!desc->layout.memsize ||
758 state->base.fb->format->has_alpha))
759 return -EINVAL;
760
761 if (state->crtc_x < 0 || state->crtc_y < 0)
762 return -EINVAL;
763
764 if (state->crtc_w + state->crtc_x > mode->hdisplay ||
765 state->crtc_h + state->crtc_y > mode->vdisplay)
766 return -EINVAL;
767
768 return 0;
769}
770
771static void atmel_hlcdc_plane_atomic_update(struct drm_plane *p,
772 struct drm_plane_state *old_s)
773{
774 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
775 struct atmel_hlcdc_plane_state *state =
776 drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
777 u32 sr;
778
779 if (!p->state->crtc || !p->state->fb)
780 return;
781
782 atmel_hlcdc_plane_update_pos_and_size(plane, state);
783 atmel_hlcdc_plane_update_general_settings(plane, state);
784 atmel_hlcdc_plane_update_format(plane, state);
785 atmel_hlcdc_plane_update_clut(plane);
786 atmel_hlcdc_plane_update_buffers(plane, state);
787 atmel_hlcdc_plane_update_disc_area(plane, state);
788
789 /* Enable the overrun interrupts. */
790 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IER,
791 ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
792 ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
793 ATMEL_HLCDC_LAYER_OVR_IRQ(2));
794
795 /* Apply the new config at the next SOF event. */
796 sr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHSR);
797 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHER,
798 ATMEL_HLCDC_LAYER_UPDATE |
799 (sr & ATMEL_HLCDC_LAYER_EN ?
800 ATMEL_HLCDC_LAYER_A2Q : ATMEL_HLCDC_LAYER_EN));
801}
802
803static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
804 struct drm_plane_state *old_state)
805{
806 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
807
808 /* Disable interrupts */
809 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
810 0xffffffff);
811
812 /* Disable the layer */
813 atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_CHDR,
814 ATMEL_HLCDC_LAYER_RST |
815 ATMEL_HLCDC_LAYER_A2Q |
816 ATMEL_HLCDC_LAYER_UPDATE);
817
818 /* Clear all pending interrupts */
819 atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
820}
821
822static void atmel_hlcdc_plane_destroy(struct drm_plane *p)
823{
824 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
825
826 if (plane->base.fb)
827 drm_framebuffer_put(plane->base.fb);
828
829 drm_plane_cleanup(p);
830}
831
832static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane *p,
833 struct drm_plane_state *s,
834 struct drm_property *property,
835 uint64_t val)
836{
837 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
838 struct atmel_hlcdc_plane_properties *props = plane->properties;
839 struct atmel_hlcdc_plane_state *state =
840 drm_plane_state_to_atmel_hlcdc_plane_state(s);
841
842 if (property == props->alpha)
843 state->alpha = val;
844 else
845 return -EINVAL;
846
847 return 0;
848}
849
850static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane *p,
851 const struct drm_plane_state *s,
852 struct drm_property *property,
853 uint64_t *val)
854{
855 struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
856 struct atmel_hlcdc_plane_properties *props = plane->properties;
857 const struct atmel_hlcdc_plane_state *state =
858 container_of(s, const struct atmel_hlcdc_plane_state, base);
859
860 if (property == props->alpha)
861 *val = state->alpha;
862 else
863 return -EINVAL;
864
865 return 0;
866}
867
868static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
869 struct atmel_hlcdc_plane_properties *props)
870{
871 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
872
873 if (desc->type == ATMEL_HLCDC_OVERLAY_LAYER ||
874 desc->type == ATMEL_HLCDC_CURSOR_LAYER)
875 drm_object_attach_property(&plane->base.base,
876 props->alpha, 255);
877
878 if (desc->layout.xstride && desc->layout.pstride) {
879 int ret;
880
881 ret = drm_plane_create_rotation_property(&plane->base,
882 DRM_MODE_ROTATE_0,
883 DRM_MODE_ROTATE_0 |
884 DRM_MODE_ROTATE_90 |
885 DRM_MODE_ROTATE_180 |
886 DRM_MODE_ROTATE_270);
887 if (ret)
888 return ret;
889 }
890
891 if (desc->layout.csc) {
892 /*
893 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
894 * userspace modify these factors (using a BLOB property ?).
895 */
896 atmel_hlcdc_layer_write_cfg(&plane->layer,
897 desc->layout.csc,
898 0x4c900091);
899 atmel_hlcdc_layer_write_cfg(&plane->layer,
900 desc->layout.csc + 1,
901 0x7a5f5090);
902 atmel_hlcdc_layer_write_cfg(&plane->layer,
903 desc->layout.csc + 2,
904 0x40040890);
905 }
906
907 return 0;
908}
909
910void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane)
911{
912 const struct atmel_hlcdc_layer_desc *desc = plane->layer.desc;
913 u32 isr;
914
915 isr = atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
916
917 /*
918 * There's not much we can do in case of overrun except informing
919 * the user. However, we are in interrupt context here, hence the
920 * use of dev_dbg().
921 */
922 if (isr &
923 (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
924 ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
925 dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
926 desc->name);
927}
928
929static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs = {
930 .atomic_check = atmel_hlcdc_plane_atomic_check,
931 .atomic_update = atmel_hlcdc_plane_atomic_update,
932 .atomic_disable = atmel_hlcdc_plane_atomic_disable,
933};
934
935static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane *p,
936 struct atmel_hlcdc_plane_state *state)
937{
938 struct atmel_hlcdc_dc *dc = p->dev->dev_private;
939 int i;
940
941 for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
942 struct atmel_hlcdc_dma_channel_dscr *dscr;
943 dma_addr_t dscr_dma;
944
945 dscr = dma_pool_alloc(dc->dscrpool, GFP_KERNEL, &dscr_dma);
946 if (!dscr)
947 goto err;
948
949 dscr->addr = 0;
950 dscr->next = dscr_dma;
951 dscr->self = dscr_dma;
952 dscr->ctrl = ATMEL_HLCDC_LAYER_DFETCH;
953
954 state->dscrs[i] = dscr;
955 }
956
957 return 0;
958
959err:
960 for (i--; i >= 0; i--) {
961 dma_pool_free(dc->dscrpool, state->dscrs[i],
962 state->dscrs[i]->self);
963 }
964
965 return -ENOMEM;
966}
967
968static void atmel_hlcdc_plane_reset(struct drm_plane *p)
969{
970 struct atmel_hlcdc_plane_state *state;
971
972 if (p->state) {
973 state = drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
974
975 if (state->base.fb)
976 drm_framebuffer_put(state->base.fb);
977
978 kfree(state);
979 p->state = NULL;
980 }
981
982 state = kzalloc(sizeof(*state), GFP_KERNEL);
983 if (state) {
984 if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
985 kfree(state);
986 dev_err(p->dev->dev,
987 "Failed to allocate initial plane state\n");
988 return;
989 }
990
991 state->alpha = 255;
992 p->state = &state->base;
993 p->state->plane = p;
994 }
995}
996
997static struct drm_plane_state *
998atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane *p)
999{
1000 struct atmel_hlcdc_plane_state *state =
1001 drm_plane_state_to_atmel_hlcdc_plane_state(p->state);
1002 struct atmel_hlcdc_plane_state *copy;
1003
1004 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
1005 if (!copy)
1006 return NULL;
1007
1008 if (atmel_hlcdc_plane_alloc_dscrs(p, copy)) {
1009 kfree(copy);
1010 return NULL;
1011 }
1012
1013 if (copy->base.fb)
1014 drm_framebuffer_get(copy->base.fb);
1015
1016 return ©->base;
1017}
1018
1019static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane *p,
1020 struct drm_plane_state *s)
1021{
1022 struct atmel_hlcdc_plane_state *state =
1023 drm_plane_state_to_atmel_hlcdc_plane_state(s);
1024 struct atmel_hlcdc_dc *dc = p->dev->dev_private;
1025 int i;
1026
1027 for (i = 0; i < ARRAY_SIZE(state->dscrs); i++) {
1028 dma_pool_free(dc->dscrpool, state->dscrs[i],
1029 state->dscrs[i]->self);
1030 }
1031
1032 if (s->fb)
1033 drm_framebuffer_put(s->fb);
1034
1035 kfree(state);
1036}
1037
1038static const struct drm_plane_funcs layer_plane_funcs = {
1039 .update_plane = drm_atomic_helper_update_plane,
1040 .disable_plane = drm_atomic_helper_disable_plane,
1041 .destroy = atmel_hlcdc_plane_destroy,
1042 .reset = atmel_hlcdc_plane_reset,
1043 .atomic_duplicate_state = atmel_hlcdc_plane_atomic_duplicate_state,
1044 .atomic_destroy_state = atmel_hlcdc_plane_atomic_destroy_state,
1045 .atomic_set_property = atmel_hlcdc_plane_atomic_set_property,
1046 .atomic_get_property = atmel_hlcdc_plane_atomic_get_property,
1047};
1048
1049static int atmel_hlcdc_plane_create(struct drm_device *dev,
1050 const struct atmel_hlcdc_layer_desc *desc,
1051 struct atmel_hlcdc_plane_properties *props)
1052{
1053 struct atmel_hlcdc_dc *dc = dev->dev_private;
1054 struct atmel_hlcdc_plane *plane;
1055 enum drm_plane_type type;
1056 int ret;
1057
1058 plane = devm_kzalloc(dev->dev, sizeof(*plane), GFP_KERNEL);
1059 if (!plane)
1060 return -ENOMEM;
1061
1062 atmel_hlcdc_layer_init(&plane->layer, desc, dc->hlcdc->regmap);
1063 plane->properties = props;
1064
1065 if (desc->type == ATMEL_HLCDC_BASE_LAYER)
1066 type = DRM_PLANE_TYPE_PRIMARY;
1067 else if (desc->type == ATMEL_HLCDC_CURSOR_LAYER)
1068 type = DRM_PLANE_TYPE_CURSOR;
1069 else
1070 type = DRM_PLANE_TYPE_OVERLAY;
1071
1072 ret = drm_universal_plane_init(dev, &plane->base, 0,
1073 &layer_plane_funcs,
1074 desc->formats->formats,
1075 desc->formats->nformats,
1076 NULL, type, NULL);
1077 if (ret)
1078 return ret;
1079
1080 drm_plane_helper_add(&plane->base,
1081 &atmel_hlcdc_layer_plane_helper_funcs);
1082
1083 /* Set default property values*/
1084 ret = atmel_hlcdc_plane_init_properties(plane, props);
1085 if (ret)
1086 return ret;
1087
1088 dc->layers[desc->id] = &plane->layer;
1089
1090 return 0;
1091}
1092
1093static struct atmel_hlcdc_plane_properties *
1094atmel_hlcdc_plane_create_properties(struct drm_device *dev)
1095{
1096 struct atmel_hlcdc_plane_properties *props;
1097
1098 props = devm_kzalloc(dev->dev, sizeof(*props), GFP_KERNEL);
1099 if (!props)
1100 return ERR_PTR(-ENOMEM);
1101
1102 props->alpha = drm_property_create_range(dev, 0, "alpha", 0, 255);
1103 if (!props->alpha)
1104 return ERR_PTR(-ENOMEM);
1105
1106 return props;
1107}
1108
1109int atmel_hlcdc_create_planes(struct drm_device *dev)
1110{
1111 struct atmel_hlcdc_dc *dc = dev->dev_private;
1112 struct atmel_hlcdc_plane_properties *props;
1113 const struct atmel_hlcdc_layer_desc *descs = dc->desc->layers;
1114 int nlayers = dc->desc->nlayers;
1115 int i, ret;
1116
1117 props = atmel_hlcdc_plane_create_properties(dev);
1118 if (IS_ERR(props))
1119 return PTR_ERR(props);
1120
1121 dc->dscrpool = dmam_pool_create("atmel-hlcdc-dscr", dev->dev,
1122 sizeof(struct atmel_hlcdc_dma_channel_dscr),
1123 sizeof(u64), 0);
1124 if (!dc->dscrpool)
1125 return -ENOMEM;
1126
1127 for (i = 0; i < nlayers; i++) {
1128 if (descs[i].type != ATMEL_HLCDC_BASE_LAYER &&
1129 descs[i].type != ATMEL_HLCDC_OVERLAY_LAYER &&
1130 descs[i].type != ATMEL_HLCDC_CURSOR_LAYER)
1131 continue;
1132
1133 ret = atmel_hlcdc_plane_create(dev, &descs[i], props);
1134 if (ret)
1135 return ret;
1136 }
1137
1138 return 0;
1139}