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 * @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
46 struct atmel_hlcdc_plane_state
{
47 struct drm_plane_state base
;
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
];
73 /* DMA descriptors. */
74 struct atmel_hlcdc_dma_channel_dscr
*dscrs
[ATMEL_HLCDC_LAYER_MAX_PLANES
];
77 static inline struct atmel_hlcdc_plane_state
*
78 drm_plane_state_to_atmel_hlcdc_plane_state(struct drm_plane_state
*s
)
80 return container_of(s
, struct atmel_hlcdc_plane_state
, base
);
83 #define SUBPIXEL_MASK 0xffff
85 static uint32_t rgb_formats
[] = {
98 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_formats
= {
99 .formats
= rgb_formats
,
100 .nformats
= ARRAY_SIZE(rgb_formats
),
103 static uint32_t rgb_and_yuv_formats
[] = {
125 struct atmel_hlcdc_formats atmel_hlcdc_plane_rgb_and_yuv_formats
= {
126 .formats
= rgb_and_yuv_formats
,
127 .nformats
= ARRAY_SIZE(rgb_and_yuv_formats
),
130 static int atmel_hlcdc_format_to_plane_mode(u32 format
, u32
*mode
)
134 *mode
= ATMEL_HLCDC_C8_MODE
;
136 case DRM_FORMAT_XRGB4444
:
137 *mode
= ATMEL_HLCDC_XRGB4444_MODE
;
139 case DRM_FORMAT_ARGB4444
:
140 *mode
= ATMEL_HLCDC_ARGB4444_MODE
;
142 case DRM_FORMAT_RGBA4444
:
143 *mode
= ATMEL_HLCDC_RGBA4444_MODE
;
145 case DRM_FORMAT_RGB565
:
146 *mode
= ATMEL_HLCDC_RGB565_MODE
;
148 case DRM_FORMAT_RGB888
:
149 *mode
= ATMEL_HLCDC_RGB888_MODE
;
151 case DRM_FORMAT_ARGB1555
:
152 *mode
= ATMEL_HLCDC_ARGB1555_MODE
;
154 case DRM_FORMAT_XRGB8888
:
155 *mode
= ATMEL_HLCDC_XRGB8888_MODE
;
157 case DRM_FORMAT_ARGB8888
:
158 *mode
= ATMEL_HLCDC_ARGB8888_MODE
;
160 case DRM_FORMAT_RGBA8888
:
161 *mode
= ATMEL_HLCDC_RGBA8888_MODE
;
163 case DRM_FORMAT_AYUV
:
164 *mode
= ATMEL_HLCDC_AYUV_MODE
;
166 case DRM_FORMAT_YUYV
:
167 *mode
= ATMEL_HLCDC_YUYV_MODE
;
169 case DRM_FORMAT_UYVY
:
170 *mode
= ATMEL_HLCDC_UYVY_MODE
;
172 case DRM_FORMAT_YVYU
:
173 *mode
= ATMEL_HLCDC_YVYU_MODE
;
175 case DRM_FORMAT_VYUY
:
176 *mode
= ATMEL_HLCDC_VYUY_MODE
;
178 case DRM_FORMAT_NV21
:
179 *mode
= ATMEL_HLCDC_NV21_MODE
;
181 case DRM_FORMAT_NV61
:
182 *mode
= ATMEL_HLCDC_NV61_MODE
;
184 case DRM_FORMAT_YUV420
:
185 *mode
= ATMEL_HLCDC_YUV420_MODE
;
187 case DRM_FORMAT_YUV422
:
188 *mode
= ATMEL_HLCDC_YUV422_MODE
;
197 static bool atmel_hlcdc_format_embeds_alpha(u32 format
)
201 for (i
= 0; i
< sizeof(format
); i
++) {
202 char tmp
= (format
>> (8 * i
)) & 0xff;
211 static u32 heo_downscaling_xcoef
[] = {
230 static u32 heo_downscaling_ycoef
[] = {
241 static u32 heo_upscaling_xcoef
[] = {
260 static u32 heo_upscaling_ycoef
[] = {
271 #define ATMEL_HLCDC_XPHIDEF 4
272 #define ATMEL_HLCDC_YPHIDEF 4
274 static u32
atmel_hlcdc_plane_phiscaler_get_factor(u32 srcsize
,
278 u32 factor
, max_memsize
;
280 factor
= (256 * ((8 * (srcsize
- 1)) - phidef
)) / (dstsize
- 1);
281 max_memsize
= ((factor
* (dstsize
- 1)) + (256 * phidef
)) / 2048;
283 if (max_memsize
> srcsize
- 1)
290 atmel_hlcdc_plane_scaler_set_phicoeff(struct atmel_hlcdc_plane
*plane
,
291 const u32
*coeff_tab
, int size
,
292 unsigned int cfg_offs
)
296 for (i
= 0; i
< size
; i
++)
297 atmel_hlcdc_layer_write_cfg(&plane
->layer
, cfg_offs
+ i
,
301 void atmel_hlcdc_plane_setup_scaler(struct atmel_hlcdc_plane
*plane
,
302 struct atmel_hlcdc_plane_state
*state
)
304 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
305 u32 xfactor
, yfactor
;
307 if (!desc
->layout
.scaler_config
)
310 if (state
->crtc_w
== state
->src_w
&& state
->crtc_h
== state
->src_h
) {
311 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
312 desc
->layout
.scaler_config
, 0);
316 if (desc
->layout
.phicoeffs
.x
) {
317 xfactor
= atmel_hlcdc_plane_phiscaler_get_factor(state
->src_w
,
319 ATMEL_HLCDC_XPHIDEF
);
321 yfactor
= atmel_hlcdc_plane_phiscaler_get_factor(state
->src_h
,
323 ATMEL_HLCDC_YPHIDEF
);
325 atmel_hlcdc_plane_scaler_set_phicoeff(plane
,
326 state
->crtc_w
< state
->src_w
?
327 heo_downscaling_xcoef
:
329 ARRAY_SIZE(heo_upscaling_xcoef
),
330 desc
->layout
.phicoeffs
.x
);
332 atmel_hlcdc_plane_scaler_set_phicoeff(plane
,
333 state
->crtc_h
< state
->src_h
?
334 heo_downscaling_ycoef
:
336 ARRAY_SIZE(heo_upscaling_ycoef
),
337 desc
->layout
.phicoeffs
.y
);
339 xfactor
= (1024 * state
->src_w
) / state
->crtc_w
;
340 yfactor
= (1024 * state
->src_h
) / state
->crtc_h
;
343 atmel_hlcdc_layer_write_cfg(&plane
->layer
, desc
->layout
.scaler_config
,
344 ATMEL_HLCDC_LAYER_SCALER_ENABLE
|
345 ATMEL_HLCDC_LAYER_SCALER_FACTORS(xfactor
,
350 atmel_hlcdc_plane_update_pos_and_size(struct atmel_hlcdc_plane
*plane
,
351 struct atmel_hlcdc_plane_state
*state
)
353 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
355 if (desc
->layout
.size
)
356 atmel_hlcdc_layer_write_cfg(&plane
->layer
, desc
->layout
.size
,
357 ATMEL_HLCDC_LAYER_SIZE(state
->crtc_w
,
360 if (desc
->layout
.memsize
)
361 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
362 desc
->layout
.memsize
,
363 ATMEL_HLCDC_LAYER_SIZE(state
->src_w
,
366 if (desc
->layout
.pos
)
367 atmel_hlcdc_layer_write_cfg(&plane
->layer
, desc
->layout
.pos
,
368 ATMEL_HLCDC_LAYER_POS(state
->crtc_x
,
371 atmel_hlcdc_plane_setup_scaler(plane
, state
);
375 atmel_hlcdc_plane_update_general_settings(struct atmel_hlcdc_plane
*plane
,
376 struct atmel_hlcdc_plane_state
*state
)
378 unsigned int cfg
= ATMEL_HLCDC_LAYER_DMA_BLEN_INCR16
| state
->ahb_id
;
379 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
380 u32 format
= state
->base
.fb
->format
->format
;
383 * Rotation optimization is not working on RGB888 (rotation is still
384 * working but without any optimization).
386 if (format
== DRM_FORMAT_RGB888
)
387 cfg
|= ATMEL_HLCDC_LAYER_DMA_ROTDIS
;
389 atmel_hlcdc_layer_write_cfg(&plane
->layer
, ATMEL_HLCDC_LAYER_DMA_CFG
,
392 cfg
= ATMEL_HLCDC_LAYER_DMA
;
394 if (plane
->base
.type
!= DRM_PLANE_TYPE_PRIMARY
) {
395 cfg
|= ATMEL_HLCDC_LAYER_OVR
| ATMEL_HLCDC_LAYER_ITER2BL
|
396 ATMEL_HLCDC_LAYER_ITER
;
398 if (atmel_hlcdc_format_embeds_alpha(format
))
399 cfg
|= ATMEL_HLCDC_LAYER_LAEN
;
401 cfg
|= ATMEL_HLCDC_LAYER_GAEN
|
402 ATMEL_HLCDC_LAYER_GA(state
->alpha
);
405 if (state
->disc_h
&& state
->disc_w
)
406 cfg
|= ATMEL_HLCDC_LAYER_DISCEN
;
408 atmel_hlcdc_layer_write_cfg(&plane
->layer
, desc
->layout
.general_config
,
412 static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane
*plane
,
413 struct atmel_hlcdc_plane_state
*state
)
418 ret
= atmel_hlcdc_format_to_plane_mode(state
->base
.fb
->format
->format
,
423 if ((state
->base
.fb
->format
->format
== DRM_FORMAT_YUV422
||
424 state
->base
.fb
->format
->format
== DRM_FORMAT_NV61
) &&
425 drm_rotation_90_or_270(state
->base
.rotation
))
426 cfg
|= ATMEL_HLCDC_YUV422ROT
;
428 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
429 ATMEL_HLCDC_LAYER_FORMAT_CFG
, cfg
);
432 static void atmel_hlcdc_plane_update_clut(struct atmel_hlcdc_plane
*plane
)
434 struct drm_crtc
*crtc
= plane
->base
.crtc
;
435 struct drm_color_lut
*lut
;
438 if (!crtc
|| !crtc
->state
)
441 if (!crtc
->state
->color_mgmt_changed
|| !crtc
->state
->gamma_lut
)
444 lut
= (struct drm_color_lut
*)crtc
->state
->gamma_lut
->data
;
446 for (idx
= 0; idx
< ATMEL_HLCDC_CLUT_SIZE
; idx
++, lut
++) {
447 u32 val
= ((lut
->red
<< 8) & 0xff0000) |
448 (lut
->green
& 0xff00) |
451 atmel_hlcdc_layer_write_clut(&plane
->layer
, idx
, val
);
455 static void atmel_hlcdc_plane_update_buffers(struct atmel_hlcdc_plane
*plane
,
456 struct atmel_hlcdc_plane_state
*state
)
458 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
459 struct drm_framebuffer
*fb
= state
->base
.fb
;
463 sr
= atmel_hlcdc_layer_read_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_CHSR
);
465 for (i
= 0; i
< state
->nplanes
; i
++) {
466 struct drm_gem_cma_object
*gem
= drm_fb_cma_get_gem_obj(fb
, i
);
468 state
->dscrs
[i
]->addr
= gem
->paddr
+ state
->offsets
[i
];
470 atmel_hlcdc_layer_write_reg(&plane
->layer
,
471 ATMEL_HLCDC_LAYER_PLANE_HEAD(i
),
472 state
->dscrs
[i
]->self
);
474 if (!(sr
& ATMEL_HLCDC_LAYER_EN
)) {
475 atmel_hlcdc_layer_write_reg(&plane
->layer
,
476 ATMEL_HLCDC_LAYER_PLANE_ADDR(i
),
477 state
->dscrs
[i
]->addr
);
478 atmel_hlcdc_layer_write_reg(&plane
->layer
,
479 ATMEL_HLCDC_LAYER_PLANE_CTRL(i
),
480 state
->dscrs
[i
]->ctrl
);
481 atmel_hlcdc_layer_write_reg(&plane
->layer
,
482 ATMEL_HLCDC_LAYER_PLANE_NEXT(i
),
483 state
->dscrs
[i
]->self
);
486 if (desc
->layout
.xstride
[i
])
487 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
488 desc
->layout
.xstride
[i
],
491 if (desc
->layout
.pstride
[i
])
492 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
493 desc
->layout
.pstride
[i
],
498 int atmel_hlcdc_plane_prepare_ahb_routing(struct drm_crtc_state
*c_state
)
500 unsigned int ahb_load
[2] = { };
501 struct drm_plane
*plane
;
503 drm_atomic_crtc_state_for_each_plane(plane
, c_state
) {
504 struct atmel_hlcdc_plane_state
*plane_state
;
505 struct drm_plane_state
*plane_s
;
506 unsigned int pixels
, load
= 0;
509 plane_s
= drm_atomic_get_plane_state(c_state
->state
, plane
);
511 return PTR_ERR(plane_s
);
514 drm_plane_state_to_atmel_hlcdc_plane_state(plane_s
);
516 pixels
= (plane_state
->src_w
* plane_state
->src_h
) -
517 (plane_state
->disc_w
* plane_state
->disc_h
);
519 for (i
= 0; i
< plane_state
->nplanes
; i
++)
520 load
+= pixels
* plane_state
->bpp
[i
];
522 if (ahb_load
[0] <= ahb_load
[1])
523 plane_state
->ahb_id
= 0;
525 plane_state
->ahb_id
= 1;
527 ahb_load
[plane_state
->ahb_id
] += load
;
534 atmel_hlcdc_plane_prepare_disc_area(struct drm_crtc_state
*c_state
)
536 int disc_x
= 0, disc_y
= 0, disc_w
= 0, disc_h
= 0;
537 const struct atmel_hlcdc_layer_cfg_layout
*layout
;
538 struct atmel_hlcdc_plane_state
*primary_state
;
539 struct drm_plane_state
*primary_s
;
540 struct atmel_hlcdc_plane
*primary
;
541 struct drm_plane
*ovl
;
543 primary
= drm_plane_to_atmel_hlcdc_plane(c_state
->crtc
->primary
);
544 layout
= &primary
->layer
.desc
->layout
;
545 if (!layout
->disc_pos
|| !layout
->disc_size
)
548 primary_s
= drm_atomic_get_plane_state(c_state
->state
,
550 if (IS_ERR(primary_s
))
551 return PTR_ERR(primary_s
);
553 primary_state
= drm_plane_state_to_atmel_hlcdc_plane_state(primary_s
);
555 drm_atomic_crtc_state_for_each_plane(ovl
, c_state
) {
556 struct atmel_hlcdc_plane_state
*ovl_state
;
557 struct drm_plane_state
*ovl_s
;
559 if (ovl
== c_state
->crtc
->primary
)
562 ovl_s
= drm_atomic_get_plane_state(c_state
->state
, ovl
);
564 return PTR_ERR(ovl_s
);
566 ovl_state
= drm_plane_state_to_atmel_hlcdc_plane_state(ovl_s
);
569 atmel_hlcdc_format_embeds_alpha(ovl_s
->fb
->format
->format
) ||
570 ovl_state
->alpha
!= 255)
573 /* TODO: implement a smarter hidden area detection */
574 if (ovl_state
->crtc_h
* ovl_state
->crtc_w
< disc_h
* disc_w
)
577 disc_x
= ovl_state
->crtc_x
;
578 disc_y
= ovl_state
->crtc_y
;
579 disc_h
= ovl_state
->crtc_h
;
580 disc_w
= ovl_state
->crtc_w
;
583 primary_state
->disc_x
= disc_x
;
584 primary_state
->disc_y
= disc_y
;
585 primary_state
->disc_w
= disc_w
;
586 primary_state
->disc_h
= disc_h
;
592 atmel_hlcdc_plane_update_disc_area(struct atmel_hlcdc_plane
*plane
,
593 struct atmel_hlcdc_plane_state
*state
)
595 const struct atmel_hlcdc_layer_cfg_layout
*layout
;
597 layout
= &plane
->layer
.desc
->layout
;
598 if (!layout
->disc_pos
|| !layout
->disc_size
)
601 atmel_hlcdc_layer_write_cfg(&plane
->layer
, layout
->disc_pos
,
602 ATMEL_HLCDC_LAYER_DISC_POS(state
->disc_x
,
605 atmel_hlcdc_layer_write_cfg(&plane
->layer
, layout
->disc_size
,
606 ATMEL_HLCDC_LAYER_DISC_SIZE(state
->disc_w
,
610 static int atmel_hlcdc_plane_atomic_check(struct drm_plane
*p
,
611 struct drm_plane_state
*s
)
613 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
614 struct atmel_hlcdc_plane_state
*state
=
615 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
616 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
617 struct drm_framebuffer
*fb
= state
->base
.fb
;
618 const struct drm_display_mode
*mode
;
619 struct drm_crtc_state
*crtc_state
;
620 unsigned int patched_crtc_w
;
621 unsigned int patched_crtc_h
;
622 unsigned int patched_src_w
;
623 unsigned int patched_src_h
;
631 if (!state
->base
.crtc
|| !fb
)
634 crtc_state
= drm_atomic_get_existing_crtc_state(s
->state
, s
->crtc
);
635 mode
= &crtc_state
->adjusted_mode
;
637 state
->src_x
= s
->src_x
;
638 state
->src_y
= s
->src_y
;
639 state
->src_h
= s
->src_h
;
640 state
->src_w
= s
->src_w
;
641 state
->crtc_x
= s
->crtc_x
;
642 state
->crtc_y
= s
->crtc_y
;
643 state
->crtc_h
= s
->crtc_h
;
644 state
->crtc_w
= s
->crtc_w
;
645 if ((state
->src_x
| state
->src_y
| state
->src_w
| state
->src_h
) &
654 state
->nplanes
= fb
->format
->num_planes
;
655 if (state
->nplanes
> ATMEL_HLCDC_LAYER_MAX_PLANES
)
659 * Swap width and size in case of 90 or 270 degrees rotation
661 if (drm_rotation_90_or_270(state
->base
.rotation
)) {
663 state
->crtc_w
= state
->crtc_h
;
666 state
->src_w
= state
->src_h
;
670 if (state
->crtc_x
+ state
->crtc_w
> mode
->hdisplay
)
671 patched_crtc_w
= mode
->hdisplay
- state
->crtc_x
;
673 patched_crtc_w
= state
->crtc_w
;
675 if (state
->crtc_x
< 0) {
676 patched_crtc_w
+= state
->crtc_x
;
677 x_offset
= -state
->crtc_x
;
681 if (state
->crtc_y
+ state
->crtc_h
> mode
->vdisplay
)
682 patched_crtc_h
= mode
->vdisplay
- state
->crtc_y
;
684 patched_crtc_h
= state
->crtc_h
;
686 if (state
->crtc_y
< 0) {
687 patched_crtc_h
+= state
->crtc_y
;
688 y_offset
= -state
->crtc_y
;
692 patched_src_w
= DIV_ROUND_CLOSEST(patched_crtc_w
* state
->src_w
,
694 patched_src_h
= DIV_ROUND_CLOSEST(patched_crtc_h
* state
->src_h
,
697 hsub
= drm_format_horz_chroma_subsampling(fb
->format
->format
);
698 vsub
= drm_format_vert_chroma_subsampling(fb
->format
->format
);
700 for (i
= 0; i
< state
->nplanes
; i
++) {
701 unsigned int offset
= 0;
702 int xdiv
= i
? hsub
: 1;
703 int ydiv
= i
? vsub
: 1;
705 state
->bpp
[i
] = fb
->format
->cpp
[i
];
709 switch (state
->base
.rotation
& DRM_MODE_ROTATE_MASK
) {
710 case DRM_MODE_ROTATE_90
:
711 offset
= ((y_offset
+ state
->src_y
+ patched_src_w
- 1) /
712 ydiv
) * fb
->pitches
[i
];
713 offset
+= ((x_offset
+ state
->src_x
) / xdiv
) *
715 state
->xstride
[i
] = ((patched_src_w
- 1) / ydiv
) *
717 state
->pstride
[i
] = -fb
->pitches
[i
] - state
->bpp
[i
];
719 case DRM_MODE_ROTATE_180
:
720 offset
= ((y_offset
+ state
->src_y
+ patched_src_h
- 1) /
721 ydiv
) * fb
->pitches
[i
];
722 offset
+= ((x_offset
+ state
->src_x
+ patched_src_w
- 1) /
723 xdiv
) * state
->bpp
[i
];
724 state
->xstride
[i
] = ((((patched_src_w
- 1) / xdiv
) - 1) *
725 state
->bpp
[i
]) - fb
->pitches
[i
];
726 state
->pstride
[i
] = -2 * state
->bpp
[i
];
728 case DRM_MODE_ROTATE_270
:
729 offset
= ((y_offset
+ state
->src_y
) / ydiv
) *
731 offset
+= ((x_offset
+ state
->src_x
+ patched_src_h
- 1) /
732 xdiv
) * state
->bpp
[i
];
733 state
->xstride
[i
] = -(((patched_src_w
- 1) / ydiv
) *
736 state
->pstride
[i
] = fb
->pitches
[i
] - state
->bpp
[i
];
738 case DRM_MODE_ROTATE_0
:
740 offset
= ((y_offset
+ state
->src_y
) / ydiv
) *
742 offset
+= ((x_offset
+ state
->src_x
) / xdiv
) *
744 state
->xstride
[i
] = fb
->pitches
[i
] -
745 ((patched_src_w
/ xdiv
) *
747 state
->pstride
[i
] = 0;
751 state
->offsets
[i
] = offset
+ fb
->offsets
[i
];
754 state
->src_w
= patched_src_w
;
755 state
->src_h
= patched_src_h
;
756 state
->crtc_w
= patched_crtc_w
;
757 state
->crtc_h
= patched_crtc_h
;
759 if (!desc
->layout
.size
&&
760 (mode
->hdisplay
!= state
->crtc_w
||
761 mode
->vdisplay
!= state
->crtc_h
))
764 if (desc
->max_height
&& state
->crtc_h
> desc
->max_height
)
767 if (desc
->max_width
&& state
->crtc_w
> desc
->max_width
)
770 if ((state
->crtc_h
!= state
->src_h
|| state
->crtc_w
!= state
->src_w
) &&
771 (!desc
->layout
.memsize
||
772 atmel_hlcdc_format_embeds_alpha(state
->base
.fb
->format
->format
)))
775 if (state
->crtc_x
< 0 || state
->crtc_y
< 0)
778 if (state
->crtc_w
+ state
->crtc_x
> mode
->hdisplay
||
779 state
->crtc_h
+ state
->crtc_y
> mode
->vdisplay
)
785 static void atmel_hlcdc_plane_atomic_update(struct drm_plane
*p
,
786 struct drm_plane_state
*old_s
)
788 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
789 struct atmel_hlcdc_plane_state
*state
=
790 drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
793 if (!p
->state
->crtc
|| !p
->state
->fb
)
796 atmel_hlcdc_plane_update_pos_and_size(plane
, state
);
797 atmel_hlcdc_plane_update_general_settings(plane
, state
);
798 atmel_hlcdc_plane_update_format(plane
, state
);
799 atmel_hlcdc_plane_update_clut(plane
);
800 atmel_hlcdc_plane_update_buffers(plane
, state
);
801 atmel_hlcdc_plane_update_disc_area(plane
, state
);
803 /* Enable the overrun interrupts. */
804 atmel_hlcdc_layer_write_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_IER
,
805 ATMEL_HLCDC_LAYER_OVR_IRQ(0) |
806 ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
807 ATMEL_HLCDC_LAYER_OVR_IRQ(2));
809 /* Apply the new config at the next SOF event. */
810 sr
= atmel_hlcdc_layer_read_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_CHSR
);
811 atmel_hlcdc_layer_write_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_CHER
,
812 ATMEL_HLCDC_LAYER_UPDATE
|
813 (sr
& ATMEL_HLCDC_LAYER_EN
?
814 ATMEL_HLCDC_LAYER_A2Q
: ATMEL_HLCDC_LAYER_EN
));
817 static void atmel_hlcdc_plane_atomic_disable(struct drm_plane
*p
,
818 struct drm_plane_state
*old_state
)
820 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
822 /* Disable interrupts */
823 atmel_hlcdc_layer_write_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_IDR
,
826 /* Disable the layer */
827 atmel_hlcdc_layer_write_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_CHDR
,
828 ATMEL_HLCDC_LAYER_RST
|
829 ATMEL_HLCDC_LAYER_A2Q
|
830 ATMEL_HLCDC_LAYER_UPDATE
);
832 /* Clear all pending interrupts */
833 atmel_hlcdc_layer_read_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_ISR
);
836 static void atmel_hlcdc_plane_destroy(struct drm_plane
*p
)
838 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
841 drm_framebuffer_put(plane
->base
.fb
);
843 drm_plane_cleanup(p
);
846 static int atmel_hlcdc_plane_atomic_set_property(struct drm_plane
*p
,
847 struct drm_plane_state
*s
,
848 struct drm_property
*property
,
851 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
852 struct atmel_hlcdc_plane_properties
*props
= plane
->properties
;
853 struct atmel_hlcdc_plane_state
*state
=
854 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
856 if (property
== props
->alpha
)
864 static int atmel_hlcdc_plane_atomic_get_property(struct drm_plane
*p
,
865 const struct drm_plane_state
*s
,
866 struct drm_property
*property
,
869 struct atmel_hlcdc_plane
*plane
= drm_plane_to_atmel_hlcdc_plane(p
);
870 struct atmel_hlcdc_plane_properties
*props
= plane
->properties
;
871 const struct atmel_hlcdc_plane_state
*state
=
872 container_of(s
, const struct atmel_hlcdc_plane_state
, base
);
874 if (property
== props
->alpha
)
882 static int atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane
*plane
,
883 struct atmel_hlcdc_plane_properties
*props
)
885 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
887 if (desc
->type
== ATMEL_HLCDC_OVERLAY_LAYER
||
888 desc
->type
== ATMEL_HLCDC_CURSOR_LAYER
)
889 drm_object_attach_property(&plane
->base
.base
,
892 if (desc
->layout
.xstride
&& desc
->layout
.pstride
) {
895 ret
= drm_plane_create_rotation_property(&plane
->base
,
899 DRM_MODE_ROTATE_180
|
900 DRM_MODE_ROTATE_270
);
905 if (desc
->layout
.csc
) {
907 * TODO: decare a "yuv-to-rgb-conv-factors" property to let
908 * userspace modify these factors (using a BLOB property ?).
910 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
913 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
914 desc
->layout
.csc
+ 1,
916 atmel_hlcdc_layer_write_cfg(&plane
->layer
,
917 desc
->layout
.csc
+ 2,
924 void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane
*plane
)
926 const struct atmel_hlcdc_layer_desc
*desc
= plane
->layer
.desc
;
929 isr
= atmel_hlcdc_layer_read_reg(&plane
->layer
, ATMEL_HLCDC_LAYER_ISR
);
932 * There's not much we can do in case of overrun except informing
933 * the user. However, we are in interrupt context here, hence the
937 (ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
938 ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
939 dev_dbg(plane
->base
.dev
->dev
, "overrun on plane %s\n",
943 static const struct drm_plane_helper_funcs atmel_hlcdc_layer_plane_helper_funcs
= {
944 .atomic_check
= atmel_hlcdc_plane_atomic_check
,
945 .atomic_update
= atmel_hlcdc_plane_atomic_update
,
946 .atomic_disable
= atmel_hlcdc_plane_atomic_disable
,
949 static int atmel_hlcdc_plane_alloc_dscrs(struct drm_plane
*p
,
950 struct atmel_hlcdc_plane_state
*state
)
952 struct atmel_hlcdc_dc
*dc
= p
->dev
->dev_private
;
955 for (i
= 0; i
< ARRAY_SIZE(state
->dscrs
); i
++) {
956 struct atmel_hlcdc_dma_channel_dscr
*dscr
;
959 dscr
= dma_pool_alloc(dc
->dscrpool
, GFP_KERNEL
, &dscr_dma
);
964 dscr
->next
= dscr_dma
;
965 dscr
->self
= dscr_dma
;
966 dscr
->ctrl
= ATMEL_HLCDC_LAYER_DFETCH
;
968 state
->dscrs
[i
] = dscr
;
974 for (i
--; i
>= 0; i
--) {
975 dma_pool_free(dc
->dscrpool
, state
->dscrs
[i
],
976 state
->dscrs
[i
]->self
);
982 static void atmel_hlcdc_plane_reset(struct drm_plane
*p
)
984 struct atmel_hlcdc_plane_state
*state
;
987 state
= drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
990 drm_framebuffer_put(state
->base
.fb
);
996 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
998 if (atmel_hlcdc_plane_alloc_dscrs(p
, state
)) {
1000 dev_err(p
->dev
->dev
,
1001 "Failed to allocate initial plane state\n");
1006 p
->state
= &state
->base
;
1007 p
->state
->plane
= p
;
1011 static struct drm_plane_state
*
1012 atmel_hlcdc_plane_atomic_duplicate_state(struct drm_plane
*p
)
1014 struct atmel_hlcdc_plane_state
*state
=
1015 drm_plane_state_to_atmel_hlcdc_plane_state(p
->state
);
1016 struct atmel_hlcdc_plane_state
*copy
;
1018 copy
= kmemdup(state
, sizeof(*state
), GFP_KERNEL
);
1022 if (atmel_hlcdc_plane_alloc_dscrs(p
, copy
)) {
1028 drm_framebuffer_get(copy
->base
.fb
);
1033 static void atmel_hlcdc_plane_atomic_destroy_state(struct drm_plane
*p
,
1034 struct drm_plane_state
*s
)
1036 struct atmel_hlcdc_plane_state
*state
=
1037 drm_plane_state_to_atmel_hlcdc_plane_state(s
);
1038 struct atmel_hlcdc_dc
*dc
= p
->dev
->dev_private
;
1041 for (i
= 0; i
< ARRAY_SIZE(state
->dscrs
); i
++) {
1042 dma_pool_free(dc
->dscrpool
, state
->dscrs
[i
],
1043 state
->dscrs
[i
]->self
);
1047 drm_framebuffer_put(s
->fb
);
1052 static const struct drm_plane_funcs layer_plane_funcs
= {
1053 .update_plane
= drm_atomic_helper_update_plane
,
1054 .disable_plane
= drm_atomic_helper_disable_plane
,
1055 .destroy
= atmel_hlcdc_plane_destroy
,
1056 .reset
= atmel_hlcdc_plane_reset
,
1057 .atomic_duplicate_state
= atmel_hlcdc_plane_atomic_duplicate_state
,
1058 .atomic_destroy_state
= atmel_hlcdc_plane_atomic_destroy_state
,
1059 .atomic_set_property
= atmel_hlcdc_plane_atomic_set_property
,
1060 .atomic_get_property
= atmel_hlcdc_plane_atomic_get_property
,
1063 static int atmel_hlcdc_plane_create(struct drm_device
*dev
,
1064 const struct atmel_hlcdc_layer_desc
*desc
,
1065 struct atmel_hlcdc_plane_properties
*props
)
1067 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
1068 struct atmel_hlcdc_plane
*plane
;
1069 enum drm_plane_type type
;
1072 plane
= devm_kzalloc(dev
->dev
, sizeof(*plane
), GFP_KERNEL
);
1076 atmel_hlcdc_layer_init(&plane
->layer
, desc
, dc
->hlcdc
->regmap
);
1077 plane
->properties
= props
;
1079 if (desc
->type
== ATMEL_HLCDC_BASE_LAYER
)
1080 type
= DRM_PLANE_TYPE_PRIMARY
;
1081 else if (desc
->type
== ATMEL_HLCDC_CURSOR_LAYER
)
1082 type
= DRM_PLANE_TYPE_CURSOR
;
1084 type
= DRM_PLANE_TYPE_OVERLAY
;
1086 ret
= drm_universal_plane_init(dev
, &plane
->base
, 0,
1088 desc
->formats
->formats
,
1089 desc
->formats
->nformats
,
1094 drm_plane_helper_add(&plane
->base
,
1095 &atmel_hlcdc_layer_plane_helper_funcs
);
1097 /* Set default property values*/
1098 ret
= atmel_hlcdc_plane_init_properties(plane
, props
);
1102 dc
->layers
[desc
->id
] = &plane
->layer
;
1107 static struct atmel_hlcdc_plane_properties
*
1108 atmel_hlcdc_plane_create_properties(struct drm_device
*dev
)
1110 struct atmel_hlcdc_plane_properties
*props
;
1112 props
= devm_kzalloc(dev
->dev
, sizeof(*props
), GFP_KERNEL
);
1114 return ERR_PTR(-ENOMEM
);
1116 props
->alpha
= drm_property_create_range(dev
, 0, "alpha", 0, 255);
1118 return ERR_PTR(-ENOMEM
);
1123 int atmel_hlcdc_create_planes(struct drm_device
*dev
)
1125 struct atmel_hlcdc_dc
*dc
= dev
->dev_private
;
1126 struct atmel_hlcdc_plane_properties
*props
;
1127 const struct atmel_hlcdc_layer_desc
*descs
= dc
->desc
->layers
;
1128 int nlayers
= dc
->desc
->nlayers
;
1131 props
= atmel_hlcdc_plane_create_properties(dev
);
1133 return PTR_ERR(props
);
1135 dc
->dscrpool
= dmam_pool_create("atmel-hlcdc-dscr", dev
->dev
,
1136 sizeof(struct atmel_hlcdc_dma_channel_dscr
),
1141 for (i
= 0; i
< nlayers
; i
++) {
1142 if (descs
[i
].type
!= ATMEL_HLCDC_BASE_LAYER
&&
1143 descs
[i
].type
!= ATMEL_HLCDC_OVERLAY_LAYER
&&
1144 descs
[i
].type
!= ATMEL_HLCDC_CURSOR_LAYER
)
1147 ret
= atmel_hlcdc_plane_create(dev
, &descs
[i
], props
);