|
@@ -80,6 +80,27 @@ drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static bool
|
|
|
+check_pending_encoder_assignment(struct drm_atomic_state *state,
|
|
|
+ struct drm_encoder *new_encoder,
|
|
|
+ struct drm_connector *new_connector)
|
|
|
+{
|
|
|
+ struct drm_connector *connector;
|
|
|
+ struct drm_connector_state *conn_state;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for_each_connector_in_state(state, connector, conn_state, i) {
|
|
|
+ if (conn_state->best_encoder != new_encoder)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ /* encoder already assigned and we're trying to re-steal it! */
|
|
|
+ if (connector->state->best_encoder != conn_state->best_encoder)
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static struct drm_crtc *
|
|
|
get_current_crtc_for_encoder(struct drm_device *dev,
|
|
|
struct drm_encoder *encoder)
|
|
@@ -229,6 +250,13 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+ if (!check_pending_encoder_assignment(state, new_encoder, connector)) {
|
|
|
+ DRM_DEBUG_ATOMIC("Encoder for [CONNECTOR:%d:%s] already assigned\n",
|
|
|
+ connector->base.id,
|
|
|
+ connector->name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
encoder_crtc = get_current_crtc_for_encoder(state->dev,
|
|
|
new_encoder);
|
|
|
|
|
@@ -1341,6 +1369,49 @@ drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
|
|
|
}
|
|
|
EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
|
|
|
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes
|
|
|
+ * @crtc: CRTC
|
|
|
+ * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks
|
|
|
+ *
|
|
|
+ * Disables all planes associated with the given CRTC. This can be
|
|
|
+ * used for instance in the CRTC helper disable callback to disable
|
|
|
+ * all planes before shutting down the display pipeline.
|
|
|
+ *
|
|
|
+ * If the atomic-parameter is set the function calls the CRTC's
|
|
|
+ * atomic_begin hook before and atomic_flush hook after disabling the
|
|
|
+ * planes.
|
|
|
+ *
|
|
|
+ * It is a bug to call this function without having implemented the
|
|
|
+ * ->atomic_disable() plane hook.
|
|
|
+ */
|
|
|
+void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
|
|
|
+ bool atomic)
|
|
|
+{
|
|
|
+ const struct drm_crtc_helper_funcs *crtc_funcs =
|
|
|
+ crtc->helper_private;
|
|
|
+ struct drm_plane *plane;
|
|
|
+
|
|
|
+ if (atomic && crtc_funcs && crtc_funcs->atomic_begin)
|
|
|
+ crtc_funcs->atomic_begin(crtc, NULL);
|
|
|
+
|
|
|
+ drm_for_each_plane(plane, crtc->dev) {
|
|
|
+ const struct drm_plane_helper_funcs *plane_funcs =
|
|
|
+ plane->helper_private;
|
|
|
+
|
|
|
+ if (plane->state->crtc != crtc || !plane_funcs)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ WARN_ON(!plane_funcs->atomic_disable);
|
|
|
+ if (plane_funcs->atomic_disable)
|
|
|
+ plane_funcs->atomic_disable(plane, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (atomic && crtc_funcs && crtc_funcs->atomic_flush)
|
|
|
+ crtc_funcs->atomic_flush(crtc, NULL);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_disable_planes_on_crtc);
|
|
|
+
|
|
|
/**
|
|
|
* drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
|
|
|
* @dev: DRM device
|
|
@@ -1817,6 +1888,161 @@ commit:
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_disable_all - disable all currently active outputs
|
|
|
+ * @dev: DRM device
|
|
|
+ * @ctx: lock acquisition context
|
|
|
+ *
|
|
|
+ * Loops through all connectors, finding those that aren't turned off and then
|
|
|
+ * turns them off by setting their DPMS mode to OFF and deactivating the CRTC
|
|
|
+ * that they are connected to.
|
|
|
+ *
|
|
|
+ * This is used for example in suspend/resume to disable all currently active
|
|
|
+ * functions when suspending.
|
|
|
+ *
|
|
|
+ * Note that if callers haven't already acquired all modeset locks this might
|
|
|
+ * return -EDEADLK, which must be handled by calling drm_modeset_backoff().
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * 0 on success or a negative error code on failure.
|
|
|
+ *
|
|
|
+ * See also:
|
|
|
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
|
|
|
+ */
|
|
|
+int drm_atomic_helper_disable_all(struct drm_device *dev,
|
|
|
+ struct drm_modeset_acquire_ctx *ctx)
|
|
|
+{
|
|
|
+ struct drm_atomic_state *state;
|
|
|
+ struct drm_connector *conn;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ state = drm_atomic_state_alloc(dev);
|
|
|
+ if (!state)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ state->acquire_ctx = ctx;
|
|
|
+
|
|
|
+ drm_for_each_connector(conn, dev) {
|
|
|
+ struct drm_crtc *crtc = conn->state->crtc;
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
|
+
|
|
|
+ if (!crtc || conn->dpms != DRM_MODE_DPMS_ON)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
|
+ if (IS_ERR(crtc_state)) {
|
|
|
+ err = PTR_ERR(crtc_state);
|
|
|
+ goto free;
|
|
|
+ }
|
|
|
+
|
|
|
+ crtc_state->active = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = drm_atomic_commit(state);
|
|
|
+
|
|
|
+free:
|
|
|
+ if (err < 0)
|
|
|
+ drm_atomic_state_free(state);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_disable_all);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_suspend - subsystem-level suspend helper
|
|
|
+ * @dev: DRM device
|
|
|
+ *
|
|
|
+ * Duplicates the current atomic state, disables all active outputs and then
|
|
|
+ * returns a pointer to the original atomic state to the caller. Drivers can
|
|
|
+ * pass this pointer to the drm_atomic_helper_resume() helper upon resume to
|
|
|
+ * restore the output configuration that was active at the time the system
|
|
|
+ * entered suspend.
|
|
|
+ *
|
|
|
+ * Note that it is potentially unsafe to use this. The atomic state object
|
|
|
+ * returned by this function is assumed to be persistent. Drivers must ensure
|
|
|
+ * that this holds true. Before calling this function, drivers must make sure
|
|
|
+ * to suspend fbdev emulation so that nothing can be using the device.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * A pointer to a copy of the state before suspend on success or an ERR_PTR()-
|
|
|
+ * encoded error code on failure. Drivers should store the returned atomic
|
|
|
+ * state object and pass it to the drm_atomic_helper_resume() helper upon
|
|
|
+ * resume.
|
|
|
+ *
|
|
|
+ * See also:
|
|
|
+ * drm_atomic_helper_duplicate_state(), drm_atomic_helper_disable_all(),
|
|
|
+ * drm_atomic_helper_resume()
|
|
|
+ */
|
|
|
+struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev)
|
|
|
+{
|
|
|
+ struct drm_modeset_acquire_ctx ctx;
|
|
|
+ struct drm_atomic_state *state;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ drm_modeset_acquire_init(&ctx, 0);
|
|
|
+
|
|
|
+retry:
|
|
|
+ err = drm_modeset_lock_all_ctx(dev, &ctx);
|
|
|
+ if (err < 0) {
|
|
|
+ state = ERR_PTR(err);
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ state = drm_atomic_helper_duplicate_state(dev, &ctx);
|
|
|
+ if (IS_ERR(state))
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ err = drm_atomic_helper_disable_all(dev, &ctx);
|
|
|
+ if (err < 0) {
|
|
|
+ drm_atomic_state_free(state);
|
|
|
+ state = ERR_PTR(err);
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+unlock:
|
|
|
+ if (PTR_ERR(state) == -EDEADLK) {
|
|
|
+ drm_modeset_backoff(&ctx);
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+
|
|
|
+ drm_modeset_drop_locks(&ctx);
|
|
|
+ drm_modeset_acquire_fini(&ctx);
|
|
|
+ return state;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_suspend);
|
|
|
+
|
|
|
+/**
|
|
|
+ * drm_atomic_helper_resume - subsystem-level resume helper
|
|
|
+ * @dev: DRM device
|
|
|
+ * @state: atomic state to resume to
|
|
|
+ *
|
|
|
+ * Calls drm_mode_config_reset() to synchronize hardware and software states,
|
|
|
+ * grabs all modeset locks and commits the atomic state object. This can be
|
|
|
+ * used in conjunction with the drm_atomic_helper_suspend() helper to
|
|
|
+ * implement suspend/resume for drivers that support atomic mode-setting.
|
|
|
+ *
|
|
|
+ * Returns:
|
|
|
+ * 0 on success or a negative error code on failure.
|
|
|
+ *
|
|
|
+ * See also:
|
|
|
+ * drm_atomic_helper_suspend()
|
|
|
+ */
|
|
|
+int drm_atomic_helper_resume(struct drm_device *dev,
|
|
|
+ struct drm_atomic_state *state)
|
|
|
+{
|
|
|
+ struct drm_mode_config *config = &dev->mode_config;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ drm_mode_config_reset(dev);
|
|
|
+ drm_modeset_lock_all(dev);
|
|
|
+ state->acquire_ctx = config->acquire_ctx;
|
|
|
+ err = drm_atomic_commit(state);
|
|
|
+ drm_modeset_unlock_all(dev);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(drm_atomic_helper_resume);
|
|
|
+
|
|
|
/**
|
|
|
* drm_atomic_helper_crtc_set_property - helper for crtc properties
|
|
|
* @crtc: DRM crtc
|
|
@@ -2429,7 +2655,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
|
|
|
* @ctx: lock acquisition context
|
|
|
*
|
|
|
* Makes a copy of the current atomic state by looping over all objects and
|
|
|
- * duplicating their respective states.
|
|
|
+ * duplicating their respective states. This is used for example by suspend/
|
|
|
+ * resume support code to save the state prior to suspend such that it can
|
|
|
+ * be restored upon resume.
|
|
|
*
|
|
|
* Note that this treats atomic state as persistent between save and restore.
|
|
|
* Drivers must make sure that this is possible and won't result in confusion
|
|
@@ -2441,6 +2669,9 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state);
|
|
|
* Returns:
|
|
|
* A pointer to the copy of the atomic state object on success or an
|
|
|
* ERR_PTR()-encoded error code on failure.
|
|
|
+ *
|
|
|
+ * See also:
|
|
|
+ * drm_atomic_helper_suspend(), drm_atomic_helper_resume()
|
|
|
*/
|
|
|
struct drm_atomic_state *
|
|
|
drm_atomic_helper_duplicate_state(struct drm_device *dev,
|