diff options
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 314 |
1 files changed, 313 insertions, 1 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 07077b1fcd7b..33a519117e2f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -6590,12 +6590,324 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = { | |||
6590 | .disable = intel_crtc_disable, | 6590 | .disable = intel_crtc_disable, |
6591 | }; | 6591 | }; |
6592 | 6592 | ||
6593 | static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, | ||
6594 | struct drm_crtc *crtc) | ||
6595 | { | ||
6596 | struct drm_device *dev; | ||
6597 | struct drm_crtc *tmp; | ||
6598 | int crtc_mask = 1; | ||
6599 | |||
6600 | WARN(!crtc, "checking null crtc?\n"); | ||
6601 | |||
6602 | dev = crtc->dev; | ||
6603 | |||
6604 | list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { | ||
6605 | if (tmp == crtc) | ||
6606 | break; | ||
6607 | crtc_mask <<= 1; | ||
6608 | } | ||
6609 | |||
6610 | if (encoder->possible_crtcs & crtc_mask) | ||
6611 | return true; | ||
6612 | return false; | ||
6613 | } | ||
6614 | |||
6615 | static int | ||
6616 | intel_crtc_helper_disable(struct drm_crtc *crtc) | ||
6617 | { | ||
6618 | struct drm_device *dev = crtc->dev; | ||
6619 | struct drm_connector *connector; | ||
6620 | struct drm_encoder *encoder; | ||
6621 | |||
6622 | /* Decouple all encoders and their attached connectors from this crtc */ | ||
6623 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
6624 | if (encoder->crtc != crtc) | ||
6625 | continue; | ||
6626 | |||
6627 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
6628 | if (connector->encoder != encoder) | ||
6629 | continue; | ||
6630 | |||
6631 | connector->encoder = NULL; | ||
6632 | } | ||
6633 | } | ||
6634 | |||
6635 | drm_helper_disable_unused_functions(dev); | ||
6636 | return 0; | ||
6637 | } | ||
6638 | |||
6639 | static int intel_crtc_set_config(struct drm_mode_set *set) | ||
6640 | { | ||
6641 | struct drm_device *dev; | ||
6642 | struct drm_crtc *save_crtcs, *new_crtc, *crtc; | ||
6643 | struct drm_encoder *save_encoders, *new_encoder, *encoder; | ||
6644 | struct drm_framebuffer *old_fb = NULL; | ||
6645 | bool mode_changed = false; /* if true do a full mode set */ | ||
6646 | bool fb_changed = false; /* if true and !mode_changed just do a flip */ | ||
6647 | struct drm_connector *save_connectors, *connector; | ||
6648 | int count = 0, ro, fail = 0; | ||
6649 | struct drm_crtc_helper_funcs *crtc_funcs; | ||
6650 | struct drm_mode_set save_set; | ||
6651 | int ret; | ||
6652 | int i; | ||
6653 | |||
6654 | DRM_DEBUG_KMS("\n"); | ||
6655 | |||
6656 | if (!set) | ||
6657 | return -EINVAL; | ||
6658 | |||
6659 | if (!set->crtc) | ||
6660 | return -EINVAL; | ||
6661 | |||
6662 | if (!set->crtc->helper_private) | ||
6663 | return -EINVAL; | ||
6664 | |||
6665 | crtc_funcs = set->crtc->helper_private; | ||
6666 | |||
6667 | if (!set->mode) | ||
6668 | set->fb = NULL; | ||
6669 | |||
6670 | if (set->fb) { | ||
6671 | DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", | ||
6672 | set->crtc->base.id, set->fb->base.id, | ||
6673 | (int)set->num_connectors, set->x, set->y); | ||
6674 | } else { | ||
6675 | DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); | ||
6676 | return intel_crtc_helper_disable(set->crtc); | ||
6677 | } | ||
6678 | |||
6679 | dev = set->crtc->dev; | ||
6680 | |||
6681 | /* Allocate space for the backup of all (non-pointer) crtc, encoder and | ||
6682 | * connector data. */ | ||
6683 | save_crtcs = kzalloc(dev->mode_config.num_crtc * | ||
6684 | sizeof(struct drm_crtc), GFP_KERNEL); | ||
6685 | if (!save_crtcs) | ||
6686 | return -ENOMEM; | ||
6687 | |||
6688 | save_encoders = kzalloc(dev->mode_config.num_encoder * | ||
6689 | sizeof(struct drm_encoder), GFP_KERNEL); | ||
6690 | if (!save_encoders) { | ||
6691 | kfree(save_crtcs); | ||
6692 | return -ENOMEM; | ||
6693 | } | ||
6694 | |||
6695 | save_connectors = kzalloc(dev->mode_config.num_connector * | ||
6696 | sizeof(struct drm_connector), GFP_KERNEL); | ||
6697 | if (!save_connectors) { | ||
6698 | kfree(save_crtcs); | ||
6699 | kfree(save_encoders); | ||
6700 | return -ENOMEM; | ||
6701 | } | ||
6702 | |||
6703 | /* Copy data. Note that driver private data is not affected. | ||
6704 | * Should anything bad happen only the expected state is | ||
6705 | * restored, not the drivers personal bookkeeping. | ||
6706 | */ | ||
6707 | count = 0; | ||
6708 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
6709 | save_crtcs[count++] = *crtc; | ||
6710 | } | ||
6711 | |||
6712 | count = 0; | ||
6713 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
6714 | save_encoders[count++] = *encoder; | ||
6715 | } | ||
6716 | |||
6717 | count = 0; | ||
6718 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
6719 | save_connectors[count++] = *connector; | ||
6720 | } | ||
6721 | |||
6722 | save_set.crtc = set->crtc; | ||
6723 | save_set.mode = &set->crtc->mode; | ||
6724 | save_set.x = set->crtc->x; | ||
6725 | save_set.y = set->crtc->y; | ||
6726 | save_set.fb = set->crtc->fb; | ||
6727 | |||
6728 | /* We should be able to check here if the fb has the same properties | ||
6729 | * and then just flip_or_move it */ | ||
6730 | if (set->crtc->fb != set->fb) { | ||
6731 | /* If we have no fb then treat it as a full mode set */ | ||
6732 | if (set->crtc->fb == NULL) { | ||
6733 | DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); | ||
6734 | mode_changed = true; | ||
6735 | } else if (set->fb == NULL) { | ||
6736 | mode_changed = true; | ||
6737 | } else if (set->fb->depth != set->crtc->fb->depth) { | ||
6738 | mode_changed = true; | ||
6739 | } else if (set->fb->bits_per_pixel != | ||
6740 | set->crtc->fb->bits_per_pixel) { | ||
6741 | mode_changed = true; | ||
6742 | } else | ||
6743 | fb_changed = true; | ||
6744 | } | ||
6745 | |||
6746 | if (set->x != set->crtc->x || set->y != set->crtc->y) | ||
6747 | fb_changed = true; | ||
6748 | |||
6749 | if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { | ||
6750 | DRM_DEBUG_KMS("modes are different, full mode set\n"); | ||
6751 | drm_mode_debug_printmodeline(&set->crtc->mode); | ||
6752 | drm_mode_debug_printmodeline(set->mode); | ||
6753 | mode_changed = true; | ||
6754 | } | ||
6755 | |||
6756 | /* a) traverse passed in connector list and get encoders for them */ | ||
6757 | count = 0; | ||
6758 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
6759 | struct drm_connector_helper_funcs *connector_funcs = | ||
6760 | connector->helper_private; | ||
6761 | new_encoder = connector->encoder; | ||
6762 | for (ro = 0; ro < set->num_connectors; ro++) { | ||
6763 | if (set->connectors[ro] == connector) { | ||
6764 | new_encoder = connector_funcs->best_encoder(connector); | ||
6765 | /* if we can't get an encoder for a connector | ||
6766 | we are setting now - then fail */ | ||
6767 | if (new_encoder == NULL) | ||
6768 | /* don't break so fail path works correct */ | ||
6769 | fail = 1; | ||
6770 | break; | ||
6771 | } | ||
6772 | } | ||
6773 | |||
6774 | if (new_encoder != connector->encoder) { | ||
6775 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); | ||
6776 | mode_changed = true; | ||
6777 | /* If the encoder is reused for another connector, then | ||
6778 | * the appropriate crtc will be set later. | ||
6779 | */ | ||
6780 | if (connector->encoder) | ||
6781 | connector->encoder->crtc = NULL; | ||
6782 | connector->encoder = new_encoder; | ||
6783 | } | ||
6784 | } | ||
6785 | |||
6786 | if (fail) { | ||
6787 | ret = -EINVAL; | ||
6788 | goto fail; | ||
6789 | } | ||
6790 | |||
6791 | count = 0; | ||
6792 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
6793 | if (!connector->encoder) | ||
6794 | continue; | ||
6795 | |||
6796 | if (connector->encoder->crtc == set->crtc) | ||
6797 | new_crtc = NULL; | ||
6798 | else | ||
6799 | new_crtc = connector->encoder->crtc; | ||
6800 | |||
6801 | for (ro = 0; ro < set->num_connectors; ro++) { | ||
6802 | if (set->connectors[ro] == connector) | ||
6803 | new_crtc = set->crtc; | ||
6804 | } | ||
6805 | |||
6806 | /* Make sure the new CRTC will work with the encoder */ | ||
6807 | if (new_crtc && | ||
6808 | !intel_encoder_crtc_ok(connector->encoder, new_crtc)) { | ||
6809 | ret = -EINVAL; | ||
6810 | goto fail; | ||
6811 | } | ||
6812 | if (new_crtc != connector->encoder->crtc) { | ||
6813 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); | ||
6814 | mode_changed = true; | ||
6815 | connector->encoder->crtc = new_crtc; | ||
6816 | } | ||
6817 | if (new_crtc) { | ||
6818 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", | ||
6819 | connector->base.id, drm_get_connector_name(connector), | ||
6820 | new_crtc->base.id); | ||
6821 | } else { | ||
6822 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", | ||
6823 | connector->base.id, drm_get_connector_name(connector)); | ||
6824 | } | ||
6825 | } | ||
6826 | |||
6827 | /* mode_set_base is not a required function */ | ||
6828 | if (fb_changed && !crtc_funcs->mode_set_base) | ||
6829 | mode_changed = true; | ||
6830 | |||
6831 | if (mode_changed) { | ||
6832 | set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); | ||
6833 | if (set->crtc->enabled) { | ||
6834 | DRM_DEBUG_KMS("attempting to set mode from" | ||
6835 | " userspace\n"); | ||
6836 | drm_mode_debug_printmodeline(set->mode); | ||
6837 | old_fb = set->crtc->fb; | ||
6838 | set->crtc->fb = set->fb; | ||
6839 | if (!drm_crtc_helper_set_mode(set->crtc, set->mode, | ||
6840 | set->x, set->y, | ||
6841 | old_fb)) { | ||
6842 | DRM_ERROR("failed to set mode on [CRTC:%d]\n", | ||
6843 | set->crtc->base.id); | ||
6844 | set->crtc->fb = old_fb; | ||
6845 | ret = -EINVAL; | ||
6846 | goto fail; | ||
6847 | } | ||
6848 | DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); | ||
6849 | for (i = 0; i < set->num_connectors; i++) { | ||
6850 | DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, | ||
6851 | drm_get_connector_name(set->connectors[i])); | ||
6852 | set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); | ||
6853 | } | ||
6854 | } | ||
6855 | drm_helper_disable_unused_functions(dev); | ||
6856 | } else if (fb_changed) { | ||
6857 | set->crtc->x = set->x; | ||
6858 | set->crtc->y = set->y; | ||
6859 | |||
6860 | old_fb = set->crtc->fb; | ||
6861 | if (set->crtc->fb != set->fb) | ||
6862 | set->crtc->fb = set->fb; | ||
6863 | ret = crtc_funcs->mode_set_base(set->crtc, | ||
6864 | set->x, set->y, old_fb); | ||
6865 | if (ret != 0) { | ||
6866 | set->crtc->fb = old_fb; | ||
6867 | goto fail; | ||
6868 | } | ||
6869 | } | ||
6870 | |||
6871 | kfree(save_connectors); | ||
6872 | kfree(save_encoders); | ||
6873 | kfree(save_crtcs); | ||
6874 | return 0; | ||
6875 | |||
6876 | fail: | ||
6877 | /* Restore all previous data. */ | ||
6878 | count = 0; | ||
6879 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
6880 | *crtc = save_crtcs[count++]; | ||
6881 | } | ||
6882 | |||
6883 | count = 0; | ||
6884 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
6885 | *encoder = save_encoders[count++]; | ||
6886 | } | ||
6887 | |||
6888 | count = 0; | ||
6889 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
6890 | *connector = save_connectors[count++]; | ||
6891 | } | ||
6892 | |||
6893 | /* Try to restore the config */ | ||
6894 | if (mode_changed && | ||
6895 | !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, | ||
6896 | save_set.y, save_set.fb)) | ||
6897 | DRM_ERROR("failed to restore config after modeset failure\n"); | ||
6898 | |||
6899 | kfree(save_connectors); | ||
6900 | kfree(save_encoders); | ||
6901 | kfree(save_crtcs); | ||
6902 | return ret; | ||
6903 | } | ||
6904 | |||
6593 | static const struct drm_crtc_funcs intel_crtc_funcs = { | 6905 | static const struct drm_crtc_funcs intel_crtc_funcs = { |
6594 | .reset = intel_crtc_reset, | 6906 | .reset = intel_crtc_reset, |
6595 | .cursor_set = intel_crtc_cursor_set, | 6907 | .cursor_set = intel_crtc_cursor_set, |
6596 | .cursor_move = intel_crtc_cursor_move, | 6908 | .cursor_move = intel_crtc_cursor_move, |
6597 | .gamma_set = intel_crtc_gamma_set, | 6909 | .gamma_set = intel_crtc_gamma_set, |
6598 | .set_config = drm_crtc_helper_set_config, | 6910 | .set_config = intel_crtc_set_config, |
6599 | .destroy = intel_crtc_destroy, | 6911 | .destroy = intel_crtc_destroy, |
6600 | .page_flip = intel_crtc_page_flip, | 6912 | .page_flip = intel_crtc_page_flip, |
6601 | }; | 6913 | }; |