diff options
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 114 |
1 files changed, 104 insertions, 10 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 71d712f1b56a..ab4032167094 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -696,6 +696,100 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, EXPORT_SYMBOL(drm_atomic_helper_check_modeset); /** + * drm_atomic_helper_check_plane_state() - Check plane state for validity + * @plane_state: plane state to check + * @crtc_state: crtc state to check + * @clip: integer clipping coordinates + * @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, + const struct drm_crtc_state *crtc_state, + const struct drm_rect *clip, + 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; + unsigned int rotation = plane_state->rotation; + int hscale, vscale; + + WARN_ON(plane_state->crtc && plane_state->crtc != crtc_state->crtc); + + *src = drm_plane_state_src(plane_state); + *dst = drm_plane_state_dest(plane_state); + + if (!fb) { + plane_state->visible = false; + return 0; + } + + /* crtc should only be NULL when disabling (i.e., !fb) */ + if (WARN_ON(!plane_state->crtc)) { + plane_state->visible = false; + return 0; + } + + if (!crtc_state->enable && !can_update_disabled) { + DRM_DEBUG_KMS("Cannot update plane of a disabled CRTC.\n"); + return -EINVAL; + } + + drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation); + + /* Check scaling */ + hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale); + vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale); + if (hscale < 0 || vscale < 0) { + DRM_DEBUG_KMS("Invalid scaling of plane\n"); + drm_rect_debug_print("src: ", &plane_state->src, true); + drm_rect_debug_print("dst: ", &plane_state->dst, false); + return -ERANGE; + } + + plane_state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale); + + drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation); + + 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; + + if (!can_position && !drm_rect_equals(dst, clip)) { + DRM_DEBUG_KMS("Plane must cover entire CRTC\n"); + drm_rect_debug_print("dst: ", dst, false); + drm_rect_debug_print("clip: ", clip, false); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_check_plane_state); + +/** * drm_atomic_helper_check_planes - validate state object for planes changes * @dev: DRM device * @state: the driver state object @@ -907,6 +1001,12 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) * * 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, @@ -1225,7 +1325,7 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev, return; for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { - if (!new_crtc_state->active || !new_crtc_state->planes_changed) + if (!new_crtc_state->active) continue; ret = drm_crtc_vblank_get(crtc); @@ -1787,11 +1887,8 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, !try_wait_for_completion(&old_conn_state->commit->flip_done)) return -EBUSY; - /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ - if (new_conn_state->crtc) - continue; - - commit = crtc_or_fake_commit(state, old_conn_state->crtc); + /* Always track connectors explicitly for e.g. link retraining. */ + commit = crtc_or_fake_commit(state, new_conn_state->crtc ?: old_conn_state->crtc); if (!commit) return -ENOMEM; @@ -1805,10 +1902,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, !try_wait_for_completion(&old_plane_state->commit->flip_done)) return -EBUSY; - /* - * Unlike connectors, always track planes explicitly for - * async pageflip support. - */ + /* Always track planes explicitly for async pageflip support. */ commit = crtc_or_fake_commit(state, new_plane_state->crtc ?: old_plane_state->crtc); if (!commit) return -ENOMEM; |