drm/i915: stage modeset output changes
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_display.c
index a2ce117aa7947bed378e4b28f5cff49777fd4bd5..5031e0c4a02e9736ff10a09865b07073c566655b 100644 (file)
@@ -6619,6 +6619,51 @@ intel_crtc_prepare_encoders(struct drm_device *dev)
        }
 }
 
+/**
+ * intel_modeset_update_staged_output_state
+ *
+ * Updates the staged output configuration state, e.g. after we've read out the
+ * current hw state.
+ */
+static void intel_modeset_update_staged_output_state(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               connector->new_encoder =
+                       to_intel_encoder(connector->base.encoder);
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               encoder->new_crtc =
+                       to_intel_crtc(encoder->base.crtc);
+       }
+}
+
+/**
+ * intel_modeset_commit_output_state
+ *
+ * This function copies the stage display pipe configuration to the real one.
+ */
+static void intel_modeset_commit_output_state(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               connector->base.encoder = &connector->new_encoder->base;
+       }
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               encoder->base.crtc = &encoder->new_crtc->base;
+       }
+}
+
 bool intel_set_mode(struct drm_crtc *crtc,
                    struct drm_display_mode *mode,
                    int x, int y, struct drm_framebuffer *old_fb)
@@ -6785,8 +6830,8 @@ static void intel_set_config_restore_state(struct drm_device *dev,
                                           struct intel_set_config *config)
 {
        struct drm_crtc *crtc;
-       struct drm_encoder *encoder;
-       struct drm_connector *connector;
+       struct intel_encoder *encoder;
+       struct intel_connector *connector;
        int count;
 
        count = 0;
@@ -6795,13 +6840,15 @@ static void intel_set_config_restore_state(struct drm_device *dev,
        }
 
        count = 0;
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               encoder->crtc = config->save_encoder_crtcs[count++];
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+               encoder->new_crtc =
+                       to_intel_crtc(config->save_encoder_crtcs[count++]);
        }
 
        count = 0;
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               connector->encoder = config->save_connector_encoders[count++];
+       list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) {
+               connector->new_encoder =
+                       to_intel_encoder(config->save_connector_encoders[count++]);
        }
 }
 
@@ -6840,73 +6887,106 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set,
 }
 
 static int
-intel_set_config_update_output_state(struct drm_device *dev,
-                                    struct drm_mode_set *set,
-                                    struct intel_set_config *config)
+intel_modeset_stage_output_state(struct drm_device *dev,
+                                struct drm_mode_set *set,
+                                struct intel_set_config *config)
 {
        struct drm_crtc *new_crtc;
-       struct drm_encoder *new_encoder;
-       struct drm_connector *connector;
+       struct intel_connector *connector;
+       struct intel_encoder *encoder;
        int count, ro;
 
-       /* a) traverse passed in connector list and get encoders for them */
+       /* The upper layers ensure that we either disabl a crtc or have a list
+        * of connectors. For paranoia, double-check this. */
+       WARN_ON(!set->fb && (set->num_connectors != 0));
+       WARN_ON(set->fb && (set->num_connectors == 0));
+
        count = 0;
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               new_encoder = connector->encoder;
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               /* Otherwise traverse passed in connector list and get encoders
+                * for them. */
                for (ro = 0; ro < set->num_connectors; ro++) {
-                       if (set->connectors[ro] == connector) {
-                               new_encoder =
-                                       &intel_attached_encoder(connector)->base;
+                       if (set->connectors[ro] == &connector->base) {
+                               connector->new_encoder = connector->encoder;
                                break;
                        }
                }
 
-               if (new_encoder != connector->encoder) {
+               /* If we disable the crtc, disable all its connectors. Also, if
+                * the connector is on the changing crtc but not on the new
+                * connector list, disable it. */
+               if ((!set->fb || ro == set->num_connectors) &&
+                   connector->base.encoder &&
+                   connector->base.encoder->crtc == set->crtc) {
+                       connector->new_encoder = NULL;
+
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
+                               connector->base.base.id,
+                               drm_get_connector_name(&connector->base));
+               }
+
+
+               if (&connector->new_encoder->base != connector->base.encoder) {
                        DRM_DEBUG_KMS("encoder changed, full mode switch\n");
                        config->mode_changed = true;
-                       /* If the encoder is reused for another connector, then
-                        * the appropriate crtc will be set later.
-                        */
-                       if (connector->encoder)
-                               connector->encoder->crtc = NULL;
-                       connector->encoder = new_encoder;
                }
+
+               /* Disable all disconnected encoders. */
+               if (connector->base.status == connector_status_disconnected)
+                       connector->new_encoder = NULL;
        }
+       /* connector->new_encoder is now updated for all connectors. */
 
+       /* Update crtc of enabled connectors. */
        count = 0;
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               if (!connector->encoder)
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           base.head) {
+               if (!connector->new_encoder)
                        continue;
 
-               if (connector->encoder->crtc == set->crtc)
-                       new_crtc = NULL;
-               else
-                       new_crtc = connector->encoder->crtc;
+               new_crtc = connector->new_encoder->base.crtc;
 
                for (ro = 0; ro < set->num_connectors; ro++) {
-                       if (set->connectors[ro] == connector)
+                       if (set->connectors[ro] == &connector->base)
                                new_crtc = set->crtc;
                }
 
                /* Make sure the new CRTC will work with the encoder */
-               if (new_crtc &&
-                   !intel_encoder_crtc_ok(connector->encoder, new_crtc)) {
+               if (!intel_encoder_crtc_ok(&connector->new_encoder->base,
+                                          new_crtc)) {
                        return -EINVAL;
                }
-               if (new_crtc != connector->encoder->crtc) {
+               connector->encoder->new_crtc = to_intel_crtc(new_crtc);
+
+               DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
+                       connector->base.base.id,
+                       drm_get_connector_name(&connector->base),
+                       new_crtc->base.id);
+       }
+
+       /* Check for any encoders that needs to be disabled. */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+                           base.head) {
+               list_for_each_entry(connector,
+                                   &dev->mode_config.connector_list,
+                                   base.head) {
+                       if (connector->new_encoder == encoder) {
+                               WARN_ON(!connector->new_encoder->new_crtc);
+
+                               goto next_encoder;
+                       }
+               }
+               encoder->new_crtc = NULL;
+next_encoder:
+               /* Only now check for crtc changes so we don't miss encoders
+                * that will be disabled. */
+               if (&encoder->new_crtc->base != encoder->base.crtc) {
                        DRM_DEBUG_KMS("crtc changed, full mode switch\n");
                        config->mode_changed = true;
-                       connector->encoder->crtc = new_crtc;
-               }
-               if (new_crtc) {
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
-                               connector->base.id, drm_get_connector_name(connector),
-                               new_crtc->base.id);
-               } else {
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
-                               connector->base.id, drm_get_connector_name(connector));
                }
        }
+       /* Now we've also updated encoder->new_crtc for all encoders. */
 
        return 0;
 }
@@ -6965,11 +7045,13 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
         * such cases. */
        intel_set_config_compute_mode_changes(set, config);
 
-       ret = intel_set_config_update_output_state(dev, set, config);
+       ret = intel_modeset_stage_output_state(dev, set, config);
        if (ret)
                goto fail;
 
        if (config->mode_changed) {
+               intel_modeset_commit_output_state(dev);
+
                set->crtc->enabled = drm_helper_crtc_in_use(set->crtc);
                if (set->crtc->enabled) {
                        DRM_DEBUG_KMS("attempting to set mode from"
@@ -7015,6 +7097,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
 fail:
        intel_set_config_restore_state(dev, config);
 
+       intel_modeset_commit_output_state(dev);
+
        /* Try to restore the config */
        if (config->mode_changed &&
            !intel_set_mode(save_set.crtc, save_set.mode,
@@ -7888,6 +7972,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev)
                crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
                intel_sanitize_crtc(crtc);
        }
+
+       intel_modeset_update_staged_output_state(dev);
 }
 
 void intel_modeset_gem_init(struct drm_device *dev)
This page took 0.029338 seconds and 5 git commands to generate.