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