/* * 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>
*/
/** * DOC: overview * * This helper library provides implementations of check and commit functions on * top of the CRTC modeset helper callbacks and the plane helper callbacks. It * also provides convenience implementations for the atomic state handling * callbacks for drivers which don't need to subclass the drm core structures to * add their own additional internal state. * * This library also provides default implementations for the check callback in * drm_atomic_helper_check() and for the commit callback with * drm_atomic_helper_commit(). But the individual stages and callbacks are * exposed to allow drivers to mix and match and e.g. use the plane helpers only * together with a driver private modeset implementation. * * This library also provides implementations for all the legacy driver * interfaces on top of the atomic interface. See drm_atomic_helper_set_config(), * drm_atomic_helper_disable_plane(), and the various functions to implement * set_property callbacks. New drivers must not implement these functions * themselves but must use the provided helpers. * * The atomic helper uses the same function table structures as all other * modesetting helpers. See the documentation for &struct drm_crtc_helper_funcs, * struct &drm_encoder_helper_funcs and &struct drm_connector_helper_funcs. It * also shares the &struct drm_plane_helper_funcs function table with the plane * helpers.
*/ staticvoid
drm_atomic_helper_plane_changed(struct drm_atomic_state *state, struct drm_plane_state *old_plane_state, struct drm_plane_state *plane_state, struct drm_plane *plane)
{ struct drm_crtc_state *crtc_state;
if (old_plane_state->crtc) {
crtc_state = drm_atomic_get_new_crtc_state(state,
old_plane_state->crtc);
if (WARN_ON(!crtc_state)) return;
crtc_state->planes_changed = true;
}
if (plane_state->crtc) {
crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
if (WARN_ON(!crtc_state)) return;
crtc_state->planes_changed = true;
}
}
staticint handle_conflicting_encoders(struct drm_atomic_state *state, bool disable_conflicting_encoders)
{ struct drm_connector_state *new_conn_state; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; struct drm_encoder *encoder; unsignedint encoder_mask = 0; int i, ret = 0;
/* * First loop, find all newly assigned encoders from the connectors * part of the state. If the same encoder is assigned to multiple * connectors bail out.
*/
for_each_new_connector_in_state(state, connector, new_conn_state, i) { conststruct drm_connector_helper_funcs *funcs = connector->helper_private; struct drm_encoder *new_encoder;
/* * Second loop, iterate over all connectors not part of the state. * * If a conflicting encoder is found and disable_conflicting_encoders * is not set, an error is returned. Userspace can provide a solution * through the atomic ioctl. * * If the flag is set conflicting connectors are removed from the CRTC * and the CRTC is disabled if no encoder is left. This preserves * compatibility with the legacy set_config behavior.
*/
drm_connector_list_iter_begin(state->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) { struct drm_crtc_state *crtc_state;
if (drm_atomic_get_new_connector_state(state, connector)) continue;
encoder = connector->state->best_encoder; if (!encoder || !(encoder_mask & drm_encoder_mask(encoder))) continue;
if (!disable_conflicting_encoders) {
drm_dbg_atomic(connector->dev, "[ENCODER:%d:%s] in use on [CRTC:%d:%s] by [CONNECTOR:%d:%s]\n",
encoder->base.id, encoder->name,
connector->state->crtc->base.id,
connector->state->crtc->name,
connector->base.id, connector->name);
ret = -EINVAL; goto out;
}
new_conn_state = drm_atomic_get_connector_state(state, connector); if (IS_ERR(new_conn_state)) {
ret = PTR_ERR(new_conn_state); goto out;
}
drm_dbg_atomic(connector->dev, "[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n",
encoder->base.id, encoder->name,
new_conn_state->crtc->base.id, new_conn_state->crtc->name,
connector->base.id, connector->name);
if (conn_state->best_encoder) { /* Unset the encoder_mask in the old crtc state. */
crtc = conn_state->connector->state->crtc;
/* A NULL crtc is an error here because we should have * duplicated a NULL best_encoder when crtc was NULL. * As an exception restoring duplicated atomic state * during resume is allowed, so don't warn when * best_encoder is equal to encoder we intend to set.
*/
WARN_ON(!crtc && encoder != conn_state->best_encoder); if (crtc) {
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (new_connector_state->best_encoder != encoder) continue;
encoder_crtc = old_connector_state->crtc;
drm_dbg_atomic(encoder->dev, "[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n",
encoder->base.id, encoder->name,
encoder_crtc->base.id, encoder_crtc->name);
crtc_state = drm_atomic_get_new_crtc_state(state,
new_connector_state->crtc); /* * For compatibility with legacy users, we want to make sure that * we allow DPMS On->Off modesets on unregistered connectors. Modesets * which would result in anything else must be considered invalid, to * avoid turning on new displays on dead connectors. * * Since the connector can be unregistered at any point during an * atomic check or commit, this is racy. But that's OK: all we care * about is ensuring that userspace can't do anything but shut off the * display on a connector that was destroyed after it's been notified, * not before. * * Additionally, we also want to ignore connector registration when * we're trying to restore an atomic state during system resume since * there's a chance the connector may have been destroyed during the * process, but it's better to ignore that then cause * drm_atomic_helper_resume() to fail. * * Last, we want to ignore connector registration when the connector * was not pulled in the atomic state by user-space (ie, was pulled * in by the driver, e.g. when updating a DP-MST stream).
*/ if (!state->duplicated && drm_connector_is_unregistered(connector) &&
added_by_user && crtc_state->active) {
drm_dbg_atomic(connector->dev, "[CONNECTOR:%d:%s] is not registered\n",
connector->base.id, connector->name); return -EINVAL;
}
if (!new_encoder) {
drm_dbg_atomic(connector->dev, "No suitable encoder found for [CONNECTOR:%d:%s]\n",
connector->base.id, connector->name); return -EINVAL;
}
if (!drm_encoder_crtc_ok(new_encoder, new_connector_state->crtc)) {
drm_dbg_atomic(connector->dev, "[ENCODER:%d:%s] incompatible with [CRTC:%d:%s]\n",
new_encoder->base.id,
new_encoder->name,
new_connector_state->crtc->base.id,
new_connector_state->crtc->name); return -EINVAL;
}
if (new_encoder == new_connector_state->best_encoder) {
set_best_encoder(state, new_connector_state, new_encoder);
drm_dbg_atomic(connector->dev, "[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n",
connector->base.id,
connector->name,
new_encoder->base.id,
new_encoder->name,
new_connector_state->crtc->base.id,
new_connector_state->crtc->name);
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call ->mode_fixup twice.
*/
encoder = new_conn_state->best_encoder;
funcs = encoder->helper_private;
bridge = drm_bridge_chain_get_first_bridge(encoder);
ret = drm_atomic_bridge_chain_check(bridge,
new_crtc_state,
new_conn_state); if (ret) {
drm_dbg_atomic(encoder->dev, "Bridge atomic check failed\n"); return ret;
}
if (funcs && funcs->atomic_check) {
ret = funcs->atomic_check(encoder, new_crtc_state,
new_conn_state); if (ret) {
drm_dbg_atomic(encoder->dev, "[ENCODER:%d:%s] check failed\n",
encoder->base.id, encoder->name); return ret;
}
} elseif (funcs && funcs->mode_fixup) {
ret = funcs->mode_fixup(encoder, &new_crtc_state->mode,
&new_crtc_state->adjusted_mode); if (!ret) {
drm_dbg_atomic(encoder->dev, "[ENCODER:%d:%s] fixup failed\n",
encoder->base.id, encoder->name); return -EINVAL;
}
}
}
/** * drm_atomic_helper_check_modeset - validate state object for modeset changes * @dev: DRM device * @state: the driver state object * * Check the state object to see if the requested state is physically possible. * This does all the CRTC and connector related computations for an atomic * update and adds any additional connectors needed for full modesets. It calls * the various per-object callbacks in the follow order: * * 1. &drm_connector_helper_funcs.atomic_best_encoder for determining the new encoder. * 2. &drm_connector_helper_funcs.atomic_check to validate the connector state. * 3. If it's determined a modeset is needed then all connectors on the affected * CRTC are added and &drm_connector_helper_funcs.atomic_check is run on them. * 4. &drm_encoder_helper_funcs.mode_valid, &drm_bridge_funcs.mode_valid and * &drm_crtc_helper_funcs.mode_valid are called on the affected components. * 5. &drm_bridge_funcs.mode_fixup is called on all encoder bridges. * 6. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state. * This function is only called when the encoder will be part of a configured CRTC, * it must not be used for implementing connector property validation. * If this function is NULL, &drm_atomic_encoder_helper_funcs.mode_fixup is called * instead. * 7. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with CRTC constraints. * * &drm_crtc_state.mode_changed is set when the input mode is changed. * &drm_crtc_state.connectors_changed is set when a connector is added or * removed from the CRTC. &drm_crtc_state.active_changed is set when * &drm_crtc_state.active changes, which is used for DPMS. * &drm_crtc_state.no_vblank is set from the result of drm_dev_has_vblank(). * See also: drm_atomic_crtc_needs_modeset() * * IMPORTANT: * * Drivers which set &drm_crtc_state.mode_changed (e.g. in their * &drm_plane_helper_funcs.atomic_check hooks if a plane update can't be done * without a full modeset) _must_ call this function after that change. It is * permitted to call this function multiple times for the same update, e.g. * when the &drm_crtc_helper_funcs.atomic_check functions depend upon the * adjusted dotclock for fifo space allocation and watermark computation. * * RETURNS: * Zero for success or -errno
*/ int
drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_connector *connector; struct drm_connector_state *old_connector_state, *new_connector_state; int i, ret; unsignedint connectors_mask = 0, user_connectors_mask = 0;
/* * For clarity this assignment is done here, but * enable == 0 is only true when there are no * connectors and a NULL mode. * * The other way around is true as well. enable != 0 * implies that connectors are attached and a mode is set.
*/
new_crtc_state->mode_changed = true;
new_crtc_state->connectors_changed = true;
}
if (old_crtc_state->active != new_crtc_state->active) {
drm_dbg_atomic(dev, "[CRTC:%d:%s] active changed\n",
crtc->base.id, crtc->name);
new_crtc_state->active_changed = true;
}
/* * This only sets crtc->connectors_changed for routing changes, * drivers must set crtc->connectors_changed themselves when * connector properties need to be updated.
*/
ret = update_connector_routing(state, connector,
old_connector_state,
new_connector_state,
BIT(i) & user_connectors_mask); if (ret) return ret; if (old_connector_state->crtc) {
new_crtc_state = drm_atomic_get_new_crtc_state(state,
old_connector_state->crtc); if (old_connector_state->link_status !=
new_connector_state->link_status)
new_crtc_state->connectors_changed = true;
if (old_connector_state->max_requested_bpc !=
new_connector_state->max_requested_bpc)
new_crtc_state->connectors_changed = true;
}
if (funcs->atomic_check)
ret = funcs->atomic_check(connector, state); if (ret) {
drm_dbg_atomic(dev, "[CONNECTOR:%d:%s] driver check failed\n",
connector->base.id, connector->name); return ret;
}
connectors_mask |= BIT(i);
}
/* * After all the routing has been prepared we need to add in any * connector which is itself unchanged, but whose CRTC changes its * 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_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue;
ret = drm_atomic_add_affected_connectors(state, crtc); if (ret != 0) return ret;
ret = drm_atomic_add_affected_planes(state, crtc); if (ret != 0) return ret;
ret = drm_atomic_check_valid_clones(state, crtc); if (ret != 0) return ret;
}
/* * Iterate over all connectors again, to make sure atomic_check() * has been called on them when a modeset is forced.
*/
for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { conststruct drm_connector_helper_funcs *funcs = connector->helper_private;
if (connectors_mask & BIT(i)) continue;
if (funcs->atomic_check)
ret = funcs->atomic_check(connector, state); if (ret) {
drm_dbg_atomic(dev, "[CONNECTOR:%d:%s] driver check failed\n",
connector->base.id, connector->name); return ret;
}
}
/* * Iterate over all connectors again, and add all affected bridges to * the state.
*/
for_each_oldnew_connector_in_state(state, connector,
old_connector_state,
new_connector_state, i) { struct drm_encoder *encoder;
encoder = old_connector_state->best_encoder;
ret = drm_atomic_add_encoder_bridges(state, encoder); if (ret) return ret;
encoder = new_connector_state->best_encoder;
ret = drm_atomic_add_encoder_bridges(state, encoder); if (ret) return ret;
}
/** * drm_atomic_helper_check_wb_connector_state() - Check writeback connector state * @connector: corresponding connector * @state: the driver state object * * Checks if the writeback connector state is valid, and returns an error if it * isn't. * * RETURNS: * Zero for success or -errno
*/ int
drm_atomic_helper_check_wb_connector_state(struct drm_connector *connector, struct drm_atomic_state *state)
{ struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector); struct drm_writeback_job *wb_job = conn_state->writeback_job; struct drm_property_blob *pixel_format_blob; struct drm_framebuffer *fb;
size_t i, nformats;
u32 *formats;
/** * drm_atomic_helper_check_plane_state() - Check plane state for validity * @plane_state: plane state to check * @crtc_state: CRTC state to check * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point * @can_position: is it legal to position the plane such that it * doesn't cover the entire CRTC? This will generally * only be false for primary planes. * @can_update_disabled: can the plane be updated while the CRTC * is disabled? * * Checks that a desired plane update is valid, and updates various * bits of derived state (clipped coordinates etc.). Drivers that provide * their own plane handling rather than helper-provided implementations may * still wish to call this function to avoid duplication of error checking * code. * * RETURNS: * Zero if update appears valid, error code on failure
*/ int drm_atomic_helper_check_plane_state(struct drm_plane_state *plane_state, conststruct drm_crtc_state *crtc_state, int min_scale, int max_scale, bool can_position, bool can_update_disabled)
{ struct drm_framebuffer *fb = plane_state->fb; struct drm_rect *src = &plane_state->src; struct drm_rect *dst = &plane_state->dst; unsignedint rotation = plane_state->rotation; struct drm_rect clip = {}; int hscale, vscale;
if (!plane_state->visible) /* * Plane isn't visible; some drivers can handle this * so we just return success here. Drivers that can't * (including those that use the primary plane helper's * update function) will return an error from their * update_plane handler.
*/ return 0;
/** * drm_atomic_helper_check_crtc_primary_plane() - Check CRTC state for primary plane * @crtc_state: CRTC state to check * * Checks that a CRTC has at least one primary plane attached to it, which is * a requirement on some hardware. Note that this only involves the CRTC side * of the test. To test if the primary plane is visible or if it can be updated * without the CRTC being enabled, use drm_atomic_helper_check_plane_state() in * the plane's atomic check. * * RETURNS: * 0 if a primary plane is attached to the CRTC, or an error code otherwise
*/ int drm_atomic_helper_check_crtc_primary_plane(struct drm_crtc_state *crtc_state)
{ struct drm_crtc *crtc = crtc_state->crtc; struct drm_device *dev = crtc->dev; struct drm_plane *plane;
/* needs at least one primary plane to be enabled */
drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) { if (plane->type == DRM_PLANE_TYPE_PRIMARY) return 0;
}
/** * drm_atomic_helper_check_planes - validate state object for planes changes * @dev: DRM device * @state: the driver state object * * Check the state object to see if the requested state is physically possible. * This does all the plane update related checks using by calling into the * &drm_crtc_helper_funcs.atomic_check and &drm_plane_helper_funcs.atomic_check * hooks provided by the driver. * * It also sets &drm_crtc_state.planes_changed to indicate that a CRTC has * updated planes. * * RETURNS: * Zero for success or -errno
*/ int
drm_atomic_helper_check_planes(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; struct drm_plane *plane; struct drm_plane_state *new_plane_state, *old_plane_state; int i, ret = 0;
/** * 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 * &drm_mode_config_funcs.atomic_check callback. * * This just wraps the two parts of the state checking for planes and modeset * state in the default order: First it calls drm_atomic_helper_check_modeset() * and then drm_atomic_helper_check_planes(). The assumption is that the * @drm_plane_helper_funcs.atomic_check and @drm_crtc_helper_funcs.atomic_check * functions depend upon an updated adjusted_mode.clock to e.g. properly compute * watermarks. * * Note that zpos normalization will add all enable planes to the state which * might not desired for some drivers. * For example enable/disable of a cursor plane which have fixed zpos value * would trigger all other enabled planes to be forced to the state change. * * IMPORTANT: * * As this function calls drm_atomic_helper_check_modeset() internally, its * restrictions also apply: * Drivers which set &drm_crtc_state.mode_changed (e.g. in their * &drm_plane_helper_funcs.atomic_check hooks if a plane update can't be done * without a full modeset) _must_ call drm_atomic_helper_check_modeset() * function again after that change. * * RETURNS: * Zero for success or -errno
*/ int drm_atomic_helper_check(struct drm_device *dev, struct drm_atomic_state *state)
{ int ret;
ret = drm_atomic_helper_check_modeset(dev, state); if (ret) return ret;
if (dev->mode_config.normalize_zpos) {
ret = drm_atomic_normalize_zpos(dev, state); if (ret) return ret;
}
ret = drm_atomic_helper_check_planes(dev, state); if (ret) return ret;
if (state->legacy_cursor_update)
state->async_update = !drm_atomic_helper_async_check(dev, state);
staticbool
crtc_needs_disable(struct drm_crtc_state *old_state, struct drm_crtc_state *new_state)
{ /* * No new_state means the CRTC is off, so the only criteria is whether * it's currently active or in self refresh mode.
*/ if (!new_state) return drm_atomic_crtc_effectively_active(old_state);
/* * We need to disable bridge(s) and CRTC if we're transitioning out of * self-refresh and changing CRTCs at the same time, because the * bridge tracks self-refresh status via CRTC state.
*/ if (old_state->self_refresh_active &&
old_state->crtc != new_state->crtc) returntrue;
/* * We also need to run through the crtc_funcs->disable() function if * the CRTC is currently on, if it's transitioning to self refresh * mode, or if it's in self refresh mode and needs to be fully * disabled.
*/ return old_state->active ||
(old_state->self_refresh_active && !new_state->active) ||
new_state->self_refresh_active;
}
/* * Shut down everything that's in the changeset and currently * still on. So need to check the old, saved state.
*/ if (!old_conn_state->crtc) continue;
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice.
*/
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_disable(bridge, state);
/* Right function depends upon target state. */ if (funcs) { if (funcs->atomic_disable)
funcs->atomic_disable(encoder, state); elseif (new_conn_state->crtc && funcs->prepare)
funcs->prepare(encoder); elseif (funcs->disable)
funcs->disable(encoder); elseif (funcs->dpms)
funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}
}
}
/* * Shut down everything that's in the changeset and currently * still on. So need to check the old, saved state.
*/ if (!old_conn_state->crtc) continue;
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call disable hooks twice.
*/
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_post_disable(bridge, state);
}
}
/** * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state * @dev: DRM device * @state: atomic state object being committed * * This function updates all the various legacy modeset state pointers in * connectors, encoders and CRTCs. * * Drivers can use this for building their own atomic commit if they don't have * a pure helper-based modeset implementation. * * Since these updates are not synchronized with lockings, only code paths * called from &drm_mode_config_helper_funcs.atomic_commit_tail can look at the * legacy state filled out by this helper. Defacto this means this helper and * the legacy state pointers are only really useful for transitioning an * existing driver to the atomic world.
*/ void
drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_connector *connector; struct drm_connector_state *old_conn_state, *new_conn_state; struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; int i;
/* clear out existing links and update dpms */
for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { if (connector->encoder) {
WARN_ON(!connector->encoder->crtc);
if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed) continue;
drm_dbg_atomic(dev, "modeset on [ENCODER:%d:%s]\n",
encoder->base.id, encoder->name);
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call mode_set hooks twice.
*/ if (funcs && funcs->atomic_mode_set) {
funcs->atomic_mode_set(encoder, new_crtc_state,
new_conn_state);
} elseif (funcs && funcs->mode_set) {
funcs->mode_set(encoder, mode, adjusted_mode);
}
/** * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs * @dev: DRM device * @state: atomic state object being committed * * This function shuts down all the outputs that need to be shut down and * prepares them (if required) with the new mode. * * For compatibility with legacy CRTC helpers this should be called before * drm_atomic_helper_commit_planes(), which is what the default commit function * does. But drivers with different needs can group the modeset commits together * and do the plane commits at the end. This is useful for drivers doing runtime * PM since planes updates then only happen when the CRTC is actually enabled.
*/ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, struct drm_atomic_state *state)
{
disable_outputs(dev, state);
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice.
*/
bridge = drm_bridge_chain_get_first_bridge(encoder);
drm_atomic_bridge_chain_pre_enable(bridge, state);
}
}
/* * Each encoder has at most one connector (since we always steal * it away), so we won't call enable hooks twice.
*/
bridge = drm_bridge_chain_get_first_bridge(encoder);
if (funcs) { if (funcs->atomic_enable)
funcs->atomic_enable(encoder, state); elseif (funcs->enable)
funcs->enable(encoder); elseif (funcs->commit)
funcs->commit(encoder);
}
/** * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs * @dev: DRM device * @state: atomic state object being committed * * This function enables all the outputs with the new configuration which had to * be turned off for the update. * * For compatibility with legacy CRTC helpers this should be called after * drm_atomic_helper_commit_planes(), which is what the default commit function * does. But drivers with different needs can group the modeset commits together * and do the plane commits at the end. This is useful for drivers doing runtime * PM since planes updates then only happen when the CRTC is actually enabled.
*/ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
{
encoder_bridge_pre_enable(dev, state);
/* * For atomic updates which touch just a single CRTC, calculate the time of the * next vblank, and inform all the fences of the deadline.
*/ staticvoid set_fence_deadline(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; struct drm_plane *plane; struct drm_plane_state *new_plane_state;
ktime_t vbltime = 0; int i;
/** * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state * @dev: DRM device * @state: atomic state object with old state structures * @pre_swap: If true, do an interruptible wait, and @state is the new state. * Otherwise @state is the old state. * * For implicit sync, driver should fish the exclusive fence out from the * incoming fb's and stash it in the drm_plane_state. This is called after * drm_atomic_helper_swap_state() so it uses the current plane state (and * just uses the atomic state to find the changed planes) * * Note that @pre_swap is needed since the point where we block for fences moves * around depending upon whether an atomic commit is blocking or * non-blocking. For non-blocking commit all waiting needs to happen after * drm_atomic_helper_swap_state() is called, but for blocking commits we want * to wait **before** we do anything that can't be easily rolled back. That is * before we call drm_atomic_helper_swap_state(). * * Returns zero if success or < 0 if dma_fence_wait() fails.
*/ int drm_atomic_helper_wait_for_fences(struct drm_device *dev, struct drm_atomic_state *state, bool pre_swap)
{ struct drm_plane *plane; struct drm_plane_state *new_plane_state; int i, ret;
set_fence_deadline(dev, state);
for_each_new_plane_in_state(state, plane, new_plane_state, i) { if (!new_plane_state->fence) continue;
WARN_ON(!new_plane_state->fb);
/* * If waiting for fences pre-swap (ie: nonblock), userspace can * still interrupt the operation. Instead of blocking until the * timer expires, make the wait interruptible.
*/
ret = dma_fence_wait(new_plane_state->fence, pre_swap); if (ret) return ret;
/** * drm_atomic_helper_wait_for_vblanks - wait for vblank on CRTCs * @dev: DRM device * @state: atomic state object being committed * * Helper to, after atomic commit, wait for vblanks on all affected * CRTCs (ie. before cleaning up old framebuffers using * drm_atomic_helper_cleanup_planes()). It will only wait on CRTCs where the * framebuffers have actually changed to optimize for the legacy cursor and * plane update use-case. * * Drivers using the nonblocking commit tracking support initialized by calling * drm_atomic_helper_setup_commit() should look at * drm_atomic_helper_wait_for_flip_done() as an alternative.
*/ void
drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i, ret; unsignedint crtc_mask = 0;
/* * Legacy cursor ioctls are completely unsynced, and userspace * relies on that (by doing tons of cursor updates).
*/ if (state->legacy_cursor_update) return;
for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if (!new_crtc_state->active) continue;
ret = drm_crtc_vblank_get(crtc); if (ret != 0) continue;
/** * drm_atomic_helper_wait_for_flip_done - wait for all page flips to be done * @dev: DRM device * @state: atomic state object being committed * * Helper to, after atomic commit, wait for page flips on all affected * crtcs (ie. before cleaning up old framebuffers using * drm_atomic_helper_cleanup_planes()). Compared to * drm_atomic_helper_wait_for_vblanks() this waits for the completion on all * CRTCs, assuming that cursors-only updates are signalling their completion * immediately (or using a different path). * * This requires that drivers use the nonblocking commit tracking support * initialized using drm_atomic_helper_setup_commit().
*/ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; int i;
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct drm_crtc_commit *commit = state->crtcs[i].commit; int ret;
crtc = state->crtcs[i].ptr;
if (!crtc || !commit) continue;
ret = wait_for_completion_timeout(&commit->flip_done, 10 * HZ); if (ret == 0)
drm_err(dev, "[CRTC:%d:%s] flip_done timed out\n",
crtc->base.id, crtc->name);
}
if (state->fake_commit)
complete_all(&state->fake_commit->flip_done);
}
EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done);
/** * drm_atomic_helper_commit_tail - commit atomic update to hardware * @state: atomic state object being committed * * This is the default implementation for the * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers * that do not support runtime_pm or do not need the CRTC to be * enabled to perform a commit. Otherwise, see * drm_atomic_helper_commit_tail_rpm(). * * Note that the default ordering of how the various stages are called is to * match the legacy modeset helper library closest.
*/ void drm_atomic_helper_commit_tail(struct drm_atomic_state *state)
{ struct drm_device *dev = state->dev;
/** * drm_atomic_helper_commit_tail_rpm - commit atomic update to hardware * @state: new modeset state to be committed * * This is an alternative implementation for the * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers * that support runtime_pm or need the CRTC to be enabled to perform a * commit. Otherwise, one should use the default implementation * drm_atomic_helper_commit_tail().
*/ void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *state)
{ struct drm_device *dev = state->dev;
/* * We're measuring the _entire_ commit, so the time will vary depending * on how many fences and objects are involved. For the purposes of self * refresh, this is desirable since it'll give us an idea of how * congested things are. This will inform our decision on how often we * should enter self refresh after idle. * * These times will be averaged out in the self refresh helpers to avoid * overreacting over one outlier frame
*/
start = ktime_get();
/* * We cannot safely access new_crtc_state after * drm_atomic_helper_commit_hw_done() so figure out which crtc's have * self-refresh active beforehand:
*/
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) if (new_crtc_state->self_refresh_active)
new_self_refresh_mask |= BIT(i);
if (funcs && funcs->atomic_commit_tail)
funcs->atomic_commit_tail(state); else
drm_atomic_helper_commit_tail(state);
/** * drm_atomic_helper_async_check - check if state can be committed asynchronously * @dev: DRM device * @state: the driver state object * * This helper will check if it is possible to commit the state asynchronously. * Async commits are not supposed to swap the states like normal sync commits * but just do in-place changes on the current state. * * It will return 0 if the commit can happen in an asynchronous fashion or error * if not. Note that error just mean it can't be committed asynchronously, if it * fails the commit should be treated like a normal synchronous commit.
*/ int drm_atomic_helper_async_check(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_plane *plane = NULL; struct drm_plane_state *old_plane_state = NULL; struct drm_plane_state *new_plane_state = NULL; conststruct drm_plane_helper_funcs *funcs; int i, ret, n_planes = 0;
for_each_new_crtc_in_state(state, crtc, crtc_state, i) { if (drm_atomic_crtc_needs_modeset(crtc_state)) return -EINVAL;
}
/* FIXME: we support only single plane updates for now */ if (n_planes != 1) {
drm_dbg_atomic(dev, "only single plane async updates are supported\n"); return -EINVAL;
}
funcs = plane->helper_private; if (!funcs->atomic_async_update) {
drm_dbg_atomic(dev, "[PLANE:%d:%s] driver does not support async updates\n",
plane->base.id, plane->name); return -EINVAL;
}
if (new_plane_state->fence) {
drm_dbg_atomic(dev, "[PLANE:%d:%s] missing fence for async update\n",
plane->base.id, plane->name); return -EINVAL;
}
/* * Don't do an async update if there is an outstanding commit modifying * the plane. This prevents our async update's changes from getting * overridden by a previous synchronous update's state.
*/ if (old_plane_state->commit &&
!try_wait_for_completion(&old_plane_state->commit->hw_done)) {
drm_dbg_atomic(dev, "[PLANE:%d:%s] inflight previous commit preventing async commit\n",
plane->base.id, plane->name); return -EBUSY;
}
/** * drm_atomic_helper_async_commit - commit state asynchronously * @dev: DRM device * @state: the driver state object * * This function commits a state asynchronously, i.e., not vblank * synchronized. It should be used on a state only when * drm_atomic_async_check() succeeds. Async commits are not supposed to swap * the states like normal sync commits, but just do in-place changes on the * current state. * * TODO: Implement full swap instead of doing in-place changes.
*/ void drm_atomic_helper_async_commit(struct drm_device *dev, struct drm_atomic_state *state)
{ struct drm_plane *plane; struct drm_plane_state *plane_state; conststruct drm_plane_helper_funcs *funcs; int i;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.