drm/atomic-helpers: Fix documentation typos and wrong copy&paste
[pandora-kernel.git] / drivers / gpu / drm / drm_atomic_helper.c
index bbdbe47..e70adfc 100644 (file)
@@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state)
                        }
                }
 
-
-               ret = funcs->mode_fixup(encoder, &crtc_state->mode,
-                                       &crtc_state->adjusted_mode);
-               if (!ret) {
-                       DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
-                                     encoder->base.id, encoder->name);
-                       return -EINVAL;
+               if (funcs->atomic_check) {
+                       ret = funcs->atomic_check(encoder, crtc_state,
+                                                 conn_state);
+                       if (ret) {
+                               DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n",
+                                             encoder->base.id, encoder->name);
+                               return ret;
+                       }
+               } else {
+                       ret = funcs->mode_fixup(encoder, &crtc_state->mode,
+                                               &crtc_state->adjusted_mode);
+                       if (!ret) {
+                               DRM_DEBUG_KMS("[ENCODER:%d:%s] fixup failed\n",
+                                             encoder->base.id, encoder->name);
+                               return -EINVAL;
+                       }
                }
        }
 
@@ -330,7 +339,35 @@ mode_fixup(struct drm_atomic_state *state)
        return 0;
 }
 
-static int
+static bool
+needs_modeset(struct drm_crtc_state *state)
+{
+       return state->mode_changed || state->active_changed;
+}
+
+/**
+ * drm_atomic_helper_check - validate state object for modeset changes
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * 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.
+ *
+ * IMPORTANT:
+ *
+ * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a
+ * plane update can't be done without a full modeset) _must_ call this function
+ * afterwards after that change. It is permitted to call this function multiple
+ * times for the same update, e.g. when the ->atomic_check functions depend upon
+ * the adjusted dotclock for fifo space allocation and watermark computation.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int
 drm_atomic_helper_check_modeset(struct drm_device *dev,
                                struct drm_atomic_state *state)
 {
@@ -382,12 +419,27 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
                crtc = state->crtcs[i];
                crtc_state = state->crtc_states[i];
 
-               if (!crtc || !crtc_state->mode_changed)
+               if (!crtc)
                        continue;
 
-               DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n",
+               /*
+                * We must set ->active_changed after walking connectors for
+                * otherwise an update that only changes active would result in
+                * a full modeset because update_connector_routing force that.
+                */
+               if (crtc->state->active != crtc_state->active) {
+                       DRM_DEBUG_KMS("[CRTC:%d] active changed\n",
+                                     crtc->base.id);
+                       crtc_state->active_changed = true;
+               }
+
+               if (!needs_modeset(crtc_state))
+                       continue;
+
+               DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
                              crtc->base.id,
-                             crtc_state->enable ? 'y' : 'n');
+                             crtc_state->enable ? 'y' : 'n',
+                             crtc_state->active ? 'y' : 'n');
 
                ret = drm_atomic_add_affected_connectors(state, crtc);
                if (ret != 0)
@@ -406,23 +458,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
 
        return mode_fixup(state);
 }
+EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
 
 /**
- * drm_atomic_helper_check - validate state object
+ * drm_atomic_helper_check - validate state object for modeset changes
  * @dev: DRM device
  * @state: the driver state object
  *
  * Check the state object to see if the requested state is physically possible.
- * Only crtcs and planes have check callbacks, so for any additional (global)
- * checking that a driver needs it can simply wrap that around this function.
- * Drivers without such needs can directly use this as their ->atomic_check()
- * callback.
+ * This does all the plane update related checks using by calling into the
+ * ->atomic_check hooks provided by the driver.
  *
  * RETURNS
  * Zero for success or -errno
  */
-int drm_atomic_helper_check(struct drm_device *dev,
-                           struct drm_atomic_state *state)
+int
+drm_atomic_helper_check_planes(struct drm_device *dev,
+                              struct drm_atomic_state *state)
 {
        int nplanes = dev->mode_config.num_total_plane;
        int ncrtcs = dev->mode_config.num_crtc;
@@ -445,7 +497,7 @@ int drm_atomic_helper_check(struct drm_device *dev,
 
                ret = funcs->atomic_check(plane, plane_state);
                if (ret) {
-                       DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
+                       DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n",
                                      plane->base.id);
                        return ret;
                }
@@ -465,16 +517,49 @@ int drm_atomic_helper_check(struct drm_device *dev,
 
                ret = funcs->atomic_check(crtc, state->crtc_states[i]);
                if (ret) {
-                       DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
+                       DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n",
                                      crtc->base.id);
                        return ret;
                }
        }
 
+       return ret;
+}
+EXPORT_SYMBOL(drm_atomic_helper_check_planes);
+
+/**
+ * drm_atomic_helper_check - validate state object
+ * @dev: DRM device
+ * @state: the driver state object
+ *
+ * Check the state object to see if the requested state is physically possible.
+ * Only crtcs and planes have check callbacks, so for any additional (global)
+ * checking that a driver needs it can simply wrap that around this function.
+ * Drivers without such needs can directly use this as their ->atomic_check()
+ * callback.
+ *
+ * This just wraps the two parts of the state checking for planes and modeset
+ * state in the default order: First it calls drm_atomic_helper_check_modeset()
+ * and then drm_atomic_helper_check_planes(). The assumption is that the
+ * ->atomic_check functions depend upon an updated adjusted_mode.clock to
+ * e.g. properly compute watermarks.
+ *
+ * RETURNS
+ * Zero for success or -errno
+ */
+int drm_atomic_helper_check(struct drm_device *dev,
+                           struct drm_atomic_state *state)
+{
+       int ret;
+
        ret = drm_atomic_helper_check_modeset(dev, state);
        if (ret)
                return ret;
 
+       ret = drm_atomic_helper_check_planes(dev, state);
+       if (ret)
+               return ret;
+
        return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_check);
@@ -490,6 +575,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                struct drm_connector *connector;
                struct drm_encoder_helper_funcs *funcs;
                struct drm_encoder *encoder;
+               struct drm_crtc_state *old_crtc_state;
 
                old_conn_state = old_state->connector_states[i];
                connector = old_state->connectors[i];
@@ -499,6 +585,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                if (!old_conn_state || !old_conn_state->crtc)
                        continue;
 
+               old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];
+
+               if (!old_crtc_state->active)
+                       continue;
+
                encoder = old_conn_state->best_encoder;
 
                /* We shouldn't get this far if we didn't previously have
@@ -509,6 +600,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
 
                funcs = encoder->helper_private;
 
+               DRM_DEBUG_KMS("disabling [ENCODER:%d:%s]\n",
+                             encoder->base.id, encoder->name);
+
                /*
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call call disable hooks twice.
@@ -517,7 +611,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                        encoder->bridge->funcs->disable(encoder->bridge);
 
                /* Right function depends upon target state. */
-               if (connector->state->crtc)
+               if (connector->state->crtc && funcs->prepare)
                        funcs->prepare(encoder);
                else if (funcs->disable)
                        funcs->disable(encoder);
@@ -531,17 +625,26 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
        for (i = 0; i < ncrtcs; i++) {
                struct drm_crtc_helper_funcs *funcs;
                struct drm_crtc *crtc;
+               struct drm_crtc_state *old_crtc_state;
 
                crtc = old_state->crtcs[i];
+               old_crtc_state = old_state->crtc_states[i];
 
                /* Shut down everything that needs a full modeset. */
-               if (!crtc || !crtc->state->mode_changed)
+               if (!crtc || !needs_modeset(crtc->state))
+                       continue;
+
+               if (!old_crtc_state->active)
                        continue;
 
                funcs = crtc->helper_private;
 
+               DRM_DEBUG_KMS("disabling [CRTC:%d]\n",
+                             crtc->base.id);
+
+
                /* Right function depends upon target state. */
-               if (crtc->state->enable)
+               if (crtc->state->enable && funcs->prepare)
                        funcs->prepare(crtc);
                else if (funcs->disable)
                        funcs->disable(crtc);
@@ -620,8 +723,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
 
                funcs = crtc->helper_private;
 
-               if (crtc->state->enable)
+               if (crtc->state->enable) {
+                       DRM_DEBUG_KMS("modeset on [CRTC:%d]\n",
+                                     crtc->base.id);
+
                        funcs->mode_set_nofb(crtc);
+               }
        }
 
        for (i = 0; i < old_state->num_connector; i++) {
@@ -642,6 +749,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                mode = &new_crtc_state->mode;
                adjusted_mode = &new_crtc_state->adjusted_mode;
 
+               if (!new_crtc_state->mode_changed)
+                       continue;
+
+               DRM_DEBUG_KMS("modeset on [ENCODER:%d:%s]\n",
+                             encoder->base.id, encoder->name);
+
                /*
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call call mode_set hooks twice.
@@ -694,13 +807,23 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                crtc = old_state->crtcs[i];
 
                /* Need to filter out CRTCs where only planes change. */
-               if (!crtc || !crtc->state->mode_changed)
+               if (!crtc || !needs_modeset(crtc->state))
+                       continue;
+
+               if (!crtc->state->active)
                        continue;
 
                funcs = crtc->helper_private;
 
-               if (crtc->state->enable)
-                       funcs->commit(crtc);
+               if (crtc->state->enable) {
+                       DRM_DEBUG_KMS("enabling [CRTC:%d]\n",
+                                     crtc->base.id);
+
+                       if (funcs->enable)
+                               funcs->enable(crtc);
+                       else
+                               funcs->commit(crtc);
+               }
        }
 
        for (i = 0; i < old_state->num_connector; i++) {
@@ -713,9 +836,15 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                if (!connector || !connector->state->best_encoder)
                        continue;
 
+               if (!connector->state->crtc->state->active)
+                       continue;
+
                encoder = connector->state->best_encoder;
                funcs = encoder->helper_private;
 
+               DRM_DEBUG_KMS("enabling [ENCODER:%d:%s]\n",
+                             encoder->base.id, encoder->name);
+
                /*
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call call enable hooks twice.
@@ -723,7 +852,10 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
                if (encoder->bridge)
                        encoder->bridge->funcs->pre_enable(encoder->bridge);
 
-               funcs->commit(encoder);
+               if (funcs->enable)
+                       funcs->enable(encoder);
+               else
+                       funcs->commit(encoder);
 
                if (encoder->bridge)
                        encoder->bridge->funcs->enable(encoder->bridge);
@@ -813,6 +945,11 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
                if (!crtc->state->enable)
                        continue;
 
+               /* Legacy cursor ioctls are completely unsynced, and userspace
+                * relies on that (by doing tons of cursor updates). */
+               if (old_state->legacy_cursor_update)
+                       continue;
+
                if (!framebuffer_changed(dev, old_state, crtc))
                        continue;
 
@@ -1053,12 +1190,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
 
                funcs = plane->helper_private;
 
-               if (!funcs || !funcs->atomic_update)
+               if (!funcs)
                        continue;
 
                old_plane_state = old_state->plane_states[i];
 
-               funcs->atomic_update(plane, old_plane_state);
+               /*
+                * Special-case disabling the plane if drivers support it.
+                */
+               if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+                   funcs->atomic_disable)
+                       funcs->atomic_disable(plane, old_plane_state);
+               else
+                       funcs->atomic_update(plane, old_plane_state);
        }
 
        for (i = 0; i < ncrtcs; i++) {
@@ -1222,7 +1366,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, crtc);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, fb);
@@ -1239,6 +1383,9 @@ retry:
        if (ret != 0)
                goto fail;
 
+       if (plane == crtc->cursor)
+               state->legacy_cursor_update = true;
+
        /* Driver takes ownership of state on successful commit. */
        return 0;
 fail:
@@ -1301,7 +1448,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, NULL);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, NULL);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, NULL);
@@ -1314,6 +1461,9 @@ retry:
        plane_state->src_h = 0;
        plane_state->src_w = 0;
 
+       if (plane == plane->crtc->cursor)
+               state->legacy_cursor_update = true;
+
        ret = drm_atomic_commit(state);
        if (ret != 0)
                goto fail;
@@ -1463,8 +1613,9 @@ retry:
                WARN_ON(set->num_connectors);
 
                crtc_state->enable = false;
+               crtc_state->active = false;
 
-               ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL);
+               ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
                if (ret != 0)
                        goto fail;
 
@@ -1477,9 +1628,10 @@ retry:
        WARN_ON(!set->num_connectors);
 
        crtc_state->enable = true;
+       crtc_state->active = true;
        drm_mode_copy(&crtc_state->mode, set->mode);
 
-       ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc);
+       ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(primary_state, set->fb);
@@ -1526,12 +1678,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_set_config);
 
 /**
- * drm_atomic_helper_crtc_set_property - helper for crtc prorties
+ * drm_atomic_helper_crtc_set_property - helper for crtc properties
  * @crtc: DRM crtc
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disablle handler using the atomic driver interface.
+ * Provides a default crtc set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1558,8 +1711,8 @@ retry:
                goto fail;
        }
 
-       ret = crtc->funcs->atomic_set_property(crtc, crtc_state,
-                                              property, val);
+       ret = drm_atomic_crtc_set_property(crtc, crtc_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1585,12 +1738,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_crtc_set_property);
 
 /**
- * drm_atomic_helper_plane_set_property - helper for plane prorties
+ * drm_atomic_helper_plane_set_property - helper for plane properties
  * @plane: DRM plane
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disable handler using the atomic driver interface.
+ * Provides a default plane set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1617,8 +1771,8 @@ retry:
                goto fail;
        }
 
-       ret = plane->funcs->atomic_set_property(plane, plane_state,
-                                              property, val);
+       ret = drm_atomic_plane_set_property(plane, plane_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1644,12 +1798,13 @@ backoff:
 EXPORT_SYMBOL(drm_atomic_helper_plane_set_property);
 
 /**
- * drm_atomic_helper_connector_set_property - helper for connector prorties
+ * drm_atomic_helper_connector_set_property - helper for connector properties
  * @connector: DRM connector
  * @property: DRM property
  * @val: value of property
  *
- * Provides a default plane disablle handler using the atomic driver interface.
+ * Provides a default connector set_property handler using the atomic driver
+ * interface.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -1676,8 +1831,8 @@ retry:
                goto fail;
        }
 
-       ret = connector->funcs->atomic_set_property(connector, connector_state,
-                                              property, val);
+       ret = drm_atomic_connector_set_property(connector, connector_state,
+                       property, val);
        if (ret)
                goto fail;
 
@@ -1751,7 +1906,7 @@ retry:
                goto fail;
        }
 
-       ret = drm_atomic_set_crtc_for_plane(state, plane, crtc);
+       ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
        if (ret != 0)
                goto fail;
        drm_atomic_set_fb_for_plane(plane_state, fb);
@@ -1788,6 +1943,83 @@ backoff:
 }
 EXPORT_SYMBOL(drm_atomic_helper_page_flip);
 
+/**
+ * drm_atomic_helper_connector_dpms() - connector dpms helper implementation
+ * @connector: affected connector
+ * @mode: DPMS mode
+ *
+ * This is the main helper function provided by the atomic helper framework for
+ * 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.
+ */
+void 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;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc *crtc;
+       struct drm_connector *tmp_connector;
+       int ret;
+       bool active = false;
+
+       if (mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
+
+       connector->dpms = mode;
+       crtc = connector->state->crtc;
+
+       if (!crtc)
+               return;
+
+       /* FIXME: ->dpms has no return value so can't forward the -ENOMEM. */
+       state = drm_atomic_state_alloc(connector->dev);
+       if (!state)
+               return;
+
+       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;
+
+       WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
+
+       list_for_each_entry(tmp_connector, &config->connector_list, head) {
+               if (connector->state->crtc != crtc)
+                       continue;
+
+               if (connector->dpms == DRM_MODE_DPMS_ON) {
+                       active = true;
+                       break;
+               }
+       }
+       crtc_state->active = active;
+
+       ret = drm_atomic_commit(state);
+       if (ret != 0)
+               goto fail;
+
+       /* Driver takes ownership of state on successful async commit. */
+       return;
+fail:
+       if (ret == -EDEADLK)
+               goto backoff;
+
+       drm_atomic_state_free(state);
+
+       WARN(1, "Driver bug: Changing ->active failed with ret=%i\n", ret);
+
+       return;
+backoff:
+       drm_atomic_state_clear(state);
+       drm_atomic_legacy_backoff(state);
+
+       goto retry;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
+
 /**
  * DOC: atomic state reset and initialization
  *
@@ -1814,6 +2046,9 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
 {
        kfree(crtc->state);
        crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
+
+       if (crtc->state)
+               crtc->state->crtc = crtc;
 }
 EXPORT_SYMBOL(drm_atomic_helper_crtc_reset);
 
@@ -1836,6 +2071,7 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc)
 
        if (state) {
                state->mode_changed = false;
+               state->active_changed = false;
                state->planes_changed = false;
                state->event = NULL;
        }
@@ -1873,6 +2109,9 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 
        kfree(plane->state);
        plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL);
+
+       if (plane->state)
+               plane->state->plane = plane;
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
 
@@ -1930,6 +2169,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector)
 {
        kfree(connector->state);
        connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL);
+
+       if (connector->state)
+               connector->state->connector = connector;
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_reset);