diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 321 |
1 files changed, 280 insertions, 41 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bbdbe4721573..7e3a52b97c7d 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -297,13 +297,22 @@ mode_fixup(struct drm_atomic_state *state) } } - - 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; + if (funcs->atomic_check) { + ret = funcs->atomic_check(encoder, crtc_state, + conn_state); + if (ret) { + DRM_DEBUG_KMS("[ENCODER:%d:%s] check failed\n", + encoder->base.id, encoder->name); + return ret; + } + } else { + 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; + } } } @@ -330,7 +339,35 @@ mode_fixup(struct drm_atomic_state *state) return 0; } -static int +static bool +needs_modeset(struct drm_crtc_state *state) +{ + return state->mode_changed || state->active_changed; +} + +/** + * drm_atomic_helper_check - 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. It computes and updates crtc_state->mode_changed, adds any additional + * connectors needed for full modesets and calls down into ->mode_fixup + * functions of the driver backend. + * + * IMPORTANT: + * + * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a + * plane update can't be done without a full modeset) _must_ call this function + * afterwards after that change. It is permitted to call this function multiple + * times for the same update, e.g. when the ->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) { @@ -382,12 +419,27 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, crtc = state->crtcs[i]; crtc_state = state->crtc_states[i]; - if (!crtc || !crtc_state->mode_changed) + if (!crtc) continue; - DRM_DEBUG_KMS("[CRTC:%d] needs full modeset, enable: %c\n", + /* + * We must set ->active_changed after walking connectors for + * otherwise an update that only changes active would result in + * a full modeset because update_connector_routing force that. + */ + if (crtc->state->active != crtc_state->active) { + DRM_DEBUG_KMS("[CRTC:%d] active changed\n", + crtc->base.id); + crtc_state->active_changed = true; + } + + if (!needs_modeset(crtc_state)) + continue; + + DRM_DEBUG_KMS("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", crtc->base.id, - crtc_state->enable ? 'y' : 'n'); + crtc_state->enable ? 'y' : 'n', + crtc_state->active ? 'y' : 'n'); ret = drm_atomic_add_affected_connectors(state, crtc); if (ret != 0) @@ -406,23 +458,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return mode_fixup(state); } +EXPORT_SYMBOL(drm_atomic_helper_check_modeset); /** - * drm_atomic_helper_check - validate state object + * drm_atomic_helper_check - 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. - * 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. + * This does all the plane update related checks using by calling into the + * ->atomic_check hooks provided by the driver. * * RETURNS * Zero for success or -errno */ -int drm_atomic_helper_check(struct drm_device *dev, - struct drm_atomic_state *state) +int +drm_atomic_helper_check_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; @@ -445,7 +497,7 @@ int drm_atomic_helper_check(struct drm_device *dev, ret = funcs->atomic_check(plane, plane_state); if (ret) { - DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n", + DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n", plane->base.id); return ret; } @@ -465,16 +517,49 @@ int drm_atomic_helper_check(struct drm_device *dev, ret = funcs->atomic_check(crtc, state->crtc_states[i]); if (ret) { - DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n", + DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n", crtc->base.id); return ret; } } + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_check_planes); + +/** + * 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. + * + * 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 + * ->atomic_check functions depend upon an updated adjusted_mode.clock to + * e.g. properly compute watermarks. + * + * 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; + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); @@ -490,6 +575,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) struct drm_connector *connector; struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; + struct drm_crtc_state *old_crtc_state; old_conn_state = old_state->connector_states[i]; connector = old_state->connectors[i]; @@ -499,6 +585,11 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) if (!old_conn_state || !old_conn_state->crtc) continue; + old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)]; + + if (!old_crtc_state->active) + continue; + encoder = old_conn_state->best_encoder; /* We shouldn't get this far if we didn't previously have @@ -509,6 +600,9 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = encoder->helper_private; + DRM_DEBUG_KMS("disabling [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 call disable hooks twice. @@ -517,7 +611,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) encoder->bridge->funcs->disable(encoder->bridge); /* Right function depends upon target state. */ - if (connector->state->crtc) + if (connector->state->crtc && funcs->prepare) funcs->prepare(encoder); else if (funcs->disable) funcs->disable(encoder); @@ -531,17 +625,26 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) for (i = 0; i < ncrtcs; i++) { struct drm_crtc_helper_funcs *funcs; struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; crtc = old_state->crtcs[i]; + old_crtc_state = old_state->crtc_states[i]; /* Shut down everything that needs a full modeset. */ - if (!crtc || !crtc->state->mode_changed) + if (!crtc || !needs_modeset(crtc->state)) + continue; + + if (!old_crtc_state->active) continue; funcs = crtc->helper_private; + DRM_DEBUG_KMS("disabling [CRTC:%d]\n", + crtc->base.id); + + /* Right function depends upon target state. */ - if (crtc->state->enable) + if (crtc->state->enable && funcs->prepare) funcs->prepare(crtc); else if (funcs->disable) funcs->disable(crtc); @@ -620,8 +723,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state) funcs = crtc->helper_private; - if (crtc->state->enable) + if (crtc->state->enable) { + DRM_DEBUG_KMS("modeset on [CRTC:%d]\n", + crtc->base.id); + funcs->mode_set_nofb(crtc); + } } for (i = 0; i < old_state->num_connector; i++) { @@ -642,6 +749,12 @@ 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) + continue; + + DRM_DEBUG_KMS("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 call mode_set hooks twice. @@ -694,13 +807,23 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, crtc = old_state->crtcs[i]; /* Need to filter out CRTCs where only planes change. */ - if (!crtc || !crtc->state->mode_changed) + if (!crtc || !needs_modeset(crtc->state)) + continue; + + if (!crtc->state->active) continue; funcs = crtc->helper_private; - if (crtc->state->enable) - funcs->commit(crtc); + if (crtc->state->enable) { + DRM_DEBUG_KMS("enabling [CRTC:%d]\n", + crtc->base.id); + + if (funcs->enable) + funcs->enable(crtc); + else + funcs->commit(crtc); + } } for (i = 0; i < old_state->num_connector; i++) { @@ -713,9 +836,15 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, if (!connector || !connector->state->best_encoder) continue; + if (!connector->state->crtc->state->active) + continue; + encoder = connector->state->best_encoder; funcs = encoder->helper_private; + DRM_DEBUG_KMS("enabling [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 call enable hooks twice. @@ -723,7 +852,10 @@ void drm_atomic_helper_commit_post_planes(struct drm_device *dev, if (encoder->bridge) encoder->bridge->funcs->pre_enable(encoder->bridge); - funcs->commit(encoder); + if (funcs->enable) + funcs->enable(encoder); + else + funcs->commit(encoder); if (encoder->bridge) encoder->bridge->funcs->enable(encoder->bridge); @@ -813,6 +945,11 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, if (!crtc->state->enable) continue; + /* Legacy cursor ioctls are completely unsynced, and userspace + * relies on that (by doing tons of cursor updates). */ + if (old_state->legacy_cursor_update) + continue; + if (!framebuffer_changed(dev, old_state, crtc)) continue; @@ -1053,12 +1190,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev, funcs = plane->helper_private; - if (!funcs || !funcs->atomic_update) + if (!funcs) continue; old_plane_state = old_state->plane_states[i]; - funcs->atomic_update(plane, old_plane_state); + /* + * Special-case disabling the plane if drivers support it. + */ + if (drm_atomic_plane_disabling(plane, old_plane_state) && + funcs->atomic_disable) + funcs->atomic_disable(plane, old_plane_state); + else + funcs->atomic_update(plane, old_plane_state); } for (i = 0; i < ncrtcs; i++) { @@ -1222,7 +1366,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1239,6 +1383,9 @@ retry: if (ret != 0) goto fail; + if (plane == crtc->cursor) + state->legacy_cursor_update = true; + /* Driver takes ownership of state on successful commit. */ return 0; fail: @@ -1301,7 +1448,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, NULL); + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, NULL); @@ -1314,6 +1461,9 @@ retry: plane_state->src_h = 0; plane_state->src_w = 0; + if (plane == plane->crtc->cursor) + state->legacy_cursor_update = true; + ret = drm_atomic_commit(state); if (ret != 0) goto fail; @@ -1463,8 +1613,9 @@ retry: WARN_ON(set->num_connectors); crtc_state->enable = false; + crtc_state->active = false; - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL); + ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); if (ret != 0) goto fail; @@ -1477,9 +1628,10 @@ retry: WARN_ON(!set->num_connectors); crtc_state->enable = true; + crtc_state->active = true; drm_mode_copy(&crtc_state->mode, set->mode); - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(primary_state, set->fb); @@ -1558,8 +1710,8 @@ retry: goto fail; } - ret = crtc->funcs->atomic_set_property(crtc, crtc_state, - property, val); + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + property, val); if (ret) goto fail; @@ -1617,8 +1769,8 @@ retry: goto fail; } - ret = plane->funcs->atomic_set_property(plane, plane_state, - property, val); + ret = drm_atomic_plane_set_property(plane, plane_state, + property, val); if (ret) goto fail; @@ -1676,8 +1828,8 @@ retry: goto fail; } - ret = connector->funcs->atomic_set_property(connector, connector_state, - property, val); + ret = drm_atomic_connector_set_property(connector, connector_state, + property, val); if (ret) goto fail; @@ -1751,7 +1903,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1789,6 +1941,83 @@ backoff: EXPORT_SYMBOL(drm_atomic_helper_page_flip); /** + * drm_atomic_helper_connector_dpms() - connector dpms helper implementation + * @connector: affected connector + * @mode: DPMS mode + * + * This is the main helper function provided by the atomic helper framework for + * implementing the legacy DPMS connector interface. It computes the new desired + * ->active state for the corresponding CRTC (if the connector is enabled) and + * updates it. + */ +void drm_atomic_helper_connector_dpms(struct drm_connector *connector, + int mode) +{ + struct drm_mode_config *config = &connector->dev->mode_config; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + struct drm_connector *tmp_connector; + int ret; + bool active = false; + + if (mode != DRM_MODE_DPMS_ON) + mode = DRM_MODE_DPMS_OFF; + + connector->dpms = mode; + crtc = connector->state->crtc; + + if (!crtc) + return; + + /* FIXME: ->dpms has no return value so can't forward the -ENOMEM. */ + state = drm_atomic_state_alloc(connector->dev); + if (!state) + return; + + state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); +retry: + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return; + + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); + + list_for_each_entry(tmp_connector, &config->connector_list, head) { + if (connector->state->crtc != crtc) + continue; + + if (connector->dpms == DRM_MODE_DPMS_ON) { + active = true; + break; + } + } + crtc_state->active = active; + + ret = drm_atomic_commit(state); + if (ret != 0) + goto fail; + + /* Driver takes ownership of state on successful async commit. */ + return; +fail: + if (ret == -EDEADLK) + goto backoff; + + drm_atomic_state_free(state); + + WARN(1, "Driver bug: Changing ->active failed with ret=%i\n", ret); + + return; +backoff: + drm_atomic_state_clear(state); + drm_atomic_legacy_backoff(state); + + goto retry; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_dpms); + +/** * DOC: atomic state reset and initialization * * Both the drm core and the atomic helpers assume that there is always the full @@ -1814,6 +2043,9 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { kfree(crtc->state); crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); @@ -1836,6 +2068,7 @@ drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc) if (state) { state->mode_changed = false; + state->active_changed = false; state->planes_changed = false; state->event = NULL; } @@ -1873,6 +2106,9 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) kfree(plane->state); plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); + + if (plane->state) + plane->state->plane = plane; } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); @@ -1930,6 +2166,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) { kfree(connector->state); connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); + + if (connector->state) + connector->state->connector = connector; } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); |