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_fourcc.h> 12 #include <drm/drm_framebuffer.h> 13 14 #include "omap_dmm_tiler.h" 15 #include "omap_drv.h" 16 17 /* 18 * plane funcs 19 */ 20 21 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base) 22 23 struct 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 33 struct omap_plane { 34 struct drm_plane base; 35 enum omap_plane_id id; 36 }; 37 38 bool 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 45 static 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 56 static 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 63 static 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 else 106 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 next 126 * zorder is used by the r_overlay so we just use the 127 * main overlay zorder + 1 128 */ 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 168 static 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 200 static 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 scaling 239 * factors. The real limits must be calculated case by case, and 240 * unfortunately we currently do those checks only at the commit 241 * 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 width 281 * and it yield an odd value we will need to adjust 282 * the indivual width +/- 1. So make sure it fits 283 */ 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 else 288 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 else 294 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 or 310 * there is a caps mismatch 311 */ 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 or 323 * if we had 2 overlays but will only need 1 324 */ 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 else 351 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 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, 368 }; 369 370 static 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 */ 382 void 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 405 static 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 419 static struct drm_plane_state * 420 omap_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 441 static 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 else 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); 456 else 457 drm_printf(p, "\tr_overlay=None\n"); 458 } 459 460 static 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 else 470 return -EINVAL; 471 472 return 0; 473 } 474 475 static 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 else 485 return -EINVAL; 486 487 return 0; 488 } 489 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, 500 }; 501 502 static 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 */ 519 struct 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 overlay 562 * plane. 563 */ 564 if (plane->type == DRM_PLANE_TYPE_PRIMARY) 565 zpos = 0; 566 else 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)); 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 584 error: 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