drm/atomic: Make prepare_fb/cleanup_fb only take state, v3.
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_atomic_helper.c
index 9dcc7280e5720255baed2786ab7d8fc11554c845..9b0c47690ae8bf16ecdfb81e684177938bca2d3c 100644 (file)
  * add their own additional internal state.
  *
  * This library also provides default implementations for the check callback in
- * drm_atomic_helper_check and for the commit callback with
- * drm_atomic_helper_commit. But the individual stages and callbacks are expose
- * to allow drivers to mix and match and e.g. use the plane helpers only
+ * drm_atomic_helper_check() and for the commit callback with
+ * drm_atomic_helper_commit(). But the individual stages and callbacks are
+ * exposed to allow drivers to mix and match and e.g. use the plane helpers only
  * together with a driver private modeset implementation.
  *
  * This library also provides implementations for all the legacy driver
- * interfaces on top of the atomic interface. See drm_atomic_helper_set_config,
- * drm_atomic_helper_disable_plane, drm_atomic_helper_disable_plane and the
+ * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(),
+ * drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the
  * various functions to implement set_property callbacks. New drivers must not
  * implement these functions themselves but must use the provided helpers.
  */
@@ -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);
@@ -306,7 +307,7 @@ mode_fixup(struct drm_atomic_state *state)
                                                 encoder->base.id, encoder->name);
                                return ret;
                        }
-               } else {
+               } else if (funcs->mode_fixup) {
                        ret = funcs->mode_fixup(encoder, &crtc_state->mode,
                                                &crtc_state->adjusted_mode);
                        if (!ret) {
@@ -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);
+
+                       connector->encoder->crtc = NULL;
+                       connector->encoder = NULL;
+               }
 
-               WARN_ON(!connector->encoder->crtc);
+               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;
 
-               connector->encoder->crtc = NULL;
-               connector->encoder = NULL;
+                       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,
@@ -926,7 +966,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
                        continue;
 
                old_crtc_state->enable = true;
-               old_crtc_state->last_vblank_count = drm_vblank_count(dev, i);
+               old_crtc_state->last_vblank_count = drm_crtc_vblank_count(crtc);
        }
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -935,7 +975,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
 
                ret = wait_event_timeout(dev->vblank[i].queue,
                                old_crtc_state->last_vblank_count !=
-                                       drm_vblank_count(dev, i),
+                                       drm_crtc_vblank_count(crtc),
                                msecs_to_jiffies(50));
 
                drm_crtc_vblank_put(crtc);
@@ -1037,7 +1077,7 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
  * work item, which allows nice concurrent updates on disjoint sets of crtcs.
  *
  * 3. The software state is updated synchronously with
- * drm_atomic_helper_swap_state. Doing this under the protection of all modeset
+ * drm_atomic_helper_swap_state(). Doing this under the protection of all modeset
  * locks means concurrent callers never see inconsistent state. And doing this
  * while it's guaranteed that no relevant async worker runs means that async
  * workers do not need grab any locks. Actually they must not grab locks, for
@@ -1071,17 +1111,14 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = plane_state->fb;
-
-               if (fb && funcs->prepare_fb) {
-                       ret = funcs->prepare_fb(plane, fb, plane_state);
+               if (funcs->prepare_fb) {
+                       ret = funcs->prepare_fb(plane, plane_state);
                        if (ret)
                                goto fail;
                }
@@ -1094,17 +1131,14 @@ fail:
                const struct drm_plane_helper_funcs *funcs;
                struct drm_plane *plane = state->planes[i];
                struct drm_plane_state *plane_state = state->plane_states[i];
-               struct drm_framebuffer *fb;
 
                if (!plane)
                        continue;
 
                funcs = plane->helper_private;
 
-               fb = state->plane_states[i]->fb;
-
-               if (fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
 
        }
 
@@ -1146,7 +1180,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 +1210,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 +1246,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 +1269,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);
 
@@ -1260,14 +1294,11 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
 
        for_each_plane_in_state(old_state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
-               struct drm_framebuffer *old_fb;
 
                funcs = plane->helper_private;
 
-               old_fb = plane_state->fb;
-
-               if (old_fb && funcs->cleanup_fb)
-                       funcs->cleanup_fb(plane, old_fb, plane_state);
+               if (funcs->cleanup_fb)
+                       funcs->cleanup_fb(plane, plane_state);
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
@@ -1294,7 +1325,7 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
  *
  * 4. Actually commit the hardware state.
  *
- * 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
+ * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
  * contains the old state. Also do any other cleanup required with that state.
  */
 void drm_atomic_helper_swap_state(struct drm_device *dev,
@@ -1923,10 +1954,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 +1987,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 +2001,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 +2010,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 +2041,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 +2111,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);