| .. | .. |
|---|
| 20 | 20 | * OF THIS SOFTWARE. |
|---|
| 21 | 21 | */ |
|---|
| 22 | 22 | |
|---|
| 23 | +#include <linux/uaccess.h> |
|---|
| 24 | + |
|---|
| 25 | +#include <drm/drm_drv.h> |
|---|
| 23 | 26 | #include <drm/drm_encoder.h> |
|---|
| 27 | +#include <drm/drm_file.h> |
|---|
| 28 | +#include <drm/drm_managed.h> |
|---|
| 24 | 29 | #include <drm/drm_mode_config.h> |
|---|
| 25 | | -#include <drm/drmP.h> |
|---|
| 30 | +#include <drm/drm_print.h> |
|---|
| 31 | +#include <linux/dma-resv.h> |
|---|
| 26 | 32 | |
|---|
| 27 | 33 | #include "drm_crtc_internal.h" |
|---|
| 28 | 34 | #include "drm_internal.h" |
|---|
| .. | .. |
|---|
| 97 | 103 | struct drm_connector_list_iter conn_iter; |
|---|
| 98 | 104 | |
|---|
| 99 | 105 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 100 | | - return -EINVAL; |
|---|
| 101 | | - |
|---|
| 106 | + return -EOPNOTSUPP; |
|---|
| 102 | 107 | |
|---|
| 103 | 108 | mutex_lock(&file_priv->fbs_lock); |
|---|
| 104 | 109 | count = 0; |
|---|
| .. | .. |
|---|
| 298 | 303 | return -ENOMEM; |
|---|
| 299 | 304 | dev->mode_config.prop_crtc_id = prop; |
|---|
| 300 | 305 | |
|---|
| 306 | + prop = drm_property_create(dev, |
|---|
| 307 | + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, |
|---|
| 308 | + "FB_DAMAGE_CLIPS", 0); |
|---|
| 309 | + if (!prop) |
|---|
| 310 | + return -ENOMEM; |
|---|
| 311 | + dev->mode_config.prop_fb_damage_clips = prop; |
|---|
| 312 | + |
|---|
| 301 | 313 | prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, |
|---|
| 302 | 314 | "ACTIVE"); |
|---|
| 303 | 315 | if (!prop) |
|---|
| .. | .. |
|---|
| 310 | 322 | if (!prop) |
|---|
| 311 | 323 | return -ENOMEM; |
|---|
| 312 | 324 | dev->mode_config.prop_mode_id = prop; |
|---|
| 325 | + |
|---|
| 326 | + prop = drm_property_create_bool(dev, 0, |
|---|
| 327 | + "VRR_ENABLED"); |
|---|
| 328 | + if (!prop) |
|---|
| 329 | + return -ENOMEM; |
|---|
| 330 | + dev->mode_config.prop_vrr_enabled = prop; |
|---|
| 313 | 331 | |
|---|
| 314 | 332 | prop = drm_property_create(dev, |
|---|
| 315 | 333 | DRM_MODE_PROP_BLOB, |
|---|
| .. | .. |
|---|
| 347 | 365 | dev->mode_config.gamma_lut_size_property = prop; |
|---|
| 348 | 366 | |
|---|
| 349 | 367 | prop = drm_property_create(dev, |
|---|
| 350 | | - DRM_MODE_PROP_BLOB, |
|---|
| 351 | | - "CUBIC_LUT", 0); |
|---|
| 352 | | - if (!prop) |
|---|
| 353 | | - return -ENOMEM; |
|---|
| 354 | | - dev->mode_config.cubic_lut_property = prop; |
|---|
| 355 | | - |
|---|
| 356 | | - prop = drm_property_create_range(dev, |
|---|
| 357 | | - DRM_MODE_PROP_IMMUTABLE, |
|---|
| 358 | | - "CUBIC_LUT_SIZE", 0, UINT_MAX); |
|---|
| 359 | | - if (!prop) |
|---|
| 360 | | - return -ENOMEM; |
|---|
| 361 | | - dev->mode_config.cubic_lut_size_property = prop; |
|---|
| 362 | | - |
|---|
| 363 | | - prop = drm_property_create(dev, |
|---|
| 364 | 368 | DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB, |
|---|
| 365 | 369 | "IN_FORMATS", 0); |
|---|
| 366 | 370 | if (!prop) |
|---|
| .. | .. |
|---|
| 370 | 374 | return 0; |
|---|
| 371 | 375 | } |
|---|
| 372 | 376 | |
|---|
| 377 | +static void drm_mode_config_init_release(struct drm_device *dev, void *ptr) |
|---|
| 378 | +{ |
|---|
| 379 | + drm_mode_config_cleanup(dev); |
|---|
| 380 | +} |
|---|
| 381 | + |
|---|
| 373 | 382 | /** |
|---|
| 374 | | - * drm_mode_config_init - initialize DRM mode_configuration structure |
|---|
| 383 | + * drmm_mode_config_init - managed DRM mode_configuration structure |
|---|
| 384 | + * initialization |
|---|
| 375 | 385 | * @dev: DRM device |
|---|
| 376 | 386 | * |
|---|
| 377 | 387 | * Initialize @dev's mode_config structure, used for tracking the graphics |
|---|
| .. | .. |
|---|
| 381 | 391 | * problem, since this should happen single threaded at init time. It is the |
|---|
| 382 | 392 | * driver's problem to ensure this guarantee. |
|---|
| 383 | 393 | * |
|---|
| 394 | + * Cleanup is automatically handled through registering drm_mode_config_cleanup |
|---|
| 395 | + * with drmm_add_action(). |
|---|
| 396 | + * |
|---|
| 397 | + * Returns: 0 on success, negative error value on failure. |
|---|
| 384 | 398 | */ |
|---|
| 385 | | -void drm_mode_config_init(struct drm_device *dev) |
|---|
| 399 | +int drmm_mode_config_init(struct drm_device *dev) |
|---|
| 386 | 400 | { |
|---|
| 401 | + int ret; |
|---|
| 402 | + |
|---|
| 387 | 403 | mutex_init(&dev->mode_config.mutex); |
|---|
| 388 | 404 | drm_modeset_lock_init(&dev->mode_config.connection_mutex); |
|---|
| 389 | 405 | mutex_init(&dev->mode_config.idr_mutex); |
|---|
| .. | .. |
|---|
| 396 | 412 | INIT_LIST_HEAD(&dev->mode_config.property_list); |
|---|
| 397 | 413 | INIT_LIST_HEAD(&dev->mode_config.property_blob_list); |
|---|
| 398 | 414 | INIT_LIST_HEAD(&dev->mode_config.plane_list); |
|---|
| 399 | | - idr_init(&dev->mode_config.crtc_idr); |
|---|
| 415 | + INIT_LIST_HEAD(&dev->mode_config.privobj_list); |
|---|
| 416 | + idr_init(&dev->mode_config.object_idr); |
|---|
| 400 | 417 | idr_init(&dev->mode_config.tile_idr); |
|---|
| 401 | 418 | ida_init(&dev->mode_config.connector_ida); |
|---|
| 402 | 419 | spin_lock_init(&dev->mode_config.connector_list_lock); |
|---|
| .. | .. |
|---|
| 404 | 421 | init_llist_head(&dev->mode_config.connector_free_list); |
|---|
| 405 | 422 | INIT_WORK(&dev->mode_config.connector_free_work, drm_connector_free_work_fn); |
|---|
| 406 | 423 | |
|---|
| 407 | | - drm_mode_create_standard_properties(dev); |
|---|
| 424 | + ret = drm_mode_create_standard_properties(dev); |
|---|
| 425 | + if (ret) { |
|---|
| 426 | + drm_mode_config_cleanup(dev); |
|---|
| 427 | + return ret; |
|---|
| 428 | + } |
|---|
| 408 | 429 | |
|---|
| 409 | 430 | /* Just to be sure */ |
|---|
| 410 | 431 | dev->mode_config.num_fb = 0; |
|---|
| .. | .. |
|---|
| 412 | 433 | dev->mode_config.num_crtc = 0; |
|---|
| 413 | 434 | dev->mode_config.num_encoder = 0; |
|---|
| 414 | 435 | dev->mode_config.num_total_plane = 0; |
|---|
| 436 | + |
|---|
| 437 | + if (IS_ENABLED(CONFIG_LOCKDEP)) { |
|---|
| 438 | + struct drm_modeset_acquire_ctx modeset_ctx; |
|---|
| 439 | + struct ww_acquire_ctx resv_ctx; |
|---|
| 440 | + struct dma_resv resv; |
|---|
| 441 | + int ret; |
|---|
| 442 | + |
|---|
| 443 | + dma_resv_init(&resv); |
|---|
| 444 | + |
|---|
| 445 | + drm_modeset_acquire_init(&modeset_ctx, 0); |
|---|
| 446 | + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, |
|---|
| 447 | + &modeset_ctx); |
|---|
| 448 | + if (ret == -EDEADLK) |
|---|
| 449 | + ret = drm_modeset_backoff(&modeset_ctx); |
|---|
| 450 | + |
|---|
| 451 | + ww_acquire_init(&resv_ctx, &reservation_ww_class); |
|---|
| 452 | + ret = dma_resv_lock(&resv, &resv_ctx); |
|---|
| 453 | + if (ret == -EDEADLK) |
|---|
| 454 | + dma_resv_lock_slow(&resv, &resv_ctx); |
|---|
| 455 | + |
|---|
| 456 | + dma_resv_unlock(&resv); |
|---|
| 457 | + ww_acquire_fini(&resv_ctx); |
|---|
| 458 | + |
|---|
| 459 | + drm_modeset_drop_locks(&modeset_ctx); |
|---|
| 460 | + drm_modeset_acquire_fini(&modeset_ctx); |
|---|
| 461 | + dma_resv_fini(&resv); |
|---|
| 462 | + } |
|---|
| 463 | + |
|---|
| 464 | + return drmm_add_action_or_reset(dev, drm_mode_config_init_release, |
|---|
| 465 | + NULL); |
|---|
| 415 | 466 | } |
|---|
| 416 | | -EXPORT_SYMBOL(drm_mode_config_init); |
|---|
| 467 | +EXPORT_SYMBOL(drmm_mode_config_init); |
|---|
| 417 | 468 | |
|---|
| 418 | 469 | /** |
|---|
| 419 | 470 | * drm_mode_config_cleanup - free up DRM mode_config info |
|---|
| .. | .. |
|---|
| 426 | 477 | * teardown time, no locking is required. It's the driver's job to ensure that |
|---|
| 427 | 478 | * this guarantee actually holds true. |
|---|
| 428 | 479 | * |
|---|
| 429 | | - * FIXME: cleanup any dangling user buffer objects too |
|---|
| 480 | + * FIXME: With the managed drmm_mode_config_init() it is no longer necessary for |
|---|
| 481 | + * drivers to explicitly call this function. |
|---|
| 430 | 482 | */ |
|---|
| 431 | 483 | void drm_mode_config_cleanup(struct drm_device *dev) |
|---|
| 432 | 484 | { |
|---|
| .. | .. |
|---|
| 492 | 544 | WARN_ON(!list_empty(&dev->mode_config.fb_list)); |
|---|
| 493 | 545 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
|---|
| 494 | 546 | struct drm_printer p = drm_debug_printer("[leaked fb]"); |
|---|
| 547 | + |
|---|
| 495 | 548 | drm_printf(&p, "framebuffer[%u]:\n", fb->base.id); |
|---|
| 496 | 549 | drm_framebuffer_print_info(&p, 1, fb); |
|---|
| 497 | 550 | drm_framebuffer_free(&fb->base.refcount); |
|---|
| .. | .. |
|---|
| 499 | 552 | |
|---|
| 500 | 553 | ida_destroy(&dev->mode_config.connector_ida); |
|---|
| 501 | 554 | idr_destroy(&dev->mode_config.tile_idr); |
|---|
| 502 | | - idr_destroy(&dev->mode_config.crtc_idr); |
|---|
| 555 | + idr_destroy(&dev->mode_config.object_idr); |
|---|
| 503 | 556 | drm_modeset_lock_fini(&dev->mode_config.connection_mutex); |
|---|
| 504 | 557 | } |
|---|
| 505 | 558 | EXPORT_SYMBOL(drm_mode_config_cleanup); |
|---|
| 559 | + |
|---|
| 560 | +static u32 full_encoder_mask(struct drm_device *dev) |
|---|
| 561 | +{ |
|---|
| 562 | + struct drm_encoder *encoder; |
|---|
| 563 | + u32 encoder_mask = 0; |
|---|
| 564 | + |
|---|
| 565 | + drm_for_each_encoder(encoder, dev) |
|---|
| 566 | + encoder_mask |= drm_encoder_mask(encoder); |
|---|
| 567 | + |
|---|
| 568 | + return encoder_mask; |
|---|
| 569 | +} |
|---|
| 570 | + |
|---|
| 571 | +/* |
|---|
| 572 | + * For some reason we want the encoder itself included in |
|---|
| 573 | + * possible_clones. Make life easy for drivers by allowing them |
|---|
| 574 | + * to leave possible_clones unset if no cloning is possible. |
|---|
| 575 | + */ |
|---|
| 576 | +static void fixup_encoder_possible_clones(struct drm_encoder *encoder) |
|---|
| 577 | +{ |
|---|
| 578 | + if (encoder->possible_clones == 0) |
|---|
| 579 | + encoder->possible_clones = drm_encoder_mask(encoder); |
|---|
| 580 | +} |
|---|
| 581 | + |
|---|
| 582 | +static void validate_encoder_possible_clones(struct drm_encoder *encoder) |
|---|
| 583 | +{ |
|---|
| 584 | + struct drm_device *dev = encoder->dev; |
|---|
| 585 | + u32 encoder_mask = full_encoder_mask(dev); |
|---|
| 586 | + struct drm_encoder *other; |
|---|
| 587 | + |
|---|
| 588 | + drm_for_each_encoder(other, dev) { |
|---|
| 589 | + WARN(!!(encoder->possible_clones & drm_encoder_mask(other)) != |
|---|
| 590 | + !!(other->possible_clones & drm_encoder_mask(encoder)), |
|---|
| 591 | + "possible_clones mismatch: " |
|---|
| 592 | + "[ENCODER:%d:%s] mask=0x%x possible_clones=0x%x vs. " |
|---|
| 593 | + "[ENCODER:%d:%s] mask=0x%x possible_clones=0x%x\n", |
|---|
| 594 | + encoder->base.id, encoder->name, |
|---|
| 595 | + drm_encoder_mask(encoder), encoder->possible_clones, |
|---|
| 596 | + other->base.id, other->name, |
|---|
| 597 | + drm_encoder_mask(other), other->possible_clones); |
|---|
| 598 | + } |
|---|
| 599 | + |
|---|
| 600 | + WARN((encoder->possible_clones & drm_encoder_mask(encoder)) == 0 || |
|---|
| 601 | + (encoder->possible_clones & ~encoder_mask) != 0, |
|---|
| 602 | + "Bogus possible_clones: " |
|---|
| 603 | + "[ENCODER:%d:%s] possible_clones=0x%x (full encoder mask=0x%x)\n", |
|---|
| 604 | + encoder->base.id, encoder->name, |
|---|
| 605 | + encoder->possible_clones, encoder_mask); |
|---|
| 606 | +} |
|---|
| 607 | + |
|---|
| 608 | +static u32 full_crtc_mask(struct drm_device *dev) |
|---|
| 609 | +{ |
|---|
| 610 | + struct drm_crtc *crtc; |
|---|
| 611 | + u32 crtc_mask = 0; |
|---|
| 612 | + |
|---|
| 613 | + drm_for_each_crtc(crtc, dev) |
|---|
| 614 | + crtc_mask |= drm_crtc_mask(crtc); |
|---|
| 615 | + |
|---|
| 616 | + return crtc_mask; |
|---|
| 617 | +} |
|---|
| 618 | + |
|---|
| 619 | +static void validate_encoder_possible_crtcs(struct drm_encoder *encoder) |
|---|
| 620 | +{ |
|---|
| 621 | + u32 crtc_mask = full_crtc_mask(encoder->dev); |
|---|
| 622 | + |
|---|
| 623 | + WARN((encoder->possible_crtcs & crtc_mask) == 0 || |
|---|
| 624 | + (encoder->possible_crtcs & ~crtc_mask) != 0, |
|---|
| 625 | + "Bogus possible_crtcs: " |
|---|
| 626 | + "[ENCODER:%d:%s] possible_crtcs=0x%x (full crtc mask=0x%x)\n", |
|---|
| 627 | + encoder->base.id, encoder->name, |
|---|
| 628 | + encoder->possible_crtcs, crtc_mask); |
|---|
| 629 | +} |
|---|
| 630 | + |
|---|
| 631 | +void drm_mode_config_validate(struct drm_device *dev) |
|---|
| 632 | +{ |
|---|
| 633 | + struct drm_encoder *encoder; |
|---|
| 634 | + |
|---|
| 635 | + if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
|---|
| 636 | + return; |
|---|
| 637 | + |
|---|
| 638 | + drm_for_each_encoder(encoder, dev) |
|---|
| 639 | + fixup_encoder_possible_clones(encoder); |
|---|
| 640 | + |
|---|
| 641 | + drm_for_each_encoder(encoder, dev) { |
|---|
| 642 | + validate_encoder_possible_clones(encoder); |
|---|
| 643 | + validate_encoder_possible_crtcs(encoder); |
|---|
| 644 | + } |
|---|
| 645 | +} |
|---|