aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_crtc_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_crtc_helper.c')
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c76
1 files changed, 72 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 */
463static 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 */
490static void
491drm_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
782fail_set_mode: 849fail_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)