diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 97 |
1 files changed, 88 insertions, 9 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 964c5eb1fad..1c3a8c55714 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 |
@@ -512,8 +565,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, | |||
512 | if (drm_mode_equal(&saved_mode, &crtc->mode)) { | 565 | if (drm_mode_equal(&saved_mode, &crtc->mode)) { |
513 | if (saved_x != crtc->x || saved_y != crtc->y || | 566 | if (saved_x != crtc->x || saved_y != crtc->y || |
514 | depth_changed || bpp_changed) { | 567 | depth_changed || bpp_changed) { |
515 | crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, | 568 | ret = !crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, |
516 | old_fb); | 569 | old_fb); |
517 | goto done; | 570 | goto done; |
518 | } | 571 | } |
519 | } | 572 | } |
@@ -547,12 +600,16 @@ 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 |
553 | * on the DPLL. | 608 | * on the DPLL. |
554 | */ | 609 | */ |
555 | crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); | 610 | ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); |
611 | if (!ret) | ||
612 | goto done; | ||
556 | 613 | ||
557 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 614 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
558 | 615 | ||
@@ -615,7 +672,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
615 | struct drm_device *dev; | 672 | struct drm_device *dev; |
616 | struct drm_crtc **save_crtcs, *new_crtc; | 673 | struct drm_crtc **save_crtcs, *new_crtc; |
617 | struct drm_encoder **save_encoders, *new_encoder; | 674 | struct drm_encoder **save_encoders, *new_encoder; |
618 | struct drm_framebuffer *old_fb; | 675 | struct drm_framebuffer *old_fb = NULL; |
619 | bool save_enabled; | 676 | bool save_enabled; |
620 | bool mode_changed = false; | 677 | bool mode_changed = false; |
621 | bool fb_changed = false; | 678 | bool fb_changed = false; |
@@ -666,9 +723,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
666 | * and then just flip_or_move it */ | 723 | * and then just flip_or_move it */ |
667 | if (set->crtc->fb != set->fb) { | 724 | if (set->crtc->fb != set->fb) { |
668 | /* 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 */ |
669 | if (set->crtc->fb == NULL) | 726 | if (set->crtc->fb == NULL) { |
727 | DRM_DEBUG("crtc has no fb, full mode set\n"); | ||
670 | mode_changed = true; | 728 | mode_changed = true; |
671 | else if ((set->fb->bits_per_pixel != | 729 | } else if ((set->fb->bits_per_pixel != |
672 | set->crtc->fb->bits_per_pixel) || | 730 | set->crtc->fb->bits_per_pixel) || |
673 | set->fb->depth != set->crtc->fb->depth) | 731 | set->fb->depth != set->crtc->fb->depth) |
674 | fb_changed = true; | 732 | fb_changed = true; |
@@ -680,7 +738,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
680 | fb_changed = true; | 738 | fb_changed = true; |
681 | 739 | ||
682 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { | 740 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { |
683 | DRM_DEBUG("modes are different\n"); | 741 | DRM_DEBUG("modes are different, full mode set\n"); |
684 | drm_mode_debug_printmodeline(&set->crtc->mode); | 742 | drm_mode_debug_printmodeline(&set->crtc->mode); |
685 | drm_mode_debug_printmodeline(set->mode); | 743 | drm_mode_debug_printmodeline(set->mode); |
686 | mode_changed = true; | 744 | mode_changed = true; |
@@ -706,6 +764,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
706 | } | 764 | } |
707 | 765 | ||
708 | if (new_encoder != connector->encoder) { | 766 | if (new_encoder != connector->encoder) { |
767 | DRM_DEBUG("encoder changed, full mode switch\n"); | ||
709 | mode_changed = true; | 768 | mode_changed = true; |
710 | connector->encoder = new_encoder; | 769 | connector->encoder = new_encoder; |
711 | } | 770 | } |
@@ -732,10 +791,20 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
732 | if (set->connectors[ro] == connector) | 791 | if (set->connectors[ro] == connector) |
733 | new_crtc = set->crtc; | 792 | new_crtc = set->crtc; |
734 | } | 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 | } | ||
735 | if (new_crtc != connector->encoder->crtc) { | 801 | if (new_crtc != connector->encoder->crtc) { |
802 | DRM_DEBUG("crtc changed, full mode switch\n"); | ||
736 | mode_changed = true; | 803 | mode_changed = true; |
737 | connector->encoder->crtc = new_crtc; | 804 | connector->encoder->crtc = new_crtc; |
738 | } | 805 | } |
806 | DRM_DEBUG("setting connector %d crtc to %p\n", | ||
807 | connector->base.id, new_crtc); | ||
739 | } | 808 | } |
740 | 809 | ||
741 | /* mode_set_base is not a required function */ | 810 | /* mode_set_base is not a required function */ |
@@ -752,6 +821,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
752 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, | 821 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, |
753 | set->x, set->y, | 822 | set->x, set->y, |
754 | old_fb)) { | 823 | old_fb)) { |
824 | DRM_ERROR("failed to set mode on crtc %p\n", | ||
825 | set->crtc); | ||
755 | ret = -EINVAL; | 826 | ret = -EINVAL; |
756 | goto fail_set_mode; | 827 | goto fail_set_mode; |
757 | } | 828 | } |
@@ -765,7 +836,10 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
765 | old_fb = set->crtc->fb; | 836 | old_fb = set->crtc->fb; |
766 | if (set->crtc->fb != set->fb) | 837 | if (set->crtc->fb != set->fb) |
767 | set->crtc->fb = set->fb; | 838 | set->crtc->fb = set->fb; |
768 | crtc_funcs->mode_set_base(set->crtc, set->x, set->y, old_fb); | 839 | ret = crtc_funcs->mode_set_base(set->crtc, |
840 | set->x, set->y, old_fb); | ||
841 | if (ret != 0) | ||
842 | goto fail_set_mode; | ||
769 | } | 843 | } |
770 | 844 | ||
771 | kfree(save_encoders); | 845 | kfree(save_encoders); |
@@ -774,9 +848,14 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) | |||
774 | 848 | ||
775 | fail_set_mode: | 849 | fail_set_mode: |
776 | set->crtc->enabled = save_enabled; | 850 | set->crtc->enabled = save_enabled; |
851 | set->crtc->fb = old_fb; | ||
777 | count = 0; | 852 | count = 0; |
778 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) | 853 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
854 | if (!connector->encoder) | ||
855 | continue; | ||
856 | |||
779 | connector->encoder->crtc = save_crtcs[count++]; | 857 | connector->encoder->crtc = save_crtcs[count++]; |
858 | } | ||
780 | fail_no_encoder: | 859 | fail_no_encoder: |
781 | kfree(save_crtcs); | 860 | kfree(save_crtcs); |
782 | count = 0; | 861 | count = 0; |