On Sun, Nov 02, 2014 at 02:19:19PM +0100, Daniel Vetter 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. > > Cc: Fengguang Wu <fengguang.wu at intel.com> > Signed-off-by: Daniel Vetter <daniel.vetter at ffwll.ch> > --- > drivers/gpu/drm/Makefile | 2 +- > drivers/gpu/drm/drm_atomic.c | 588 > +++++++++++++++++++++++++++++++++++++++++++ > include/drm/drm_atomic.h | 63 +++++ > include/drm/drm_crtc.h | 35 +++ > 4 files changed, 687 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 a3149e20a249..2e89cd50c14f 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..c6db8a48cad6 > --- /dev/null > +++ b/drivers/gpu/drm/drm_atomic.c > @@ -0,0 +1,588 @@ > +/* > + * 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 at gmail.com> > + * Daniel Vetter <daniel.vetter at 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->plane_states), GFP_KERNEL);
sizeof(*state->planes) > + 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 functions returns the crtc state for the given crtc, allocating it if nit: s/functions/function Looks like this happens a few times throughout the file > + * 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 functions 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 functions 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; > + > + 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; > + > + DRM_DEBUG_KMS("Link connector state %p to [CRTC:%d]\n", > + conn_state, crtc->base.id); > + > + return 0; > +} > +EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); In drm_atomic_helper.c, you're assigning directly to conn_state->crtc. I think you should probably be using this helper (as it doesn't seem to be used anywhere). I realize this happens in a different patch, but I want to make sure I don't miss it later :-) If that is the case, I think this could also benefit from the !crtc checks the plane equivalent has. > + > +/** > + * drm_atomic_add_affected_connectors - add connectors for crtc > + * @state: atomic state > + * @crtc: DRM crtc > + * > + * This functions 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 etuccess, negative error code on failure. s/et/s/ > + */ > +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 onfiguration commit s/onfiguration/configuration/ > + * @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..753812034e71 > --- /dev/null > +++ b/include/drm/drm_atomic.h > @@ -0,0 +1,63 @@ > +/* > + * 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 at gmail.com> > + * Daniel Vetter <daniel.vetter at ffwll.ch> > + */ > + > +#ifndef DRM_ATOMIC_H_ > +#define DRM_ATOMIC_H_ > + > +struct drm_atomic_state * > +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 * > +drm_atomic_get_crtc_state(struct drm_atomic_state *state, > + struct drm_crtc *crtc); > +struct drm_plane_state * > +drm_atomic_get_plane_state(struct drm_atomic_state *state, > + struct drm_plane *plane); > +struct drm_connector_state * > +drm_atomic_get_connector_state(struct drm_atomic_state *state, > + struct drm_connector *connector); > + > +int drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, > + struct drm_crtc *crtc); > +int drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, > + struct drm_crtc *crtc); > +int > +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 drm_atomic_check_only(struct drm_atomic_state *state); > +int drm_atomic_commit(struct drm_atomic_state *state); > +int 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 9847009ad451..c3ce5b36da5f 100644 > --- a/include/drm/drm_crtc.h > +++ b/include/drm/drm_crtc.h > @@ -799,6 +799,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 > @@ -829,6 +855,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. > @@ -838,6 +867,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 > > _______________________________________________ > dri-devel mailing list > dri-devel at lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel