On Fri,  7 Mar 2014 15:42:43 +0200
ville.syrj...@linux.intel.com wrote:

> From: Ville Syrjälä <ville.syrj...@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.
> 
> Note that we now go digging through pipe_to_crtc_mapping[] in the
> vblank interrupt handler, which is a bit dangerous since we set up
> interrupts before the crtcs. However in this case since it's the vblank
> interrupt, we don't actually unmask it until some piece of code
> requests it.
> 
> v2: preempt_check_resched() calls after local_irq_enable() (Jesse)
>     Hook up the vblank irq stuff on BDW as well
> v3: Pass intel_crtc instead of drm_crtc (Daniel)
>     Warn if crtc.mutex isn't locked (Daniel)
>     Add an explicit compiler barrier and document the barriers (Daniel)
>     Note the irq vs. modeset setup madness in the commit message (Daniel)
> v4: Use prepare_to_wait() & co. directly and eliminate vbl_received
> v5: Refactor intel_pipe_handle_vblank() vs. drm_handle_vblank() (Chris)
>     Check for min/max scanline <= 0 (Chris)
>     Don't call intel_pipe_update_end() if start failed totally (Chris)
>     Check that the vblank counters match on both sides of the critical
>     section (Chris)
> v6: Fix atomic update for interlaced modes
> v7: Reorder code for better readability (Chris)
> v8: Drop preempt_check_resched(). It's not available to modules
>     anymore and isn't even needed unless we ourselves cause
>     a wakeup needing reschedule while interrupts are off
> 
> Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
> ---
>  drivers/gpu/drm/i915/i915_irq.c      |  25 +++++--
>  drivers/gpu/drm/i915/intel_display.c |   2 +
>  drivers/gpu/drm/i915/intel_drv.h     |   2 +
>  drivers/gpu/drm/i915/intel_sprite.c  | 131 
> +++++++++++++++++++++++++++++++++++
>  4 files changed, 154 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
> index 6ec3f8a..d35120e 100644
> --- a/drivers/gpu/drm/i915/i915_irq.c
> +++ b/drivers/gpu/drm/i915/i915_irq.c
> @@ -1536,6 +1536,19 @@ static void gen6_rps_irq_handler(struct 
> drm_i915_private *dev_priv, u32 pm_iir)
>       }
>  }
>  
> +static bool intel_pipe_handle_vblank(struct drm_device *dev, enum pipe pipe)
> +{
> +     struct intel_crtc *crtc;
> +
> +     if (!drm_handle_vblank(dev, pipe))
> +             return false;
> +
> +     crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
> +     wake_up(&crtc->vbl_wait);
> +
> +     return true;
> +}
> +
>  static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir)
>  {
>       struct drm_i915_private *dev_priv = dev->dev_private;
> @@ -1587,7 +1600,7 @@ static void valleyview_pipestat_irq_handler(struct 
> drm_device *dev, u32 iir)
>  
>       for_each_pipe(pipe) {
>               if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
> -                     drm_handle_vblank(dev, pipe);
> +                     intel_pipe_handle_vblank(dev, pipe);
>  
>               if (pipe_stats[pipe] & PLANE_FLIP_DONE_INT_STATUS_VLV) {
>                       intel_prepare_page_flip(dev, pipe);
> @@ -1814,7 +1827,7 @@ static void ilk_display_irq_handler(struct drm_device 
> *dev, u32 de_iir)
>  
>       for_each_pipe(pipe) {
>               if (de_iir & DE_PIPE_VBLANK(pipe))
> -                     drm_handle_vblank(dev, pipe);
> +                     intel_pipe_handle_vblank(dev, pipe);
>  
>               if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
>                       if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, 
> false))
> @@ -1864,7 +1877,7 @@ static void ivb_display_irq_handler(struct drm_device 
> *dev, u32 de_iir)
>  
>       for_each_pipe(pipe) {
>               if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
> -                     drm_handle_vblank(dev, pipe);
> +                     intel_pipe_handle_vblank(dev, pipe);
>  
>               /* plane/pipes map 1:1 on ilk+ */
>               if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
> @@ -2007,7 +2020,7 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
>  
>               pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
>               if (pipe_iir & GEN8_PIPE_VBLANK)
> -                     drm_handle_vblank(dev, pipe);
> +                     intel_pipe_handle_vblank(dev, pipe);
>  
>               if (pipe_iir & GEN8_PIPE_FLIP_DONE) {
>                       intel_prepare_page_flip(dev, pipe);
> @@ -3284,7 +3297,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev,
>       drm_i915_private_t *dev_priv = dev->dev_private;
>       u16 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
>  
> -     if (!drm_handle_vblank(dev, pipe))
> +     if (!intel_pipe_handle_vblank(dev, pipe))
>               return false;
>  
>       if ((iir & flip_pending) == 0)
> @@ -3469,7 +3482,7 @@ static bool i915_handle_vblank(struct drm_device *dev,
>       drm_i915_private_t *dev_priv = dev->dev_private;
>       u32 flip_pending = DISPLAY_PLANE_FLIP_PENDING(plane);
>  
> -     if (!drm_handle_vblank(dev, pipe))
> +     if (!intel_pipe_handle_vblank(dev, pipe))
>               return false;
>  
>       if ((iir & flip_pending) == 0)
> diff --git a/drivers/gpu/drm/i915/intel_display.c 
> b/drivers/gpu/drm/i915/intel_display.c
> index 7e8bfd8..3fc739c 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -10299,6 +10299,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 9790477..8c9892d 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -384,6 +384,8 @@ struct intel_crtc {
>               /* watermarks currently being used  */
>               struct intel_pipe_wm active;
>       } wm;
> +
> +     wait_queue_head_t vbl_wait;
>  };
>  
>  struct intel_plane_wm_parameters {
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c 
> b/drivers/gpu/drm/i915/intel_sprite.c
> index 336ae6c..a3ed840 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -37,6 +37,89 @@
>  #include <drm/i915_drm.h>
>  #include "i915_drv.h"
>  
> +static int usecs_to_scanlines(const struct drm_display_mode *mode, int usecs)
> +{
> +     /* paranoia */
> +     if (!mode->crtc_htotal)
> +             return 1;
> +
> +     return DIV_ROUND_UP(usecs * mode->crtc_clock, 1000 * mode->crtc_htotal);
> +}
> +
> +static bool intel_pipe_update_start(struct intel_crtc *crtc, uint32_t 
> *start_vbl_count)
> +{
> +     struct drm_device *dev = crtc->base.dev;
> +     const struct drm_display_mode *mode = &crtc->config.adjusted_mode;
> +     enum pipe pipe = crtc->pipe;
> +     long timeout = msecs_to_jiffies_timeout(1);
> +     int scanline, min, max, vblank_start;
> +     DEFINE_WAIT(wait);
> +
> +     WARN_ON(!mutex_is_locked(&crtc->base.mutex));
> +
> +     vblank_start = mode->crtc_vblank_start;
> +     if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> +             vblank_start = DIV_ROUND_UP(vblank_start, 2);
> +
> +     /* FIXME needs to be calibrated sensibly */
> +     min = vblank_start - usecs_to_scanlines(mode, 100);
> +     max = vblank_start - 1;
> +
> +     if (min <= 0 || max <= 0)
> +             return false;
> +
> +     if (WARN_ON(drm_vblank_get(dev, pipe)))
> +             return false;
> +
> +     local_irq_disable();
> +
> +     for (;;) {
> +             /*
> +              * prepare_to_wait() has a memory barrier, which guarantees
> +              * other CPUs can see the task state update by the time we
> +              * read the scanline.
> +              */
> +             prepare_to_wait(&crtc->vbl_wait, &wait, TASK_UNINTERRUPTIBLE);
> +
> +             scanline = intel_get_crtc_scanline(crtc);
> +             if (scanline < min || scanline > max)
> +                     break;
> +
> +             if (timeout <= 0) {
> +                     DRM_ERROR("Potential atomic update failure on pipe 
> %c\n",
> +                               pipe_name(crtc->pipe));
> +                     break;
> +             }
> +
> +             local_irq_enable();
> +
> +             timeout = schedule_timeout(timeout);
> +
> +             local_irq_disable();
> +     }
> +
> +     finish_wait(&crtc->vbl_wait, &wait);
> +
> +     drm_vblank_put(dev, pipe);
> +
> +     *start_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
> +
> +     return true;
> +}
> +
> +static void intel_pipe_update_end(struct intel_crtc *crtc, u32 
> start_vbl_count)
> +{
> +     struct drm_device *dev = crtc->base.dev;
> +     enum pipe pipe = crtc->pipe;
> +     u32 end_vbl_count = dev->driver->get_vblank_counter(dev, pipe);
> +
> +     local_irq_enable();
> +
> +     if (start_vbl_count != end_vbl_count)
> +             DRM_ERROR("Atomic update failure on pipe %c (start=%u 
> end=%u)\n",
> +                       pipe_name(pipe), start_vbl_count, end_vbl_count);
> +}
> +
>  static void
>  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>                struct drm_framebuffer *fb,
> @@ -48,11 +131,14 @@ vlv_update_plane(struct drm_plane *dplane, struct 
> drm_crtc *crtc,
>       struct drm_device *dev = dplane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(dplane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
>       int plane = intel_plane->plane;
>       u32 sprctl;
>       unsigned long sprsurf_offset, linear_offset;
>       int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> +     u32 start_vbl_count;
> +     bool atomic_update;
>  
>       sprctl = I915_READ(SPCNTR(pipe, plane));
>  
> @@ -131,6 +217,8 @@ vlv_update_plane(struct drm_plane *dplane, struct 
> drm_crtc *crtc,
>                                                       fb->pitches[0]);
>       linear_offset -= sprsurf_offset;
>  
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
> +
>       I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
>       I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
>  
> @@ -144,6 +232,9 @@ vlv_update_plane(struct drm_plane *dplane, struct 
> drm_crtc *crtc,
>       I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
>                  sprsurf_offset);
>       POSTING_READ(SPSURF(pipe, plane));
> +
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
>  }
>  
>  static void
> @@ -152,8 +243,13 @@ vlv_disable_plane(struct drm_plane *dplane, struct 
> drm_crtc *crtc)
>       struct drm_device *dev = dplane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(dplane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
>       int plane = intel_plane->plane;
> +     u32 start_vbl_count;
> +     bool atomic_update;
> +
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
>  
>       I915_WRITE(SPCNTR(pipe, plane), I915_READ(SPCNTR(pipe, plane)) &
>                  ~SP_ENABLE);
> @@ -161,6 +257,9 @@ vlv_disable_plane(struct drm_plane *dplane, struct 
> drm_crtc *crtc)
>       I915_WRITE(SPSURF(pipe, plane), 0);
>       POSTING_READ(SPSURF(pipe, plane));
>  
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
> +
>       intel_update_sprite_watermarks(dplane, crtc, 0, 0, false, false);
>  }
>  
> @@ -226,10 +325,13 @@ ivb_update_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc,
>       struct drm_device *dev = plane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(plane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
>       u32 sprctl, sprscale = 0;
>       unsigned long sprsurf_offset, linear_offset;
>       int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> +     u32 start_vbl_count;
> +     bool atomic_update;
>  
>       sprctl = I915_READ(SPRCTL(pipe));
>  
> @@ -299,6 +401,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc 
> *crtc,
>                                              pixel_size, fb->pitches[0]);
>       linear_offset -= sprsurf_offset;
>  
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
> +
>       I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]);
>       I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
>  
> @@ -318,6 +422,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc 
> *crtc,
>       I915_WRITE(SPRSURF(pipe),
>                  i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
>       POSTING_READ(SPRSURF(pipe));
> +
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
>  }
>  
>  static void
> @@ -326,7 +433,12 @@ ivb_disable_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc)
>       struct drm_device *dev = plane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(plane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
> +     u32 start_vbl_count;
> +     bool atomic_update;
> +
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
>  
>       I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE);
>       /* Can't leave the scaler enabled... */
> @@ -336,6 +448,9 @@ ivb_disable_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc)
>       I915_WRITE(SPRSURF(pipe), 0);
>       POSTING_READ(SPRSURF(pipe));
>  
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
> +
>       /*
>        * Avoid underruns when disabling the sprite.
>        * FIXME remove once watermark updates are done properly.
> @@ -410,10 +525,13 @@ ilk_update_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc,
>       struct drm_device *dev = plane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(plane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
>       unsigned long dvssurf_offset, linear_offset;
>       u32 dvscntr, dvsscale;
>       int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> +     u32 start_vbl_count;
> +     bool atomic_update;
>  
>       dvscntr = I915_READ(DVSCNTR(pipe));
>  
> @@ -478,6 +596,8 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc 
> *crtc,
>                                              pixel_size, fb->pitches[0]);
>       linear_offset -= dvssurf_offset;
>  
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
> +
>       I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
>       I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
>  
> @@ -492,6 +612,9 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc 
> *crtc,
>       I915_WRITE(DVSSURF(pipe),
>                  i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
>       POSTING_READ(DVSSURF(pipe));
> +
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
>  }
>  
>  static void
> @@ -500,7 +623,12 @@ ilk_disable_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc)
>       struct drm_device *dev = plane->dev;
>       struct drm_i915_private *dev_priv = dev->dev_private;
>       struct intel_plane *intel_plane = to_intel_plane(plane);
> +     struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>       int pipe = intel_plane->pipe;
> +     u32 start_vbl_count;
> +     bool atomic_update;
> +
> +     atomic_update = intel_pipe_update_start(intel_crtc, &start_vbl_count);
>  
>       I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE);
>       /* Disable the scaler */
> @@ -509,6 +637,9 @@ ilk_disable_plane(struct drm_plane *plane, struct 
> drm_crtc *crtc)
>       I915_WRITE(DVSSURF(pipe), 0);
>       POSTING_READ(DVSSURF(pipe));
>  
> +     if (atomic_update)
> +             intel_pipe_update_end(intel_crtc, start_vbl_count);
> +
>       /*
>        * Avoid underruns when disabling the sprite.
>        * FIXME remove once watermark updates are done properly.

Yeah looks like this will work ok.

I don't understand the prepare_to_wait() comment, since we're both
holding the crtc mutex and prepare_to_wait() will take the crtc
vbl_wait queue lock, but since things look safe as-is I guess it's not
a big deal.

Reviewed-by: Jesse Barnes <jbar...@virtuousgeek.org>

-- 
Jesse Barnes, Intel Open Source Technology Center
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to