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 22 struct 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 32 struct omap_plane { 33 struct drm_plane base; 34 enum omap_plane_id id; 35 }; 36 37 bool 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 44 static 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 55 static 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 62 static 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 167 static 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 199 static 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 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, 367 }; 368 369 static 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 */ 381 void 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 404 static void omap_plane_reset(struct drm_plane *plane) 405 { 406 struct omap_plane *omap_plane = to_omap_plane(plane); 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 * Set the zpos default depending on whether we are a primary or overlay 420 * plane. 421 */ 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; 426 } 427 428 static struct drm_plane_state * 429 omap_plane_atomic_duplicate_state(struct drm_plane *plane) 430 { 431 struct omap_plane_state *state, *current_state; 432 433 if (WARN_ON(!plane->state)) 434 return NULL; 435 436 current_state = to_omap_plane_state(plane->state); 437 438 state = kmalloc(sizeof(*state), GFP_KERNEL); 439 if (!state) 440 return NULL; 441 442 __drm_atomic_helper_plane_duplicate_state(plane, &state->base); 443 444 state->overlay = current_state->overlay; 445 state->r_overlay = current_state->r_overlay; 446 447 return &state->base; 448 } 449 450 static void omap_plane_atomic_print_state(struct drm_printer *p, 451 const struct drm_plane_state *state) 452 { 453 struct omap_plane_state *omap_state = to_omap_plane_state(state); 454 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); 459 else 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); 465 else 466 drm_printf(p, "\tr_overlay=None\n"); 467 } 468 469 static int omap_plane_atomic_set_property(struct drm_plane *plane, 470 struct drm_plane_state *state, 471 struct drm_property *property, 472 u64 val) 473 { 474 struct omap_drm_private *priv = plane->dev->dev_private; 475 476 if (property == priv->zorder_prop) 477 state->zpos = val; 478 else 479 return -EINVAL; 480 481 return 0; 482 } 483 484 static int omap_plane_atomic_get_property(struct drm_plane *plane, 485 const struct drm_plane_state *state, 486 struct drm_property *property, 487 u64 *val) 488 { 489 struct omap_drm_private *priv = plane->dev->dev_private; 490 491 if (property == priv->zorder_prop) 492 *val = state->zpos; 493 else 494 return -EINVAL; 495 496 return 0; 497 } 498 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, 509 }; 510 511 static bool omap_plane_supports_yuv(struct drm_plane *plane) 512 { 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); 516 u32 i; 517 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) 522 return true; 523 524 return false; 525 } 526 527 /* initialize plane */ 528 struct drm_plane *omap_plane_init(struct drm_device *dev, 529 int idx, enum drm_plane_type type, 530 u32 possible_crtcs) 531 { 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; 536 int ret; 537 u32 nformats; 538 const u32 *formats; 539 540 if (WARN_ON(idx >= num_planes)) 541 return ERR_PTR(-EINVAL); 542 543 omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); 544 if (!omap_plane) 545 return ERR_PTR(-ENOMEM); 546 547 omap_plane->id = idx; 548 549 DBG("%d: type=%d", omap_plane->id, type); 550 DBG(" crtc_mask: 0x%04x", possible_crtcs); 551 552 formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id); 553 for (nformats = 0; formats[nformats]; ++nformats) 554 ; 555 556 plane = &omap_plane->base; 557 558 ret = drm_universal_plane_init(dev, plane, possible_crtcs, 559 &omap_plane_funcs, formats, 560 nformats, NULL, type, NULL); 561 if (ret < 0) 562 goto error; 563 564 drm_plane_helper_add(plane, &omap_plane_helper_funcs); 565 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)); 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 583 error: 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 } 590