591 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/4 * Author: Rob Clark <rob.clark@linaro.org>5 */6 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>13 14#include "omap_dmm_tiler.h"15#include "omap_drv.h"16 17/*18 * plane funcs19 */20 21#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)22 23struct omap_plane_state {24 /* Must be first. */25 struct drm_plane_state base;26 27 struct omap_hw_overlay *overlay;28 struct omap_hw_overlay *r_overlay; /* right overlay */29};30 31#define to_omap_plane(x) container_of(x, struct omap_plane, base)32 33struct omap_plane {34 struct drm_plane base;35 enum omap_plane_id id;36};37 38bool is_omap_plane_dual_overlay(struct drm_plane_state *state)39{40 struct omap_plane_state *omap_state = to_omap_plane_state(state);41 42 return !!omap_state->r_overlay;43}44 45static int omap_plane_prepare_fb(struct drm_plane *plane,46 struct drm_plane_state *new_state)47{48 if (!new_state->fb)49 return 0;50 51 drm_gem_plane_helper_prepare_fb(plane, new_state);52 53 return omap_framebuffer_pin(new_state->fb);54}55 56static void omap_plane_cleanup_fb(struct drm_plane *plane,57 struct drm_plane_state *old_state)58{59 if (old_state->fb)60 omap_framebuffer_unpin(old_state->fb);61}62 63static void omap_plane_atomic_update(struct drm_plane *plane,64 struct drm_atomic_state *state)65{66 struct omap_drm_private *priv = plane->dev->dev_private;67 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,68 plane);69 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,70 plane);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;75 int ret;76 bool dual_ovl;77 78 new_omap_state = to_omap_plane_state(new_state);79 old_omap_state = to_omap_plane_state(old_state);80 81 dual_ovl = is_omap_plane_dual_overlay(new_state);82 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);88 89 if (!new_omap_state->overlay) {90 DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);91 return;92 }93 94 ovl_id = new_omap_state->overlay->id;95 DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,96 new_state->fb);97 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;105 else106 info.pre_mult_alpha = 0;107 info.color_encoding = new_state->color_encoding;108 info.color_range = new_state->color_range;109 110 r_info = info;111 112 /* update scanout: */113 omap_framebuffer_update_scanout(new_state->fb, new_state, &info,114 dual_ovl ? &r_info : NULL);115 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);121 122 if (dual_ovl) {123 r_ovl_id = new_omap_state->r_overlay->id;124 /*125 * If the current plane uses 2 hw planes the very next126 * zorder is used by the r_overlay so we just use the127 * main overlay zorder + 1128 */129 r_info.zorder = info.zorder + 1;130 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);137 }138 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));143 if (ret) {144 dev_err(plane->dev->dev, "Failed to setup plane %s\n",145 plane->name);146 dispc_ovl_enable(priv->dispc, ovl_id, false);147 return;148 }149 150 dispc_ovl_enable(priv->dispc, ovl_id, true);151 152 if (dual_ovl) {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));156 if (ret) {157 dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n",158 plane->name);159 dispc_ovl_enable(priv->dispc, r_ovl_id, false);160 dispc_ovl_enable(priv->dispc, ovl_id, false);161 return;162 }163 164 dispc_ovl_enable(priv->dispc, r_ovl_id, true);165 }166}167 168static void omap_plane_atomic_disable(struct drm_plane *plane,169 struct drm_atomic_state *state)170{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,174 plane);175 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,176 plane);177 struct omap_plane_state *new_omap_state;178 struct omap_plane_state *old_omap_state;179 180 new_omap_state = to_omap_plane_state(new_state);181 old_omap_state = to_omap_plane_state(old_state);182 183 if (!old_omap_state->overlay)184 return;185 186 new_state->rotation = DRM_MODE_ROTATE_0;187 new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;188 189 omap_overlay_update_state(priv, old_omap_state->overlay);190 new_omap_state->overlay = NULL;191 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;195 }196}197 198#define FRAC_16_16(mult, div) (((mult) << 16) / (div))199 200static int omap_plane_atomic_check(struct drm_plane *plane,201 struct drm_atomic_state *state)202{203 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,204 plane);205 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,206 plane);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;215 u16 width, height;216 u32 caps = 0;217 u32 fourcc;218 int ret;219 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);223 224 dispc_ovl_get_max_size(priv->dispc, &width, &height);225 max_width = width << 16;226 max_height = height << 16;227 228 crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;229 if (!crtc)230 return 0;231 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))235 return 0;236 237 /*238 * Note: these are just sanity checks to filter out totally bad scaling239 * factors. The real limits must be calculated case by case, and240 * unfortunately we currently do those checks only at the commit241 * phase in dispc.242 */243 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,244 FRAC_16_16(1, 8), FRAC_16_16(8, 1),245 true, true);246 if (ret)247 return ret;248 249 DBG("%s: visible %d -> %d", plane->name,250 old_plane_state->visible, new_plane_state->visible);251 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;257 return 0;258 }259 260 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)261 return -EINVAL;262 263 if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay)264 return -EINVAL;265 266 if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay)267 return -EINVAL;268 269 /* Make sure dimensions are within bounds. */270 if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)271 return -EINVAL;272 273 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;276 277 if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||278 new_plane_state->crtc_w / 2 & 1)) {279 /*280 * When calculating the split overlay width281 * and it yield an odd value we will need to adjust282 * the indivual width +/- 1. So make sure it fits283 */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;287 else288 return -EINVAL;289 } else {290 if (new_plane_state->src_w <= (2 * max_width) &&291 new_plane_state->crtc_w <= (2 * width))292 new_r_hw_overlay = true;293 else294 return -EINVAL;295 }296 }297 298 if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&299 !omap_framebuffer_supports_rotation(new_plane_state->fb))300 return -EINVAL;301 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;305 306 fourcc = new_plane_state->fb->format->format;307 308 /*309 * (re)allocate hw overlay if we don't have one or310 * there is a caps mismatch311 */312 if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {313 new_hw_overlay = true;314 } else {315 /* check supported format */316 if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,317 fourcc))318 new_hw_overlay = true;319 }320 321 /*322 * check if we need two overlays and only have 1 or323 * if we had 2 overlays but will only need 1324 */325 if ((new_r_hw_overlay && !omap_state->r_overlay) ||326 (!new_r_hw_overlay && omap_state->r_overlay))327 new_hw_overlay = true;328 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;334 335 omap_overlay_release(state, old_ovl);336 omap_overlay_release(state, old_r_ovl);337 338 ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,339 new_r_hw_overlay ? &new_r_ovl : NULL);340 if (ret) {341 DBG("%s: failed to assign hw_overlay", plane->name);342 omap_state->overlay = NULL;343 omap_state->r_overlay = NULL;344 return ret;345 }346 347 omap_state->overlay = new_ovl;348 if (new_r_hw_overlay)349 omap_state->r_overlay = new_r_ovl;350 else351 omap_state->r_overlay = NULL;352 }353 354 DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);355 356 if (omap_state->r_overlay)357 DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);358 359 return 0;360}361 362static 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,368};369 370static void omap_plane_destroy(struct drm_plane *plane)371{372 struct omap_plane *omap_plane = to_omap_plane(plane);373 374 DBG("%s", plane->name);375 376 drm_plane_cleanup(plane);377 378 kfree(omap_plane);379}380 381/* helper to install properties which are common to planes and crtcs */382void omap_plane_install_properties(struct drm_plane *plane,383 struct drm_mode_object *obj)384{385 struct drm_device *dev = plane->dev;386 struct omap_drm_private *priv = dev->dev_private;387 388 if (priv->has_dmm) {389 if (!plane->rotation_property)390 drm_plane_create_rotation_property(plane,391 DRM_MODE_ROTATE_0,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);395 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,399 DRM_MODE_ROTATE_0);400 }401 402 drm_object_attach_property(obj, priv->zorder_prop, 0);403}404 405static void omap_plane_reset(struct drm_plane *plane)406{407 struct omap_plane_state *omap_state;408 409 if (plane->state)410 drm_atomic_helper_plane_destroy_state(plane, plane->state);411 412 omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL);413 if (!omap_state)414 return;415 416 __drm_atomic_helper_plane_reset(plane, &omap_state->base);417}418 419static struct drm_plane_state *420omap_plane_atomic_duplicate_state(struct drm_plane *plane)421{422 struct omap_plane_state *state, *current_state;423 424 if (WARN_ON(!plane->state))425 return NULL;426 427 current_state = to_omap_plane_state(plane->state);428 429 state = kmalloc(sizeof(*state), GFP_KERNEL);430 if (!state)431 return NULL;432 433 __drm_atomic_helper_plane_duplicate_state(plane, &state->base);434 435 state->overlay = current_state->overlay;436 state->r_overlay = current_state->r_overlay;437 438 return &state->base;439}440 441static void omap_plane_atomic_print_state(struct drm_printer *p,442 const struct drm_plane_state *state)443{444 struct omap_plane_state *omap_state = to_omap_plane_state(state);445 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);450 else451 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);456 else457 drm_printf(p, "\tr_overlay=None\n");458}459 460static int omap_plane_atomic_set_property(struct drm_plane *plane,461 struct drm_plane_state *state,462 struct drm_property *property,463 u64 val)464{465 struct omap_drm_private *priv = plane->dev->dev_private;466 467 if (property == priv->zorder_prop)468 state->zpos = val;469 else470 return -EINVAL;471 472 return 0;473}474 475static int omap_plane_atomic_get_property(struct drm_plane *plane,476 const struct drm_plane_state *state,477 struct drm_property *property,478 u64 *val)479{480 struct omap_drm_private *priv = plane->dev->dev_private;481 482 if (property == priv->zorder_prop)483 *val = state->zpos;484 else485 return -EINVAL;486 487 return 0;488}489 490static 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,500};501 502static bool omap_plane_supports_yuv(struct drm_plane *plane)503{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);507 u32 i;508 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)513 return true;514 515 return false;516}517 518/* initialize plane */519struct drm_plane *omap_plane_init(struct drm_device *dev,520 int idx, enum drm_plane_type type,521 u32 possible_crtcs)522{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;527 unsigned int zpos;528 int ret;529 u32 nformats;530 const u32 *formats;531 532 if (WARN_ON(idx >= num_planes))533 return ERR_PTR(-EINVAL);534 535 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);536 if (!omap_plane)537 return ERR_PTR(-ENOMEM);538 539 omap_plane->id = idx;540 541 DBG("%d: type=%d", omap_plane->id, type);542 DBG(" crtc_mask: 0x%04x", possible_crtcs);543 544 formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);545 for (nformats = 0; formats[nformats]; ++nformats)546 ;547 548 plane = &omap_plane->base;549 550 ret = drm_universal_plane_init(dev, plane, possible_crtcs,551 &omap_plane_funcs, formats,552 nformats, NULL, type, NULL);553 if (ret < 0)554 goto error;555 556 drm_plane_helper_add(plane, &omap_plane_helper_funcs);557 558 omap_plane_install_properties(plane, &plane->base);559 560 /*561 * Set the zpos default depending on whether we are a primary or overlay562 * plane.563 */564 if (plane->type == DRM_PLANE_TYPE_PRIMARY)565 zpos = 0;566 else567 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));572 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);581 582 return plane;583 584error:585 dev_err(dev->dev, "%s(): could not create plane: %d\n",586 __func__, omap_plane->id);587 588 kfree(omap_plane);589 return NULL;590}591