omap_plane.c (16933B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 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_gem_atomic_helper.h> 10#include <drm/drm_plane_helper.h> 11#include <drm/drm_fourcc.h> 12 13#include "omap_dmm_tiler.h" 14#include "omap_drv.h" 15 16/* 17 * plane funcs 18 */ 19 20#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) 21 22struct omap_plane_state { 23 /* Must be first. */ 24 struct drm_plane_state base; 25 26 struct omap_hw_overlay *overlay; 27 struct omap_hw_overlay *r_overlay; /* right overlay */ 28}; 29 30#define to_omap_plane(x) container_of(x, struct omap_plane, base) 31 32struct omap_plane { 33 struct drm_plane base; 34 enum omap_plane_id id; 35}; 36 37bool is_omap_plane_dual_overlay(struct drm_plane_state *state) 38{ 39 struct omap_plane_state *omap_state = to_omap_plane_state(state); 40 41 return !!omap_state->r_overlay; 42} 43 44static int omap_plane_prepare_fb(struct drm_plane *plane, 45 struct drm_plane_state *new_state) 46{ 47 if (!new_state->fb) 48 return 0; 49 50 drm_gem_plane_helper_prepare_fb(plane, new_state); 51 52 return omap_framebuffer_pin(new_state->fb); 53} 54 55static void omap_plane_cleanup_fb(struct drm_plane *plane, 56 struct drm_plane_state *old_state) 57{ 58 if (old_state->fb) 59 omap_framebuffer_unpin(old_state->fb); 60} 61 62static void omap_plane_atomic_update(struct drm_plane *plane, 63 struct drm_atomic_state *state) 64{ 65 struct omap_drm_private *priv = plane->dev->dev_private; 66 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 67 plane); 68 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 69 plane); 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; 74 int ret; 75 bool dual_ovl; 76 77 new_omap_state = to_omap_plane_state(new_state); 78 old_omap_state = to_omap_plane_state(old_state); 79 80 dual_ovl = is_omap_plane_dual_overlay(new_state); 81 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); 87 88 if (!new_omap_state->overlay) { 89 DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name); 90 return; 91 } 92 93 ovl_id = new_omap_state->overlay->id; 94 DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc, 95 new_state->fb); 96 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; 104 else 105 info.pre_mult_alpha = 0; 106 info.color_encoding = new_state->color_encoding; 107 info.color_range = new_state->color_range; 108 109 r_info = info; 110 111 /* update scanout: */ 112 omap_framebuffer_update_scanout(new_state->fb, new_state, &info, 113 dual_ovl ? &r_info : NULL); 114 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); 120 121 if (dual_ovl) { 122 r_ovl_id = new_omap_state->r_overlay->id; 123 /* 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 127 */ 128 r_info.zorder = info.zorder + 1; 129 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); 136 } 137 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)); 142 if (ret) { 143 dev_err(plane->dev->dev, "Failed to setup plane %s\n", 144 plane->name); 145 dispc_ovl_enable(priv->dispc, ovl_id, false); 146 return; 147 } 148 149 dispc_ovl_enable(priv->dispc, ovl_id, true); 150 151 if (dual_ovl) { 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)); 155 if (ret) { 156 dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n", 157 plane->name); 158 dispc_ovl_enable(priv->dispc, r_ovl_id, false); 159 dispc_ovl_enable(priv->dispc, ovl_id, false); 160 return; 161 } 162 163 dispc_ovl_enable(priv->dispc, r_ovl_id, true); 164 } 165} 166 167static void omap_plane_atomic_disable(struct drm_plane *plane, 168 struct drm_atomic_state *state) 169{ 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, 173 plane); 174 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 175 plane); 176 struct omap_plane_state *new_omap_state; 177 struct omap_plane_state *old_omap_state; 178 179 new_omap_state = to_omap_plane_state(new_state); 180 old_omap_state = to_omap_plane_state(old_state); 181 182 if (!old_omap_state->overlay) 183 return; 184 185 new_state->rotation = DRM_MODE_ROTATE_0; 186 new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; 187 188 omap_overlay_update_state(priv, old_omap_state->overlay); 189 new_omap_state->overlay = NULL; 190 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; 194 } 195} 196 197#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) 198 199static int omap_plane_atomic_check(struct drm_plane *plane, 200 struct drm_atomic_state *state) 201{ 202 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 203 plane); 204 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, 205 plane); 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; 214 u16 width, height; 215 u32 caps = 0; 216 u32 fourcc; 217 int ret; 218 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); 222 223 dispc_ovl_get_max_size(priv->dispc, &width, &height); 224 max_width = width << 16; 225 max_height = height << 16; 226 227 crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc; 228 if (!crtc) 229 return 0; 230 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)) 234 return 0; 235 236 /* 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 240 * phase in dispc. 241 */ 242 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 243 FRAC_16_16(1, 8), FRAC_16_16(8, 1), 244 true, true); 245 if (ret) 246 return ret; 247 248 DBG("%s: visible %d -> %d", plane->name, 249 old_plane_state->visible, new_plane_state->visible); 250 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; 256 return 0; 257 } 258 259 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) 260 return -EINVAL; 261 262 if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) 263 return -EINVAL; 264 265 if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) 266 return -EINVAL; 267 268 /* Make sure dimensions are within bounds. */ 269 if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height) 270 return -EINVAL; 271 272 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; 275 276 if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) || 277 new_plane_state->crtc_w / 2 & 1)) { 278 /* 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 282 */ 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; 286 else 287 return -EINVAL; 288 } else { 289 if (new_plane_state->src_w <= (2 * max_width) && 290 new_plane_state->crtc_w <= (2 * width)) 291 new_r_hw_overlay = true; 292 else 293 return -EINVAL; 294 } 295 } 296 297 if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && 298 !omap_framebuffer_supports_rotation(new_plane_state->fb)) 299 return -EINVAL; 300 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; 304 305 fourcc = new_plane_state->fb->format->format; 306 307 /* 308 * (re)allocate hw overlay if we don't have one or 309 * there is a caps mismatch 310 */ 311 if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { 312 new_hw_overlay = true; 313 } else { 314 /* check supported format */ 315 if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id, 316 fourcc)) 317 new_hw_overlay = true; 318 } 319 320 /* 321 * check if we need two overlays and only have 1 or 322 * if we had 2 overlays but will only need 1 323 */ 324 if ((new_r_hw_overlay && !omap_state->r_overlay) || 325 (!new_r_hw_overlay && omap_state->r_overlay)) 326 new_hw_overlay = true; 327 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; 333 334 omap_overlay_release(state, old_ovl); 335 omap_overlay_release(state, old_r_ovl); 336 337 ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl, 338 new_r_hw_overlay ? &new_r_ovl : NULL); 339 if (ret) { 340 DBG("%s: failed to assign hw_overlay", plane->name); 341 omap_state->overlay = NULL; 342 omap_state->r_overlay = NULL; 343 return ret; 344 } 345 346 omap_state->overlay = new_ovl; 347 if (new_r_hw_overlay) 348 omap_state->r_overlay = new_r_ovl; 349 else 350 omap_state->r_overlay = NULL; 351 } 352 353 DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id); 354 355 if (omap_state->r_overlay) 356 DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id); 357 358 return 0; 359} 360 361static 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, 367}; 368 369static void omap_plane_destroy(struct drm_plane *plane) 370{ 371 struct omap_plane *omap_plane = to_omap_plane(plane); 372 373 DBG("%s", plane->name); 374 375 drm_plane_cleanup(plane); 376 377 kfree(omap_plane); 378} 379 380/* helper to install properties which are common to planes and crtcs */ 381void omap_plane_install_properties(struct drm_plane *plane, 382 struct drm_mode_object *obj) 383{ 384 struct drm_device *dev = plane->dev; 385 struct omap_drm_private *priv = dev->dev_private; 386 387 if (priv->has_dmm) { 388 if (!plane->rotation_property) 389 drm_plane_create_rotation_property(plane, 390 DRM_MODE_ROTATE_0, 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); 394 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, 398 DRM_MODE_ROTATE_0); 399 } 400 401 drm_object_attach_property(obj, priv->zorder_prop, 0); 402} 403 404static void omap_plane_reset(struct drm_plane *plane) 405{ 406 struct omap_plane_state *omap_state; 407 408 if (plane->state) 409 drm_atomic_helper_plane_destroy_state(plane, plane->state); 410 411 omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); 412 if (!omap_state) 413 return; 414 415 __drm_atomic_helper_plane_reset(plane, &omap_state->base); 416} 417 418static struct drm_plane_state * 419omap_plane_atomic_duplicate_state(struct drm_plane *plane) 420{ 421 struct omap_plane_state *state, *current_state; 422 423 if (WARN_ON(!plane->state)) 424 return NULL; 425 426 current_state = to_omap_plane_state(plane->state); 427 428 state = kmalloc(sizeof(*state), GFP_KERNEL); 429 if (!state) 430 return NULL; 431 432 __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 433 434 state->overlay = current_state->overlay; 435 state->r_overlay = current_state->r_overlay; 436 437 return &state->base; 438} 439 440static void omap_plane_atomic_print_state(struct drm_printer *p, 441 const struct drm_plane_state *state) 442{ 443 struct omap_plane_state *omap_state = to_omap_plane_state(state); 444 445 if (omap_state->overlay) 446 drm_printf(p, "\toverlay=%s (caps=0x%x)\n", 447 omap_state->overlay->name, 448 omap_state->overlay->caps); 449 else 450 drm_printf(p, "\toverlay=None\n"); 451 if (omap_state->r_overlay) 452 drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n", 453 omap_state->r_overlay->name, 454 omap_state->r_overlay->caps); 455 else 456 drm_printf(p, "\tr_overlay=None\n"); 457} 458 459static int omap_plane_atomic_set_property(struct drm_plane *plane, 460 struct drm_plane_state *state, 461 struct drm_property *property, 462 u64 val) 463{ 464 struct omap_drm_private *priv = plane->dev->dev_private; 465 466 if (property == priv->zorder_prop) 467 state->zpos = val; 468 else 469 return -EINVAL; 470 471 return 0; 472} 473 474static int omap_plane_atomic_get_property(struct drm_plane *plane, 475 const struct drm_plane_state *state, 476 struct drm_property *property, 477 u64 *val) 478{ 479 struct omap_drm_private *priv = plane->dev->dev_private; 480 481 if (property == priv->zorder_prop) 482 *val = state->zpos; 483 else 484 return -EINVAL; 485 486 return 0; 487} 488 489static const struct drm_plane_funcs omap_plane_funcs = { 490 .update_plane = drm_atomic_helper_update_plane, 491 .disable_plane = drm_atomic_helper_disable_plane, 492 .reset = omap_plane_reset, 493 .destroy = omap_plane_destroy, 494 .atomic_duplicate_state = omap_plane_atomic_duplicate_state, 495 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 496 .atomic_set_property = omap_plane_atomic_set_property, 497 .atomic_get_property = omap_plane_atomic_get_property, 498 .atomic_print_state = omap_plane_atomic_print_state, 499}; 500 501static bool omap_plane_supports_yuv(struct drm_plane *plane) 502{ 503 struct omap_drm_private *priv = plane->dev->dev_private; 504 struct omap_plane *omap_plane = to_omap_plane(plane); 505 const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 506 u32 i; 507 508 for (i = 0; formats[i]; i++) 509 if (formats[i] == DRM_FORMAT_YUYV || 510 formats[i] == DRM_FORMAT_UYVY || 511 formats[i] == DRM_FORMAT_NV12) 512 return true; 513 514 return false; 515} 516 517/* initialize plane */ 518struct drm_plane *omap_plane_init(struct drm_device *dev, 519 int idx, enum drm_plane_type type, 520 u32 possible_crtcs) 521{ 522 struct omap_drm_private *priv = dev->dev_private; 523 unsigned int num_planes = dispc_get_num_ovls(priv->dispc); 524 struct drm_plane *plane; 525 struct omap_plane *omap_plane; 526 unsigned int zpos; 527 int ret; 528 u32 nformats; 529 const u32 *formats; 530 531 if (WARN_ON(idx >= num_planes)) 532 return ERR_PTR(-EINVAL); 533 534 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 535 if (!omap_plane) 536 return ERR_PTR(-ENOMEM); 537 538 omap_plane->id = idx; 539 540 DBG("%d: type=%d", omap_plane->id, type); 541 DBG(" crtc_mask: 0x%04x", possible_crtcs); 542 543 formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 544 for (nformats = 0; formats[nformats]; ++nformats) 545 ; 546 547 plane = &omap_plane->base; 548 549 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 550 &omap_plane_funcs, formats, 551 nformats, NULL, type, NULL); 552 if (ret < 0) 553 goto error; 554 555 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 556 557 omap_plane_install_properties(plane, &plane->base); 558 559 /* 560 * Set the zpos default depending on whether we are a primary or overlay 561 * plane. 562 */ 563 if (plane->type == DRM_PLANE_TYPE_PRIMARY) 564 zpos = 0; 565 else 566 zpos = omap_plane->id; 567 drm_plane_create_zpos_property(plane, zpos, 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)); 571 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); 580 581 return plane; 582 583error: 584 dev_err(dev->dev, "%s(): could not create plane: %d\n", 585 __func__, omap_plane->id); 586 587 kfree(omap_plane); 588 return NULL; 589}