1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_gem_atomic_helper.h>
10 #include <drm/drm_plane_helper.h>
11 #include <drm/drm_fourcc.h>
13 #include "omap_dmm_tiler.h"
20 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
22 struct omap_plane_state {
24 struct drm_plane_state base;
26 struct omap_hw_overlay *overlay;
27 struct omap_hw_overlay *r_overlay; /* right overlay */
30 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
33 struct drm_plane base;
34 enum omap_plane_id id;
37 bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
39 struct omap_plane_state *omap_state = to_omap_plane_state(state);
41 return !!omap_state->r_overlay;
44 static int omap_plane_prepare_fb(struct drm_plane *plane,
45 struct drm_plane_state *new_state)
50 drm_gem_plane_helper_prepare_fb(plane, new_state);
52 return omap_framebuffer_pin(new_state->fb);
55 static void omap_plane_cleanup_fb(struct drm_plane *plane,
56 struct drm_plane_state *old_state)
59 omap_framebuffer_unpin(old_state->fb);
62 static void omap_plane_atomic_update(struct drm_plane *plane,
63 struct drm_atomic_state *state)
65 struct omap_drm_private *priv = plane->dev->dev_private;
66 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
68 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
70 struct omap_plane_state *new_omap_state;
71 struct omap_plane_state *old_omap_state;
72 struct omap_overlay_info info, r_info;
73 enum omap_plane_id ovl_id, r_ovl_id;
77 new_omap_state = to_omap_plane_state(new_state);
78 old_omap_state = to_omap_plane_state(old_state);
80 dual_ovl = is_omap_plane_dual_overlay(new_state);
82 /* Cleanup previously held overlay if needed */
83 if (old_omap_state->overlay)
84 omap_overlay_update_state(priv, old_omap_state->overlay);
85 if (old_omap_state->r_overlay)
86 omap_overlay_update_state(priv, old_omap_state->r_overlay);
88 if (!new_omap_state->overlay) {
89 DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
93 ovl_id = new_omap_state->overlay->id;
94 DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
97 memset(&info, 0, sizeof(info));
98 info.rotation_type = OMAP_DSS_ROT_NONE;
99 info.rotation = DRM_MODE_ROTATE_0;
100 info.global_alpha = new_state->alpha >> 8;
101 info.zorder = new_state->normalized_zpos;
102 if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
103 info.pre_mult_alpha = 1;
105 info.pre_mult_alpha = 0;
106 info.color_encoding = new_state->color_encoding;
107 info.color_range = new_state->color_range;
111 /* update scanout: */
112 omap_framebuffer_update_scanout(new_state->fb, new_state, &info,
113 dual_ovl ? &r_info : NULL);
115 DBG("%s: %dx%d -> %dx%d (%d)",
116 new_omap_state->overlay->name, info.width, info.height,
117 info.out_width, info.out_height, info.screen_width);
118 DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
119 &info.paddr, &info.p_uv_addr);
122 r_ovl_id = new_omap_state->r_overlay->id;
124 * If the current plane uses 2 hw planes the very next
125 * zorder is used by the r_overlay so we just use the
126 * main overlay zorder + 1
128 r_info.zorder = info.zorder + 1;
130 DBG("%s: %dx%d -> %dx%d (%d)",
131 new_omap_state->r_overlay->name,
132 r_info.width, r_info.height,
133 r_info.out_width, r_info.out_height, r_info.screen_width);
134 DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y,
135 &r_info.paddr, &r_info.p_uv_addr);
138 /* and finally, update omapdss: */
139 ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
140 omap_crtc_timings(new_state->crtc), false,
141 omap_crtc_channel(new_state->crtc));
143 dev_err(plane->dev->dev, "Failed to setup plane %s\n",
145 dispc_ovl_enable(priv->dispc, ovl_id, false);
149 dispc_ovl_enable(priv->dispc, ovl_id, true);
152 ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info,
153 omap_crtc_timings(new_state->crtc), false,
154 omap_crtc_channel(new_state->crtc));
156 dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n",
158 dispc_ovl_enable(priv->dispc, r_ovl_id, false);
159 dispc_ovl_enable(priv->dispc, ovl_id, false);
163 dispc_ovl_enable(priv->dispc, r_ovl_id, true);
167 static void omap_plane_atomic_disable(struct drm_plane *plane,
168 struct drm_atomic_state *state)
170 struct omap_drm_private *priv = plane->dev->dev_private;
171 struct omap_plane *omap_plane = to_omap_plane(plane);
172 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
174 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
176 struct omap_plane_state *new_omap_state;
177 struct omap_plane_state *old_omap_state;
179 new_omap_state = to_omap_plane_state(new_state);
180 old_omap_state = to_omap_plane_state(old_state);
182 if (!old_omap_state->overlay)
185 new_state->rotation = DRM_MODE_ROTATE_0;
186 new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
188 omap_overlay_update_state(priv, old_omap_state->overlay);
189 new_omap_state->overlay = NULL;
191 if (is_omap_plane_dual_overlay(old_state)) {
192 omap_overlay_update_state(priv, old_omap_state->r_overlay);
193 new_omap_state->r_overlay = NULL;
197 #define FRAC_16_16(mult, div) (((mult) << 16) / (div))
199 static int omap_plane_atomic_check(struct drm_plane *plane,
200 struct drm_atomic_state *state)
202 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
204 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
206 struct omap_drm_private *priv = plane->dev->dev_private;
207 struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
208 struct omap_global_state *omap_overlay_global_state;
209 struct drm_crtc_state *crtc_state;
210 bool new_r_hw_overlay = false;
211 bool new_hw_overlay = false;
212 u32 max_width, max_height;
213 struct drm_crtc *crtc;
219 omap_overlay_global_state = omap_get_global_state(state);
220 if (IS_ERR(omap_overlay_global_state))
221 return PTR_ERR(omap_overlay_global_state);
223 dispc_ovl_get_max_size(priv->dispc, &width, &height);
224 max_width = width << 16;
225 max_height = height << 16;
227 crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;
231 crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
232 /* we should have a crtc state if the plane is attached to a crtc */
233 if (WARN_ON(!crtc_state))
237 * Note: these are just sanity checks to filter out totally bad scaling
238 * factors. The real limits must be calculated case by case, and
239 * unfortunately we currently do those checks only at the commit
242 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
243 FRAC_16_16(1, 8), FRAC_16_16(8, 1),
248 DBG("%s: visible %d -> %d", plane->name,
249 old_plane_state->visible, new_plane_state->visible);
251 if (!new_plane_state->visible) {
252 omap_overlay_release(state, omap_state->overlay);
253 omap_overlay_release(state, omap_state->r_overlay);
254 omap_state->overlay = NULL;
255 omap_state->r_overlay = NULL;
259 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)
262 if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay)
265 if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay)
268 /* Make sure dimensions are within bounds. */
269 if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)
273 if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) {
274 bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv;
276 if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||
277 new_plane_state->crtc_w / 2 & 1)) {
279 * When calculating the split overlay width
280 * and it yield an odd value we will need to adjust
281 * the indivual width +/- 1. So make sure it fits
283 if (new_plane_state->src_w <= ((2 * width - 1) << 16) &&
284 new_plane_state->crtc_w <= (2 * width - 1))
285 new_r_hw_overlay = true;
289 if (new_plane_state->src_w <= (2 * max_width) &&
290 new_plane_state->crtc_w <= (2 * width))
291 new_r_hw_overlay = true;
297 if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&
298 !omap_framebuffer_supports_rotation(new_plane_state->fb))
301 if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
302 (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)
303 caps |= OMAP_DSS_OVL_CAP_SCALE;
305 fourcc = new_plane_state->fb->format->format;
308 * (re)allocate hw overlay if we don't have one or
309 * there is a caps mismatch
311 if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {
312 new_hw_overlay = true;
314 /* check supported format */
315 if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,
317 new_hw_overlay = true;
321 * check if we need two overlays and only have 1 or
322 * if we had 2 overlays but will only need 1
324 if ((new_r_hw_overlay && !omap_state->r_overlay) ||
325 (!new_r_hw_overlay && omap_state->r_overlay))
326 new_hw_overlay = true;
328 if (new_hw_overlay) {
329 struct omap_hw_overlay *old_ovl = omap_state->overlay;
330 struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay;
331 struct omap_hw_overlay *new_ovl = NULL;
332 struct omap_hw_overlay *new_r_ovl = NULL;
334 omap_overlay_release(state, old_ovl);
335 omap_overlay_release(state, old_r_ovl);
337 ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,
338 new_r_hw_overlay ? &new_r_ovl : NULL);
340 DBG("%s: failed to assign hw_overlay", plane->name);
341 omap_state->overlay = NULL;
342 omap_state->r_overlay = NULL;
346 omap_state->overlay = new_ovl;
347 if (new_r_hw_overlay)
348 omap_state->r_overlay = new_r_ovl;
350 omap_state->r_overlay = NULL;
353 DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
355 if (omap_state->r_overlay)
356 DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);
361 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = {
362 .prepare_fb = omap_plane_prepare_fb,
363 .cleanup_fb = omap_plane_cleanup_fb,
364 .atomic_check = omap_plane_atomic_check,
365 .atomic_update = omap_plane_atomic_update,
366 .atomic_disable = omap_plane_atomic_disable,
369 static void omap_plane_destroy(struct drm_plane *plane)
371 struct omap_plane *omap_plane = to_omap_plane(plane);
373 DBG("%s", plane->name);
375 drm_plane_cleanup(plane);
380 /* helper to install properties which are common to planes and crtcs */
381 void omap_plane_install_properties(struct drm_plane *plane,
382 struct drm_mode_object *obj)
384 struct drm_device *dev = plane->dev;
385 struct omap_drm_private *priv = dev->dev_private;
388 if (!plane->rotation_property)
389 drm_plane_create_rotation_property(plane,
391 DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
392 DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
393 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
395 /* Attach the rotation property also to the crtc object */
396 if (plane->rotation_property && obj != &plane->base)
397 drm_object_attach_property(obj, plane->rotation_property,
401 drm_object_attach_property(obj, priv->zorder_prop, 0);
404 static void omap_plane_reset(struct drm_plane *plane)
406 struct omap_plane *omap_plane = to_omap_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 * Set the zpos default depending on whether we are a primary or overlay
422 plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
423 ? 0 : omap_plane->id;
424 plane->state->color_encoding = DRM_COLOR_YCBCR_BT601;
425 plane->state->color_range = DRM_COLOR_YCBCR_FULL_RANGE;
428 static struct drm_plane_state *
429 omap_plane_atomic_duplicate_state(struct drm_plane *plane)
431 struct omap_plane_state *state, *current_state;
433 if (WARN_ON(!plane->state))
436 current_state = to_omap_plane_state(plane->state);
438 state = kmalloc(sizeof(*state), GFP_KERNEL);
442 __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
444 state->overlay = current_state->overlay;
445 state->r_overlay = current_state->r_overlay;
450 static void omap_plane_atomic_print_state(struct drm_printer *p,
451 const struct drm_plane_state *state)
453 struct omap_plane_state *omap_state = to_omap_plane_state(state);
455 if (omap_state->overlay)
456 drm_printf(p, "\toverlay=%s (caps=0x%x)\n",
457 omap_state->overlay->name,
458 omap_state->overlay->caps);
460 drm_printf(p, "\toverlay=None\n");
461 if (omap_state->r_overlay)
462 drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n",
463 omap_state->r_overlay->name,
464 omap_state->r_overlay->caps);
466 drm_printf(p, "\tr_overlay=None\n");
469 static int omap_plane_atomic_set_property(struct drm_plane *plane,
470 struct drm_plane_state *state,
471 struct drm_property *property,
474 struct omap_drm_private *priv = plane->dev->dev_private;
476 if (property == priv->zorder_prop)
484 static int omap_plane_atomic_get_property(struct drm_plane *plane,
485 const struct drm_plane_state *state,
486 struct drm_property *property,
489 struct omap_drm_private *priv = plane->dev->dev_private;
491 if (property == priv->zorder_prop)
499 static const struct drm_plane_funcs omap_plane_funcs = {
500 .update_plane = drm_atomic_helper_update_plane,
501 .disable_plane = drm_atomic_helper_disable_plane,
502 .reset = omap_plane_reset,
503 .destroy = omap_plane_destroy,
504 .atomic_duplicate_state = omap_plane_atomic_duplicate_state,
505 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
506 .atomic_set_property = omap_plane_atomic_set_property,
507 .atomic_get_property = omap_plane_atomic_get_property,
508 .atomic_print_state = omap_plane_atomic_print_state,
511 static bool omap_plane_supports_yuv(struct drm_plane *plane)
513 struct omap_drm_private *priv = plane->dev->dev_private;
514 struct omap_plane *omap_plane = to_omap_plane(plane);
515 const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
518 for (i = 0; formats[i]; i++)
519 if (formats[i] == DRM_FORMAT_YUYV ||
520 formats[i] == DRM_FORMAT_UYVY ||
521 formats[i] == DRM_FORMAT_NV12)
527 /* initialize plane */
528 struct drm_plane *omap_plane_init(struct drm_device *dev,
529 int idx, enum drm_plane_type type,
532 struct omap_drm_private *priv = dev->dev_private;
533 unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
534 struct drm_plane *plane;
535 struct omap_plane *omap_plane;
540 if (WARN_ON(idx >= num_planes))
541 return ERR_PTR(-EINVAL);
543 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
545 return ERR_PTR(-ENOMEM);
547 omap_plane->id = idx;
549 DBG("%d: type=%d", omap_plane->id, type);
550 DBG(" crtc_mask: 0x%04x", possible_crtcs);
552 formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
553 for (nformats = 0; formats[nformats]; ++nformats)
556 plane = &omap_plane->base;
558 ret = drm_universal_plane_init(dev, plane, possible_crtcs,
559 &omap_plane_funcs, formats,
560 nformats, NULL, type, NULL);
564 drm_plane_helper_add(plane, &omap_plane_helper_funcs);
566 omap_plane_install_properties(plane, &plane->base);
567 drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1);
568 drm_plane_create_alpha_property(plane);
569 drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
570 BIT(DRM_MODE_BLEND_COVERAGE));
572 if (omap_plane_supports_yuv(plane))
573 drm_plane_create_color_properties(plane,
574 BIT(DRM_COLOR_YCBCR_BT601) |
575 BIT(DRM_COLOR_YCBCR_BT709),
576 BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
577 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
578 DRM_COLOR_YCBCR_BT601,
579 DRM_COLOR_YCBCR_FULL_RANGE);
584 dev_err(dev->dev, "%s(): could not create plane: %d\n",
585 __func__, omap_plane->id);