[Intel-gfx] [PATCH 5/7] drm/i915: Make sprite updates atomic

Jesse Barnes jbarnes at virtuousgeek.org
Thu Dec 12 22:04:50 CET 2013


On Thu, 17 Oct 2013 22:53:17 +0300
ville.syrjala at linux.intel.com wrote:

> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
> 
> Add a mechanism by which we can evade the leading edge of vblank. This
> guarantees that no two sprite register writes will straddle on either
> side of the vblank start, and that means all the writes will be latched
> together in one atomic operation.
> 
> We do the vblank evade by checking the scanline counter, and if it's too
> close to the start of vblank (too close has been hardcoded to 100usec
> for now), we will wait for the vblank start to pass. In order to
> eliminate random delayes from the rest of the system, we operate with
> interrupts disabled, except when waiting for the vblank obviously.
> 
> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
>  drivers/gpu/drm/i915/i915_irq.c      | 34 ++++++++++++++---
>  drivers/gpu/drm/i915/intel_display.c |  2 +
>  drivers/gpu/drm/i915/intel_drv.h     |  3 ++
>  drivers/gpu/drm/i915/intel_sprite.c  | 72 ++++++++++++++++++++++++++++++++++++
>  4 files changed, 105 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 49bcf4e..776fe2f 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -1271,6 +1271,15 @@ static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir)
>  	}
>  }
>  
> +static void intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
> +{
> +	struct intel_crtc *intel_crtc =
> +		to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
> +
> +	intel_crtc->vbl_received = true;
> +	wake_up(&intel_crtc->vbl_wait);
> +}
> +
>  static irqreturn_t valleyview_irq_handler(int irq, void *arg)
>  {
>  	struct drm_device *dev = (struct drm_device *) arg;
> @@ -1313,8 +1322,10 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
>  		spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
>  
>  		for_each_pipe(pipe) {
> -			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
> +			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) {
>  				drm_handle_vblank(dev, pipe);
> +				intel_pipe_handle_vblank(dev, pipe);
> +			}
>  
>  			if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) {
>  				intel_prepare_page_flip(dev, pipe);
> @@ -1509,11 +1520,15 @@ static void ilk_display_irq_handler(struct drm_device *dev, u32 de_iir)
>  	if (de_iir & DE_GSE)
>  		intel_opregion_asle_intr(dev);
>  
> -	if (de_iir & DE_PIPEA_VBLANK)
> -		drm_handle_vblank(dev, 0);
> +	if (de_iir & DE_PIPEA_VBLANK) {
> +		drm_handle_vblank(dev, PIPE_A);
> +		intel_pipe_handle_vblank(dev, PIPE_A);
> +	}
>  
> -	if (de_iir & DE_PIPEB_VBLANK)
> -		drm_handle_vblank(dev, 1);
> +	if (de_iir & DE_PIPEB_VBLANK) {
> +		drm_handle_vblank(dev, PIPE_B);
> +		intel_pipe_handle_vblank(dev, PIPE_B);
> +	}
>  
>  	if (de_iir & DE_POISON)
>  		DRM_ERROR("Poison interrupt\n");
> @@ -1568,8 +1583,11 @@ static void ivb_display_irq_handler(struct drm_device *dev, u32 de_iir)
>  		intel_opregion_asle_intr(dev);
>  
>  	for (i = 0; i < 3; i++) {
> -		if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i)))
> +		if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) {
>  			drm_handle_vblank(dev, i);
> +			intel_pipe_handle_vblank(dev, i);
> +		}
> +
>  		if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) {
>  			intel_prepare_page_flip(dev, i);
>  			intel_finish_page_flip_plane(dev, i);
> @@ -2695,6 +2713,8 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
>  	if (!drm_handle_vblank(dev, pipe))
>  		return false;
>  
> +	intel_pipe_handle_vblank(dev, pipe);
> +
>  	if ((iir & flip_pending) == 0)
>  		return false;
>  
> @@ -2869,6 +2889,8 @@ static bool i915_handle_vblank(struct drm_device *dev,
>  	if (!drm_handle_vblank(dev, pipe))
>  		return false;
>  
> +	intel_pipe_handle_vblank(dev, pipe);
> +
>  	if ((iir & flip_pending) == 0)
>  		return false;
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index be056fd..34eb952 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -9754,6 +9754,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
>  		intel_crtc->plane = !pipe;
>  	}
>  
> +	init_waitqueue_head(&intel_crtc->vbl_wait);
> +
>  	BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
>  	       dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
>  	dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 7f5f74d..83cb2a0 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -361,6 +361,9 @@ struct intel_crtc {
>  		/* watermarks currently being used  */
>  		struct intel_pipe_wm active;
>  	} wm;
> +
> +	wait_queue_head_t vbl_wait;
> +	bool vbl_received;
>  };
>  
>  struct intel_plane_wm_parameters {
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index c160e4f..ce4e4ec 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -37,6 +37,54 @@
>  #include <drm/i915_drm.h>
>  #include "i915_drv.h"
>  
> +static unsigned int usecs_to_scanlines(const struct drm_display_mode *mode, unsigned int usecs)
> +{
> +	/* paranoia */
> +	if (!mode->crtc_htotal)
> +		return 1;
> +
> +	return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal);
> +}
> +
> +static void intel_pipe_update_start(struct drm_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->dev;
> +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> +	const struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
> +	enum pipe pipe = intel_crtc->pipe;
> +	/* FIXME needs to be calibrated sensibly */
> +	unsigned int min = mode->crtc_vblank_start - usecs_to_scanlines(mode, 100);
> +	unsigned int max = mode->crtc_vblank_start - 1;
> +	long timeout = msecs_to_jiffies_timeout(1);
> +	unsigned int scanline;
> +
> +	if (WARN_ON(drm_vblank_get(dev, pipe)))
> +		return;
> +
> +	local_irq_disable();
> +
> +	intel_crtc->vbl_received = false;
> +	scanline = i915_get_crtc_scanline(crtc);
> +
> +	while (scanline >= min && scanline <= max && timeout > 0) {
> +		local_irq_enable();
> +		timeout = wait_event_timeout(intel_crtc->vbl_wait,
> +					     intel_crtc->vbl_received,
> +					     timeout);
> +		local_irq_disable();
> +
> +		intel_crtc->vbl_received = false;
> +		scanline = i915_get_crtc_scanline(crtc);
> +	}
> +
> +	drm_vblank_put(dev, pipe);
> +}
> +
> +static void intel_pipe_update_end(struct drm_crtc *crtc)
> +{
> +	local_irq_enable();
> +}
> +
>  static void
>  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  		 struct drm_framebuffer *fb,
> @@ -125,6 +173,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  							fb->pitches[0]);
>  	linear_offset -= sprsurf_offset;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
>  	I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
>  
> @@ -138,6 +188,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
>  			     sprsurf_offset);
>  	POSTING_READ(SPSURF(pipe, plane));
> +
> +	intel_pipe_update_end(crtc);
>  }
>  
>  static void
> @@ -149,12 +201,16 @@ vlv_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
>  	int pipe = intel_plane->pipe;
>  	int plane = intel_plane->plane;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &
>  		   ~SP_ENABLE);
>  	/* Activate double buffered register update */
>  	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), 0);
>  	POSTING_READ(SPSURF(pipe, plane));
>  
> +	intel_pipe_update_end(crtc);
> +
>  	intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
>  }
>  
> @@ -301,6 +357,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  					       pixel_size, fb->pitches[0]);
>  	linear_offset -= sprsurf_offset;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
>  	I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
>  
> @@ -321,6 +379,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  			     i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
>  	POSTING_READ(SPRSURF(pipe));
>  
> +	intel_pipe_update_end(crtc);
> +
>  	/* potentially re-enable LP watermarks */
>  	if (scaling_was_enabled && !dev_priv->sprite_scaling_enabled)
>  		intel_update_watermarks(crtc);
> @@ -335,6 +395,8 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
>  	int pipe = intel_plane->pipe;
>  	bool scaling_was_enabled = dev_priv->sprite_scaling_enabled;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
>  	/* Can't leave the scaler enabled... */
>  	if (intel_plane->can_scale)
> @@ -343,6 +405,8 @@ ivb_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
>  	I915_MODIFY_DISPBASE(SPRSURF(pipe), 0);
>  	POSTING_READ(SPRSURF(pipe));
>  
> +	intel_pipe_update_end(crtc);
> +
>  	dev_priv->sprite_scaling_enabled &= ~(1 << pipe);
>  
>  	intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);
> @@ -479,6 +543,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  					       pixel_size, fb->pitches[0]);
>  	linear_offset -= dvssurf_offset;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
>  	I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
>  
> @@ -493,6 +559,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	I915_MODIFY_DISPBASE(DVSSURF(pipe),
>  			     i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
>  	POSTING_READ(DVSSURF(pipe));
> +
> +	intel_pipe_update_end(crtc);
>  }
>  
>  static void
> @@ -503,6 +571,8 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
>  	struct intel_plane *intel_plane = to_intel_plane(plane);
>  	int pipe = intel_plane->pipe;
>  
> +	intel_pipe_update_start(crtc);
> +
>  	I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
>  	/* Disable the scaler */
>  	I915_WRITE(DVSSCALE(pipe), 0);
> @@ -510,6 +580,8 @@ ilk_disable_plane(struct drm_plane *plane, struct drm_crtc *crtc)
>  	I915_MODIFY_DISPBASE(DVSSURF(pipe), 0);
>  	POSTING_READ(DVSSURF(pipe));
>  
> +	intel_pipe_update_end(crtc);
> +
>  	intel_update_sprite_watermarks(plane, crtc, 0, 0, false, false);
>  }
>  

It looks like we may need to check for preemption again on
local_irq_enable() according to preempt-locking.txt?  Otherwise it
looks like this does the right thing.

With the above addressed or explained away:
Reviewed-by: Jesse Barnes <jbarnes at virtuousgeek.org>
-- 
Jesse Barnes, Intel Open Source Technology Center



More information about the Intel-gfx mailing list