Re: [PATCH] drm: Global atomic state handling

Sean Paul
2014-11-04T22:32:52Z

On Tue, Nov 4, 2014 at 5:07 PM, Daniel Vetter <daniel.vetter@ffwll.ch> wrote:
> Some differences compared to Rob's patches again:
> - Dropped the committed and checked booleans. Checking will be
> internally enforced by always calling ->atomic_check before
> ->atomic_commit. And async handling needs to be solved differently
> because the current scheme completely side-steps ww mutex deadlock
> avoidance (and so either reinvents a new deadlock avoidance wheel or
> like the current code just deadlocks).
>
> - State for connectors needed to be added, since now they have a
> full-blown drm_connector_state (so that drivers have something to
> attach their own stuff to).
>
> - Refcounting is gone. I plane to solve async updates differently,
> since the lock-passing scheme doesn't cut it (since it abuses ww
> mutexes). Essentially what we need for async is a simple ownership
> transfer from the caller to the driver. That doesn't need full-blown
> refcounting.
>
> - The acquire ctx is a pointer. Real atomic callers should have that
> on their stack, legacy entry points need to put the right one
> (obtained by drm_modeset_legacy_acuire_ctx) in there.
>
> - I've dropped all hooks except check/commit. All the begin/end
> handling is done by core functions and is the same.
>
> - commit/check are just thin wrappers that ensure that ->check is
> always called.
>
> - To help out with locking in the legacy implementations I've added a
> helper to just grab all locks in the backoff case.
>
> v2: Add notices that check/commit can fail with EDEADLK.
>
> v3:
> - More consistent naming for state_alloc.
> - Add state_clear which is needed for backoff and retry.
>
> v4: Planes/connectors can switch between crtcs, and we need to be
> careful that we grab the state (and locks) for both the old and new
> crtc. Improve the interface functions to ensure this.
>
> v5: Add functions to grab affected connectors for a crtc and to recompute
> the crtc->enable state. This is useful for both helper and atomic ioctl
> code when e.g. removing a connector.
>
> v6: Squash in fixup from Fengguang to use ERR_CAST.
>
> v7: Add debug output.
>
> v8: Make checkpatch happy about kcalloc argument ordering.
>
> v9: Improve kerneldoc in drm_crtc.h
>
> v10:
> - Fix another kcalloc argument misorder I've missed.
> - More polish for kerneldoc.
>
> v11: Clarify the ownership rules for the state object. The new rule is
> that a successful drm_atomic_commit (whether synchronous or asnyc)
> always inherits the state and is responsible for the clean-up. That
> way async and sync ->commit functions are more similar.
>
> v12: A few bugfixes:
> - Assign state->state pointers correctly when grabbing state objects -
> we need to link them up with the global state.
> - Handle a NULL crtc in set_crtc_for_plane to simplify code flow a bit
> for the callers of this function.
>
> v13: Review from Sean:
> - kerneldoc spelling fixes
> - Don't overallocate states->planes.
> - Handle NULL crtc in set_crtc_for_connector.
>
> v14: Sprinkle __must_check over all functions which do wait/wound
> locking to make sure callers don't forget this. Since I have ;-)
>

Thanks for the quick turnaround.

Reviewed-by: Sean Paul <seanpaul@chromium.org>

> Cc: Fengguang Wu <fengguang.wu@intel.com>
> Cc: Sean Paul <seanpaul@chromium.org>
> Cc: Matt Roper <matthew.d.roper@intel.com>
> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
>
> fixup for global atomic state handling
> ---
> drivers/gpu/drm/Makefile | 2 +-
> drivers/gpu/drm/drm_atomic.c | 594 +++++++++++++++++++++++++++++++++++++++++++
> include/drm/drm_atomic.h | 65 +++++
> include/drm/drm_crtc.h | 35 +++
> 4 files changed, 695 insertions(+), 1 deletion(-)
> create mode 100644 drivers/gpu/drm/drm_atomic.c
> create mode 100644 include/drm/drm_atomic.h
>
> diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
> index 9292a761ea6d..c5e37dc459ee 100644
> --- a/drivers/gpu/drm/Makefile
> +++ b/drivers/gpu/drm/Makefile
> @@ -14,7 +14,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
> drm_info.o drm_debugfs.o drm_encoder_slave.o \
> drm_trace_points.o drm_global.o drm_prime.o \
> drm_rect.o drm_vma_manager.o drm_flip_work.o \
> - drm_modeset_lock.o
> + drm_modeset_lock.o drm_atomic.o
>
> drm-$(CONFIG_COMPAT) += drm_ioc32.o
> drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> new file mode 100644
> index 000000000000..bf3cdc2133e4
> --- /dev/null
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -0,0 +1,594 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Copyright (C) 2014 Intel Corp.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Rob Clark <robdclark@gmail.com>
> + * Daniel Vetter <daniel.vetter@ffwll.ch>
> + */
> +
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_plane_helper.h>
> +
> +static void kfree_state(struct drm_atomic_state *state)
> +{
> + kfree(state->connectors);
> + kfree(state->connector_states);
> + kfree(state->crtcs);
> + kfree(state->crtc_states);
> + kfree(state->planes);
> + kfree(state->plane_states);
> + kfree(state);
> +}
> +
> +/**
> + * drm_atomic_state_alloc - allocate atomic state
> + * @dev: DRM device
> + *
> + * This allocates an empty atomic state to track updates.
> + */
> +struct drm_atomic_state *
> +drm_atomic_state_alloc(struct drm_device *dev)
> +{
> + struct drm_atomic_state *state;
> +
> + state = kzalloc(sizeof(*state), GFP_KERNEL);
> + if (!state)
> + return NULL;
> +
> + state->crtcs = kcalloc(dev->mode_config.num_crtc,
> + sizeof(*state->crtcs), GFP_KERNEL);
> + if (!state->crtcs)
> + goto fail;
> + state->crtc_states = kcalloc(dev->mode_config.num_crtc,
> + sizeof(*state->crtc_states), GFP_KERNEL);
> + if (!state->crtc_states)
> + goto fail;
> + state->planes = kcalloc(dev->mode_config.num_total_plane,
> + sizeof(*state->planes), GFP_KERNEL);
> + if (!state->planes)
> + goto fail;
> + state->plane_states = kcalloc(dev->mode_config.num_total_plane,
> + sizeof(*state->plane_states), GFP_KERNEL);
> + if (!state->plane_states)
> + goto fail;
> + state->connectors = kcalloc(dev->mode_config.num_connector,
> + sizeof(*state->connectors),
> + GFP_KERNEL);
> + if (!state->connectors)
> + goto fail;
> + state->connector_states = kcalloc(dev->mode_config.num_connector,
> + sizeof(*state->connector_states),
> + GFP_KERNEL);
> + if (!state->connector_states)
> + goto fail;
> +
> + state->dev = dev;
> +
> + DRM_DEBUG_KMS("Allocate atomic state %p\n", state);
> +
> + return state;
> +fail:
> + kfree_state(state);
> +
> + return NULL;
> +}
> +EXPORT_SYMBOL(drm_atomic_state_alloc);
> +
> +/**
> + * drm_atomic_state_clear - clear state object
> + * @state: atomic state
> + *
> + * When the w/w mutex algorithm detects a deadlock we need to back off and drop
> + * all locks. So someone else could sneak in and change the current modeset
> + * configuration. Which means that all the state assembled in @state is no
> + * longer an atomic update to the current state, but to some arbitrary earlier
> + * state. Which could break assumptions the driver's ->atomic_check likely
> + * relies on.
> + *
> + * Hence we must clear all cached state and completely start over, using this
> + * function.
> + */
> +void drm_atomic_state_clear(struct drm_atomic_state *state)
> +{
> + struct drm_device *dev = state->dev;
> + int i;
> +
> + DRM_DEBUG_KMS("Clearing atomic state %p\n", state);
> +
> + for (i = 0; i < dev->mode_config.num_connector; i++) {
> + struct drm_connector *connector = state->connectors[i];
> +
> + if (!connector)
> + continue;
> +
> + connector->funcs->atomic_destroy_state(connector,
> + state->connector_states[i]);
> + }
> +
> + for (i = 0; i < dev->mode_config.num_crtc; i++) {
> + struct drm_crtc *crtc = state->crtcs[i];
> +
> + if (!crtc)
> + continue;
> +
> + crtc->funcs->atomic_destroy_state(crtc,
> + state->crtc_states[i]);
> + }
> +
> + for (i = 0; i < dev->mode_config.num_total_plane; i++) {
> + struct drm_plane *plane = state->planes[i];
> +
> + if (!plane)
> + continue;
> +
> + plane->funcs->atomic_destroy_state(plane,
> + state->plane_states[i]);
> + }
> +}
> +EXPORT_SYMBOL(drm_atomic_state_clear);
> +
> +/**
> + * drm_atomic_state_free - free all memory for an atomic state
> + * @state: atomic state to deallocate
> + *
> + * This frees all memory associated with an atomic state, including all the
> + * per-object state for planes, crtcs and connectors.
> + */
> +void drm_atomic_state_free(struct drm_atomic_state *state)
> +{
> + drm_atomic_state_clear(state);
> +
> + DRM_DEBUG_KMS("Freeing atomic state %p\n", state);
> +
> + kfree_state(state);
> +}
> +EXPORT_SYMBOL(drm_atomic_state_free);
> +
> +/**
> + * drm_atomic_get_crtc_state - get crtc state
> + * @state: global atomic state object
> + * @crtc: crtc to get state object for
> + *
> + * This function returns the crtc state for the given crtc, allocating it if
> + * needed. It will also grab the relevant crtc lock to make sure that the state
> + * is consistent.
> + *
> + * Returns:
> + *
> + * Either the allocated state or the error code encoded into the pointer. When
> + * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
> + * entire atomic sequence must be restarted. All other errors are fatal.
> + */
> +struct drm_crtc_state *
> +drm_atomic_get_crtc_state(struct drm_atomic_state *state,
> + struct drm_crtc *crtc)
> +{
> + int ret, index;
> + struct drm_crtc_state *crtc_state;
> +
> + index = drm_crtc_index(crtc);
> +
> + if (state->crtc_states[index])
> + return state->crtc_states[index];
> +
> + ret = drm_modeset_lock(&crtc->mutex, state->acquire_ctx);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + crtc_state = crtc->funcs->atomic_duplicate_state(crtc);
> + if (!crtc_state)
> + return ERR_PTR(-ENOMEM);
> +
> + state->crtc_states[index] = crtc_state;
> + state->crtcs[index] = crtc;
> + crtc_state->state = state;
> +
> + DRM_DEBUG_KMS("Added [CRTC:%d] %p state to %p\n",
> + crtc->base.id, crtc_state, state);
> +
> + return crtc_state;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_crtc_state);
> +
> +/**
> + * drm_atomic_get_plane_state - get plane state
> + * @state: global atomic state object
> + * @plane: plane to get state object for
> + *
> + * This function returns the plane state for the given plane, allocating it if
> + * needed. It will also grab the relevant plane lock to make sure that the state
> + * is consistent.
> + *
> + * Returns:
> + *
> + * Either the allocated state or the error code encoded into the pointer. When
> + * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
> + * entire atomic sequence must be restarted. All other errors are fatal.
> + */
> +struct drm_plane_state *
> +drm_atomic_get_plane_state(struct drm_atomic_state *state,
> + struct drm_plane *plane)
> +{
> + int ret, index;
> + struct drm_plane_state *plane_state;
> +
> + index = drm_plane_index(plane);
> +
> + if (state->plane_states[index])
> + return state->plane_states[index];
> +
> + /*
> + * TODO: We currently don't have per-plane mutexes. So instead of trying
> + * crazy tricks with deferring plane->crtc and hoping for the best just
> + * grab all crtc locks. Once we have per-plane locks we must update this
> + * to only take the plane mutex.
> + */
> + ret = drm_modeset_lock_all_crtcs(state->dev, state->acquire_ctx);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + plane_state = plane->funcs->atomic_duplicate_state(plane);
> + if (!plane_state)
> + return ERR_PTR(-ENOMEM);
> +
> + state->plane_states[index] = plane_state;
> + state->planes[index] = plane;
> + plane_state->state = state;
> +
> + DRM_DEBUG_KMS("Added [PLANE:%d] %p state to %p\n",
> + plane->base.id, plane_state, state);
> +
> + if (plane_state->crtc) {
> + struct drm_crtc_state *crtc_state;
> +
> + crtc_state = drm_atomic_get_crtc_state(state,
> + plane_state->crtc);
> + if (IS_ERR(crtc_state))
> + return ERR_CAST(crtc_state);
> + }
> +
> + return plane_state;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_plane_state);
> +
> +/**
> + * drm_atomic_get_connector_state - get connector state
> + * @state: global atomic state object
> + * @connector: connector to get state object for
> + *
> + * This function returns the connector state for the given connector,
> + * allocating it if needed. It will also grab the relevant connector lock to
> + * make sure that the state is consistent.
> + *
> + * Returns:
> + *
> + * Either the allocated state or the error code encoded into the pointer. When
> + * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
> + * entire atomic sequence must be restarted. All other errors are fatal.
> + */
> +struct drm_connector_state *
> +drm_atomic_get_connector_state(struct drm_atomic_state *state,
> + struct drm_connector *connector)
> +{
> + int ret, index;
> + struct drm_mode_config *config = &connector->dev->mode_config;
> + struct drm_connector_state *connector_state;
> +
> + index = drm_connector_index(connector);
> +
> + if (state->connector_states[index])
> + return state->connector_states[index];
> +
> + ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx);
> + if (ret)
> + return ERR_PTR(ret);
> +
> + connector_state = connector->funcs->atomic_duplicate_state(connector);
> + if (!connector_state)
> + return ERR_PTR(-ENOMEM);
> +
> + state->connector_states[index] = connector_state;
> + state->connectors[index] = connector;
> + connector_state->state = state;
> +
> + DRM_DEBUG_KMS("Added [CONNECTOR:%d] %p state to %p\n",
> + connector->base.id, connector_state, state);
> +
> + if (connector_state->crtc) {
> + struct drm_crtc_state *crtc_state;
> +
> + crtc_state = drm_atomic_get_crtc_state(state,
> + connector_state->crtc);
> + if (IS_ERR(crtc_state))
> + return ERR_CAST(crtc_state);
> + }
> +
> + return connector_state;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_connector_state);
> +
> +/**
> + * drm_atomic_set_crtc_for_plane - set crtc for plane
> + * @plane_state: atomic state object for the plane
> + * @crtc: crtc to use for the plane
> + *
> + * Changing the assigned crtc for a plane requires us to grab the lock and state
> + * for the new crtc, as needed. This function takes care of all these details
> + * besides updating the pointer in the state object itself.
> + *
> + * Returns:
> + * 0 on success or can fail with -EDEADLK or -ENOMEM.
> + */
> +int
> +drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
> + struct drm_crtc *crtc)
> +{
> + struct drm_crtc_state *crtc_state;
> +
> + if (crtc) {
> + crtc_state = drm_atomic_get_crtc_state(plane_state->state,
> + crtc);
> + if (IS_ERR(crtc_state))
> + return PTR_ERR(crtc_state);
> + }
> +
> + plane_state->crtc = crtc;
> +
> + if (crtc)
> + DRM_DEBUG_KMS("Link plane state %p to [CRTC:%d]\n",
> + plane_state, crtc->base.id);
> + else
> + DRM_DEBUG_KMS("Link plane state %p to [NOCRTC]\n", plane_state);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_set_crtc_for_plane);
> +
> +/**
> + * drm_atomic_set_crtc_for_connector - set crtc for connector
> + * @conn_state: atomic state object for the connector
> + * @crtc: crtc to use for the connector
> + *
> + * Changing the assigned crtc for a connector requires us to grab the lock and
> + * state for the new crtc, as needed. This function takes care of all these
> + * details besides updating the pointer in the state object itself.
> + *
> + * Returns:
> + * 0 on success or can fail with -EDEADLK or -ENOMEM.
> + */
> +int
> +drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
> + struct drm_crtc *crtc)
> +{
> + struct drm_crtc_state *crtc_state;
> +
> + if (crtc) {
> + crtc_state = drm_atomic_get_crtc_state(conn_state->state, crtc);
> + if (IS_ERR(crtc_state))
> + return PTR_ERR(crtc_state);
> + }
> +
> + conn_state->crtc = crtc;
> +
> + if (crtc)
> + DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n",
> + conn_state, crtc->base.id);
> + else
> + DRM_DEBUG_KMS("Link connector state %p to [NOCRTC]\n",
> + conn_state);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector);
> +
> +/**
> + * drm_atomic_add_affected_connectors - add connectors for crtc
> + * @state: atomic state
> + * @crtc: DRM crtc
> + *
> + * This function walks the current configuration and adds all connectors
> + * currently using @crtc to the atomic configuration @state. Note that this
> + * function must acquire the connection mutex. This can potentially cause
> + * unneeded seralization if the update is just for the planes on one crtc. Hence
> + * drivers and helpers should only call this when really needed (e.g. when a
> + * full modeset needs to happen due to some change).
> + *
> + * Returns:
> + * 0 on success or can fail with -EDEADLK or -ENOMEM.
> + */
> +int
> +drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
> + struct drm_crtc *crtc)
> +{
> + struct drm_mode_config *config = &state->dev->mode_config;
> + struct drm_connector *connector;
> + struct drm_connector_state *conn_state;
> + int ret;
> +
> + ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx);
> + if (ret)
> + return ret;
> +
> + DRM_DEBUG_KMS("Adding all current connectors for [CRTC:%d] to %p\n",
> + crtc->base.id, state);
> +
> + /*
> + * Changed connectors are already in @state, so only need to look at the
> + * current configuration.
> + */
> + list_for_each_entry(connector, &config->connector_list, head) {
> + if (connector->state->crtc != crtc)
> + continue;
> +
> + conn_state = drm_atomic_get_connector_state(state, connector);
> + if (IS_ERR(conn_state))
> + return PTR_ERR(conn_state);
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_add_affected_connectors);
> +
> +/**
> + * drm_atomic_connectors_for_crtc - count number of connected outputs
> + * @state: atomic state
> + * @crtc: DRM crtc
> + *
> + * This function counts all connectors which will be connected to @crtc
> + * according to @state. Useful to recompute the enable state for @crtc.
> + */
> +int
> +drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
> + struct drm_crtc *crtc)
> +{
> + int nconnectors = state->dev->mode_config.num_connector;
> + int i, num_connected_connectors = 0;
> +
> + for (i = 0; i < nconnectors; i++) {
> + struct drm_connector_state *conn_state;
> +
> + conn_state = state->connector_states[i];
> +
> + if (conn_state && conn_state->crtc == crtc)
> + num_connected_connectors++;
> + }
> +
> + DRM_DEBUG_KMS("State %p has %i connectors for [CRTC:%d]\n",
> + state, num_connected_connectors, crtc->base.id);
> +
> + return num_connected_connectors;
> +}
> +EXPORT_SYMBOL(drm_atomic_connectors_for_crtc);
> +
> +/**
> + * drm_atomic_legacy_backoff - locking backoff for legacy ioctls
> + * @state: atomic state
> + *
> + * This function should be used by legacy entry points which don't understand
> + * -EDEADLK semantics. For simplicity this one will grab all modeset locks after
> + * the slowpath completed.
> + */
> +void drm_atomic_legacy_backoff(struct drm_atomic_state *state)
> +{
> + int ret;
> +
> +retry:
> + drm_modeset_backoff(state->acquire_ctx);
> +
> + ret = drm_modeset_lock(&state->dev->mode_config.connection_mutex,
> + state->acquire_ctx);
> + if (ret)
> + goto retry;
> + ret = drm_modeset_lock_all_crtcs(state->dev,
> + state->acquire_ctx);
> + if (ret)
> + goto retry;
> +}
> +EXPORT_SYMBOL(drm_atomic_legacy_backoff);
> +
> +/**
> + * drm_atomic_check_only - check whether a given config would work
> + * @state: atomic configuration to check
> + *
> + * Note that this function can return -EDEADLK if the driver needed to acquire
> + * more locks but encountered a deadlock. The caller must then do the usual w/w
> + * backoff dance and restart.
> + *
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_atomic_check_only(struct drm_atomic_state *state)
> +{
> + struct drm_mode_config *config = &state->dev->mode_config;
> +
> + DRM_DEBUG_KMS("checking %p\n", state);
> +
> + if (config->funcs->atomic_check)
> + return config->funcs->atomic_check(state->dev, state);
> + else
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_atomic_check_only);
> +
> +/**
> + * drm_atomic_commit - commit configuration atomically
> + * @state: atomic configuration to check
> + *
> + * Note that this function can return -EDEADLK if the driver needed to acquire
> + * more locks but encountered a deadlock. The caller must then do the usual w/w
> + * backoff dance and restart.
> + *
> + * Also note that on successful execution ownership of @state is transferred
> + * from the caller of this function to the function itself. The caller must not
> + * free or in any other way access @state. If the function fails then the caller
> + * must clean up @state itself.
> + *
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_atomic_commit(struct drm_atomic_state *state)
> +{
> + struct drm_mode_config *config = &state->dev->mode_config;
> + int ret;
> +
> + ret = drm_atomic_check_only(state);
> + if (ret)
> + return ret;
> +
> + DRM_DEBUG_KMS("commiting %p\n", state);
> +
> + return config->funcs->atomic_commit(state->dev, state, false);
> +}
> +EXPORT_SYMBOL(drm_atomic_commit);
> +
> +/**
> + * drm_atomic_async_commit - atomic&async configuration commit
> + * @state: atomic configuration to check
> + *
> + * Note that this function can return -EDEADLK if the driver needed to acquire
> + * more locks but encountered a deadlock. The caller must then do the usual w/w
> + * backoff dance and restart.
> + *
> + * Also note that on successful execution ownership of @state is transferred
> + * from the caller of this function to the function itself. The caller must not
> + * free or in any other way access @state. If the function fails then the caller
> + * must clean up @state itself.
> + *
> + * Returns:
> + * 0 on success, negative error code on failure.
> + */
> +int drm_atomic_async_commit(struct drm_atomic_state *state)
> +{
> + struct drm_mode_config *config = &state->dev->mode_config;
> + int ret;
> +
> + ret = drm_atomic_check_only(state);
> + if (ret)
> + return ret;
> +
> + DRM_DEBUG_KMS("commiting %p asynchronously\n", state);
> +
> + return config->funcs->atomic_commit(state->dev, state, true);
> +}
> +EXPORT_SYMBOL(drm_atomic_async_commit);
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> new file mode 100644
> index 000000000000..5bb15f550c42
> --- /dev/null
> +++ b/include/drm/drm_atomic.h
> @@ -0,0 +1,65 @@
> +/*
> + * Copyright (C) 2014 Red Hat
> + * Copyright (C) 2014 Intel Corp.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
> + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
> + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * Authors:
> + * Rob Clark <robdclark@gmail.com>
> + * Daniel Vetter <daniel.vetter@ffwll.ch>
> + */
> +
> +#ifndef DRM_ATOMIC_H_
> +#define DRM_ATOMIC_H_
> +
> +struct drm_atomic_state * __must_check
> +drm_atomic_state_alloc(struct drm_device *dev);
> +void drm_atomic_state_clear(struct drm_atomic_state *state);
> +void drm_atomic_state_free(struct drm_atomic_state *state);
> +
> +struct drm_crtc_state * __must_check
> +drm_atomic_get_crtc_state(struct drm_atomic_state *state,
> + struct drm_crtc *crtc);
> +struct drm_plane_state * __must_check
> +drm_atomic_get_plane_state(struct drm_atomic_state *state,
> + struct drm_plane *plane);
> +struct drm_connector_state * __must_check
> +drm_atomic_get_connector_state(struct drm_atomic_state *state,
> + struct drm_connector *connector);
> +
> +int __must_check
> +drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state,
> + struct drm_crtc *crtc);
> +int __must_check
> +drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
> + struct drm_crtc *crtc);
> +int __must_check
> +drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
> + struct drm_crtc *crtc);
> +int
> +drm_atomic_connectors_for_crtc(struct drm_atomic_state *state,
> + struct drm_crtc *crtc);
> +
> +void drm_atomic_legacy_backoff(struct drm_atomic_state *state);
> +
> +int __must_check drm_atomic_check_only(struct drm_atomic_state *state);
> +int __must_check drm_atomic_commit(struct drm_atomic_state *state);
> +int __must_check drm_atomic_async_commit(struct drm_atomic_state *state);
> +
> +#endif /* DRM_ATOMIC_H_ */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 3554868dbf09..07c88ad64234 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -794,6 +794,32 @@ struct drm_bridge {
> };
>
> /**
> + * struct struct drm_atomic_state - the global state object for atomic updates
> + * @dev: parent DRM device
> + * @flags: state flags like async update
> + * @planes: pointer to array of plane pointers
> + * @plane_states: pointer to array of plane states pointers
> + * @crtcs: pointer to array of CRTC pointers
> + * @crtc_states: pointer to array of CRTC states pointers
> + * @connectors: pointer to array of connector pointers
> + * @connector_states: pointer to array of connector states pointers
> + * @acquire_ctx: acquire context for this atomic modeset state update
> + */
> +struct drm_atomic_state {
> + struct drm_device *dev;
> + uint32_t flags;
> + struct drm_plane **planes;
> + struct drm_plane_state **plane_states;
> + struct drm_crtc **crtcs;
> + struct drm_crtc_state **crtc_states;
> + struct drm_connector **connectors;
> + struct drm_connector_state **connector_states;
> +
> + struct drm_modeset_acquire_ctx *acquire_ctx;
> +};
> +
> +
> +/**
> * struct drm_mode_set - new values for a CRTC config change
> * @fb: framebuffer to use for new config
> * @crtc: CRTC whose configuration we're about to change
> @@ -824,6 +850,9 @@ struct drm_mode_set {
> * struct drm_mode_config_funcs - basic driver provided mode setting functions
> * @fb_create: create a new framebuffer object
> * @output_poll_changed: function to handle output configuration changes
> + * @atomic_check: check whether a give atomic state update is possible
> + * @atomic_commit: commit an atomic state update previously verified with
> + * atomic_check()
> *
> * Some global (i.e. not per-CRTC, connector, etc) mode setting functions that
> * involve drivers.
> @@ -833,6 +862,12 @@ struct drm_mode_config_funcs {
> struct drm_file *file_priv,
> struct drm_mode_fb_cmd2 *mode_cmd);
> void (*output_poll_changed)(struct drm_device *dev);
> +
> + int (*atomic_check)(struct drm_device *dev,
> + struct drm_atomic_state *a);
> + int (*atomic_commit)(struct drm_device *dev,
> + struct drm_atomic_state *a,
> + bool async);
> };
>
> /**
> --
> 2.1.1