]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_atomic_helper.c
Merge tag 'drm-intel-fixes-2015-08-14' into drm-intel-next-fixes
[karo-tx-linux.git] / drivers / gpu / drm / drm_atomic_helper.c
index 9dcc7280e5720255baed2786ab7d8fc11554c845..cf27b6b605d8403270d348378e5720680852f24a 100644 (file)
@@ -89,7 +89,7 @@ get_current_crtc_for_encoder(struct drm_device *dev,
 
        WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
 
-       list_for_each_entry(connector, &config->connector_list, head) {
+       drm_for_each_connector(connector, dev) {
                if (connector->state->best_encoder != encoder)
                        continue;
 
@@ -124,7 +124,7 @@ steal_encoder(struct drm_atomic_state *state,
        if (IS_ERR(crtc_state))
                return PTR_ERR(crtc_state);
 
-       crtc_state->mode_changed = true;
+       crtc_state->connectors_changed = true;
 
        list_for_each_entry(connector, &config->connector_list, head) {
                if (connector->state->best_encoder != encoder)
@@ -174,14 +174,14 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
                        idx = drm_crtc_index(connector->state->crtc);
 
                        crtc_state = state->crtc_states[idx];
-                       crtc_state->mode_changed = true;
+                       crtc_state->connectors_changed = true;
                }
 
                if (connector_state->crtc) {
                        idx = drm_crtc_index(connector_state->crtc);
 
                        crtc_state = state->crtc_states[idx];
-                       crtc_state->mode_changed = true;
+                       crtc_state->connectors_changed = true;
                }
        }
 
@@ -241,7 +241,7 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
        idx = drm_crtc_index(connector_state->crtc);
 
        crtc_state = state->crtc_states[idx];
-       crtc_state->mode_changed = true;
+       crtc_state->connectors_changed = true;
 
        DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",
                         connector->base.id,
@@ -264,7 +264,8 @@ mode_fixup(struct drm_atomic_state *state)
        bool ret;
 
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
-               if (!crtc_state->mode_changed)
+               if (!crtc_state->mode_changed &&
+                   !crtc_state->connectors_changed)
                        continue;
 
                drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode);
@@ -320,7 +321,8 @@ mode_fixup(struct drm_atomic_state *state)
        for_each_crtc_in_state(state, crtc, crtc_state, i) {
                const struct drm_crtc_helper_funcs *funcs;
 
-               if (!crtc_state->mode_changed)
+               if (!crtc_state->mode_changed &&
+                   !crtc_state->connectors_changed)
                        continue;
 
                funcs = crtc->helper_private;
@@ -346,9 +348,14 @@ mode_fixup(struct drm_atomic_state *state)
  *
  * Check the state object to see if the requested state is physically possible.
  * This does all the crtc and connector related computations for an atomic
- * update. It computes and updates crtc_state->mode_changed, adds any additional
- * connectors needed for full modesets and calls down into ->mode_fixup
- * functions of the driver backend.
+ * update and adds any additional connectors needed for full modesets and calls
+ * down into ->mode_fixup functions of the driver backend.
+ *
+ * crtc_state->mode_changed is set when the input mode is changed.
+ * crtc_state->connectors_changed is set when a connector is added or
+ * removed from the crtc.
+ * crtc_state->active_changed is set when crtc_state->active changes,
+ * which is used for dpms.
  *
  * IMPORTANT:
  *
@@ -381,7 +388,17 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                if (crtc->state->enable != crtc_state->enable) {
                        DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n",
                                         crtc->base.id);
+
+                       /*
+                        * For clarity this assignment is done here, but
+                        * enable == 0 is only true when there are no
+                        * connectors and a NULL mode.
+                        *
+                        * The other way around is true as well. enable != 0
+                        * iff connectors are attached and a mode is set.
+                        */
                        crtc_state->mode_changed = true;
+                       crtc_state->connectors_changed = true;
                }
        }
 
@@ -456,6 +473,9 @@ EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
  * This does all the plane update related checks using by calling into the
  * ->atomic_check hooks provided by the driver.
  *
+ * It also sets crtc_state->planes_changed to indicate that a crtc has
+ * updated planes.
+ *
  * RETURNS
  * Zero for success or -errno
  */
@@ -648,15 +668,29 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
        struct drm_crtc_state *old_crtc_state;
        int i;
 
-       /* clear out existing links */
+       /* clear out existing links and update dpms */
        for_each_connector_in_state(old_state, connector, old_conn_state, i) {
-               if (!connector->encoder)
-                       continue;
+               if (connector->encoder) {
+                       WARN_ON(!connector->encoder->crtc);
 
-               WARN_ON(!connector->encoder->crtc);
+                       connector->encoder->crtc = NULL;
+                       connector->encoder = NULL;
+               }
 
-               connector->encoder->crtc = NULL;
-               connector->encoder = NULL;
+               crtc = connector->state->crtc;
+               if ((!crtc && old_conn_state->crtc) ||
+                   (crtc && drm_atomic_crtc_needs_modeset(crtc->state))) {
+                       struct drm_property *dpms_prop =
+                               dev->mode_config.dpms_property;
+                       int mode = DRM_MODE_DPMS_OFF;
+
+                       if (crtc && crtc->state->active)
+                               mode = DRM_MODE_DPMS_ON;
+
+                       connector->dpms = mode;
+                       drm_object_property_set_value(&connector->base,
+                                                     dpms_prop, mode);
+               }
        }
 
        /* set new links */
@@ -673,10 +707,16 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
 
        /* set legacy state in the crtc structure */
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
+               struct drm_plane *primary = crtc->primary;
+
                crtc->mode = crtc->state->mode;
                crtc->enabled = crtc->state->enable;
-               crtc->x = crtc->primary->state->src_x >> 16;
-               crtc->y = crtc->primary->state->src_y >> 16;
+
+               if (drm_atomic_get_existing_plane_state(old_state, primary) &&
+                   primary->state->crtc == crtc) {
+                       crtc->x = primary->state->src_x >> 16;
+                       crtc->y = primary->state->src_y >> 16;
+               }
 
                if (crtc->state->enable)
                        drm_calc_timestamping_constants(crtc,
@@ -1146,7 +1186,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_begin)
                        continue;
 
-               funcs->atomic_begin(crtc);
+               funcs->atomic_begin(crtc, old_crtc_state);
        }
 
        for_each_plane_in_state(old_state, plane, old_plane_state, i) {
@@ -1176,7 +1216,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                if (!funcs || !funcs->atomic_flush)
                        continue;
 
-               funcs->atomic_flush(crtc);
+               funcs->atomic_flush(crtc, old_crtc_state);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
@@ -1212,7 +1252,7 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
 
        crtc_funcs = crtc->helper_private;
        if (crtc_funcs && crtc_funcs->atomic_begin)
-               crtc_funcs->atomic_begin(crtc);
+               crtc_funcs->atomic_begin(crtc, old_crtc_state);
 
        drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {
                struct drm_plane_state *old_plane_state =
@@ -1235,7 +1275,7 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
        }
 
        if (crtc_funcs && crtc_funcs->atomic_flush)
-               crtc_funcs->atomic_flush(crtc);
+               crtc_funcs->atomic_flush(crtc, old_crtc_state);
 }
 EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
 
@@ -1923,10 +1963,6 @@ retry:
        if (ret != 0)
                goto fail;
 
-       /* TODO: ->page_flip is the only driver callback where the core
-        * doesn't update plane->fb. For now patch it up here. */
-       plane->fb = plane->state->fb;
-
        /* Driver takes ownership of state on successful async commit. */
        return 0;
 fail:
@@ -1960,9 +1996,12 @@ EXPORT_SYMBOL(drm_atomic_helper_page_flip);
  * implementing the legacy DPMS connector interface. It computes the new desired
  * ->active state for the corresponding CRTC (if the connector is enabled) and
  *  updates it.
+ *
+ * Returns:
+ * Returns 0 on success, negative errno numbers on failure.
  */
-void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
-                                     int mode)
+int drm_atomic_helper_connector_dpms(struct drm_connector *connector,
+                                    int mode)
 {
        struct drm_mode_config *config = &connector->dev->mode_config;
        struct drm_atomic_state *state;
@@ -1971,6 +2010,7 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
        struct drm_connector *tmp_connector;
        int ret;
        bool active = false;
+       int old_mode = connector->dpms;
 
        if (mode != DRM_MODE_DPMS_ON)
                mode = DRM_MODE_DPMS_OFF;
@@ -1979,22 +2019,23 @@ void drm_atomic_helper_connector_dpms(struct drm_connector *connector,
        crtc = connector->state->crtc;
 
        if (!crtc)
-               return;
+               return 0;
 
-       /* FIXME: ->dpms has no return value so can't forward the -ENOMEM. */
        state = drm_atomic_state_alloc(connector->dev);
        if (!state)
-               return;
+               return -ENOMEM;
 
        state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc);
 retry:
        crtc_state = drm_atomic_get_crtc_state(state, crtc);
-       if (IS_ERR(crtc_state))
-               return;
+       if (IS_ERR(crtc_state)) {
+               ret = PTR_ERR(crtc_state);
+               goto fail;
+       }
 
        WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
 
-       list_for_each_entry(tmp_connector, &config->connector_list, head) {
+       drm_for_each_connector(tmp_connector, connector->dev) {
                if (tmp_connector->state->crtc != crtc)
                        continue;
 
@@ -2009,17 +2050,16 @@ retry:
        if (ret != 0)
                goto fail;
 
-       /* Driver takes ownership of state on successful async commit. */
-       return;
+       /* Driver takes ownership of state on successful commit. */
+       return 0;
 fail:
        if (ret == -EDEADLK)
                goto backoff;
 
+       connector->dpms = old_mode;
        drm_atomic_state_free(state);
 
-       WARN(1, "Driver bug: Changing ->active failed with ret=%i\n", ret);
-
-       return;
+       return ret;
 backoff:
        drm_atomic_state_clear(state);
        drm_atomic_legacy_backoff(state);
@@ -2080,6 +2120,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
        state->mode_changed = false;
        state->active_changed = false;
        state->planes_changed = false;
+       state->connectors_changed = false;
        state->event = NULL;
 }
 EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state);