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_blend.h> 10 #include <drm/drm_gem_atomic_helper.h> 11 #include <drm/drm_plane_helper.h> 12 #include <drm/drm_fourcc.h> 13 #include <drm/drm_framebuffer.h> 14 15 #include "omap_dmm_tiler.h" 16 #include "omap_drv.h" 17 18 /* 19 * plane funcs 20 */ 21 22 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) 23 24 struct omap_plane_state { 25 /* Must be first. */ 26 struct drm_plane_state base; 27 28 struct omap_hw_overlay *overlay; 29 struct omap_hw_overlay *r_overlay; /* right overlay */ 30 }; 31 32 #define to_omap_plane(x) container_of(x, struct omap_plane, base) 33 34 struct omap_plane { 35 struct drm_plane base; 36 enum omap_plane_id id; 37 }; 38 39 bool is_omap_plane_dual_overlay(struct drm_plane_state *state) 40 { 41 struct omap_plane_state *omap_state = to_omap_plane_state(state); 42 43 return !!omap_state->r_overlay; 44 } 45 46 static int omap_plane_prepare_fb(struct drm_plane *plane, 47 struct drm_plane_state *new_state) 48 { 49 if (!new_state->fb) 50 return 0; 51 52 drm_gem_plane_helper_prepare_fb(plane, new_state); 53 54 return omap_framebuffer_pin(new_state->fb); 55 } 56 57 static void omap_plane_cleanup_fb(struct drm_plane *plane, 58 struct drm_plane_state *old_state) 59 { 60 if (old_state->fb) 61 omap_framebuffer_unpin(old_state->fb); 62 } 63 64 static void omap_plane_atomic_update(struct drm_plane *plane, 65 struct drm_atomic_state *state) 66 { 67 struct omap_drm_private *priv = plane->dev->dev_private; 68 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 69 plane); 70 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 71 plane); 72 struct omap_plane_state *new_omap_state; 73 struct omap_plane_state *old_omap_state; 74 struct omap_overlay_info info, r_info; 75 enum omap_plane_id ovl_id, r_ovl_id; 76 int ret; 77 bool dual_ovl; 78 79 new_omap_state = to_omap_plane_state(new_state); 80 old_omap_state = to_omap_plane_state(old_state); 81 82 dual_ovl = is_omap_plane_dual_overlay(new_state); 83 84 /* Cleanup previously held overlay if needed */ 85 if (old_omap_state->overlay) 86 omap_overlay_update_state(priv, old_omap_state->overlay); 87 if (old_omap_state->r_overlay) 88 omap_overlay_update_state(priv, old_omap_state->r_overlay); 89 90 if (!new_omap_state->overlay) { 91 DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name); 92 return; 93 } 94 95 ovl_id = new_omap_state->overlay->id; 96 DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc, 97 new_state->fb); 98 99 memset(&info, 0, sizeof(info)); 100 info.rotation_type = OMAP_DSS_ROT_NONE; 101 info.rotation = DRM_MODE_ROTATE_0; 102 info.global_alpha = new_state->alpha >> 8; 103 info.zorder = new_state->normalized_zpos; 104 if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) 105 info.pre_mult_alpha = 1; 106 else 107 info.pre_mult_alpha = 0; 108 info.color_encoding = new_state->color_encoding; 109 info.color_range = new_state->color_range; 110 111 r_info = info; 112 113 /* update scanout: */ 114 omap_framebuffer_update_scanout(new_state->fb, new_state, &info, 115 dual_ovl ? &r_info : NULL); 116 117 DBG("%s: %dx%d -> %dx%d (%d)", 118 new_omap_state->overlay->name, info.width, info.height, 119 info.out_width, info.out_height, info.screen_width); 120 DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, 121 &info.paddr, &info.p_uv_addr); 122 123 if (dual_ovl) { 124 r_ovl_id = new_omap_state->r_overlay->id; 125 /* 126 * If the current plane uses 2 hw planes the very next 127 * zorder is used by the r_overlay so we just use the 128 * main overlay zorder + 1 129 */ 130 r_info.zorder = info.zorder + 1; 131 132 DBG("%s: %dx%d -> %dx%d (%d)", 133 new_omap_state->r_overlay->name, 134 r_info.width, r_info.height, 135 r_info.out_width, r_info.out_height, r_info.screen_width); 136 DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y, 137 &r_info.paddr, &r_info.p_uv_addr); 138 } 139 140 /* and finally, update omapdss: */ 141 ret = dispc_ovl_setup(priv->dispc, ovl_id, &info, 142 omap_crtc_timings(new_state->crtc), false, 143 omap_crtc_channel(new_state->crtc)); 144 if (ret) { 145 dev_err(plane->dev->dev, "Failed to setup plane %s\n", 146 plane->name); 147 dispc_ovl_enable(priv->dispc, ovl_id, false); 148 return; 149 } 150 151 dispc_ovl_enable(priv->dispc, ovl_id, true); 152 153 if (dual_ovl) { 154 ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info, 155 omap_crtc_timings(new_state->crtc), false, 156 omap_crtc_channel(new_state->crtc)); 157 if (ret) { 158 dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n", 159 plane->name); 160 dispc_ovl_enable(priv->dispc, r_ovl_id, false); 161 dispc_ovl_enable(priv->dispc, ovl_id, false); 162 return; 163 } 164 165 dispc_ovl_enable(priv->dispc, r_ovl_id, true); 166 } 167 } 168 169 static void omap_plane_atomic_disable(struct drm_plane *plane, 170 struct drm_atomic_state *state) 171 { 172 struct omap_drm_private *priv = plane->dev->dev_private; 173 struct omap_plane *omap_plane = to_omap_plane(plane); 174 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 175 plane); 176 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 177 plane); 178 struct omap_plane_state *new_omap_state; 179 struct omap_plane_state *old_omap_state; 180 181 new_omap_state = to_omap_plane_state(new_state); 182 old_omap_state = to_omap_plane_state(old_state); 183 184 if (!old_omap_state->overlay) 185 return; 186 187 new_state->rotation = DRM_MODE_ROTATE_0; 188 new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; 189 190 omap_overlay_update_state(priv, old_omap_state->overlay); 191 new_omap_state->overlay = NULL; 192 193 if (is_omap_plane_dual_overlay(old_state)) { 194 omap_overlay_update_state(priv, old_omap_state->r_overlay); 195 new_omap_state->r_overlay = NULL; 196 } 197 } 198 199 #define FRAC_16_16(mult, div) (((mult) << 16) / (div)) 200 201 static int omap_plane_atomic_check(struct drm_plane *plane, 202 struct drm_atomic_state *state) 203 { 204 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 205 plane); 206 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, 207 plane); 208 struct omap_drm_private *priv = plane->dev->dev_private; 209 struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state); 210 struct omap_global_state *omap_overlay_global_state; 211 struct drm_crtc_state *crtc_state; 212 bool new_r_hw_overlay = false; 213 bool new_hw_overlay = false; 214 u32 max_width, max_height; 215 struct drm_crtc *crtc; 216 u16 width, height; 217 u32 caps = 0; 218 u32 fourcc; 219 int ret; 220 221 omap_overlay_global_state = omap_get_global_state(state); 222 if (IS_ERR(omap_overlay_global_state)) 223 return PTR_ERR(omap_overlay_global_state); 224 225 dispc_ovl_get_max_size(priv->dispc, &width, &height); 226 max_width = width << 16; 227 max_height = height << 16; 228 229 crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc; 230 if (!crtc) 231 return 0; 232 233 crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); 234 /* we should have a crtc state if the plane is attached to a crtc */ 235 if (WARN_ON(!crtc_state)) 236 return 0; 237 238 /* 239 * Note: these are just sanity checks to filter out totally bad scaling 240 * factors. The real limits must be calculated case by case, and 241 * unfortunately we currently do those checks only at the commit 242 * phase in dispc. 243 */ 244 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, 245 FRAC_16_16(1, 8), FRAC_16_16(8, 1), 246 true, true); 247 if (ret) 248 return ret; 249 250 DBG("%s: visible %d -> %d", plane->name, 251 old_plane_state->visible, new_plane_state->visible); 252 253 if (!new_plane_state->visible) { 254 omap_overlay_release(state, omap_state->overlay); 255 omap_overlay_release(state, omap_state->r_overlay); 256 omap_state->overlay = NULL; 257 omap_state->r_overlay = NULL; 258 return 0; 259 } 260 261 if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) 262 return -EINVAL; 263 264 if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay) 265 return -EINVAL; 266 267 if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay) 268 return -EINVAL; 269 270 /* Make sure dimensions are within bounds. */ 271 if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height) 272 return -EINVAL; 273 274 275 if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) { 276 bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv; 277 278 if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) || 279 new_plane_state->crtc_w / 2 & 1)) { 280 /* 281 * When calculating the split overlay width 282 * and it yield an odd value we will need to adjust 283 * the indivual width +/- 1. So make sure it fits 284 */ 285 if (new_plane_state->src_w <= ((2 * width - 1) << 16) && 286 new_plane_state->crtc_w <= (2 * width - 1)) 287 new_r_hw_overlay = true; 288 else 289 return -EINVAL; 290 } else { 291 if (new_plane_state->src_w <= (2 * max_width) && 292 new_plane_state->crtc_w <= (2 * width)) 293 new_r_hw_overlay = true; 294 else 295 return -EINVAL; 296 } 297 } 298 299 if (new_plane_state->rotation != DRM_MODE_ROTATE_0 && 300 !omap_framebuffer_supports_rotation(new_plane_state->fb)) 301 return -EINVAL; 302 303 if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w || 304 (new_plane_state->src_h >> 16) != new_plane_state->crtc_h) 305 caps |= OMAP_DSS_OVL_CAP_SCALE; 306 307 fourcc = new_plane_state->fb->format->format; 308 309 /* 310 * (re)allocate hw overlay if we don't have one or 311 * there is a caps mismatch 312 */ 313 if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { 314 new_hw_overlay = true; 315 } else { 316 /* check supported format */ 317 if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id, 318 fourcc)) 319 new_hw_overlay = true; 320 } 321 322 /* 323 * check if we need two overlays and only have 1 or 324 * if we had 2 overlays but will only need 1 325 */ 326 if ((new_r_hw_overlay && !omap_state->r_overlay) || 327 (!new_r_hw_overlay && omap_state->r_overlay)) 328 new_hw_overlay = true; 329 330 if (new_hw_overlay) { 331 struct omap_hw_overlay *old_ovl = omap_state->overlay; 332 struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay; 333 struct omap_hw_overlay *new_ovl = NULL; 334 struct omap_hw_overlay *new_r_ovl = NULL; 335 336 omap_overlay_release(state, old_ovl); 337 omap_overlay_release(state, old_r_ovl); 338 339 ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl, 340 new_r_hw_overlay ? &new_r_ovl : NULL); 341 if (ret) { 342 DBG("%s: failed to assign hw_overlay", plane->name); 343 omap_state->overlay = NULL; 344 omap_state->r_overlay = NULL; 345 return ret; 346 } 347 348 omap_state->overlay = new_ovl; 349 if (new_r_hw_overlay) 350 omap_state->r_overlay = new_r_ovl; 351 else 352 omap_state->r_overlay = NULL; 353 } 354 355 DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id); 356 357 if (omap_state->r_overlay) 358 DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id); 359 360 return 0; 361 } 362 363 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = { 364 .prepare_fb = omap_plane_prepare_fb, 365 .cleanup_fb = omap_plane_cleanup_fb, 366 .atomic_check = omap_plane_atomic_check, 367 .atomic_update = omap_plane_atomic_update, 368 .atomic_disable = omap_plane_atomic_disable, 369 }; 370 371 static void omap_plane_destroy(struct drm_plane *plane) 372 { 373 struct omap_plane *omap_plane = to_omap_plane(plane); 374 375 DBG("%s", plane->name); 376 377 drm_plane_cleanup(plane); 378 379 kfree(omap_plane); 380 } 381 382 /* helper to install properties which are common to planes and crtcs */ 383 void omap_plane_install_properties(struct drm_plane *plane, 384 struct drm_mode_object *obj) 385 { 386 struct drm_device *dev = plane->dev; 387 struct omap_drm_private *priv = dev->dev_private; 388 389 if (priv->has_dmm) { 390 if (!plane->rotation_property) 391 drm_plane_create_rotation_property(plane, 392 DRM_MODE_ROTATE_0, 393 DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 | 394 DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 | 395 DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y); 396 397 /* Attach the rotation property also to the crtc object */ 398 if (plane->rotation_property && obj != &plane->base) 399 drm_object_attach_property(obj, plane->rotation_property, 400 DRM_MODE_ROTATE_0); 401 } 402 403 drm_object_attach_property(obj, priv->zorder_prop, 0); 404 } 405 406 static void omap_plane_reset(struct drm_plane *plane) 407 { 408 struct omap_plane_state *omap_state; 409 410 if (plane->state) 411 drm_atomic_helper_plane_destroy_state(plane, plane->state); 412 413 omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); 414 if (!omap_state) 415 return; 416 417 __drm_atomic_helper_plane_reset(plane, &omap_state->base); 418 } 419 420 static struct drm_plane_state * 421 omap_plane_atomic_duplicate_state(struct drm_plane *plane) 422 { 423 struct omap_plane_state *state, *current_state; 424 425 if (WARN_ON(!plane->state)) 426 return NULL; 427 428 current_state = to_omap_plane_state(plane->state); 429 430 state = kmalloc(sizeof(*state), GFP_KERNEL); 431 if (!state) 432 return NULL; 433 434 __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 435 436 state->overlay = current_state->overlay; 437 state->r_overlay = current_state->r_overlay; 438 439 return &state->base; 440 } 441 442 static void omap_plane_atomic_print_state(struct drm_printer *p, 443 const struct drm_plane_state *state) 444 { 445 struct omap_plane_state *omap_state = to_omap_plane_state(state); 446 447 if (omap_state->overlay) 448 drm_printf(p, "\toverlay=%s (caps=0x%x)\n", 449 omap_state->overlay->name, 450 omap_state->overlay->caps); 451 else 452 drm_printf(p, "\toverlay=None\n"); 453 if (omap_state->r_overlay) 454 drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n", 455 omap_state->r_overlay->name, 456 omap_state->r_overlay->caps); 457 else 458 drm_printf(p, "\tr_overlay=None\n"); 459 } 460 461 static int omap_plane_atomic_set_property(struct drm_plane *plane, 462 struct drm_plane_state *state, 463 struct drm_property *property, 464 u64 val) 465 { 466 struct omap_drm_private *priv = plane->dev->dev_private; 467 468 if (property == priv->zorder_prop) 469 state->zpos = val; 470 else 471 return -EINVAL; 472 473 return 0; 474 } 475 476 static int omap_plane_atomic_get_property(struct drm_plane *plane, 477 const struct drm_plane_state *state, 478 struct drm_property *property, 479 u64 *val) 480 { 481 struct omap_drm_private *priv = plane->dev->dev_private; 482 483 if (property == priv->zorder_prop) 484 *val = state->zpos; 485 else 486 return -EINVAL; 487 488 return 0; 489 } 490 491 static const struct drm_plane_funcs omap_plane_funcs = { 492 .update_plane = drm_atomic_helper_update_plane, 493 .disable_plane = drm_atomic_helper_disable_plane, 494 .reset = omap_plane_reset, 495 .destroy = omap_plane_destroy, 496 .atomic_duplicate_state = omap_plane_atomic_duplicate_state, 497 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 498 .atomic_set_property = omap_plane_atomic_set_property, 499 .atomic_get_property = omap_plane_atomic_get_property, 500 .atomic_print_state = omap_plane_atomic_print_state, 501 }; 502 503 static bool omap_plane_supports_yuv(struct drm_plane *plane) 504 { 505 struct omap_drm_private *priv = plane->dev->dev_private; 506 struct omap_plane *omap_plane = to_omap_plane(plane); 507 const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 508 u32 i; 509 510 for (i = 0; formats[i]; i++) 511 if (formats[i] == DRM_FORMAT_YUYV || 512 formats[i] == DRM_FORMAT_UYVY || 513 formats[i] == DRM_FORMAT_NV12) 514 return true; 515 516 return false; 517 } 518 519 /* initialize plane */ 520 struct drm_plane *omap_plane_init(struct drm_device *dev, 521 int idx, enum drm_plane_type type, 522 u32 possible_crtcs) 523 { 524 struct omap_drm_private *priv = dev->dev_private; 525 unsigned int num_planes = dispc_get_num_ovls(priv->dispc); 526 struct drm_plane *plane; 527 struct omap_plane *omap_plane; 528 unsigned int zpos; 529 int ret; 530 u32 nformats; 531 const u32 *formats; 532 533 if (WARN_ON(idx >= num_planes)) 534 return ERR_PTR(-EINVAL); 535 536 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 537 if (!omap_plane) 538 return ERR_PTR(-ENOMEM); 539 540 omap_plane->id = idx; 541 542 DBG("%d: type=%d", omap_plane->id, type); 543 DBG(" crtc_mask: 0x%04x", possible_crtcs); 544 545 formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 546 for (nformats = 0; formats[nformats]; ++nformats) 547 ; 548 549 plane = &omap_plane->base; 550 551 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 552 &omap_plane_funcs, formats, 553 nformats, NULL, type, NULL); 554 if (ret < 0) 555 goto error; 556 557 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 558 559 omap_plane_install_properties(plane, &plane->base); 560 561 /* 562 * Set the zpos default depending on whether we are a primary or overlay 563 * plane. 564 */ 565 if (plane->type == DRM_PLANE_TYPE_PRIMARY) 566 zpos = 0; 567 else 568 zpos = omap_plane->id; 569 drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1); 570 drm_plane_create_alpha_property(plane); 571 drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) | 572 BIT(DRM_MODE_BLEND_COVERAGE)); 573 574 if (omap_plane_supports_yuv(plane)) 575 drm_plane_create_color_properties(plane, 576 BIT(DRM_COLOR_YCBCR_BT601) | 577 BIT(DRM_COLOR_YCBCR_BT709), 578 BIT(DRM_COLOR_YCBCR_FULL_RANGE) | 579 BIT(DRM_COLOR_YCBCR_LIMITED_RANGE), 580 DRM_COLOR_YCBCR_BT601, 581 DRM_COLOR_YCBCR_FULL_RANGE); 582 583 return plane; 584 585 error: 586 dev_err(dev->dev, "%s(): could not create plane: %d\n", 587 __func__, omap_plane->id); 588 589 kfree(omap_plane); 590 return NULL; 591 } 592