| .. | .. |
|---|
| 32 | 32 | #include <linux/export.h> |
|---|
| 33 | 33 | #include <linux/moduleparam.h> |
|---|
| 34 | 34 | |
|---|
| 35 | | -#include <drm/drmP.h> |
|---|
| 35 | +#include <drm/drm_bridge.h> |
|---|
| 36 | 36 | #include <drm/drm_client.h> |
|---|
| 37 | 37 | #include <drm/drm_crtc.h> |
|---|
| 38 | | -#include <drm/drm_fourcc.h> |
|---|
| 39 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 40 | | -#include <drm/drm_fb_helper.h> |
|---|
| 41 | 38 | #include <drm/drm_edid.h> |
|---|
| 39 | +#include <drm/drm_fb_helper.h> |
|---|
| 40 | +#include <drm/drm_fourcc.h> |
|---|
| 42 | 41 | #include <drm/drm_modeset_helper_vtables.h> |
|---|
| 42 | +#include <drm/drm_print.h> |
|---|
| 43 | +#include <drm/drm_probe_helper.h> |
|---|
| 44 | +#include <drm/drm_sysfs.h> |
|---|
| 43 | 45 | |
|---|
| 44 | 46 | #include "drm_crtc_helper_internal.h" |
|---|
| 45 | 47 | |
|---|
| .. | .. |
|---|
| 84 | 86 | return MODE_OK; |
|---|
| 85 | 87 | } |
|---|
| 86 | 88 | |
|---|
| 87 | | -static enum drm_mode_status |
|---|
| 89 | +static int |
|---|
| 88 | 90 | drm_mode_validate_pipeline(struct drm_display_mode *mode, |
|---|
| 89 | | - struct drm_connector *connector) |
|---|
| 91 | + struct drm_connector *connector, |
|---|
| 92 | + struct drm_modeset_acquire_ctx *ctx, |
|---|
| 93 | + enum drm_mode_status *status) |
|---|
| 90 | 94 | { |
|---|
| 91 | 95 | struct drm_device *dev = connector->dev; |
|---|
| 92 | | - enum drm_mode_status ret = MODE_OK; |
|---|
| 93 | 96 | struct drm_encoder *encoder; |
|---|
| 94 | | - int i; |
|---|
| 97 | + int ret; |
|---|
| 95 | 98 | |
|---|
| 96 | 99 | /* Step 1: Validate against connector */ |
|---|
| 97 | | - ret = drm_connector_mode_valid(connector, mode); |
|---|
| 98 | | - if (ret != MODE_OK) |
|---|
| 100 | + ret = drm_connector_mode_valid(connector, mode, ctx, status); |
|---|
| 101 | + if (ret || *status != MODE_OK) |
|---|
| 99 | 102 | return ret; |
|---|
| 100 | 103 | |
|---|
| 101 | 104 | /* Step 2: Validate against encoders and crtcs */ |
|---|
| 102 | | - drm_connector_for_each_possible_encoder(connector, encoder, i) { |
|---|
| 105 | + drm_connector_for_each_possible_encoder(connector, encoder) { |
|---|
| 106 | + struct drm_bridge *bridge; |
|---|
| 103 | 107 | struct drm_crtc *crtc; |
|---|
| 104 | 108 | |
|---|
| 105 | | - ret = drm_encoder_mode_valid(encoder, mode); |
|---|
| 106 | | - if (ret != MODE_OK) { |
|---|
| 109 | + *status = drm_encoder_mode_valid(encoder, mode); |
|---|
| 110 | + if (*status != MODE_OK) { |
|---|
| 107 | 111 | /* No point in continuing for crtc check as this encoder |
|---|
| 108 | 112 | * will not accept the mode anyway. If all encoders |
|---|
| 109 | 113 | * reject the mode then, at exit, ret will not be |
|---|
| .. | .. |
|---|
| 111 | 115 | continue; |
|---|
| 112 | 116 | } |
|---|
| 113 | 117 | |
|---|
| 114 | | - ret = drm_bridge_mode_valid(encoder->bridge, mode); |
|---|
| 115 | | - if (ret != MODE_OK) { |
|---|
| 118 | + bridge = drm_bridge_chain_get_first_bridge(encoder); |
|---|
| 119 | + *status = drm_bridge_chain_mode_valid(bridge, |
|---|
| 120 | + &connector->display_info, |
|---|
| 121 | + mode); |
|---|
| 122 | + if (*status != MODE_OK) { |
|---|
| 116 | 123 | /* There is also no point in continuing for crtc check |
|---|
| 117 | 124 | * here. */ |
|---|
| 118 | 125 | continue; |
|---|
| .. | .. |
|---|
| 122 | 129 | if (!drm_encoder_crtc_ok(encoder, crtc)) |
|---|
| 123 | 130 | continue; |
|---|
| 124 | 131 | |
|---|
| 125 | | - ret = drm_crtc_mode_valid(crtc, mode); |
|---|
| 126 | | - if (ret == MODE_OK) { |
|---|
| 132 | + *status = drm_crtc_mode_valid(crtc, mode); |
|---|
| 133 | + if (*status == MODE_OK) { |
|---|
| 127 | 134 | /* If we get to this point there is at least |
|---|
| 128 | 135 | * one combination of encoder+crtc that works |
|---|
| 129 | 136 | * for this mode. Lets return now. */ |
|---|
| 130 | | - return ret; |
|---|
| 137 | + return 0; |
|---|
| 131 | 138 | } |
|---|
| 132 | 139 | } |
|---|
| 133 | 140 | } |
|---|
| 134 | 141 | |
|---|
| 135 | | - return ret; |
|---|
| 142 | + return 0; |
|---|
| 136 | 143 | } |
|---|
| 137 | 144 | |
|---|
| 138 | 145 | static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) |
|---|
| .. | .. |
|---|
| 156 | 163 | continue; |
|---|
| 157 | 164 | } |
|---|
| 158 | 165 | |
|---|
| 166 | + /* Mark the matching mode as being preferred by the user */ |
|---|
| 167 | + mode->type |= DRM_MODE_TYPE_USERDEF; |
|---|
| 159 | 168 | return 0; |
|---|
| 160 | 169 | } |
|---|
| 161 | 170 | |
|---|
| .. | .. |
|---|
| 191 | 200 | return encoder_funcs->mode_valid(encoder, mode); |
|---|
| 192 | 201 | } |
|---|
| 193 | 202 | |
|---|
| 194 | | -enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector, |
|---|
| 195 | | - struct drm_display_mode *mode) |
|---|
| 203 | +int |
|---|
| 204 | +drm_connector_mode_valid(struct drm_connector *connector, |
|---|
| 205 | + struct drm_display_mode *mode, |
|---|
| 206 | + struct drm_modeset_acquire_ctx *ctx, |
|---|
| 207 | + enum drm_mode_status *status) |
|---|
| 196 | 208 | { |
|---|
| 197 | 209 | const struct drm_connector_helper_funcs *connector_funcs = |
|---|
| 198 | 210 | connector->helper_private; |
|---|
| 211 | + int ret = 0; |
|---|
| 199 | 212 | |
|---|
| 200 | | - if (!connector_funcs || !connector_funcs->mode_valid) |
|---|
| 201 | | - return MODE_OK; |
|---|
| 213 | + if (!connector_funcs) |
|---|
| 214 | + *status = MODE_OK; |
|---|
| 215 | + else if (connector_funcs->mode_valid_ctx) |
|---|
| 216 | + ret = connector_funcs->mode_valid_ctx(connector, mode, ctx, |
|---|
| 217 | + status); |
|---|
| 218 | + else if (connector_funcs->mode_valid) |
|---|
| 219 | + *status = connector_funcs->mode_valid(connector, mode); |
|---|
| 220 | + else |
|---|
| 221 | + *status = MODE_OK; |
|---|
| 202 | 222 | |
|---|
| 203 | | - return connector_funcs->mode_valid(connector, mode); |
|---|
| 223 | + return ret; |
|---|
| 204 | 224 | } |
|---|
| 205 | 225 | |
|---|
| 206 | 226 | #define DRM_OUTPUT_POLL_PERIOD (10*HZ) |
|---|
| .. | .. |
|---|
| 285 | 305 | if (WARN_ON(ret < 0)) |
|---|
| 286 | 306 | ret = connector_status_unknown; |
|---|
| 287 | 307 | |
|---|
| 308 | + if (ret != connector->status) |
|---|
| 309 | + connector->epoch_counter += 1; |
|---|
| 310 | + |
|---|
| 288 | 311 | drm_modeset_drop_locks(&ctx); |
|---|
| 289 | 312 | drm_modeset_acquire_fini(&ctx); |
|---|
| 290 | 313 | |
|---|
| .. | .. |
|---|
| 318 | 341 | return ret; |
|---|
| 319 | 342 | |
|---|
| 320 | 343 | if (funcs->detect_ctx) |
|---|
| 321 | | - return funcs->detect_ctx(connector, ctx, force); |
|---|
| 344 | + ret = funcs->detect_ctx(connector, ctx, force); |
|---|
| 322 | 345 | else if (connector->funcs->detect) |
|---|
| 323 | | - return connector->funcs->detect(connector, force); |
|---|
| 346 | + ret = connector->funcs->detect(connector, force); |
|---|
| 324 | 347 | else |
|---|
| 325 | | - return connector_status_connected; |
|---|
| 348 | + ret = connector_status_connected; |
|---|
| 349 | + |
|---|
| 350 | + if (ret != connector->status) |
|---|
| 351 | + connector->epoch_counter += 1; |
|---|
| 352 | + |
|---|
| 353 | + return ret; |
|---|
| 326 | 354 | } |
|---|
| 327 | 355 | EXPORT_SYMBOL(drm_helper_probe_detect); |
|---|
| 328 | 356 | |
|---|
| .. | .. |
|---|
| 370 | 398 | * (if specified) |
|---|
| 371 | 399 | * - drm_mode_validate_flag() checks the modes against basic connector |
|---|
| 372 | 400 | * capabilities (interlace_allowed,doublescan_allowed,stereo_allowed) |
|---|
| 373 | | - * - the optional &drm_connector_helper_funcs.mode_valid helper can perform |
|---|
| 374 | | - * driver and/or sink specific checks |
|---|
| 401 | + * - the optional &drm_connector_helper_funcs.mode_valid or |
|---|
| 402 | + * &drm_connector_helper_funcs.mode_valid_ctx helpers can perform driver |
|---|
| 403 | + * and/or sink specific checks |
|---|
| 375 | 404 | * - the optional &drm_crtc_helper_funcs.mode_valid, |
|---|
| 376 | 405 | * &drm_bridge_funcs.mode_valid and &drm_encoder_helper_funcs.mode_valid |
|---|
| 377 | 406 | * helpers can perform driver and/or source specific checks which are also |
|---|
| .. | .. |
|---|
| 502 | 531 | mode_flags |= DRM_MODE_FLAG_3D_MASK; |
|---|
| 503 | 532 | |
|---|
| 504 | 533 | list_for_each_entry(mode, &connector->modes, head) { |
|---|
| 505 | | - if (mode->status == MODE_OK) |
|---|
| 506 | | - mode->status = drm_mode_validate_driver(dev, mode); |
|---|
| 534 | + if (mode->status != MODE_OK) |
|---|
| 535 | + continue; |
|---|
| 507 | 536 | |
|---|
| 508 | | - if (mode->status == MODE_OK) |
|---|
| 509 | | - mode->status = drm_mode_validate_size(mode, maxX, maxY); |
|---|
| 537 | + mode->status = drm_mode_validate_driver(dev, mode); |
|---|
| 538 | + if (mode->status != MODE_OK) |
|---|
| 539 | + continue; |
|---|
| 510 | 540 | |
|---|
| 511 | | - if (mode->status == MODE_OK) |
|---|
| 512 | | - mode->status = drm_mode_validate_flag(mode, mode_flags); |
|---|
| 541 | + mode->status = drm_mode_validate_size(mode, maxX, maxY); |
|---|
| 542 | + if (mode->status != MODE_OK) |
|---|
| 543 | + continue; |
|---|
| 513 | 544 | |
|---|
| 514 | | - if (mode->status == MODE_OK) |
|---|
| 515 | | - mode->status = drm_mode_validate_pipeline(mode, |
|---|
| 516 | | - connector); |
|---|
| 545 | + mode->status = drm_mode_validate_flag(mode, mode_flags); |
|---|
| 546 | + if (mode->status != MODE_OK) |
|---|
| 547 | + continue; |
|---|
| 517 | 548 | |
|---|
| 518 | | - if (mode->status == MODE_OK) |
|---|
| 519 | | - mode->status = drm_mode_validate_ycbcr420(mode, |
|---|
| 520 | | - connector); |
|---|
| 549 | + ret = drm_mode_validate_pipeline(mode, connector, &ctx, |
|---|
| 550 | + &mode->status); |
|---|
| 551 | + if (ret) { |
|---|
| 552 | + drm_dbg_kms(dev, |
|---|
| 553 | + "drm_mode_validate_pipeline failed: %d\n", |
|---|
| 554 | + ret); |
|---|
| 555 | + |
|---|
| 556 | + if (drm_WARN_ON_ONCE(dev, ret != -EDEADLK)) { |
|---|
| 557 | + mode->status = MODE_ERROR; |
|---|
| 558 | + } else { |
|---|
| 559 | + drm_modeset_backoff(&ctx); |
|---|
| 560 | + goto retry; |
|---|
| 561 | + } |
|---|
| 562 | + } |
|---|
| 563 | + |
|---|
| 564 | + if (mode->status != MODE_OK) |
|---|
| 565 | + continue; |
|---|
| 566 | + mode->status = drm_mode_validate_ycbcr420(mode, connector); |
|---|
| 521 | 567 | } |
|---|
| 522 | 568 | |
|---|
| 523 | 569 | prune: |
|---|
| .. | .. |
|---|
| 528 | 574 | |
|---|
| 529 | 575 | if (list_empty(&connector->modes)) |
|---|
| 530 | 576 | return 0; |
|---|
| 531 | | - |
|---|
| 532 | | - list_for_each_entry(mode, &connector->modes, head) |
|---|
| 533 | | - mode->vrefresh = drm_mode_vrefresh(mode); |
|---|
| 534 | 577 | |
|---|
| 535 | 578 | drm_mode_sort(&connector->modes); |
|---|
| 536 | 579 | |
|---|
| .. | .. |
|---|
| 580 | 623 | struct drm_connector_list_iter conn_iter; |
|---|
| 581 | 624 | enum drm_connector_status old_status; |
|---|
| 582 | 625 | bool repoll = false, changed; |
|---|
| 626 | + u64 old_epoch_counter; |
|---|
| 583 | 627 | |
|---|
| 584 | 628 | if (!dev->mode_config.poll_enabled) |
|---|
| 585 | 629 | return; |
|---|
| .. | .. |
|---|
| 616 | 660 | |
|---|
| 617 | 661 | repoll = true; |
|---|
| 618 | 662 | |
|---|
| 663 | + old_epoch_counter = connector->epoch_counter; |
|---|
| 619 | 664 | connector->status = drm_helper_probe_detect(connector, NULL, false); |
|---|
| 620 | | - if (old_status != connector->status) { |
|---|
| 665 | + if (old_epoch_counter != connector->epoch_counter) { |
|---|
| 621 | 666 | const char *old, *new; |
|---|
| 622 | 667 | |
|---|
| 623 | 668 | /* |
|---|
| .. | .. |
|---|
| 646 | 691 | connector->base.id, |
|---|
| 647 | 692 | connector->name, |
|---|
| 648 | 693 | old, new); |
|---|
| 694 | + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] epoch counter %llu -> %llu\n", |
|---|
| 695 | + connector->base.id, connector->name, |
|---|
| 696 | + old_epoch_counter, connector->epoch_counter); |
|---|
| 649 | 697 | |
|---|
| 650 | 698 | changed = true; |
|---|
| 651 | 699 | } |
|---|
| .. | .. |
|---|
| 775 | 823 | struct drm_connector_list_iter conn_iter; |
|---|
| 776 | 824 | enum drm_connector_status old_status; |
|---|
| 777 | 825 | bool changed = false; |
|---|
| 826 | + u64 old_epoch_counter; |
|---|
| 778 | 827 | |
|---|
| 779 | 828 | if (!dev->mode_config.poll_enabled) |
|---|
| 780 | 829 | return false; |
|---|
| .. | .. |
|---|
| 788 | 837 | |
|---|
| 789 | 838 | old_status = connector->status; |
|---|
| 790 | 839 | |
|---|
| 840 | + old_epoch_counter = connector->epoch_counter; |
|---|
| 841 | + |
|---|
| 842 | + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Old epoch counter %llu\n", connector->base.id, |
|---|
| 843 | + connector->name, |
|---|
| 844 | + old_epoch_counter); |
|---|
| 845 | + |
|---|
| 791 | 846 | connector->status = drm_helper_probe_detect(connector, NULL, false); |
|---|
| 792 | 847 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", |
|---|
| 793 | 848 | connector->base.id, |
|---|
| 794 | 849 | connector->name, |
|---|
| 795 | 850 | drm_get_connector_status_name(old_status), |
|---|
| 796 | 851 | drm_get_connector_status_name(connector->status)); |
|---|
| 797 | | - if (old_status != connector->status) |
|---|
| 852 | + |
|---|
| 853 | + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] New epoch counter %llu\n", |
|---|
| 854 | + connector->base.id, |
|---|
| 855 | + connector->name, |
|---|
| 856 | + connector->epoch_counter); |
|---|
| 857 | + |
|---|
| 858 | + /* |
|---|
| 859 | + * Check if epoch counter had changed, meaning that we need |
|---|
| 860 | + * to send a uevent. |
|---|
| 861 | + */ |
|---|
| 862 | + if (old_epoch_counter != connector->epoch_counter) |
|---|
| 798 | 863 | changed = true; |
|---|
| 864 | + |
|---|
| 799 | 865 | } |
|---|
| 800 | 866 | drm_connector_list_iter_end(&conn_iter); |
|---|
| 801 | 867 | mutex_unlock(&dev->mode_config.mutex); |
|---|
| 802 | 868 | |
|---|
| 803 | | - if (changed) |
|---|
| 869 | + if (changed) { |
|---|
| 804 | 870 | drm_kms_helper_hotplug_event(dev); |
|---|
| 871 | + DRM_DEBUG_KMS("Sent hotplug event\n"); |
|---|
| 872 | + } |
|---|
| 805 | 873 | |
|---|
| 806 | 874 | return changed; |
|---|
| 807 | 875 | } |
|---|