diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 174 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 16 |
2 files changed, 146 insertions, 44 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a2ce117aa794..5031e0c4a02e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -6619,6 +6619,51 @@ intel_crtc_prepare_encoders(struct drm_device *dev) | |||
6619 | } | 6619 | } |
6620 | } | 6620 | } |
6621 | 6621 | ||
6622 | /** | ||
6623 | * intel_modeset_update_staged_output_state | ||
6624 | * | ||
6625 | * Updates the staged output configuration state, e.g. after we've read out the | ||
6626 | * current hw state. | ||
6627 | */ | ||
6628 | static void intel_modeset_update_staged_output_state(struct drm_device *dev) | ||
6629 | { | ||
6630 | struct intel_encoder *encoder; | ||
6631 | struct intel_connector *connector; | ||
6632 | |||
6633 | list_for_each_entry(connector, &dev->mode_config.connector_list, | ||
6634 | base.head) { | ||
6635 | connector->new_encoder = | ||
6636 | to_intel_encoder(connector->base.encoder); | ||
6637 | } | ||
6638 | |||
6639 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, | ||
6640 | base.head) { | ||
6641 | encoder->new_crtc = | ||
6642 | to_intel_crtc(encoder->base.crtc); | ||
6643 | } | ||
6644 | } | ||
6645 | |||
6646 | /** | ||
6647 | * intel_modeset_commit_output_state | ||
6648 | * | ||
6649 | * This function copies the stage display pipe configuration to the real one. | ||
6650 | */ | ||
6651 | static void intel_modeset_commit_output_state(struct drm_device *dev) | ||
6652 | { | ||
6653 | struct intel_encoder *encoder; | ||
6654 | struct intel_connector *connector; | ||
6655 | |||
6656 | list_for_each_entry(connector, &dev->mode_config.connector_list, | ||
6657 | base.head) { | ||
6658 | connector->base.encoder = &connector->new_encoder->base; | ||
6659 | } | ||
6660 | |||
6661 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, | ||
6662 | base.head) { | ||
6663 | encoder->base.crtc = &encoder->new_crtc->base; | ||
6664 | } | ||
6665 | } | ||
6666 | |||
6622 | bool intel_set_mode(struct drm_crtc *crtc, | 6667 | bool intel_set_mode(struct drm_crtc *crtc, |
6623 | struct drm_display_mode *mode, | 6668 | struct drm_display_mode *mode, |
6624 | int x, int y, struct drm_framebuffer *old_fb) | 6669 | int x, int y, struct drm_framebuffer *old_fb) |
@@ -6785,8 +6830,8 @@ static void intel_set_config_restore_state(struct drm_device *dev, | |||
6785 | struct intel_set_config *config) | 6830 | struct intel_set_config *config) |
6786 | { | 6831 | { |
6787 | struct drm_crtc *crtc; | 6832 | struct drm_crtc *crtc; |
6788 | struct drm_encoder *encoder; | 6833 | struct intel_encoder *encoder; |
6789 | struct drm_connector *connector; | 6834 | struct intel_connector *connector; |
6790 | int count; | 6835 | int count; |
6791 | 6836 | ||
6792 | count = 0; | 6837 | count = 0; |
@@ -6795,13 +6840,15 @@ static void intel_set_config_restore_state(struct drm_device *dev, | |||
6795 | } | 6840 | } |
6796 | 6841 | ||
6797 | count = 0; | 6842 | count = 0; |
6798 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | 6843 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { |
6799 | encoder->crtc = config->save_encoder_crtcs[count++]; | 6844 | encoder->new_crtc = |
6845 | to_intel_crtc(config->save_encoder_crtcs[count++]); | ||
6800 | } | 6846 | } |
6801 | 6847 | ||
6802 | count = 0; | 6848 | count = 0; |
6803 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 6849 | list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { |
6804 | connector->encoder = config->save_connector_encoders[count++]; | 6850 | connector->new_encoder = |
6851 | to_intel_encoder(config->save_connector_encoders[count++]); | ||
6805 | } | 6852 | } |
6806 | } | 6853 | } |
6807 | 6854 | ||
@@ -6840,73 +6887,106 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, | |||
6840 | } | 6887 | } |
6841 | 6888 | ||
6842 | static int | 6889 | static int |
6843 | intel_set_config_update_output_state(struct drm_device *dev, | 6890 | intel_modeset_stage_output_state(struct drm_device *dev, |
6844 | struct drm_mode_set *set, | 6891 | struct drm_mode_set *set, |
6845 | struct intel_set_config *config) | 6892 | struct intel_set_config *config) |
6846 | { | 6893 | { |
6847 | struct drm_crtc *new_crtc; | 6894 | struct drm_crtc *new_crtc; |
6848 | struct drm_encoder *new_encoder; | 6895 | struct intel_connector *connector; |
6849 | struct drm_connector *connector; | 6896 | struct intel_encoder *encoder; |
6850 | int count, ro; | 6897 | int count, ro; |
6851 | 6898 | ||
6852 | /* a) traverse passed in connector list and get encoders for them */ | 6899 | /* The upper layers ensure that we either disabl a crtc or have a list |
6900 | * of connectors. For paranoia, double-check this. */ | ||
6901 | WARN_ON(!set->fb && (set->num_connectors != 0)); | ||
6902 | WARN_ON(set->fb && (set->num_connectors == 0)); | ||
6903 | |||
6853 | count = 0; | 6904 | count = 0; |
6854 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 6905 | list_for_each_entry(connector, &dev->mode_config.connector_list, |
6855 | new_encoder = connector->encoder; | 6906 | base.head) { |
6907 | /* Otherwise traverse passed in connector list and get encoders | ||
6908 | * for them. */ | ||
6856 | for (ro = 0; ro < set->num_connectors; ro++) { | 6909 | for (ro = 0; ro < set->num_connectors; ro++) { |
6857 | if (set->connectors[ro] == connector) { | 6910 | if (set->connectors[ro] == &connector->base) { |
6858 | new_encoder = | 6911 | connector->new_encoder = connector->encoder; |
6859 | &intel_attached_encoder(connector)->base; | ||
6860 | break; | 6912 | break; |
6861 | } | 6913 | } |
6862 | } | 6914 | } |
6863 | 6915 | ||
6864 | if (new_encoder != connector->encoder) { | 6916 | /* If we disable the crtc, disable all its connectors. Also, if |
6917 | * the connector is on the changing crtc but not on the new | ||
6918 | * connector list, disable it. */ | ||
6919 | if ((!set->fb || ro == set->num_connectors) && | ||
6920 | connector->base.encoder && | ||
6921 | connector->base.encoder->crtc == set->crtc) { | ||
6922 | connector->new_encoder = NULL; | ||
6923 | |||
6924 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", | ||
6925 | connector->base.base.id, | ||
6926 | drm_get_connector_name(&connector->base)); | ||
6927 | } | ||
6928 | |||
6929 | |||
6930 | if (&connector->new_encoder->base != connector->base.encoder) { | ||
6865 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); | 6931 | DRM_DEBUG_KMS("encoder changed, full mode switch\n"); |
6866 | config->mode_changed = true; | 6932 | config->mode_changed = true; |
6867 | /* If the encoder is reused for another connector, then | ||
6868 | * the appropriate crtc will be set later. | ||
6869 | */ | ||
6870 | if (connector->encoder) | ||
6871 | connector->encoder->crtc = NULL; | ||
6872 | connector->encoder = new_encoder; | ||
6873 | } | 6933 | } |
6934 | |||
6935 | /* Disable all disconnected encoders. */ | ||
6936 | if (connector->base.status == connector_status_disconnected) | ||
6937 | connector->new_encoder = NULL; | ||
6874 | } | 6938 | } |
6939 | /* connector->new_encoder is now updated for all connectors. */ | ||
6875 | 6940 | ||
6941 | /* Update crtc of enabled connectors. */ | ||
6876 | count = 0; | 6942 | count = 0; |
6877 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | 6943 | list_for_each_entry(connector, &dev->mode_config.connector_list, |
6878 | if (!connector->encoder) | 6944 | base.head) { |
6945 | if (!connector->new_encoder) | ||
6879 | continue; | 6946 | continue; |
6880 | 6947 | ||
6881 | if (connector->encoder->crtc == set->crtc) | 6948 | new_crtc = connector->new_encoder->base.crtc; |
6882 | new_crtc = NULL; | ||
6883 | else | ||
6884 | new_crtc = connector->encoder->crtc; | ||
6885 | 6949 | ||
6886 | for (ro = 0; ro < set->num_connectors; ro++) { | 6950 | for (ro = 0; ro < set->num_connectors; ro++) { |
6887 | if (set->connectors[ro] == connector) | 6951 | if (set->connectors[ro] == &connector->base) |
6888 | new_crtc = set->crtc; | 6952 | new_crtc = set->crtc; |
6889 | } | 6953 | } |
6890 | 6954 | ||
6891 | /* Make sure the new CRTC will work with the encoder */ | 6955 | /* Make sure the new CRTC will work with the encoder */ |
6892 | if (new_crtc && | 6956 | if (!intel_encoder_crtc_ok(&connector->new_encoder->base, |
6893 | !intel_encoder_crtc_ok(connector->encoder, new_crtc)) { | 6957 | new_crtc)) { |
6894 | return -EINVAL; | 6958 | return -EINVAL; |
6895 | } | 6959 | } |
6896 | if (new_crtc != connector->encoder->crtc) { | 6960 | connector->encoder->new_crtc = to_intel_crtc(new_crtc); |
6961 | |||
6962 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", | ||
6963 | connector->base.base.id, | ||
6964 | drm_get_connector_name(&connector->base), | ||
6965 | new_crtc->base.id); | ||
6966 | } | ||
6967 | |||
6968 | /* Check for any encoders that needs to be disabled. */ | ||
6969 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, | ||
6970 | base.head) { | ||
6971 | list_for_each_entry(connector, | ||
6972 | &dev->mode_config.connector_list, | ||
6973 | base.head) { | ||
6974 | if (connector->new_encoder == encoder) { | ||
6975 | WARN_ON(!connector->new_encoder->new_crtc); | ||
6976 | |||
6977 | goto next_encoder; | ||
6978 | } | ||
6979 | } | ||
6980 | encoder->new_crtc = NULL; | ||
6981 | next_encoder: | ||
6982 | /* Only now check for crtc changes so we don't miss encoders | ||
6983 | * that will be disabled. */ | ||
6984 | if (&encoder->new_crtc->base != encoder->base.crtc) { | ||
6897 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); | 6985 | DRM_DEBUG_KMS("crtc changed, full mode switch\n"); |
6898 | config->mode_changed = true; | 6986 | config->mode_changed = true; |
6899 | connector->encoder->crtc = new_crtc; | ||
6900 | } | ||
6901 | if (new_crtc) { | ||
6902 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", | ||
6903 | connector->base.id, drm_get_connector_name(connector), | ||
6904 | new_crtc->base.id); | ||
6905 | } else { | ||
6906 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", | ||
6907 | connector->base.id, drm_get_connector_name(connector)); | ||
6908 | } | 6987 | } |
6909 | } | 6988 | } |
6989 | /* Now we've also updated encoder->new_crtc for all encoders. */ | ||
6910 | 6990 | ||
6911 | return 0; | 6991 | return 0; |
6912 | } | 6992 | } |
@@ -6965,11 +7045,13 @@ static int intel_crtc_set_config(struct drm_mode_set *set) | |||
6965 | * such cases. */ | 7045 | * such cases. */ |
6966 | intel_set_config_compute_mode_changes(set, config); | 7046 | intel_set_config_compute_mode_changes(set, config); |
6967 | 7047 | ||
6968 | ret = intel_set_config_update_output_state(dev, set, config); | 7048 | ret = intel_modeset_stage_output_state(dev, set, config); |
6969 | if (ret) | 7049 | if (ret) |
6970 | goto fail; | 7050 | goto fail; |
6971 | 7051 | ||
6972 | if (config->mode_changed) { | 7052 | if (config->mode_changed) { |
7053 | intel_modeset_commit_output_state(dev); | ||
7054 | |||
6973 | set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); | 7055 | set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); |
6974 | if (set->crtc->enabled) { | 7056 | if (set->crtc->enabled) { |
6975 | DRM_DEBUG_KMS("attempting to set mode from" | 7057 | DRM_DEBUG_KMS("attempting to set mode from" |
@@ -7015,6 +7097,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) | |||
7015 | fail: | 7097 | fail: |
7016 | intel_set_config_restore_state(dev, config); | 7098 | intel_set_config_restore_state(dev, config); |
7017 | 7099 | ||
7100 | intel_modeset_commit_output_state(dev); | ||
7101 | |||
7018 | /* Try to restore the config */ | 7102 | /* Try to restore the config */ |
7019 | if (config->mode_changed && | 7103 | if (config->mode_changed && |
7020 | !intel_set_mode(save_set.crtc, save_set.mode, | 7104 | !intel_set_mode(save_set.crtc, save_set.mode, |
@@ -7888,6 +7972,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev) | |||
7888 | crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); | 7972 | crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); |
7889 | intel_sanitize_crtc(crtc); | 7973 | intel_sanitize_crtc(crtc); |
7890 | } | 7974 | } |
7975 | |||
7976 | intel_modeset_update_staged_output_state(dev); | ||
7891 | } | 7977 | } |
7892 | 7978 | ||
7893 | void intel_modeset_gem_init(struct drm_device *dev) | 7979 | void intel_modeset_gem_init(struct drm_device *dev) |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4946282bd324..ae807afc5fbf 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
@@ -133,6 +133,12 @@ struct intel_fbdev { | |||
133 | 133 | ||
134 | struct intel_encoder { | 134 | struct intel_encoder { |
135 | struct drm_encoder base; | 135 | struct drm_encoder base; |
136 | /* | ||
137 | * The new crtc this encoder will be driven from. Only differs from | ||
138 | * base->crtc while a modeset is in progress. | ||
139 | */ | ||
140 | struct intel_crtc *new_crtc; | ||
141 | |||
136 | int type; | 142 | int type; |
137 | bool needs_tv_clock; | 143 | bool needs_tv_clock; |
138 | /* | 144 | /* |
@@ -153,7 +159,17 @@ struct intel_encoder { | |||
153 | 159 | ||
154 | struct intel_connector { | 160 | struct intel_connector { |
155 | struct drm_connector base; | 161 | struct drm_connector base; |
162 | /* | ||
163 | * The fixed encoder this connector is connected to. | ||
164 | */ | ||
156 | struct intel_encoder *encoder; | 165 | struct intel_encoder *encoder; |
166 | |||
167 | /* | ||
168 | * The new encoder this connector will be driven. Only differs from | ||
169 | * encoder while a modeset is in progress. | ||
170 | */ | ||
171 | struct intel_encoder *new_encoder; | ||
172 | |||
157 | /* Reads out the current hw, returning true if the connector is enabled | 173 | /* Reads out the current hw, returning true if the connector is enabled |
158 | * and active (i.e. dpms ON state). */ | 174 | * and active (i.e. dpms ON state). */ |
159 | bool (*get_hw_state)(struct intel_connector *); | 175 | bool (*get_hw_state)(struct intel_connector *); |