123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- From 73801d8192779fb0c763a4ef7fd058ce41df056d Mon Sep 17 00:00:00 2001
- From: Eric Anholt <eric@anholt.net>
- Date: Wed, 30 Dec 2015 11:50:22 -0800
- Subject: [PATCH] drm/vc4: Add a proper short-circut path for legacy cursor
- updates.
- Previously, on every modeset we would allocate new display list
- memory, recompute changed planes, write all of them to the new memory,
- and pointed scanout at the new list (which will latch approximately at
- the next line of scanout). We let
- drm_atomic_helper_wait_for_vblanks() decide whether we needed to wait
- for a vblank after a modeset before cleaning up the old state and
- letting the next modeset proceed, and on legacy cursor updates we
- wouldn't wait. If you moved the cursor fast enough, we could
- potentially wrap around the display list memory area and overwrite the
- existing display list while it was still being scanned out, resulting
- in the HVS scanning out garbage or just halting.
- Instead of making cursor updates wait for scanout to move to the new
- display list area (which introduces significant cursor lag in X), we
- just rewrite our current display list.
- Signed-off-by: Eric Anholt <eric@anholt.net>
- (cherry picked from commit 6674a904d68041d982ffb284d2827410765a097a)
- ---
- drivers/gpu/drm/vc4/vc4_kms.c | 9 ++++
- drivers/gpu/drm/vc4/vc4_plane.c | 94 ++++++++++++++++++++++++++++++++++++++---
- 2 files changed, 96 insertions(+), 7 deletions(-)
- --- a/drivers/gpu/drm/vc4/vc4_kms.c
- +++ b/drivers/gpu/drm/vc4/vc4_kms.c
- @@ -49,6 +49,15 @@ vc4_atomic_complete_commit(struct vc4_co
-
- drm_atomic_helper_commit_modeset_enables(dev, state);
-
- + /* Make sure that drm_atomic_helper_wait_for_vblanks()
- + * actually waits for vblank. If we're doing a full atomic
- + * modeset (as opposed to a vc4_update_plane() short circuit),
- + * then we need to wait for scanout to be done with our
- + * display lists before we free it and potentially reallocate
- + * and overwrite the dlist memory with a new modeset.
- + */
- + state->legacy_cursor_update = false;
- +
- drm_atomic_helper_wait_for_vblanks(dev, state);
-
- drm_atomic_helper_cleanup_planes(dev, state);
- --- a/drivers/gpu/drm/vc4/vc4_plane.c
- +++ b/drivers/gpu/drm/vc4/vc4_plane.c
- @@ -33,8 +33,12 @@ struct vc4_plane_state {
- u32 dlist_size; /* Number of dwords allocated for the display list */
- u32 dlist_count; /* Number of used dwords in the display list. */
-
- - /* Offset in the dlist to pointer word 0. */
- - u32 pw0_offset;
- + /* Offset in the dlist to various words, for pageflip or
- + * cursor updates.
- + */
- + u32 pos0_offset;
- + u32 pos2_offset;
- + u32 ptr0_offset;
-
- /* Offset where the plane's dlist was last stored in the
- * hardware at vc4_crtc_atomic_flush() time.
- @@ -239,6 +243,7 @@ static int vc4_plane_mode_set(struct drm
- SCALER_CTL0_UNITY);
-
- /* Position Word 0: Image Positions and Alpha Value */
- + vc4_state->pos0_offset = vc4_state->dlist_count;
- vc4_dlist_write(vc4_state,
- VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
- VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
- @@ -249,6 +254,7 @@ static int vc4_plane_mode_set(struct drm
- */
-
- /* Position Word 2: Source Image Size, Alpha Mode */
- + vc4_state->pos2_offset = vc4_state->dlist_count;
- vc4_dlist_write(vc4_state,
- VC4_SET_FIELD(format->has_alpha ?
- SCALER_POS2_ALPHA_MODE_PIPELINE :
- @@ -260,9 +266,8 @@ static int vc4_plane_mode_set(struct drm
- /* Position Word 3: Context. Written by the HVS. */
- vc4_dlist_write(vc4_state, 0xc0c0c0c0);
-
- - vc4_state->pw0_offset = vc4_state->dlist_count;
- -
- /* Pointer Word 0: RGB / Y Pointer */
- + vc4_state->ptr0_offset = vc4_state->dlist_count;
- vc4_dlist_write(vc4_state, bo->paddr + vc4_state->offset);
-
- /* Pointer Context Word 0: Written by the HVS */
- @@ -348,13 +353,13 @@ void vc4_plane_async_set_fb(struct drm_p
- * scanout will start from this address as soon as the FIFO
- * needs to refill with pixels.
- */
- - writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]);
- + writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
-
- /* Also update the CPU-side dlist copy, so that any later
- * atomic updates that don't do a new modeset on our plane
- * also use our updated address.
- */
- - vc4_state->dlist[vc4_state->pw0_offset] = addr;
- + vc4_state->dlist[vc4_state->ptr0_offset] = addr;
- }
-
- static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
- @@ -370,8 +375,83 @@ static void vc4_plane_destroy(struct drm
- drm_plane_cleanup(plane);
- }
-
- +/* Implements immediate (non-vblank-synced) updates of the cursor
- + * position, or falls back to the atomic helper otherwise.
- + */
- +static int
- +vc4_update_plane(struct drm_plane *plane,
- + struct drm_crtc *crtc,
- + struct drm_framebuffer *fb,
- + int crtc_x, int crtc_y,
- + unsigned int crtc_w, unsigned int crtc_h,
- + uint32_t src_x, uint32_t src_y,
- + uint32_t src_w, uint32_t src_h)
- +{
- + struct drm_plane_state *plane_state;
- + struct vc4_plane_state *vc4_state;
- +
- + if (plane != crtc->cursor)
- + goto out;
- +
- + plane_state = plane->state;
- + vc4_state = to_vc4_plane_state(plane_state);
- +
- + if (!plane_state)
- + goto out;
- +
- + /* If we're changing the cursor contents, do that in the
- + * normal vblank-synced atomic path.
- + */
- + if (fb != plane_state->fb)
- + goto out;
- +
- + /* No configuring new scaling in the fast path. */
- + if (crtc_w != plane_state->crtc_w ||
- + crtc_h != plane_state->crtc_h ||
- + src_w != plane_state->src_w ||
- + src_h != plane_state->src_h) {
- + goto out;
- + }
- +
- + /* Set the cursor's position on the screen. This is the
- + * expected change from the drm_mode_cursor_universal()
- + * helper.
- + */
- + plane_state->crtc_x = crtc_x;
- + plane_state->crtc_y = crtc_y;
- +
- + /* Allow changing the start position within the cursor BO, if
- + * that matters.
- + */
- + plane_state->src_x = src_x;
- + plane_state->src_y = src_y;
- +
- + /* Update the display list based on the new crtc_x/y. */
- + vc4_plane_atomic_check(plane, plane_state);
- +
- + /* Note that we can't just call vc4_plane_write_dlist()
- + * because that would smash the context data that the HVS is
- + * currently using.
- + */
- + writel(vc4_state->dlist[vc4_state->pos0_offset],
- + &vc4_state->hw_dlist[vc4_state->pos0_offset]);
- + writel(vc4_state->dlist[vc4_state->pos2_offset],
- + &vc4_state->hw_dlist[vc4_state->pos2_offset]);
- + writel(vc4_state->dlist[vc4_state->ptr0_offset],
- + &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
- +
- + return 0;
- +
- +out:
- + return drm_atomic_helper_update_plane(plane, crtc, fb,
- + crtc_x, crtc_y,
- + crtc_w, crtc_h,
- + src_x, src_y,
- + src_w, src_h);
- +}
- +
- static const struct drm_plane_funcs vc4_plane_funcs = {
- - .update_plane = drm_atomic_helper_update_plane,
- + .update_plane = vc4_update_plane,
- .disable_plane = drm_atomic_helper_disable_plane,
- .destroy = vc4_plane_destroy,
- .set_property = NULL,
|