diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 333 |
1 files changed, 208 insertions, 125 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 5186d2114a50..5302ab324898 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -574,6 +574,30 @@ mode_valid(struct drm_atomic_state *state) return 0; } +static int drm_atomic_check_valid_clones(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + struct drm_encoder *drm_enc; + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, + crtc); + + drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask) { + if (!drm_enc->possible_clones) { + DRM_DEBUG("enc%d possible_clones is 0\n", drm_enc->base.id); + continue; + } + + if ((crtc_state->encoder_mask & drm_enc->possible_clones) != + crtc_state->encoder_mask) { + DRM_DEBUG("crtc%d failed valid clone check for mask 0x%x\n", + crtc->base.id, crtc_state->encoder_mask); + return -EINVAL; + } + } + + return 0; +} + /** * drm_atomic_helper_check_modeset - validate state object for modeset changes * @dev: DRM device @@ -666,8 +690,9 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, } if (new_crtc_state->enable != has_connectors) { - drm_dbg_atomic(dev, "[CRTC:%d:%s] enabled/connectors mismatch\n", - crtc->base.id, crtc->name); + drm_dbg_atomic(dev, "[CRTC:%d:%s] enabled/connectors mismatch (%d/%d)\n", + crtc->base.id, crtc->name, + new_crtc_state->enable, has_connectors); return -EINVAL; } @@ -745,6 +770,10 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, 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; } /* @@ -1059,6 +1088,15 @@ EXPORT_SYMBOL(drm_atomic_helper_check_planes); * 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 */ @@ -1122,7 +1160,7 @@ crtc_needs_disable(struct drm_crtc_state *old_state, } static void -disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) +disable_outputs(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_connector *connector; struct drm_connector_state *old_conn_state, *new_conn_state; @@ -1130,7 +1168,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) struct drm_crtc_state *old_crtc_state, *new_crtc_state; int i; - for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_bridge *bridge; @@ -1142,11 +1180,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!old_conn_state->crtc) continue; - old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc); + old_crtc_state = drm_atomic_get_old_crtc_state(state, old_conn_state->crtc); if (new_conn_state->crtc) new_crtc_state = drm_atomic_get_new_crtc_state( - old_state, + state, new_conn_state->crtc); else new_crtc_state = NULL; @@ -1173,12 +1211,12 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * it away), so we won't call disable hooks twice. */ bridge = drm_bridge_chain_get_first_bridge(encoder); - drm_atomic_bridge_chain_disable(bridge, old_state); + drm_atomic_bridge_chain_disable(bridge, state); /* Right function depends upon target state. */ if (funcs) { if (funcs->atomic_disable) - funcs->atomic_disable(encoder, old_state); + funcs->atomic_disable(encoder, state); else if (new_conn_state->crtc && funcs->prepare) funcs->prepare(encoder); else if (funcs->disable) @@ -1187,10 +1225,10 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs->dpms(encoder, DRM_MODE_DPMS_OFF); } - drm_atomic_bridge_chain_post_disable(bridge, old_state); + drm_atomic_bridge_chain_post_disable(bridge, state); } - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; int ret; @@ -1211,7 +1249,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (new_crtc_state->enable && funcs->prepare) funcs->prepare(crtc); else if (funcs->atomic_disable) - funcs->atomic_disable(crtc, old_state); + funcs->atomic_disable(crtc, state); else if (funcs->disable) funcs->disable(crtc); else if (funcs->dpms) @@ -1239,7 +1277,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) /** * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state * @dev: DRM device - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This function updates all the various legacy modeset state pointers in * connectors, encoders and CRTCs. @@ -1255,7 +1293,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) */ void drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, - struct drm_atomic_state *old_state) + struct drm_atomic_state *state) { struct drm_connector *connector; struct drm_connector_state *old_conn_state, *new_conn_state; @@ -1264,7 +1302,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, int i; /* clear out existing links and update dpms */ - for_each_oldnew_connector_in_state(old_state, connector, old_conn_state, new_conn_state, i) { + for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { if (connector->encoder) { WARN_ON(!connector->encoder->crtc); @@ -1285,7 +1323,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, } /* set new links */ - for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { if (!new_conn_state->crtc) continue; @@ -1297,7 +1335,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, } /* set legacy state in the crtc structure */ - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { struct drm_plane *primary = crtc->primary; struct drm_plane_state *new_plane_state; @@ -1305,7 +1343,7 @@ drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev, crtc->enabled = new_crtc_state->enable; new_plane_state = - drm_atomic_get_new_plane_state(old_state, primary); + drm_atomic_get_new_plane_state(state, primary); if (new_plane_state && new_plane_state->crtc == crtc) { crtc->x = new_plane_state->src_x >> 16; @@ -1337,7 +1375,7 @@ void drm_atomic_helper_calc_timestamping_constants(struct drm_atomic_state *stat EXPORT_SYMBOL(drm_atomic_helper_calc_timestamping_constants); static void -crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) +crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *new_crtc_state; @@ -1345,7 +1383,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) struct drm_connector_state *new_conn_state; int i; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; if (!new_crtc_state->mode_changed) @@ -1361,7 +1399,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) } } - for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_display_mode *mode, *adjusted_mode; @@ -1376,7 +1414,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) mode = &new_crtc_state->mode; adjusted_mode = &new_crtc_state->adjusted_mode; - if (!new_crtc_state->mode_changed) + if (!new_crtc_state->mode_changed && !new_crtc_state->connectors_changed) continue; drm_dbg_atomic(dev, "modeset on [ENCODER:%d:%s]\n", @@ -1401,7 +1439,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) /** * drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs * @dev: DRM device - * @old_state: atomic state object with old state structures + * @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. @@ -1413,25 +1451,25 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) * 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 *old_state) + struct drm_atomic_state *state) { - disable_outputs(dev, old_state); + disable_outputs(dev, state); - drm_atomic_helper_update_legacy_modeset_state(dev, old_state); - drm_atomic_helper_calc_timestamping_constants(old_state); + drm_atomic_helper_update_legacy_modeset_state(dev, state); + drm_atomic_helper_calc_timestamping_constants(state); - crtc_set_mode(dev, old_state); + crtc_set_mode(dev, state); } EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, - struct drm_atomic_state *old_state) + struct drm_atomic_state *state) { struct drm_connector *connector; struct drm_connector_state *new_conn_state; int i; - for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_connector_helper_funcs *funcs; funcs = connector->helper_private; @@ -1440,7 +1478,7 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) { WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); - funcs->atomic_commit(connector, old_state); + funcs->atomic_commit(connector, state); } } } @@ -1448,7 +1486,7 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, /** * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs * @dev: DRM device - * @old_state: atomic state object with old state structures + * @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. @@ -1460,7 +1498,7 @@ static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, * 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 *old_state) + struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; @@ -1469,7 +1507,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_connector_state *new_conn_state; int i; - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; /* Need to filter out CRTCs where only planes change. */ @@ -1485,13 +1523,13 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, drm_dbg_atomic(dev, "enabling [CRTC:%d:%s]\n", crtc->base.id, crtc->name); if (funcs->atomic_enable) - funcs->atomic_enable(crtc, old_state); + funcs->atomic_enable(crtc, state); else if (funcs->commit) funcs->commit(crtc); } } - for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { + for_each_new_connector_in_state(state, connector, new_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; struct drm_bridge *bridge; @@ -1514,21 +1552,21 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, * 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, old_state); + drm_atomic_bridge_chain_pre_enable(bridge, state); if (funcs) { if (funcs->atomic_enable) - funcs->atomic_enable(encoder, old_state); + funcs->atomic_enable(encoder, state); else if (funcs->enable) funcs->enable(encoder); else if (funcs->commit) funcs->commit(encoder); } - drm_atomic_bridge_chain_enable(bridge, old_state); + drm_atomic_bridge_chain_enable(bridge, state); } - drm_atomic_helper_commit_writebacks(dev, old_state); + drm_atomic_helper_commit_writebacks(dev, state); } EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); @@ -1630,7 +1668,7 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences); /** * drm_atomic_helper_wait_for_vblanks - wait for vblank on CRTCs * @dev: DRM device - * @old_state: atomic state object with old state structures + * @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 @@ -1644,7 +1682,7 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences); */ void drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, - struct drm_atomic_state *old_state) + struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; @@ -1655,10 +1693,10 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, * Legacy cursor ioctls are completely unsynced, and userspace * relies on that (by doing tons of cursor updates). */ - if (old_state->legacy_cursor_update) + if (state->legacy_cursor_update) return; - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if (!new_crtc_state->active) continue; @@ -1667,17 +1705,17 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, continue; crtc_mask |= drm_crtc_mask(crtc); - old_state->crtcs[i].last_vblank_count = drm_crtc_vblank_count(crtc); + state->crtcs[i].last_vblank_count = drm_crtc_vblank_count(crtc); } - for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { if (!(crtc_mask & drm_crtc_mask(crtc))) continue; ret = wait_event_timeout(dev->vblank[i].queue, - old_state->crtcs[i].last_vblank_count != - drm_crtc_vblank_count(crtc), - msecs_to_jiffies(100)); + state->crtcs[i].last_vblank_count != + drm_crtc_vblank_count(crtc), + msecs_to_jiffies(100)); WARN(!ret, "[CRTC:%d:%s] vblank wait timed out\n", crtc->base.id, crtc->name); @@ -1690,7 +1728,7 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); /** * drm_atomic_helper_wait_for_flip_done - wait for all page flips to be done * @dev: DRM device - * @old_state: atomic state object with old state structures + * @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 @@ -1703,16 +1741,16 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); * initialized using drm_atomic_helper_setup_commit(). */ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, - struct drm_atomic_state *old_state) + 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 = old_state->crtcs[i].commit; + struct drm_crtc_commit *commit = state->crtcs[i].commit; int ret; - crtc = old_state->crtcs[i].ptr; + crtc = state->crtcs[i].ptr; if (!crtc || !commit) continue; @@ -1723,14 +1761,14 @@ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, crtc->base.id, crtc->name); } - if (old_state->fake_commit) - complete_all(&old_state->fake_commit->flip_done); + 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 - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This is the default implementation for the * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers @@ -1741,29 +1779,29 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done); * 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 *old_state) +void drm_atomic_helper_commit_tail(struct drm_atomic_state *state) { - struct drm_device *dev = old_state->dev; + struct drm_device *dev = state->dev; - drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_planes(dev, old_state, 0); + drm_atomic_helper_commit_planes(dev, state, 0); - drm_atomic_helper_commit_modeset_enables(dev, old_state); + drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_fake_vblank(old_state); + drm_atomic_helper_fake_vblank(state); - drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_commit_hw_done(state); - drm_atomic_helper_wait_for_vblanks(dev, old_state); + drm_atomic_helper_wait_for_vblanks(dev, state); - drm_atomic_helper_cleanup_planes(dev, old_state); + drm_atomic_helper_cleanup_planes(dev, state); } EXPORT_SYMBOL(drm_atomic_helper_commit_tail); /** * drm_atomic_helper_commit_tail_rpm - commit atomic update to hardware - * @old_state: new modeset state to be committed + * @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 @@ -1771,30 +1809,30 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_tail); * commit. Otherwise, one should use the default implementation * drm_atomic_helper_commit_tail(). */ -void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state) +void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *state) { - struct drm_device *dev = old_state->dev; + struct drm_device *dev = state->dev; - drm_atomic_helper_commit_modeset_disables(dev, old_state); + drm_atomic_helper_commit_modeset_disables(dev, state); - drm_atomic_helper_commit_modeset_enables(dev, old_state); + drm_atomic_helper_commit_modeset_enables(dev, state); - drm_atomic_helper_commit_planes(dev, old_state, + drm_atomic_helper_commit_planes(dev, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); - drm_atomic_helper_fake_vblank(old_state); + drm_atomic_helper_fake_vblank(state); - drm_atomic_helper_commit_hw_done(old_state); + drm_atomic_helper_commit_hw_done(state); - drm_atomic_helper_wait_for_vblanks(dev, old_state); + drm_atomic_helper_wait_for_vblanks(dev, state); - drm_atomic_helper_cleanup_planes(dev, old_state); + drm_atomic_helper_cleanup_planes(dev, state); } EXPORT_SYMBOL(drm_atomic_helper_commit_tail_rpm); -static void commit_tail(struct drm_atomic_state *old_state) +static void commit_tail(struct drm_atomic_state *state) { - struct drm_device *dev = old_state->dev; + struct drm_device *dev = state->dev; const struct drm_mode_config_helper_funcs *funcs; struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; @@ -1816,33 +1854,33 @@ static void commit_tail(struct drm_atomic_state *old_state) */ start = ktime_get(); - drm_atomic_helper_wait_for_fences(dev, old_state, false); + drm_atomic_helper_wait_for_fences(dev, state, false); - drm_atomic_helper_wait_for_dependencies(old_state); + drm_atomic_helper_wait_for_dependencies(state); /* * 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(old_state, crtc, new_crtc_state, i) + 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(old_state); + funcs->atomic_commit_tail(state); else - drm_atomic_helper_commit_tail(old_state); + drm_atomic_helper_commit_tail(state); commit_time_ms = ktime_ms_delta(ktime_get(), start); if (commit_time_ms > 0) - drm_self_refresh_helper_update_avg_times(old_state, + drm_self_refresh_helper_update_avg_times(state, (unsigned long)commit_time_ms, new_self_refresh_mask); - drm_atomic_helper_commit_cleanup_done(old_state); + drm_atomic_helper_commit_cleanup_done(state); - drm_atomic_state_put(old_state); + drm_atomic_state_put(state); } static void commit_work(struct work_struct *work) @@ -1928,7 +1966,7 @@ int drm_atomic_helper_async_check(struct drm_device *dev, return -EBUSY; } - ret = funcs->atomic_async_check(plane, state); + ret = funcs->atomic_async_check(plane, state, false); if (ret != 0) drm_dbg_atomic(dev, "[PLANE:%d:%s] driver async check failed\n", @@ -2385,17 +2423,17 @@ EXPORT_SYMBOL(drm_atomic_helper_setup_commit); /** * drm_atomic_helper_wait_for_dependencies - wait for required preceding commits - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This function waits for all preceding commits that touch the same CRTC as - * @old_state to both be committed to the hardware (as signalled by + * @state to both be committed to the hardware (as signalled by * drm_atomic_helper_commit_hw_done()) and executed by the hardware (as signalled * by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event). * * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. */ -void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) +void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; @@ -2406,7 +2444,7 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) int i; long ret; - for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { ret = drm_crtc_commit_wait(old_crtc_state->commit); if (ret) drm_err(crtc->dev, @@ -2414,7 +2452,7 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) crtc->base.id, crtc->name); } - for_each_old_connector_in_state(old_state, conn, old_conn_state, i) { + for_each_old_connector_in_state(state, conn, old_conn_state, i) { ret = drm_crtc_commit_wait(old_conn_state->commit); if (ret) drm_err(conn->dev, @@ -2422,7 +2460,7 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) conn->base.id, conn->name); } - for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { ret = drm_crtc_commit_wait(old_plane_state->commit); if (ret) drm_err(plane->dev, @@ -2434,7 +2472,7 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); /** * drm_atomic_helper_fake_vblank - fake VBLANK events if needed - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This function walks all CRTCs and fakes VBLANK events on those with * &drm_crtc_state.no_vblank set to true and &drm_crtc_state.event != NULL. @@ -2450,32 +2488,32 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. */ -void drm_atomic_helper_fake_vblank(struct drm_atomic_state *old_state) +void drm_atomic_helper_fake_vblank(struct drm_atomic_state *state) { struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; int i; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { unsigned long flags; if (!new_crtc_state->no_vblank) continue; - spin_lock_irqsave(&old_state->dev->event_lock, flags); + spin_lock_irqsave(&state->dev->event_lock, flags); if (new_crtc_state->event) { drm_crtc_send_vblank_event(crtc, new_crtc_state->event); new_crtc_state->event = NULL; } - spin_unlock_irqrestore(&old_state->dev->event_lock, flags); + spin_unlock_irqrestore(&state->dev->event_lock, flags); } } EXPORT_SYMBOL(drm_atomic_helper_fake_vblank); /** * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This function is used to signal completion of the hardware commit step. After * this step the driver is not allowed to read or change any permanent software @@ -2488,14 +2526,14 @@ EXPORT_SYMBOL(drm_atomic_helper_fake_vblank); * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. */ -void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state) +void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc_commit *commit; int i; - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { commit = new_crtc_state->commit; if (!commit) continue; @@ -2515,32 +2553,32 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state) complete_all(&commit->hw_done); } - if (old_state->fake_commit) { - complete_all(&old_state->fake_commit->hw_done); - complete_all(&old_state->fake_commit->flip_done); + if (state->fake_commit) { + complete_all(&state->fake_commit->hw_done); + complete_all(&state->fake_commit->flip_done); } } EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); /** * drm_atomic_helper_commit_cleanup_done - signal completion of commit - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * - * This signals completion of the atomic update @old_state, including any + * This signals completion of the atomic update @state, including any * cleanup work. If used, it must be called right before calling * drm_atomic_state_put(). * * This is part of the atomic helper support for nonblocking commits, see * drm_atomic_helper_setup_commit() for an overview. */ -void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) +void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct drm_crtc_commit *commit; int i; - for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { commit = old_crtc_state->commit; if (WARN_ON(!commit)) continue; @@ -2553,9 +2591,9 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) spin_unlock(&crtc->commit_lock); } - if (old_state->fake_commit) { - complete_all(&old_state->fake_commit->cleanup_done); - WARN_ON(!try_wait_for_completion(&old_state->fake_commit->hw_done)); + if (state->fake_commit) { + complete_all(&state->fake_commit->cleanup_done); + WARN_ON(!try_wait_for_completion(&state->fake_commit->hw_done)); } } EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); @@ -2693,7 +2731,7 @@ static bool plane_crtc_active(const struct drm_plane_state *state) /** * drm_atomic_helper_commit_planes - commit plane state * @dev: DRM device - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * @flags: flags for committing plane state * * This function commits the new plane state using the plane and atomic helper @@ -2701,7 +2739,7 @@ static bool plane_crtc_active(const struct drm_plane_state *state) * been pushed into the relevant object state pointers, since this step can no * longer fail. * - * It still requires the global state object @old_state to know which planes and + * It still requires the global state object @state to know which planes and * crtcs need to be updated though. * * Note that this function does all plane updates across all CRTCs in one step. @@ -2732,7 +2770,7 @@ static bool plane_crtc_active(const struct drm_plane_state *state) * This should not be copied blindly by drivers. */ void drm_atomic_helper_commit_planes(struct drm_device *dev, - struct drm_atomic_state *old_state, + struct drm_atomic_state *state, uint32_t flags) { struct drm_crtc *crtc; @@ -2743,7 +2781,7 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY; bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET; - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; funcs = crtc->helper_private; @@ -2754,10 +2792,10 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (active_only && !new_crtc_state->active) continue; - funcs->atomic_begin(crtc, old_state); + funcs->atomic_begin(crtc, state); } - for_each_oldnew_plane_in_state(old_state, plane, old_plane_state, new_plane_state, i) { + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { const struct drm_plane_helper_funcs *funcs; bool disabling; @@ -2795,18 +2833,18 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, no_disable) continue; - funcs->atomic_disable(plane, old_state); + funcs->atomic_disable(plane, state); } else if (new_plane_state->crtc || disabling) { - funcs->atomic_update(plane, old_state); + funcs->atomic_update(plane, state); if (!disabling && funcs->atomic_enable) { if (drm_atomic_plane_enabling(old_plane_state, new_plane_state)) - funcs->atomic_enable(plane, old_state); + funcs->atomic_enable(plane, state); } } } - for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; funcs = crtc->helper_private; @@ -2817,14 +2855,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, if (active_only && !new_crtc_state->active) continue; - funcs->atomic_flush(crtc, old_state); + funcs->atomic_flush(crtc, state); } /* * Signal end of framebuffer access here before hw_done. After hw_done, * a later commit might have already released the plane state. */ - for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { const struct drm_plane_helper_funcs *funcs = plane->helper_private; if (funcs->end_fb_access) @@ -2951,10 +2989,10 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_planes_on_crtc); /** * drm_atomic_helper_cleanup_planes - cleanup plane resources after commit * @dev: DRM device - * @old_state: atomic state object with old state structures + * @state: atomic state object being committed * * This function cleans up plane state, specifically framebuffers, from the old - * configuration. Hence the old configuration must be perserved in @old_state to + * configuration. Hence the old configuration must be perserved in @state to * be able to call this function. * * This function may not be called on the new state when the atomic update @@ -2962,13 +3000,13 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_planes_on_crtc); * drm_atomic_helper_unprepare_planes() in this case. */ void drm_atomic_helper_cleanup_planes(struct drm_device *dev, - struct drm_atomic_state *old_state) + struct drm_atomic_state *state) { struct drm_plane *plane; struct drm_plane_state *old_plane_state; int i; - for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { + for_each_old_plane_in_state(state, plane, old_plane_state, i) { const struct drm_plane_helper_funcs *funcs = plane->helper_private; if (funcs->cleanup_fb) @@ -3363,6 +3401,51 @@ free: EXPORT_SYMBOL(drm_atomic_helper_disable_all); /** + * drm_atomic_helper_reset_crtc - reset the active outputs of a CRTC + * @crtc: DRM CRTC + * @ctx: lock acquisition context + * + * Reset the active outputs by indicating that connectors have changed. + * This implies a reset of all active components available between the CRTC and + * connectors. + * + * NOTE: This relies on resetting &drm_crtc_state.connectors_changed. + * For drivers which optimize out unnecessary modesets this will result in + * a no-op commit, achieving nothing. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +int drm_atomic_helper_reset_crtc(struct drm_crtc *crtc, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + int ret; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = ctx; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + crtc_state->connectors_changed = true; + + ret = drm_atomic_commit(state); +out: + drm_atomic_state_put(state); + + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_reset_crtc); + +/** * drm_atomic_helper_shutdown - shutdown all CRTC * @dev: DRM device * |