1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
4 * Author: Rob Clark <rob.clark@linaro.org>
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_blend.h>
10 #include <drm/drm_gem_atomic_helper.h>
11 #include <drm/drm_fourcc.h>
12 #include <drm/drm_framebuffer.h>
14 #include "omap_dmm_tiler.h"
21 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
23 struct omap_plane_state
{
25 struct drm_plane_state base
;
27 struct omap_hw_overlay
*overlay
;
28 struct omap_hw_overlay
*r_overlay
; /* right overlay */
31 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
34 struct drm_plane base
;
35 enum omap_plane_id id
;
38 bool is_omap_plane_dual_overlay(struct drm_plane_state
*state
)
40 struct omap_plane_state
*omap_state
= to_omap_plane_state(state
);
42 return !!omap_state
->r_overlay
;
45 static int omap_plane_prepare_fb(struct drm_plane
*plane
,
46 struct drm_plane_state
*new_state
)
51 drm_gem_plane_helper_prepare_fb(plane
, new_state
);
53 return omap_framebuffer_pin(new_state
->fb
);
56 static void omap_plane_cleanup_fb(struct drm_plane
*plane
,
57 struct drm_plane_state
*old_state
)
60 omap_framebuffer_unpin(old_state
->fb
);
63 static void omap_plane_atomic_update(struct drm_plane
*plane
,
64 struct drm_atomic_state
*state
)
66 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
67 struct drm_plane_state
*new_state
= drm_atomic_get_new_plane_state(state
,
69 struct drm_plane_state
*old_state
= drm_atomic_get_old_plane_state(state
,
71 struct omap_plane_state
*new_omap_state
;
72 struct omap_plane_state
*old_omap_state
;
73 struct omap_overlay_info info
, r_info
;
74 enum omap_plane_id ovl_id
, r_ovl_id
;
78 new_omap_state
= to_omap_plane_state(new_state
);
79 old_omap_state
= to_omap_plane_state(old_state
);
81 dual_ovl
= is_omap_plane_dual_overlay(new_state
);
83 /* Cleanup previously held overlay if needed */
84 if (old_omap_state
->overlay
)
85 omap_overlay_update_state(priv
, old_omap_state
->overlay
);
86 if (old_omap_state
->r_overlay
)
87 omap_overlay_update_state(priv
, old_omap_state
->r_overlay
);
89 if (!new_omap_state
->overlay
) {
90 DBG("[PLANE:%d:%s] no overlay attached", plane
->base
.id
, plane
->name
);
94 ovl_id
= new_omap_state
->overlay
->id
;
95 DBG("%s, crtc=%p fb=%p", plane
->name
, new_state
->crtc
,
98 memset(&info
, 0, sizeof(info
));
99 info
.rotation_type
= OMAP_DSS_ROT_NONE
;
100 info
.rotation
= DRM_MODE_ROTATE_0
;
101 info
.global_alpha
= new_state
->alpha
>> 8;
102 info
.zorder
= new_state
->normalized_zpos
;
103 if (new_state
->pixel_blend_mode
== DRM_MODE_BLEND_PREMULTI
)
104 info
.pre_mult_alpha
= 1;
106 info
.pre_mult_alpha
= 0;
107 info
.color_encoding
= new_state
->color_encoding
;
108 info
.color_range
= new_state
->color_range
;
112 /* update scanout: */
113 omap_framebuffer_update_scanout(new_state
->fb
, new_state
, &info
,
114 dual_ovl
? &r_info
: NULL
);
116 DBG("%s: %dx%d -> %dx%d (%d)",
117 new_omap_state
->overlay
->name
, info
.width
, info
.height
,
118 info
.out_width
, info
.out_height
, info
.screen_width
);
119 DBG("%d,%d %pad %pad", info
.pos_x
, info
.pos_y
,
120 &info
.paddr
, &info
.p_uv_addr
);
123 r_ovl_id
= new_omap_state
->r_overlay
->id
;
125 * If the current plane uses 2 hw planes the very next
126 * zorder is used by the r_overlay so we just use the
127 * main overlay zorder + 1
129 r_info
.zorder
= info
.zorder
+ 1;
131 DBG("%s: %dx%d -> %dx%d (%d)",
132 new_omap_state
->r_overlay
->name
,
133 r_info
.width
, r_info
.height
,
134 r_info
.out_width
, r_info
.out_height
, r_info
.screen_width
);
135 DBG("%d,%d %pad %pad", r_info
.pos_x
, r_info
.pos_y
,
136 &r_info
.paddr
, &r_info
.p_uv_addr
);
139 /* and finally, update omapdss: */
140 ret
= dispc_ovl_setup(priv
->dispc
, ovl_id
, &info
,
141 omap_crtc_timings(new_state
->crtc
), false,
142 omap_crtc_channel(new_state
->crtc
));
144 dev_err(plane
->dev
->dev
, "Failed to setup plane %s\n",
146 dispc_ovl_enable(priv
->dispc
, ovl_id
, false);
150 dispc_ovl_enable(priv
->dispc
, ovl_id
, true);
153 ret
= dispc_ovl_setup(priv
->dispc
, r_ovl_id
, &r_info
,
154 omap_crtc_timings(new_state
->crtc
), false,
155 omap_crtc_channel(new_state
->crtc
));
157 dev_err(plane
->dev
->dev
, "Failed to setup plane right-overlay %s\n",
159 dispc_ovl_enable(priv
->dispc
, r_ovl_id
, false);
160 dispc_ovl_enable(priv
->dispc
, ovl_id
, false);
164 dispc_ovl_enable(priv
->dispc
, r_ovl_id
, true);
168 static void omap_plane_atomic_disable(struct drm_plane
*plane
,
169 struct drm_atomic_state
*state
)
171 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
172 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
173 struct drm_plane_state
*new_state
= drm_atomic_get_new_plane_state(state
,
175 struct drm_plane_state
*old_state
= drm_atomic_get_old_plane_state(state
,
177 struct omap_plane_state
*new_omap_state
;
178 struct omap_plane_state
*old_omap_state
;
180 new_omap_state
= to_omap_plane_state(new_state
);
181 old_omap_state
= to_omap_plane_state(old_state
);
183 if (!old_omap_state
->overlay
)
186 new_state
->rotation
= DRM_MODE_ROTATE_0
;
187 new_state
->zpos
= plane
->type
== DRM_PLANE_TYPE_PRIMARY
? 0 : omap_plane
->id
;
189 omap_overlay_update_state(priv
, old_omap_state
->overlay
);
190 new_omap_state
->overlay
= NULL
;
192 if (is_omap_plane_dual_overlay(old_state
)) {
193 omap_overlay_update_state(priv
, old_omap_state
->r_overlay
);
194 new_omap_state
->r_overlay
= NULL
;
198 #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
200 static int omap_plane_atomic_check(struct drm_plane
*plane
,
201 struct drm_atomic_state
*state
)
203 struct drm_plane_state
*new_plane_state
= drm_atomic_get_new_plane_state(state
,
205 struct drm_plane_state
*old_plane_state
= drm_atomic_get_old_plane_state(state
,
207 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
208 struct omap_plane_state
*omap_state
= to_omap_plane_state(new_plane_state
);
209 struct omap_global_state
*omap_overlay_global_state
;
210 struct drm_crtc_state
*crtc_state
;
211 bool new_r_hw_overlay
= false;
212 bool new_hw_overlay
= false;
213 u32 max_width
, max_height
;
214 struct drm_crtc
*crtc
;
220 omap_overlay_global_state
= omap_get_global_state(state
);
221 if (IS_ERR(omap_overlay_global_state
))
222 return PTR_ERR(omap_overlay_global_state
);
224 dispc_ovl_get_max_size(priv
->dispc
, &width
, &height
);
225 max_width
= width
<< 16;
226 max_height
= height
<< 16;
228 crtc
= new_plane_state
->crtc
? new_plane_state
->crtc
: plane
->state
->crtc
;
232 crtc_state
= drm_atomic_get_existing_crtc_state(state
, crtc
);
233 /* we should have a crtc state if the plane is attached to a crtc */
234 if (WARN_ON(!crtc_state
))
238 * Note: these are just sanity checks to filter out totally bad scaling
239 * factors. The real limits must be calculated case by case, and
240 * unfortunately we currently do those checks only at the commit
243 ret
= drm_atomic_helper_check_plane_state(new_plane_state
, crtc_state
,
244 FRAC_16_16(1, 8), FRAC_16_16(8, 1),
249 DBG("%s: visible %d -> %d", plane
->name
,
250 old_plane_state
->visible
, new_plane_state
->visible
);
252 if (!new_plane_state
->visible
) {
253 omap_overlay_release(state
, omap_state
->overlay
);
254 omap_overlay_release(state
, omap_state
->r_overlay
);
255 omap_state
->overlay
= NULL
;
256 omap_state
->r_overlay
= NULL
;
260 if (new_plane_state
->crtc_x
< 0 || new_plane_state
->crtc_y
< 0)
263 if (new_plane_state
->crtc_x
+ new_plane_state
->crtc_w
> crtc_state
->adjusted_mode
.hdisplay
)
266 if (new_plane_state
->crtc_y
+ new_plane_state
->crtc_h
> crtc_state
->adjusted_mode
.vdisplay
)
269 /* Make sure dimensions are within bounds. */
270 if (new_plane_state
->src_h
> max_height
|| new_plane_state
->crtc_h
> height
)
274 if (new_plane_state
->src_w
> max_width
|| new_plane_state
->crtc_w
> width
) {
275 bool is_fourcc_yuv
= new_plane_state
->fb
->format
->is_yuv
;
277 if (is_fourcc_yuv
&& (((new_plane_state
->src_w
>> 16) / 2 & 1) ||
278 new_plane_state
->crtc_w
/ 2 & 1)) {
280 * When calculating the split overlay width
281 * and it yield an odd value we will need to adjust
282 * the indivual width +/- 1. So make sure it fits
284 if (new_plane_state
->src_w
<= ((2 * width
- 1) << 16) &&
285 new_plane_state
->crtc_w
<= (2 * width
- 1))
286 new_r_hw_overlay
= true;
290 if (new_plane_state
->src_w
<= (2 * max_width
) &&
291 new_plane_state
->crtc_w
<= (2 * width
))
292 new_r_hw_overlay
= true;
298 if (new_plane_state
->rotation
!= DRM_MODE_ROTATE_0
&&
299 !omap_framebuffer_supports_rotation(new_plane_state
->fb
))
302 if ((new_plane_state
->src_w
>> 16) != new_plane_state
->crtc_w
||
303 (new_plane_state
->src_h
>> 16) != new_plane_state
->crtc_h
)
304 caps
|= OMAP_DSS_OVL_CAP_SCALE
;
306 fourcc
= new_plane_state
->fb
->format
->format
;
309 * (re)allocate hw overlay if we don't have one or
310 * there is a caps mismatch
312 if (!omap_state
->overlay
|| (caps
& ~omap_state
->overlay
->caps
)) {
313 new_hw_overlay
= true;
315 /* check supported format */
316 if (!dispc_ovl_color_mode_supported(priv
->dispc
, omap_state
->overlay
->id
,
318 new_hw_overlay
= true;
322 * check if we need two overlays and only have 1 or
323 * if we had 2 overlays but will only need 1
325 if ((new_r_hw_overlay
&& !omap_state
->r_overlay
) ||
326 (!new_r_hw_overlay
&& omap_state
->r_overlay
))
327 new_hw_overlay
= true;
329 if (new_hw_overlay
) {
330 struct omap_hw_overlay
*old_ovl
= omap_state
->overlay
;
331 struct omap_hw_overlay
*old_r_ovl
= omap_state
->r_overlay
;
332 struct omap_hw_overlay
*new_ovl
= NULL
;
333 struct omap_hw_overlay
*new_r_ovl
= NULL
;
335 omap_overlay_release(state
, old_ovl
);
336 omap_overlay_release(state
, old_r_ovl
);
338 ret
= omap_overlay_assign(state
, plane
, caps
, fourcc
, &new_ovl
,
339 new_r_hw_overlay
? &new_r_ovl
: NULL
);
341 DBG("%s: failed to assign hw_overlay", plane
->name
);
342 omap_state
->overlay
= NULL
;
343 omap_state
->r_overlay
= NULL
;
347 omap_state
->overlay
= new_ovl
;
348 if (new_r_hw_overlay
)
349 omap_state
->r_overlay
= new_r_ovl
;
351 omap_state
->r_overlay
= NULL
;
354 DBG("plane: %s overlay_id: %d", plane
->name
, omap_state
->overlay
->id
);
356 if (omap_state
->r_overlay
)
357 DBG("plane: %s r_overlay_id: %d", plane
->name
, omap_state
->r_overlay
->id
);
362 static const struct drm_plane_helper_funcs omap_plane_helper_funcs
= {
363 .prepare_fb
= omap_plane_prepare_fb
,
364 .cleanup_fb
= omap_plane_cleanup_fb
,
365 .atomic_check
= omap_plane_atomic_check
,
366 .atomic_update
= omap_plane_atomic_update
,
367 .atomic_disable
= omap_plane_atomic_disable
,
370 static void omap_plane_destroy(struct drm_plane
*plane
)
372 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
374 DBG("%s", plane
->name
);
376 drm_plane_cleanup(plane
);
381 /* helper to install properties which are common to planes and crtcs */
382 void omap_plane_install_properties(struct drm_plane
*plane
,
383 struct drm_mode_object
*obj
)
385 struct drm_device
*dev
= plane
->dev
;
386 struct omap_drm_private
*priv
= dev
->dev_private
;
389 if (!plane
->rotation_property
)
390 drm_plane_create_rotation_property(plane
,
392 DRM_MODE_ROTATE_0
| DRM_MODE_ROTATE_90
|
393 DRM_MODE_ROTATE_180
| DRM_MODE_ROTATE_270
|
394 DRM_MODE_REFLECT_X
| DRM_MODE_REFLECT_Y
);
396 /* Attach the rotation property also to the crtc object */
397 if (plane
->rotation_property
&& obj
!= &plane
->base
)
398 drm_object_attach_property(obj
, plane
->rotation_property
,
402 drm_object_attach_property(obj
, priv
->zorder_prop
, 0);
405 static void omap_plane_reset(struct drm_plane
*plane
)
407 struct omap_plane_state
*omap_state
;
410 drm_atomic_helper_plane_destroy_state(plane
, plane
->state
);
412 omap_state
= kzalloc(sizeof(*omap_state
), GFP_KERNEL
);
416 __drm_atomic_helper_plane_reset(plane
, &omap_state
->base
);
419 static struct drm_plane_state
*
420 omap_plane_atomic_duplicate_state(struct drm_plane
*plane
)
422 struct omap_plane_state
*state
, *current_state
;
424 if (WARN_ON(!plane
->state
))
427 current_state
= to_omap_plane_state(plane
->state
);
429 state
= kmalloc(sizeof(*state
), GFP_KERNEL
);
433 __drm_atomic_helper_plane_duplicate_state(plane
, &state
->base
);
435 state
->overlay
= current_state
->overlay
;
436 state
->r_overlay
= current_state
->r_overlay
;
441 static void omap_plane_atomic_print_state(struct drm_printer
*p
,
442 const struct drm_plane_state
*state
)
444 struct omap_plane_state
*omap_state
= to_omap_plane_state(state
);
446 if (omap_state
->overlay
)
447 drm_printf(p
, "\toverlay=%s (caps=0x%x)\n",
448 omap_state
->overlay
->name
,
449 omap_state
->overlay
->caps
);
451 drm_printf(p
, "\toverlay=None\n");
452 if (omap_state
->r_overlay
)
453 drm_printf(p
, "\tr_overlay=%s (caps=0x%x)\n",
454 omap_state
->r_overlay
->name
,
455 omap_state
->r_overlay
->caps
);
457 drm_printf(p
, "\tr_overlay=None\n");
460 static int omap_plane_atomic_set_property(struct drm_plane
*plane
,
461 struct drm_plane_state
*state
,
462 struct drm_property
*property
,
465 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
467 if (property
== priv
->zorder_prop
)
475 static int omap_plane_atomic_get_property(struct drm_plane
*plane
,
476 const struct drm_plane_state
*state
,
477 struct drm_property
*property
,
480 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
482 if (property
== priv
->zorder_prop
)
490 static const struct drm_plane_funcs omap_plane_funcs
= {
491 .update_plane
= drm_atomic_helper_update_plane
,
492 .disable_plane
= drm_atomic_helper_disable_plane
,
493 .reset
= omap_plane_reset
,
494 .destroy
= omap_plane_destroy
,
495 .atomic_duplicate_state
= omap_plane_atomic_duplicate_state
,
496 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
497 .atomic_set_property
= omap_plane_atomic_set_property
,
498 .atomic_get_property
= omap_plane_atomic_get_property
,
499 .atomic_print_state
= omap_plane_atomic_print_state
,
502 static bool omap_plane_supports_yuv(struct drm_plane
*plane
)
504 struct omap_drm_private
*priv
= plane
->dev
->dev_private
;
505 struct omap_plane
*omap_plane
= to_omap_plane(plane
);
506 const u32
*formats
= dispc_ovl_get_color_modes(priv
->dispc
, omap_plane
->id
);
509 for (i
= 0; formats
[i
]; i
++)
510 if (formats
[i
] == DRM_FORMAT_YUYV
||
511 formats
[i
] == DRM_FORMAT_UYVY
||
512 formats
[i
] == DRM_FORMAT_NV12
)
518 /* initialize plane */
519 struct drm_plane
*omap_plane_init(struct drm_device
*dev
,
520 int idx
, enum drm_plane_type type
,
523 struct omap_drm_private
*priv
= dev
->dev_private
;
524 unsigned int num_planes
= dispc_get_num_ovls(priv
->dispc
);
525 struct drm_plane
*plane
;
526 struct omap_plane
*omap_plane
;
532 if (WARN_ON(idx
>= num_planes
))
533 return ERR_PTR(-EINVAL
);
535 omap_plane
= kzalloc(sizeof(*omap_plane
), GFP_KERNEL
);
537 return ERR_PTR(-ENOMEM
);
539 omap_plane
->id
= idx
;
541 DBG("%d: type=%d", omap_plane
->id
, type
);
542 DBG(" crtc_mask: 0x%04x", possible_crtcs
);
544 formats
= dispc_ovl_get_color_modes(priv
->dispc
, omap_plane
->id
);
545 for (nformats
= 0; formats
[nformats
]; ++nformats
)
548 plane
= &omap_plane
->base
;
550 ret
= drm_universal_plane_init(dev
, plane
, possible_crtcs
,
551 &omap_plane_funcs
, formats
,
552 nformats
, NULL
, type
, NULL
);
556 drm_plane_helper_add(plane
, &omap_plane_helper_funcs
);
558 omap_plane_install_properties(plane
, &plane
->base
);
561 * Set the zpos default depending on whether we are a primary or overlay
564 if (plane
->type
== DRM_PLANE_TYPE_PRIMARY
)
567 zpos
= omap_plane
->id
;
568 drm_plane_create_zpos_property(plane
, zpos
, 0, num_planes
- 1);
569 drm_plane_create_alpha_property(plane
);
570 drm_plane_create_blend_mode_property(plane
, BIT(DRM_MODE_BLEND_PREMULTI
) |
571 BIT(DRM_MODE_BLEND_COVERAGE
));
573 if (omap_plane_supports_yuv(plane
))
574 drm_plane_create_color_properties(plane
,
575 BIT(DRM_COLOR_YCBCR_BT601
) |
576 BIT(DRM_COLOR_YCBCR_BT709
),
577 BIT(DRM_COLOR_YCBCR_FULL_RANGE
) |
578 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE
),
579 DRM_COLOR_YCBCR_BT601
,
580 DRM_COLOR_YCBCR_FULL_RANGE
);
585 dev_err(dev
->dev
, "%s(): could not create plane: %d\n",
586 __func__
, omap_plane
->id
);