2 * Copyright (C) 2014 Free Electrons
3 * Copyright (C) 2014 Atmel
5 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
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.
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
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/>.
20 #include "atmel_hlcdc_dc.h"
23 * Atmel HLCDC Plane state structure.
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 * @prepared: plane update has been prepared
42 struct atmel_hlcdc_plane_state
{
43 struct drm_plane_state base
;
64 /* These fields are private and should not be touched */
65 int bpp
[ATMEL_HLCDC_MAX_PLANES
];
66 unsigned int offsets
[ATMEL_HLCDC_MAX_PLANES
];
67 int xstride
[ATMEL_HLCDC_MAX_PLANES
];
68 int pstride
[ATMEL_HLCDC_MAX_PLANES
];
73 static inline struct atmel_hlcdc_plane_state
*
74 drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state
*s
)
76 return container_of(s
, struct atmel_hlcdc_plane_state
, base
);
79 #define SUBPIXEL_MASK 0xffff
81 static uint32_t rgb_formats
[] = {
93 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats
= {
94 .formats
= rgb_formats
,
95 .nformats
= ARRAY_SIZE(rgb_formats
),
98 static uint32_t rgb_and_yuv_formats
[] = {
119 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats
= {
120 .formats
= rgb_and_yuv_formats
,
121 .nformats
= ARRAY_SIZE(rgb_and_yuv_formats
),
124 static int atmel_hlcdc_format_to_plane_mode(u32 format
, u32
*mode
)
127 case DRM_FORMAT_XRGB4444
:
128 *mode
= ATMEL_HLCDC_XRGB4444_MODE
;
130 case DRM_FORMAT_ARGB4444
:
131 *mode
= ATMEL_HLCDC_ARGB4444_MODE
;
133 case DRM_FORMAT_RGBA4444
:
134 *mode
= ATMEL_HLCDC_RGBA4444_MODE
;
136 case DRM_FORMAT_RGB565
:
137 *mode
= ATMEL_HLCDC_RGB565_MODE
;
139 case DRM_FORMAT_RGB888
:
140 *mode
= ATMEL_HLCDC_RGB888_MODE
;
142 case DRM_FORMAT_ARGB1555
:
143 *mode
= ATMEL_HLCDC_ARGB1555_MODE
;
145 case DRM_FORMAT_XRGB8888
:
146 *mode
= ATMEL_HLCDC_XRGB8888_MODE
;
148 case DRM_FORMAT_ARGB8888
:
149 *mode
= ATMEL_HLCDC_ARGB8888_MODE
;
151 case DRM_FORMAT_RGBA8888
:
152 *mode
= ATMEL_HLCDC_RGBA8888_MODE
;
154 case DRM_FORMAT_AYUV
:
155 *mode
= ATMEL_HLCDC_AYUV_MODE
;
157 case DRM_FORMAT_YUYV
:
158 *mode
= ATMEL_HLCDC_YUYV_MODE
;
160 case DRM_FORMAT_UYVY
:
161 *mode
= ATMEL_HLCDC_UYVY_MODE
;
163 case DRM_FORMAT_YVYU
:
164 *mode
= ATMEL_HLCDC_YVYU_MODE
;
166 case DRM_FORMAT_VYUY
:
167 *mode
= ATMEL_HLCDC_VYUY_MODE
;
169 case DRM_FORMAT_NV21
:
170 *mode
= ATMEL_HLCDC_NV21_MODE
;
172 case DRM_FORMAT_NV61
:
173 *mode
= ATMEL_HLCDC_NV61_MODE
;
175 case DRM_FORMAT_YUV420
:
176 *mode
= ATMEL_HLCDC_YUV420_MODE
;
178 case DRM_FORMAT_YUV422
:
179 *mode
= ATMEL_HLCDC_YUV422_MODE
;
188 static bool atmel_hlcdc_format_embeds_alpha(u32 format
)
192 for (i
= 0; i
< sizeof(format
); i
++) {
193 char tmp
= (format
>> (8 * i
)) & 0xff;
202 static u32 heo_downscaling_xcoef
[] = {
221 static u32 heo_downscaling_ycoef
[] = {
232 static u32 heo_upscaling_xcoef
[] = {
251 static u32 heo_upscaling_ycoef
[] = {
263 atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane
*plane
,
264 struct atmel_hlcdc_plane_state
*state
)
266 const struct atmel_hlcdc_layer_cfg_layout
*layout
=
267 &plane
->layer
.desc
->layout
;
270 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
273 (state
->crtc_w
- 1) |
274 ((state
->crtc_h
- 1) << 16));
277 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
281 ((state
->src_h
- 1) << 16));
284 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
288 (state
->crtc_y
<< 16));
290 /* TODO: rework the rescaling part */
291 if (state
->crtc_w
!= state
->src_w
|| state
->crtc_h
!= state
->src_h
) {
294 if (state
->crtc_w
!= state
->src_w
) {
297 u32
*coeff_tab
= heo_upscaling_xcoef
;
300 if (state
->crtc_w
< state
->src_w
)
301 coeff_tab
= heo_downscaling_xcoef
;
302 for (i
= 0; i
< ARRAY_SIZE(heo_upscaling_xcoef
); i
++)
303 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
307 factor
= ((8 * 256 * state
->src_w
) - (256 * 4)) /
310 max_memsize
= ((factor
* state
->crtc_w
) + (256 * 4)) /
312 if (max_memsize
> state
->src_w
)
314 factor_reg
|= factor
| 0x80000000;
317 if (state
->crtc_h
!= state
->src_h
) {
320 u32
*coeff_tab
= heo_upscaling_ycoef
;
323 if (state
->crtc_h
< state
->src_h
)
324 coeff_tab
= heo_downscaling_ycoef
;
325 for (i
= 0; i
< ARRAY_SIZE(heo_upscaling_ycoef
); i
++)
326 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
330 factor
= ((8 * 256 * state
->src_h
) - (256 * 4)) /
333 max_memsize
= ((factor
* state
->crtc_h
) + (256 * 4)) /
335 if (max_memsize
> state
->src_h
)
337 factor_reg
|= (factor
<< 16) | 0x80000000;
340 atmel_hlcdc_layer_update_cfg(&plane
->layer
, 13, 0xffffffff,
343 atmel_hlcdc_layer_update_cfg(&plane
->layer
, 13, 0xffffffff, 0);
348 atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane
*plane
,
349 struct atmel_hlcdc_plane_state
*state
)
351 const struct atmel_hlcdc_layer_cfg_layout
*layout
=
352 &plane
->layer
.desc
->layout
;
353 unsigned int cfg
= ATMEL_HLCDC_LAYER_DMA
;
355 if (plane
->base
.type
!= DRM_PLANE_TYPE_PRIMARY
) {
356 cfg
|= ATMEL_HLCDC_LAYER_OVR
| ATMEL_HLCDC_LAYER_ITER2BL
|
357 ATMEL_HLCDC_LAYER_ITER
;
359 if (atmel_hlcdc_format_embeds_alpha(state
->base
.fb
->pixel_format
))
360 cfg
|= ATMEL_HLCDC_LAYER_LAEN
;
362 cfg
|= ATMEL_HLCDC_LAYER_GAEN
|
363 ATMEL_HLCDC_LAYER_GA(state
->alpha
);
366 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
367 ATMEL_HLCDC_LAYER_DMA_CFG_ID
,
368 ATMEL_HLCDC_LAYER_DMA_BLEN_MASK
|
369 ATMEL_HLCDC_LAYER_DMA_SIF
,
370 ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16
|
373 atmel_hlcdc_layer_update_cfg(&plane
->layer
, layout
->general_config
,
374 ATMEL_HLCDC_LAYER_ITER2BL
|
375 ATMEL_HLCDC_LAYER_ITER
|
376 ATMEL_HLCDC_LAYER_GAEN
|
377 ATMEL_HLCDC_LAYER_GA_MASK
|
378 ATMEL_HLCDC_LAYER_LAEN
|
379 ATMEL_HLCDC_LAYER_OVR
|
380 ATMEL_HLCDC_LAYER_DMA
, cfg
);
383 static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane
*plane
,
384 struct atmel_hlcdc_plane_state
*state
)
389 ret
= atmel_hlcdc_format_to_plane_mode(state
->base
.fb
->pixel_format
,
394 if ((state
->base
.fb
->pixel_format
== DRM_FORMAT_YUV422
||
395 state
->base
.fb
->pixel_format
== DRM_FORMAT_NV61
) &&
396 drm_rotation_90_or_270(state
->base
.rotation
))
397 cfg
|= ATMEL_HLCDC_YUV422ROT
;
399 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
400 ATMEL_HLCDC_LAYER_FORMAT_CFG_ID
,
405 * Rotation optimization is not working on RGB888 (rotation is still
406 * working but without any optimization).
408 if (state
->base
.fb
->pixel_format
== DRM_FORMAT_RGB888
)
409 cfg
= ATMEL_HLCDC_LAYER_DMA_ROTDIS
;
413 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
414 ATMEL_HLCDC_LAYER_DMA_CFG_ID
,
415 ATMEL_HLCDC_LAYER_DMA_ROTDIS
, cfg
);
418 static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane
*plane
,
419 struct atmel_hlcdc_plane_state
*state
)
421 struct atmel_hlcdc_layer
*layer
= &plane
->layer
;
422 const struct atmel_hlcdc_layer_cfg_layout
*layout
=
423 &layer
->desc
->layout
;
426 atmel_hlcdc_layer_update_set_fb(&plane
->layer
, state
->base
.fb
,
429 for (i
= 0; i
< state
->nplanes
; i
++) {
430 if (layout
->xstride
[i
]) {
431 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
437 if (layout
->pstride
[i
]) {
438 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
446 int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state
*c_state
)
448 unsigned int ahb_load
[2] = { };
449 struct drm_plane
*plane
;
451 drm_atomic_crtc_state_for_each_plane(plane
, c_state
) {
452 struct atmel_hlcdc_plane_state
*plane_state
;
453 struct drm_plane_state
*plane_s
;
454 unsigned int pixels
, load
= 0;
457 plane_s
= drm_atomic_get_plane_state(c_state
->state
, plane
);
459 return PTR_ERR(plane_s
);
462 drm_plane_state_to_atmel_hlcdc_plane_state(plane_s
);
464 pixels
= (plane_state
->src_w
* plane_state
->src_h
) -
465 (plane_state
->disc_w
* plane_state
->disc_h
);
467 for (i
= 0; i
< plane_state
->nplanes
; i
++)
468 load
+= pixels
* plane_state
->bpp
[i
];
470 if (ahb_load
[0] <= ahb_load
[1])
471 plane_state
->ahb_id
= 0;
473 plane_state
->ahb_id
= 1;
475 ahb_load
[plane_state
->ahb_id
] += load
;
482 atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state
*c_state
)
484 int disc_x
= 0, disc_y
= 0, disc_w
= 0, disc_h
= 0;
485 const struct atmel_hlcdc_layer_cfg_layout
*layout
;
486 struct atmel_hlcdc_plane_state
*primary_state
;
487 struct drm_plane_state
*primary_s
;
488 struct atmel_hlcdc_plane
*primary
;
489 struct drm_plane
*ovl
;
491 primary
= drm_plane_to_atmel_hlcdc_plane(c_state
->crtc
->primary
);
492 layout
= &primary
->layer
.desc
->layout
;
493 if (!layout
->disc_pos
|| !layout
->disc_size
)
496 primary_s
= drm_atomic_get_plane_state(c_state
->state
,
498 if (IS_ERR(primary_s
))
499 return PTR_ERR(primary_s
);
501 primary_state
= drm_plane_state_to_atmel_hlcdc_plane_state(primary_s
);
503 drm_atomic_crtc_state_for_each_plane(ovl
, c_state
) {
504 struct atmel_hlcdc_plane_state
*ovl_state
;
505 struct drm_plane_state
*ovl_s
;
507 if (ovl
== c_state
->crtc
->primary
)
510 ovl_s
= drm_atomic_get_plane_state(c_state
->state
, ovl
);
512 return PTR_ERR(ovl_s
);
514 ovl_state
= drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s
);
517 atmel_hlcdc_format_embeds_alpha(ovl_s
->fb
->pixel_format
) ||
518 ovl_state
->alpha
!= 255)
521 /* TODO: implement a smarter hidden area detection */
522 if (ovl_state
->crtc_h
* ovl_state
->crtc_w
< disc_h
* disc_w
)
525 disc_x
= ovl_state
->crtc_x
;
526 disc_y
= ovl_state
->crtc_y
;
527 disc_h
= ovl_state
->crtc_h
;
528 disc_w
= ovl_state
->crtc_w
;
531 if (disc_x
== primary_state
->disc_x
&&
532 disc_y
== primary_state
->disc_y
&&
533 disc_w
== primary_state
->disc_w
&&
534 disc_h
== primary_state
->disc_h
)
538 primary_state
->disc_x
= disc_x
;
539 primary_state
->disc_y
= disc_y
;
540 primary_state
->disc_w
= disc_w
;
541 primary_state
->disc_h
= disc_h
;
542 primary_state
->disc_updated
= true;
548 atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane
*plane
,
549 struct atmel_hlcdc_plane_state
*state
)
551 const struct atmel_hlcdc_layer_cfg_layout
*layout
=
552 &plane
->layer
.desc
->layout
;
553 int disc_surface
= 0;
555 if (!state
->disc_updated
)
558 disc_surface
= state
->disc_h
* state
->disc_w
;
560 atmel_hlcdc_layer_update_cfg(&plane
->layer
, layout
->general_config
,
561 ATMEL_HLCDC_LAYER_DISCEN
,
562 disc_surface
? ATMEL_HLCDC_LAYER_DISCEN
: 0);
567 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
570 state
->disc_x
| (state
->disc_y
<< 16));
572 atmel_hlcdc_layer_update_cfg(&plane
->layer
,
575 (state
->disc_w
- 1) |
576 ((state
->disc_h
- 1) << 16));
579 static int atmel_hlcdc_plane_atomic_check(struct drm_plane
*p
,
580 struct drm_plane_state
*s
)
582 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
583 struct atmel_hlcdc_plane_state
*state
=
584 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
585 const struct atmel_hlcdc_layer_cfg_layout
*layout
=
586 &plane
->layer
.desc
->layout
;
587 struct drm_framebuffer
*fb
= state
->base
.fb
;
588 const struct drm_display_mode
*mode
;
589 struct drm_crtc_state
*crtc_state
;
590 unsigned int patched_crtc_w
;
591 unsigned int patched_crtc_h
;
592 unsigned int patched_src_w
;
593 unsigned int patched_src_h
;
601 if (!state
->base
.crtc
|| !fb
)
604 crtc_state
= drm_atomic_get_existing_crtc_state(s
->state
, s
->crtc
);
605 mode
= &crtc_state
->adjusted_mode
;
607 state
->src_x
= s
->src_x
;
608 state
->src_y
= s
->src_y
;
609 state
->src_h
= s
->src_h
;
610 state
->src_w
= s
->src_w
;
611 state
->crtc_x
= s
->crtc_x
;
612 state
->crtc_y
= s
->crtc_y
;
613 state
->crtc_h
= s
->crtc_h
;
614 state
->crtc_w
= s
->crtc_w
;
615 if ((state
->src_x
| state
->src_y
| state
->src_w
| state
->src_h
) &
624 state
->nplanes
= drm_format_num_planes(fb
->pixel_format
);
625 if (state
->nplanes
> ATMEL_HLCDC_MAX_PLANES
)
629 * Swap width and size in case of 90 or 270 degrees rotation
631 if (drm_rotation_90_or_270(state
->base
.rotation
)) {
633 state
->crtc_w
= state
->crtc_h
;
636 state
->src_w
= state
->src_h
;
640 if (state
->crtc_x
+ state
->crtc_w
> mode
->hdisplay
)
641 patched_crtc_w
= mode
->hdisplay
- state
->crtc_x
;
643 patched_crtc_w
= state
->crtc_w
;
645 if (state
->crtc_x
< 0) {
646 patched_crtc_w
+= state
->crtc_x
;
647 x_offset
= -state
->crtc_x
;
651 if (state
->crtc_y
+ state
->crtc_h
> mode
->vdisplay
)
652 patched_crtc_h
= mode
->vdisplay
- state
->crtc_y
;
654 patched_crtc_h
= state
->crtc_h
;
656 if (state
->crtc_y
< 0) {
657 patched_crtc_h
+= state
->crtc_y
;
658 y_offset
= -state
->crtc_y
;
662 patched_src_w
= DIV_ROUND_CLOSEST(patched_crtc_w
* state
->src_w
,
664 patched_src_h
= DIV_ROUND_CLOSEST(patched_crtc_h
* state
->src_h
,
667 hsub
= drm_format_horz_chroma_subsampling(fb
->pixel_format
);
668 vsub
= drm_format_vert_chroma_subsampling(fb
->pixel_format
);
670 for (i
= 0; i
< state
->nplanes
; i
++) {
671 unsigned int offset
= 0;
672 int xdiv
= i
? hsub
: 1;
673 int ydiv
= i
? vsub
: 1;
675 state
->bpp
[i
] = drm_format_plane_cpp(fb
->pixel_format
, i
);
679 switch (state
->base
.rotation
& DRM_ROTATE_MASK
) {
681 offset
= ((y_offset
+ state
->src_y
+ patched_src_w
- 1) /
682 ydiv
) * fb
->pitches
[i
];
683 offset
+= ((x_offset
+ state
->src_x
) / xdiv
) *
685 state
->xstride
[i
] = ((patched_src_w
- 1) / ydiv
) *
687 state
->pstride
[i
] = -fb
->pitches
[i
] - state
->bpp
[i
];
690 offset
= ((y_offset
+ state
->src_y
+ patched_src_h
- 1) /
691 ydiv
) * fb
->pitches
[i
];
692 offset
+= ((x_offset
+ state
->src_x
+ patched_src_w
- 1) /
693 xdiv
) * state
->bpp
[i
];
694 state
->xstride
[i
] = ((((patched_src_w
- 1) / xdiv
) - 1) *
695 state
->bpp
[i
]) - fb
->pitches
[i
];
696 state
->pstride
[i
] = -2 * state
->bpp
[i
];
699 offset
= ((y_offset
+ state
->src_y
) / ydiv
) *
701 offset
+= ((x_offset
+ state
->src_x
+ patched_src_h
- 1) /
702 xdiv
) * state
->bpp
[i
];
703 state
->xstride
[i
] = -(((patched_src_w
- 1) / ydiv
) *
706 state
->pstride
[i
] = fb
->pitches
[i
] - state
->bpp
[i
];
710 offset
= ((y_offset
+ state
->src_y
) / ydiv
) *
712 offset
+= ((x_offset
+ state
->src_x
) / xdiv
) *
714 state
->xstride
[i
] = fb
->pitches
[i
] -
715 ((patched_src_w
/ xdiv
) *
717 state
->pstride
[i
] = 0;
721 state
->offsets
[i
] = offset
+ fb
->offsets
[i
];
724 state
->src_w
= patched_src_w
;
725 state
->src_h
= patched_src_h
;
726 state
->crtc_w
= patched_crtc_w
;
727 state
->crtc_h
= patched_crtc_h
;
730 (mode
->hdisplay
!= state
->crtc_w
||
731 mode
->vdisplay
!= state
->crtc_h
))
734 if (plane
->layer
.desc
->max_height
&&
735 state
->crtc_h
> plane
->layer
.desc
->max_height
)
738 if (plane
->layer
.desc
->max_width
&&
739 state
->crtc_w
> plane
->layer
.desc
->max_width
)
742 if ((state
->crtc_h
!= state
->src_h
|| state
->crtc_w
!= state
->src_w
) &&
744 atmel_hlcdc_format_embeds_alpha(state
->base
.fb
->pixel_format
)))
747 if (state
->crtc_x
< 0 || state
->crtc_y
< 0)
750 if (state
->crtc_w
+ state
->crtc_x
> mode
->hdisplay
||
751 state
->crtc_h
+ state
->crtc_y
> mode
->vdisplay
)
757 static int atmel_hlcdc_plane_prepare_fb(struct drm_plane
*p
,
758 struct drm_plane_state
*new_state
)
761 * FIXME: we should avoid this const -> non-const cast but it's
762 * currently the only solution we have to modify the ->prepared
763 * state and rollback the update request.
764 * Ideally, we should rework the code to attach all the resources
765 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
766 * but this require a complete rework of the atmel_hlcdc_layer
769 struct drm_plane_state
*s
= (struct drm_plane_state
*)new_state
;
770 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
771 struct atmel_hlcdc_plane_state
*state
=
772 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
775 ret
= atmel_hlcdc_layer_update_start(&plane
->layer
);
777 state
->prepared
= true;
782 static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane
*p
,
783 struct drm_plane_state
*old_state
)
786 * FIXME: we should avoid this const -> non-const cast but it's
787 * currently the only solution we have to modify the ->prepared
788 * state and rollback the update request.
789 * Ideally, we should rework the code to attach all the resources
790 * to atmel_hlcdc_plane_state (including the DMA desc allocation),
791 * but this require a complete rework of the atmel_hlcdc_layer
794 struct drm_plane_state
*s
= (struct drm_plane_state
*)old_state
;
795 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
796 struct atmel_hlcdc_plane_state
*state
=
797 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
800 * The Request has already been applied or cancelled, nothing to do
803 if (!state
->prepared
)
806 atmel_hlcdc_layer_update_rollback(&plane
->layer
);
807 state
->prepared
= false;
810 static void atmel_hlcdc_plane_atomic_update(struct drm_plane
*p
,
811 struct drm_plane_state
*old_s
)
813 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
814 struct atmel_hlcdc_plane_state
*state
=
815 drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
817 if (!p
->state
->crtc
|| !p
->state
->fb
)
820 atmel_hlcdc_plane_update_pos_and_size(plane
, state
);
821 atmel_hlcdc_plane_update_general_settings(plane
, state
);
822 atmel_hlcdc_plane_update_format(plane
, state
);
823 atmel_hlcdc_plane_update_buffers(plane
, state
);
824 atmel_hlcdc_plane_update_disc_area(plane
, state
);
826 atmel_hlcdc_layer_update_commit(&plane
->layer
);
829 static void atmel_hlcdc_plane_atomic_disable(struct drm_plane
*p
,
830 struct drm_plane_state
*old_state
)
832 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
834 atmel_hlcdc_layer_disable(&plane
->layer
);
837 static void atmel_hlcdc_plane_destroy(struct drm_plane
*p
)
839 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
842 drm_framebuffer_unreference(plane
->base
.fb
);
844 atmel_hlcdc_layer_cleanup(p
->dev
, &plane
->layer
);
846 drm_plane_cleanup(p
);
847 devm_kfree(p
->dev
->dev
, plane
);
850 static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane
*p
,
851 struct drm_plane_state
*s
,
852 struct drm_property
*property
,
855 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
856 struct atmel_hlcdc_plane_properties
*props
= plane
->properties
;
857 struct atmel_hlcdc_plane_state
*state
=
858 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
860 if (property
== props
->alpha
)
868 static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane
*p
,
869 const struct drm_plane_state
*s
,
870 struct drm_property
*property
,
873 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
874 struct atmel_hlcdc_plane_properties
*props
= plane
->properties
;
875 const struct atmel_hlcdc_plane_state
*state
=
876 container_of(s
, const struct atmel_hlcdc_plane_state
, base
);
878 if (property
== props
->alpha
)
886 static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane
*plane
,
887 const struct atmel_hlcdc_layer_desc
*desc
,
888 struct atmel_hlcdc_plane_properties
*props
)
890 struct regmap
*regmap
= plane
->layer
.hlcdc
->regmap
;
892 if (desc
->type
== ATMEL_HLCDC_OVERLAY_LAYER
||
893 desc
->type
== ATMEL_HLCDC_CURSOR_LAYER
) {
894 drm_object_attach_property(&plane
->base
.base
,
897 /* Set default alpha value */
898 regmap_update_bits(regmap
,
900 ATMEL_HLCDC_LAYER_GENERAL_CFG(&plane
->layer
),
901 ATMEL_HLCDC_LAYER_GA_MASK
,
902 ATMEL_HLCDC_LAYER_GA_MASK
);
905 if (desc
->layout
.xstride
&& desc
->layout
.pstride
) {
908 ret
= drm_plane_create_rotation_property(&plane
->base
,
918 if (desc
->layout
.csc
) {
920 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
921 * userspace modify these factors (using a BLOB property ?).
925 ATMEL_HLCDC_LAYER_CSC_CFG(&plane
->layer
, 0),
929 ATMEL_HLCDC_LAYER_CSC_CFG(&plane
->layer
, 1),
933 ATMEL_HLCDC_LAYER_CSC_CFG(&plane
->layer
, 2),
940 static struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs
= {
941 .prepare_fb
= atmel_hlcdc_plane_prepare_fb
,
942 .cleanup_fb
= atmel_hlcdc_plane_cleanup_fb
,
943 .atomic_check
= atmel_hlcdc_plane_atomic_check
,
944 .atomic_update
= atmel_hlcdc_plane_atomic_update
,
945 .atomic_disable
= atmel_hlcdc_plane_atomic_disable
,
948 static void atmel_hlcdc_plane_reset(struct drm_plane
*p
)
950 struct atmel_hlcdc_plane_state
*state
;
953 state
= drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
956 drm_framebuffer_unreference(state
->base
.fb
);
962 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
965 p
->state
= &state
->base
;
970 static struct drm_plane_state
*
971 atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane
*p
)
973 struct atmel_hlcdc_plane_state
*state
=
974 drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
975 struct atmel_hlcdc_plane_state
*copy
;
977 copy
= kmemdup(state
, sizeof(*state
), GFP_KERNEL
);
981 copy
->disc_updated
= false;
982 copy
->prepared
= false;
985 drm_framebuffer_reference(copy
->base
.fb
);
990 static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane
*plane
,
991 struct drm_plane_state
*s
)
993 struct atmel_hlcdc_plane_state
*state
=
994 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
997 drm_framebuffer_unreference(s
->fb
);
1002 static struct drm_plane_funcs layer_plane_funcs
= {
1003 .update_plane
= drm_atomic_helper_update_plane
,
1004 .disable_plane
= drm_atomic_helper_disable_plane
,
1005 .set_property
= drm_atomic_helper_plane_set_property
,
1006 .destroy
= atmel_hlcdc_plane_destroy
,
1007 .reset
= atmel_hlcdc_plane_reset
,
1008 .atomic_duplicate_state
= atmel_hlcdc_plane_atomic_duplicate_state
,
1009 .atomic_destroy_state
= atmel_hlcdc_plane_atomic_destroy_state
,
1010 .atomic_set_property
= atmel_hlcdc_plane_atomic_set_property
,
1011 .atomic_get_property
= atmel_hlcdc_plane_atomic_get_property
,
1014 static struct atmel_hlcdc_plane
*
1015 atmel_hlcdc_plane_create(struct drm_device
*dev
,
1016 const struct atmel_hlcdc_layer_desc
*desc
,
1017 struct atmel_hlcdc_plane_properties
*props
)
1019 struct atmel_hlcdc_plane
*plane
;
1020 enum drm_plane_type type
;
1023 plane
= devm_kzalloc(dev
->dev
, sizeof(*plane
), GFP_KERNEL
);
1025 return ERR_PTR(-ENOMEM
);
1027 ret
= atmel_hlcdc_layer_init(dev
, &plane
->layer
, desc
);
1029 return ERR_PTR(ret
);
1031 if (desc
->type
== ATMEL_HLCDC_BASE_LAYER
)
1032 type
= DRM_PLANE_TYPE_PRIMARY
;
1033 else if (desc
->type
== ATMEL_HLCDC_CURSOR_LAYER
)
1034 type
= DRM_PLANE_TYPE_CURSOR
;
1036 type
= DRM_PLANE_TYPE_OVERLAY
;
1038 ret
= drm_universal_plane_init(dev
, &plane
->base
, 0,
1040 desc
->formats
->formats
,
1041 desc
->formats
->nformats
, type
, NULL
);
1043 return ERR_PTR(ret
);
1045 drm_plane_helper_add(&plane
->base
,
1046 &atmel_hlcdc_layer_plane_helper_funcs
);
1048 /* Set default property values*/
1049 ret
= atmel_hlcdc_plane_init_properties(plane
, desc
, props
);
1051 return ERR_PTR(ret
);
1056 static struct atmel_hlcdc_plane_properties
*
1057 atmel_hlcdc_plane_create_properties(struct drm_device
*dev
)
1059 struct atmel_hlcdc_plane_properties
*props
;
1061 props
= devm_kzalloc(dev
->dev
, sizeof(*props
), GFP_KERNEL
);
1063 return ERR_PTR(-ENOMEM
);
1065 props
->alpha
= drm_property_create_range(dev
, 0, "alpha", 0, 255);
1067 return ERR_PTR(-ENOMEM
);
1072 struct atmel_hlcdc_planes
*
1073 atmel_hlcdc_create_planes(struct drm_device
*dev
)
1075 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
1076 struct atmel_hlcdc_plane_properties
*props
;
1077 struct atmel_hlcdc_planes
*planes
;
1078 const struct atmel_hlcdc_layer_desc
*descs
= dc
->desc
->layers
;
1079 int nlayers
= dc
->desc
->nlayers
;
1082 planes
= devm_kzalloc(dev
->dev
, sizeof(*planes
), GFP_KERNEL
);
1084 return ERR_PTR(-ENOMEM
);
1086 for (i
= 0; i
< nlayers
; i
++) {
1087 if (descs
[i
].type
== ATMEL_HLCDC_OVERLAY_LAYER
)
1088 planes
->noverlays
++;
1091 if (planes
->noverlays
) {
1092 planes
->overlays
= devm_kzalloc(dev
->dev
,
1094 sizeof(*planes
->overlays
),
1096 if (!planes
->overlays
)
1097 return ERR_PTR(-ENOMEM
);
1100 props
= atmel_hlcdc_plane_create_properties(dev
);
1102 return ERR_CAST(props
);
1104 planes
->noverlays
= 0;
1105 for (i
= 0; i
< nlayers
; i
++) {
1106 struct atmel_hlcdc_plane
*plane
;
1108 if (descs
[i
].type
== ATMEL_HLCDC_PP_LAYER
)
1111 plane
= atmel_hlcdc_plane_create(dev
, &descs
[i
], props
);
1113 return ERR_CAST(plane
);
1115 plane
->properties
= props
;
1117 switch (descs
[i
].type
) {
1118 case ATMEL_HLCDC_BASE_LAYER
:
1119 if (planes
->primary
)
1120 return ERR_PTR(-EINVAL
);
1121 planes
->primary
= plane
;
1124 case ATMEL_HLCDC_OVERLAY_LAYER
:
1125 planes
->overlays
[planes
->noverlays
++] = plane
;
1128 case ATMEL_HLCDC_CURSOR_LAYER
:
1130 return ERR_PTR(-EINVAL
);
1131 planes
->cursor
= plane
;