Move the vblank wait (needed to avoid displaying an unpinned frame) to a work handler. Special case benchmarks and other apps that may call set_plane at a high frequency: we can unpin their objects directly unless they're "live". Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org> --- drivers/gpu/drm/i915/i915_reg.h | 4 ++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_sprite.c | 80 ++++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index addad69..8b95dd9 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2820,6 +2820,7 @@ #define DVSSTRIDE(pipe) _PIPE(pipe, _DVSASTRIDE, _DVSBSTRIDE) #define DVSPOS(pipe) _PIPE(pipe, _DVSAPOS, _DVSBPOS) #define DVSSURF(pipe) _PIPE(pipe, _DVSASURF, _DVSBSURF) +#define DVSSURFLIVE(pipe) _PIPE(pipe, _DVSASURFLIVE, _DVSBSURFLIVE) #define DVSKEYMAX(pipe) _PIPE(pipe, _DVSAKEYMAXVAL, _DVSBKEYMAXVAL) #define DVSSIZE(pipe) _PIPE(pipe, _DVSASIZE, _DVSBSIZE) #define DVSSCALE(pipe) _PIPE(pipe, _DVSASCALE, _DVSBSCALE) @@ -2860,6 +2861,7 @@ #define _SPRA_SURF 0x7029c #define _SPRA_KEYMAX 0x702a0 #define _SPRA_TILEOFF 0x702a4 +#define _SPRA_SURFLIVE 0x702ac #define _SPRA_SCALE 0x70304 #define SPRITE_SCALE_ENABLE (1<<31) #define SPRITE_FILTER_MASK (3<<29) @@ -2880,6 +2882,7 @@ #define _SPRB_SURF 0x7129c #define _SPRB_KEYMAX 0x712a0 #define _SPRB_TILEOFF 0x712a4 +#define _SPRB_SURFLIVE 0x712ac #define _SPRB_SCALE 0x71304 #define _SPRB_GAMC 0x71400 @@ -2891,6 +2894,7 @@ #define SPRKEYVAL(pipe) _PIPE(pipe, _SPRA_KEYVAL, _SPRB_KEYVAL) #define SPRKEYMSK(pipe) _PIPE(pipe, _SPRA_KEYMSK, _SPRB_KEYMSK) #define SPRSURF(pipe) _PIPE(pipe, _SPRA_SURF, _SPRB_SURF) +#define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE) #define SPRKEYMAX(pipe) _PIPE(pipe, _SPRA_KEYMAX, _SPRB_KEYMAX) #define SPRTILEOFF(pipe) _PIPE(pipe, _SPRA_TILEOFF, _SPRB_TILEOFF) #define SPRSCALE(pipe) _PIPE(pipe, _SPRA_SCALE, _SPRB_SCALE) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9cec6c3..badfdf4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -190,6 +190,7 @@ struct intel_plane { struct drm_intel_sprite_colorkey *key); void (*get_colorkey)(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key); + u32 (*current_surface)(struct drm_plane *plane); }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index dbdc5c0..dfe4f14 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -36,6 +36,13 @@ #include "i915_drm.h" #include "i915_drv.h" +struct intel_sprite_unpin_work { + struct work_struct work; + struct drm_device *dev; + struct drm_i915_gem_object *old_fb_obj; + int pipe; +}; + static void ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, @@ -208,6 +215,16 @@ ivb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; } +static u32 +ivb_current_surface(struct drm_plane *plane) +{ + struct intel_plane *intel_plane; + + intel_plane = to_intel_plane(plane); + + return SPRSURFLIVE(intel_plane->pipe); +} + static void snb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, @@ -387,6 +404,63 @@ snb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; } +static u32 +snb_current_surface(struct drm_plane *plane) +{ + struct intel_plane *intel_plane; + + intel_plane = to_intel_plane(plane); + + return DVSSURFLIVE(intel_plane->pipe); +} + +static void intel_sprite_unpin_work_fn(struct work_struct *__work) +{ + struct intel_sprite_unpin_work *work = + container_of(__work, struct intel_sprite_unpin_work, work); + + intel_wait_for_vblank(work->dev, work->pipe); + + mutex_lock(&work->dev->struct_mutex); + intel_unpin_fb_obj(work->old_fb_obj); + mutex_unlock(&work->dev->struct_mutex); + kfree(work); +} + +static void +intel_queue_unpin(struct drm_plane *plane, + struct drm_i915_gem_object *obj, int pipe) +{ + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_sprite_unpin_work *work; + + /* + * If the surface is currently being scanned out, we need to + * wait until the next vblank event latches in the new base address + * before we unpin it, or we may end up displaying the wrong data. + * However, if the old object isn't currently 'live', we can just + * unpin right away. + */ + if (intel_plane->current_surface(plane) != obj->gtt_offset) { + intel_unpin_fb_obj(obj); + return; + } + + work = kzalloc(sizeof *work, GFP_KERNEL); + if (!work) { + /* Avoids a memory leak, but may result in a corrupt frame */ + intel_unpin_fb_obj(obj); + return; + } + + INIT_WORK(&work->work, intel_sprite_unpin_work_fn); + work->old_fb_obj = obj; + work->dev = obj->base.dev; + work->pipe = pipe; + + schedule_work(&work->work); +} + static int intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -490,7 +564,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, /* Unpin old obj after new one is active to avoid ugliness */ if (old_obj) - intel_unpin_fb_obj(old_obj); + intel_queue_unpin(plane, old_obj, pipe); out_unlock: mutex_unlock(&dev->struct_mutex); @@ -516,7 +590,7 @@ intel_disable_plane(struct drm_plane *plane) goto out; mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(intel_plane->obj); + intel_queue_unpin(plane, intel_plane->obj, intel_plane->pipe); intel_plane->obj = NULL; mutex_unlock(&dev->struct_mutex); out: @@ -631,12 +705,14 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) intel_plane->disable_plane = snb_disable_plane; intel_plane->update_colorkey = snb_update_colorkey; intel_plane->get_colorkey = snb_get_colorkey; + intel_plane->current_surface = snb_current_surface; } else if (IS_GEN7(dev)) { intel_plane->max_downscale = 2; intel_plane->update_plane = ivb_update_plane; intel_plane->disable_plane = ivb_disable_plane; intel_plane->update_colorkey = ivb_update_colorkey; intel_plane->get_colorkey = ivb_get_colorkey; + intel_plane->current_surface = ivb_current_surface; } intel_plane->pipe = pipe; -- 1.7.4.1