/*
* 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>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_atomic_helper.h>
static void
drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
struct drm_plane_state *plane_state,
struct drm_plane *plane)
{
struct drm_crtc_state *crtc_state;
if (plane->state->crtc) {
crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)];
if (WARN_ON(!crtc_state))
return;
crtc_state->planes_changed = true;
}
if (plane_state->crtc) {
crtc_state =
state->crtc_states[drm_crtc_index(plane_state->crtc)];
if (WARN_ON(!crtc_state))
return;
crtc_state->planes_changed = true;
}
}
static struct drm_crtc *
get_current_crtc_for_encoder(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_connector *connector;
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
list_for_each_entry(connector, &config->connector_list, head) {
if (connector->state->best_encoder != encoder)
continue;
return connector->state->crtc;
}
return NULL;
}
static int
steal_encoder(struct drm_atomic_state *state,
struct drm_encoder *encoder,
struct drm_crtc *encoder_crtc)
{
struct drm_mode_config *config = &state->dev->mode_config;
struct drm_crtc_state *crtc_state;
struct drm_connector *connector;
struct drm_connector_state *connector_state;
/*
* We can only steal an encoder coming from a connector, which means we
* must already hold the connection_mutex.
*/
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
DRM_DEBUG_KMS("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n",
encoder->base.id, encoder->name,
encoder_crtc->base.id);
crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
crtc_state->mode_changed = true;
list_for_each_entry(connector, &config->connector_list, head) {
if (connector->state->best_encoder != encoder)
continue;
DRM_DEBUG_KMS("Stealing encoder from [CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
connector_state = drm_atomic_get_connector_state(state,
connector);
if (IS_ERR(connector_state))
return PTR_ERR(connector_state);
connector_state->crtc = NULL;
connector_state->best_encoder = NULL;
}
return 0;
}
static int
update_connector_routing(struct drm_atomic_state *state, int conn_idx)
{
struct drm_connector_helper_funcs *funcs;
struct drm_encoder *new_encoder;
struct drm_crtc *encoder_crtc;
struct drm_connector *connector;
struct drm_connector_state *connector_state;
struct drm_crtc_state *crtc_state;
int idx, ret;
connector = state->connectors[conn_idx];
connector_state = state->connector_states[conn_idx];
if (!connector)
return 0;
DRM_DEBUG_KMS("Updating routing for [CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
if (connector->state->crtc != connector_state->crtc) {
if (connector->state->crtc) {
idx = drm_crtc_index(connector->state->crtc);
crtc_state = state->crtc_states[idx];
crtc_state->mode_changed = true;
}
if (connector_state->crtc) {
idx = drm_crtc_index(connector_state->crtc);
crtc_state = state->crtc_states[idx];
crtc_state->mode_changed = true;
}
}
if (!connector_state->crtc) {
DRM_DEBUG_KMS("Disabling [CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
connector_state->best_encoder = NULL;
return 0;
}
funcs = connector->helper_private;
new_encoder = funcs->best_encoder(connector);
if (!new_encoder) {
DRM_DEBUG_KMS("No suitable encoder found for [CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
return -EINVAL;
}
if (new_encoder == connector_state->best_encoder) {
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n",
connector->base.id,
connector->name,
new_encoder->base.id,
new_encoder->name,
connector_state->crtc->base.id);
return 0;
}
encoder_crtc = get_current_crtc_for_encoder(state->dev,
new_encoder);
if (encoder_crtc) {
ret = steal_encoder(state, new_encoder, encoder_crtc);
if (ret) {
DRM_DEBUG_KMS("Encoder stealing failed for [CONNECTOR:%d:%s]\n",
connector->base.id,
connector->name);
return ret;
}
}
connector_state->best_encoder = new_encoder;
idx = drm_crtc_index(connector_state->crtc);
crtc_state = state->crtc_states[idx];
crtc_state->mode_changed = true;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n",
connector->base.id,
connector->name,
new_encoder->base.id,
new_encoder->name,
connector_state->crtc->base.id);
return 0;
}
static int
mode_fixup(struct drm_atomic_state *state)
{
int ncrtcs = state->dev->mode_config.num_crtc;
int nconnectors = state->dev->mode_config.num_connector;
struct drm_crtc_state *crtc_state;
struct drm_connector_state *conn_state;
int i;
bool ret;
for (i = 0; i < ncrtcs; i++) {
crtc_state = state->crtc_states[i];
if (!crtc_state || !crtc_state->mode_changed)
continue;
drm_mode_copy(&crtc_state->adjusted_mode, &crtc_state->mode);
}
for (i = 0; i < nconnectors; i++) {
struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
conn_state = state->connector_states[i];
if (!conn_state)
continue;
WARN_ON(!!conn_state->best_encoder != !!conn_state->crtc);
if (!conn_state->crtc || !conn_state->best_encoder)
continue;
crtc_state =
state->crtc_states[drm_crtc_index(conn_state->crtc)];
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call ->mode_fixup twice.
*/
encoder = conn_state->best_encoder;
funcs = encoder->helper_private;
if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
ret = encoder->bridge->funcs->mode_fixup(
encoder->bridge, &crtc_state->mode,
&crtc_state->adjusted_mode);
if (!ret) {
DRM_DEBUG_KMS("Bridge fixup failed\n");
return -EINVAL;
}
}
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;
}
}
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc;
crtc_state = state->crtc_states[i];
crtc = state->crtcs[i];
if (!crtc_state || !crtc_state->mode_changed)
continue;
funcs = crtc->helper_private;
ret = funcs->mode_fixup(crtc, &crtc_state->mode,
&crtc_state->adjusted_mode);
if (!ret) {
DRM_DEBUG_KMS("[CRTC:%d] fixup failed\n",
crtc->base.id);
return -EINVAL;
}
}
return 0;
}
static int
drm_atomic_helper_check_prepare(struct drm_device *dev,
struct drm_atomic_state *state)
{
int ncrtcs = dev->mode_config.num_crtc;
int nconnectors = dev->mode_config.num_connector;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
int i, ret;
for (i = 0; i < ncrtcs; i++) {
crtc = state->crtcs[i];
crtc_state = state->crtc_states[i];
if (!crtc)
continue;
if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) {
DRM_DEBUG_KMS("[CRTC:%d] mode changed\n",
crtc->base.id);
crtc_state->mode_changed = true;
}
if (crtc->state->enable != crtc_state->enable) {
DRM_DEBUG_KMS("[CRTC:%d] enable changed\n",
crtc->base.id);
crtc_state->mode_changed = true;
}
}
for (i = 0; i < nconnectors; i++) {
/*
* This only sets crtc->mode_changed for routing changes,
* drivers must set crtc->mode_changed themselves when connector
* properties need to be updated.
*/
ret = update_connector_routing(state, i);
if (ret)
return ret;
}
/*
* After all the routing has been prepared we need to add in any
* connector which is itself unchanged, but who's crtc changes it's
* configuration. This must be done before calling mode_fixup in case a
* crtc only changed its mode but has the same set of connectors.
*/
for (i = 0; i < ncrtcs; i++) {
int num_connectors;
crtc = state->crtcs[i];
crtc_state = state->crtc_states[i];
if (!crtc || !crtc_state->mode_changed)
continue;
DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n",
crtc->base.id,
crtc_state->enable ? 'y' : 'n');
ret = drm_atomic_add_affected_connectors(state, crtc);
if (ret != 0)
return ret;
num_connectors = drm_atomic_connectors_for_crtc(state,
crtc);
if (crtc_state->enable != !!num_connectors) {
DRM_DEBUG_KMS("[CRTC:%d] enabled/connectors mismatch\n",
crtc->base.id);
return -EINVAL;
}
}
return mode_fixup(state);
}
/**
* 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.
*
* RETURNS
* Zero for success or -errno
*/
int drm_atomic_helper_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
int nplanes = dev->mode_config.num_total_plane;
int ncrtcs = dev->mode_config.num_crtc;
int i, ret = 0;
ret = drm_atomic_helper_check_prepare(dev, state);
if (ret)
return ret;
for (i = 0; i < nplanes; i++) {
struct drm_plane_helper_funcs *funcs;
struct drm_plane *plane = state->planes[i];
struct drm_plane_state *plane_state = state->plane_states[i];
if (!plane)
continue;
funcs = plane->helper_private;
drm_atomic_helper_plane_changed(state, plane_state, plane);
if (!funcs || !funcs->atomic_check)
continue;
ret = funcs->atomic_check(plane, plane_state);
if (ret) {
DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
plane->base.id);
return ret;
}
}
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc = state->crtcs[i];
if (!crtc)
continue;
funcs = crtc->helper_private;
if (!funcs || !funcs->atomic_check)
continue;
ret = funcs->atomic_check(crtc, state->crtc_states[i]);
if (ret) {
DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
crtc->base.id);
return ret;
}
}
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_check);
static void
disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
{
int ncrtcs = old_state->dev->mode_config.num_crtc;
int nconnectors = old_state->dev->mode_config.num_connector;
int i;
for (i = 0; i < nconnectors; i++) {
struct drm_connector_state *old_conn_state;
struct drm_connector *connector;
struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
old_conn_state = old_state->connector_states[i];
connector = old_state->connectors[i];
/* Shut down everything that's in the changeset and currently
* still on. So need to check the old, saved state. */
if (!old_conn_state || !old_conn_state->crtc)
continue;
encoder = connector->state->best_encoder;
if (!encoder)
continue;
funcs = encoder->helper_private;
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call call disable hooks twice.
*/
if (encoder->bridge)
encoder->bridge->funcs->disable(encoder->bridge);
/* Right function depends upon target state. */
if (connector->state->crtc)
funcs->prepare(encoder);
else if (funcs->disable)
funcs->disable(encoder);
else
funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
if (encoder->bridge)
encoder->bridge->funcs->post_disable(encoder->bridge);
}
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc;
crtc = old_state->crtcs[i];
/* Shut down everything that needs a full modeset. */
if (!crtc || !crtc->state->mode_changed)
continue;
funcs = crtc->helper_private;
/* Right function depends upon target state. */
if (crtc->state->enable)
funcs->prepare(crtc);
else if (funcs->disable)
funcs->disable(crtc);
else
funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
}
static void
set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
{
int nconnectors = dev->mode_config.num_connector;
int ncrtcs = old_state->dev->mode_config.num_crtc;
int i;
/* clear out existing links */
for (i = 0; i < nconnectors; i++) {
struct drm_connector *connector;
connector = old_state->connectors[i];
if (!connector || !connector->encoder)
continue;
WARN_ON(!connector->encoder->crtc);
connector->encoder->crtc = NULL;
connector->encoder = NULL;
}
/* set new links */
for (i = 0; i < nconnectors; i++) {
struct drm_connector *connector;
connector = old_state->connectors[i];
if (!connector || !connector->state->crtc)
continue;
if (WARN_ON(!connector->state->best_encoder))
continue;
connector->encoder = connector->state->best_encoder;
connector->encoder->crtc = connector->state->crtc;
}
/* set legacy state in the crtc structure */
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc *crtc;
crtc = old_state->crtcs[i];
if (!crtc)
continue;
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;
}
}
static void
crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
{
int ncrtcs = old_state->dev->mode_config.num_crtc;
int nconnectors = old_state->dev->mode_config.num_connector;
int i;
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc;
crtc = old_state->crtcs[i];
if (!crtc || !crtc->state->mode_changed)
continue;
funcs = crtc->helper_private;
if (crtc->state->enable)
funcs->mode_set_nofb(crtc);
}
for (i = 0; i < nconnectors; i++) {
struct drm_connector *connector;
struct drm_crtc_state *new_crtc_state;
struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
struct drm_display_mode *mode, *adjusted_mode;
connector = old_state->connectors[i];
if (!connector || !connector->state->best_encoder)
continue;
encoder = connector->state->best_encoder;
funcs = encoder->helper_private;
new_crtc_state = connector->state->crtc->state;
mode = &new_crtc_state->mode;
adjusted_mode = &new_crtc_state->adjusted_mode;
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call call mode_set hooks twice.
*/
funcs->mode_set(encoder, mode, adjusted_mode);
if (encoder->bridge && encoder->bridge->funcs->mode_set)
encoder->bridge->funcs->mode_set(encoder->bridge,
mode, adjusted_mode);
}
}
/**
* drm_atomic_helper_commit_pre_planes - modeset commit before plane updates
* @dev: DRM device
* @state: atomic state
*
* This function commits the modeset changes that need to be committed before
* updating planes. It shuts down all the outputs that need to be shut down and
* prepares them (if required) with the new mode.
*/
void drm_atomic_helper_commit_pre_planes(struct drm_device *dev,
struct drm_atomic_state *state)
{
disable_outputs(dev, state);
set_routing_links(dev, state);
crtc_set_mode(dev, state);
}
EXPORT_SYMBOL(drm_atomic_helper_commit_pre_planes);
/**
* drm_atomic_helper_commit_post_planes - modeset commit after plane updates
* @dev: DRM device
* @old_state: atomic state object with old state structures
*
* This function commits the modeset changes that need to be committed after
* updating planes: It enables all the outputs with the new configuration which
* had to be turned off for the update.
*/
void drm_atomic_helper_commit_post_planes(struct drm_device *dev,
struct drm_atomic_state *old_state)
{
int ncrtcs = old_state->dev->mode_config.num_crtc;
int nconnectors = old_state->dev->mode_config.num_connector;
int i;
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc;
crtc = old_state->crtcs[i];
/* Need to filter out CRTCs where only planes change. */
if (!crtc || !crtc->state->mode_changed)
continue;
funcs = crtc->helper_private;
if (crtc->state->enable)
funcs->commit(crtc);
}
for (i = 0; i < nconnectors; i++) {
struct drm_connector *connector;
struct drm_encoder_helper_funcs *funcs;
struct drm_encoder *encoder;
connector = old_state->connectors[i];
if (!connector || !connector->state->best_encoder)
continue;
encoder = connector->state->best_encoder;
funcs = encoder->helper_private;
/*
* Each encoder has at most one connector (since we always steal
* it away), so we won't call call enable hooks twice.
*/
if (encoder->bridge)
encoder->bridge->funcs->pre_enable(encoder->bridge);
funcs->commit(encoder);
if (encoder->bridge)
encoder->bridge->funcs->enable(encoder->bridge);
}
}
EXPORT_SYMBOL(drm_atomic_helper_commit_post_planes);
static void
wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *old_state)
{
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
int ncrtcs = old_state->dev->mode_config.num_crtc;
int i, ret;
for (i = 0; i < ncrtcs; i++) {
crtc = old_state->crtcs[i];
old_crtc_state = old_state->crtc_states[i];
if (!crtc)
continue;
/* No one cares about the old state, so abuse it for tracking
* and store whether we hold a vblank reference (and should do a
* vblank wait) in the ->enable boolean. */
old_crtc_state->enable = false;
if (!crtc->state->enable)
continue;
ret = drm_crtc_vblank_get(crtc);
if (ret != 0)
continue;
old_crtc_state->enable = true;
old_crtc_state->last_vblank_count = drm_vblank_count(dev, i);
}
for (i = 0; i < ncrtcs; i++) {
crtc = old_state->crtcs[i];
old_crtc_state = old_state->crtc_states[i];
if (!crtc || !old_crtc_state->enable)
continue;
ret = wait_event_timeout(dev->vblank[i].queue,
old_crtc_state->last_vblank_count !=
drm_vblank_count(dev, i),
msecs_to_jiffies(50));
drm_crtc_vblank_put(crtc);
}
}
/**
* drm_atomic_helper_commit - commit validated state object
* @dev: DRM device
* @state: the driver state object
* @async: asynchronous commit
*
* This function commits a with drm_atomic_helper_check() pre-validated state
* object. This can still fail when e.g. the framebuffer reservation fails. For
* now this doesn't implement asynchronous commits.
*
* RETURNS
* Zero for success or -errno.
*/
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool async)
{
int ret;
if (async)
return -EBUSY;
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret)
return ret;
/*
* This is the point of no return - everything below never fails except
* when the hw goes bonghits. Which means we can commit the new state on
* the software side now.
*/
drm_atomic_helper_swap_state(dev, state);
/*
* Everything below can be run asynchronously without the need to grab
* any modeset locks at all under one conditions: It must be guaranteed
* that the asynchronous work has either been cancelled (if the driver
* supports it, which at least requires that the framebuffers get
* cleaned up with drm_atomic_helper_cleanup_planes()) or completed
* before the new state gets committed on the software side with
* drm_atomic_helper_swap_state().
*
* This scheme allows new atomic state updates to be prepared and
* checked in parallel to the asynchronous completion of the previous
* update. Which is important since compositors need to figure out the
* composition of the next frame right after having submitted the
* current layout.
*/
drm_atomic_helper_commit_pre_planes(dev, state);
drm_atomic_helper_commit_planes(dev, state);
drm_atomic_helper_commit_post_planes(dev, state);
wait_for_vblanks(dev, state);
drm_atomic_helper_cleanup_planes(dev, state);
drm_atomic_state_free(state);
return 0;
}
EXPORT_SYMBOL(drm_atomic_helper_commit);
/**
* drm_atomic_helper_prepare_planes - prepare plane resources after commit
* @dev: DRM device
* @state: atomic state object with old state structures
*
* This function prepares plane state, specifically framebuffers, for the new
* configuration. If any failure is encountered this function will call
* ->cleanup_fb on any already successfully prepared framebuffer.
*
* Returns:
* 0 on success, negative error code on failure.
*/
int drm_atomic_helper_prepare_planes(struct drm_device *dev,
struct drm_atomic_state *state)
{
int nplanes = dev->mode_config.num_total_plane;
int ret, i;
for (i = 0; i < nplanes; i++) {
struct drm_plane_helper_funcs *funcs;
struct drm_plane *plane = state->planes[i];
struct drm_framebuffer *fb;
if (!plane)
continue;
funcs = plane->helper_private;
fb = state->plane_states[i]->fb;
if (fb && funcs->prepare_fb) {
ret = funcs->prepare_fb(plane, fb);
if (ret)
goto fail;
}
}
return 0;
fail:
for (i--; i >= 0; i--) {
struct drm_plane_helper_funcs *funcs;
struct drm_plane *plane = state->planes[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);
}
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
/**
* drm_atomic_helper_commit_planes - commit plane state
* @dev: DRM device
* @state: atomic state
*
* This function commits the new plane state using the plane and atomic helper
* functions for planes and crtcs. It assumes that the atomic state has already
* been pushed into the relevant object state pointers, since this step can no
* longer fail.
*
* It still requires the global state object @state to know which planes and
* crtcs need to be updated though.
*/
void drm_atomic_helper_commit_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;
int i;
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc = state->crtcs[i];
if (!crtc)
continue;
funcs = crtc->helper_private;
if (!funcs || !funcs->atomic_begin)
continue;
funcs->atomic_begin(crtc);
}
for (i = 0; i < nplanes; i++) {
struct drm_plane_helper_funcs *funcs;
struct drm_plane *plane = state->planes[i];
if (!plane)
continue;
funcs = plane->helper_private;
if (!funcs || !funcs->atomic_update)
continue;
funcs->atomic_update(plane);
}
for (i = 0; i < ncrtcs; i++) {
struct drm_crtc_helper_funcs *funcs;
struct drm_crtc *crtc = state->crtcs[i];
if (!crtc)
continue;
funcs = crtc->helper_private;
if (!funcs || !funcs->atomic_flush)
continue;
funcs->atomic_flush(crtc);
}
}
EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
/**
* drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
* @dev: DRM device
* @old_state: atomic state object with old state structures
*
* This function cleans up plane state, specifically framebuffers, from the old
* configuration. Hence the old configuration must be perserved in @old_state to
* be able to call this function.
*
* This function must also be called on the new state when the atomic update
* fails at any point after calling drm_atomic_helper_prepare_planes().
*/
void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
struct drm_atomic_state *old_state)
{
int nplanes = dev->mode_config.num_total_plane;
int i;
for (i = 0; i < nplanes; i++) {
struct drm_plane_helper_funcs *funcs;
struct drm_plane *plane = old_state->planes[i];
struct drm_framebuffer *old_fb;
if (!plane)
continue;
funcs = plane->helper_private;
old_fb = old_state->plane_states[i]->fb;
if (old_fb && funcs->cleanup_fb)
funcs->cleanup_fb(plane, old_fb);
}
}
EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
/**
* drm_atomic_helper_swap_state - store atomic state into current sw state
* @dev: DRM device
* @state: atomic state
*
* This function stores the atomic state into the current state pointers in all
* driver objects. It should be called after all failing steps have been done
* and succeeded, but before the actual hardware state is committed.
*
* For cleanup and error recovery the current state for all changed objects will
* be swaped into @state.
*
* With that sequence it fits perfectly into the plane prepare/cleanup sequence:
*
* 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state.
*
* 2. Do any other steps that might fail.
*
* 3. Put the staged state into the current state pointers with this function.
*
* 4. Actually commit the hardware state.
*
* 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,
struct drm_atomic_state *state)
{
int i;
for (i = 0; i < dev->mode_config.num_connector; i++) {
struct drm_connector *connector = state->connectors[i];
if (!connector)
continue;
connector->state->state = state;
swap(state->connector_states[i], connector->state);
connector->state->state = NULL;
}
for (i = 0; i < dev->mode_config.num_crtc; i++) {
struct drm_crtc *crtc = state->crtcs[i];
if (!crtc)
continue;
crtc->state->state = state;
swap(state->crtc_states[i], crtc->state);
crtc->state->state = NULL;
}
for (i = 0; i < dev->mode_config.num_total_plane; i++) {
struct drm_plane *plane = state->planes[i];
if (!plane)
continue;
plane->state->state = state;
swap(state->plane_states[i], plane->state);
plane->state->state = NULL;
}
}
EXPORT_SYMBOL(drm_atomic_helper_swap_state);