diff options
| -rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 76 | ||||
| -rw-r--r-- | include/drm/drm_crtc_helper.h | 1 |
2 files changed, 73 insertions, 4 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 733028b4d45e..1c3a8c557140 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c | |||
| @@ -452,6 +452,59 @@ static void drm_setup_crtcs(struct drm_device *dev) | |||
| 452 | kfree(modes); | 452 | kfree(modes); |
| 453 | kfree(enabled); | 453 | kfree(enabled); |
| 454 | } | 454 | } |
| 455 | |||
| 456 | /** | ||
| 457 | * drm_encoder_crtc_ok - can a given crtc drive a given encoder? | ||
| 458 | * @encoder: encoder to test | ||
| 459 | * @crtc: crtc to test | ||
| 460 | * | ||
| 461 | * Return false if @encoder can't be driven by @crtc, true otherwise. | ||
| 462 | */ | ||
| 463 | static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, | ||
| 464 | struct drm_crtc *crtc) | ||
| 465 | { | ||
| 466 | struct drm_device *dev; | ||
| 467 | struct drm_crtc *tmp; | ||
| 468 | int crtc_mask = 1; | ||
| 469 | |||
| 470 | WARN(!crtc, "checking null crtc?"); | ||
| 471 | |||
| 472 | dev = crtc->dev; | ||
| 473 | |||
| 474 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { | ||
| 475 | if (tmp == crtc) | ||
| 476 | break; | ||
| 477 | crtc_mask <<= 1; | ||
| 478 | } | ||
| 479 | |||
| 480 | if (encoder->possible_crtcs & crtc_mask) | ||
| 481 | return true; | ||
| 482 | return false; | ||
| 483 | } | ||
| 484 | |||
| 485 | /* | ||
| 486 | * Check the CRTC we're going to map each output to vs. its current | ||
| 487 | * CRTC. If they don't match, we have to disable the output and the CRTC | ||
| 488 | * since the driver will have to re-route things. | ||
| 489 | */ | ||
| 490 | static void | ||
| 491 | drm_crtc_prepare_encoders(struct drm_device *dev) | ||
| 492 | { | ||
| 493 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
| 494 | struct drm_encoder *encoder; | ||
| 495 | |||
| 496 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
| 497 | encoder_funcs = encoder->helper_private; | ||
| 498 | /* Disable unused encoders */ | ||
| 499 | if (encoder->crtc == NULL) | ||
| 500 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); | ||
| 501 | /* Disable encoders whose CRTC is about to change */ | ||
| 502 | if (encoder_funcs->get_crtc && | ||
| 503 | encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) | ||
| 504 | (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 455 | /** | 508 | /** |
| 456 | * drm_crtc_set_mode - set a mode | 509 | * drm_crtc_set_mode - set a mode |
| 457 | * @crtc: CRTC to program | 510 | * @crtc: CRTC to program |
| @@ -547,6 +600,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, | |||
| 547 | encoder_funcs->prepare(encoder); | 600 | encoder_funcs->prepare(encoder); |
| 548 | } | 601 | } |
| 549 | 602 | ||
| 603 | drm_crtc_prepare_encoders(dev); | ||
| 604 | |||
| 550 | crtc_funcs->prepare(crtc); | 605 | crtc_funcs->prepare(crtc); |
| 551 | 606 | ||
| 552 | /* Set up the DPLL and any encoders state that needs to adjust or depend | 607 | /* Set up the DPLL and any encoders state that needs to adjust or depend |
| @@ -617,7 +672,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 617 | struct drm_device *dev; | 672 | struct drm_device *dev; |
| 618 | struct drm_crtc **save_crtcs, *new_crtc; | 673 | struct drm_crtc **save_crtcs, *new_crtc; |
| 619 | struct drm_encoder **save_encoders, *new_encoder; | 674 | struct drm_encoder **save_encoders, *new_encoder; |
| 620 | struct drm_framebuffer *old_fb; | 675 | struct drm_framebuffer *old_fb = NULL; |
| 621 | bool save_enabled; | 676 | bool save_enabled; |
| 622 | bool mode_changed = false; | 677 | bool mode_changed = false; |
| 623 | bool fb_changed = false; | 678 | bool fb_changed = false; |
| @@ -668,9 +723,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 668 | * and then just flip_or_move it */ | 723 | * and then just flip_or_move it */ |
| 669 | if (set->crtc->fb != set->fb) { | 724 | if (set->crtc->fb != set->fb) { |
| 670 | /* If we have no fb then treat it as a full mode set */ | 725 | /* If we have no fb then treat it as a full mode set */ |
| 671 | if (set->crtc->fb == NULL) | 726 | if (set->crtc->fb == NULL) { |
| 727 | DRM_DEBUG("crtc has no fb, full mode set\n"); | ||
| 672 | mode_changed = true; | 728 | mode_changed = true; |
| 673 | else if ((set->fb->bits_per_pixel != | 729 | } else if ((set->fb->bits_per_pixel != |
| 674 | set->crtc->fb->bits_per_pixel) || | 730 | set->crtc->fb->bits_per_pixel) || |
| 675 | set->fb->depth != set->crtc->fb->depth) | 731 | set->fb->depth != set->crtc->fb->depth) |
| 676 | fb_changed = true; | 732 | fb_changed = true; |
| @@ -682,7 +738,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 682 | fb_changed = true; | 738 | fb_changed = true; |
| 683 | 739 | ||
| 684 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { | 740 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
| 685 | DRM_DEBUG("modes are different\n"); | 741 | DRM_DEBUG("modes are different, full mode set\n"); |
| 686 | drm_mode_debug_printmodeline(&set->crtc->mode); | 742 | drm_mode_debug_printmodeline(&set->crtc->mode); |
| 687 | drm_mode_debug_printmodeline(set->mode); | 743 | drm_mode_debug_printmodeline(set->mode); |
| 688 | mode_changed = true; | 744 | mode_changed = true; |
| @@ -708,6 +764,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 708 | } | 764 | } |
| 709 | 765 | ||
| 710 | if (new_encoder != connector->encoder) { | 766 | if (new_encoder != connector->encoder) { |
| 767 | DRM_DEBUG("encoder changed, full mode switch\n"); | ||
| 711 | mode_changed = true; | 768 | mode_changed = true; |
| 712 | connector->encoder = new_encoder; | 769 | connector->encoder = new_encoder; |
| 713 | } | 770 | } |
| @@ -734,10 +791,20 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 734 | if (set->connectors[ro] == connector) | 791 | if (set->connectors[ro] == connector) |
| 735 | new_crtc = set->crtc; | 792 | new_crtc = set->crtc; |
| 736 | } | 793 | } |
| 794 | |||
| 795 | /* Make sure the new CRTC will work with the encoder */ | ||
| 796 | if (new_crtc && | ||
| 797 | !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { | ||
| 798 | ret = -EINVAL; | ||
| 799 | goto fail_set_mode; | ||
| 800 | } | ||
| 737 | if (new_crtc != connector->encoder->crtc) { | 801 | if (new_crtc != connector->encoder->crtc) { |
| 802 | DRM_DEBUG("crtc changed, full mode switch\n"); | ||
| 738 | mode_changed = true; | 803 | mode_changed = true; |
| 739 | connector->encoder->crtc = new_crtc; | 804 | connector->encoder->crtc = new_crtc; |
| 740 | } | 805 | } |
| 806 | DRM_DEBUG("setting connector %d crtc to %p\n", | ||
| 807 | connector->base.id, new_crtc); | ||
| 741 | } | 808 | } |
| 742 | 809 | ||
| 743 | /* mode_set_base is not a required function */ | 810 | /* mode_set_base is not a required function */ |
| @@ -781,6 +848,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
| 781 | 848 | ||
| 782 | fail_set_mode: | 849 | fail_set_mode: |
| 783 | set->crtc->enabled = save_enabled; | 850 | set->crtc->enabled = save_enabled; |
| 851 | set->crtc->fb = old_fb; | ||
| 784 | count = 0; | 852 | count = 0; |
| 785 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 853 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
| 786 | if (!connector->encoder) | 854 | if (!connector->encoder) |
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 0b0d236c2154..c7d4b2e606a5 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h | |||
| @@ -76,6 +76,7 @@ struct drm_encoder_helper_funcs { | |||
| 76 | void (*mode_set)(struct drm_encoder *encoder, | 76 | void (*mode_set)(struct drm_encoder *encoder, |
| 77 | struct drm_display_mode *mode, | 77 | struct drm_display_mode *mode, |
| 78 | struct drm_display_mode *adjusted_mode); | 78 | struct drm_display_mode *adjusted_mode); |
| 79 | struct drm_crtc *(*get_crtc)(struct drm_encoder *encoder); | ||
| 79 | /* detect for DAC style encoders */ | 80 | /* detect for DAC style encoders */ |
| 80 | enum drm_connector_status (*detect)(struct drm_encoder *encoder, | 81 | enum drm_connector_status (*detect)(struct drm_encoder *encoder, |
| 81 | struct drm_connector *connector); | 82 | struct drm_connector *connector); |
