From: Ville Syrjälä <ville.syrj...@linux.intel.com>

Add a mechanism by which you can queue up watermark update to happen
after the vblank counter has reached a certain value. The vblank
interrupt handler will schedule a work which will do the actual
watermark programming in process context.

v2: Rebase and s/intel_crtc/crtc/

Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |   2 +
 drivers/gpu/drm/i915/i915_irq.c      |  12 ++-
 drivers/gpu/drm/i915/intel_display.c |   1 +
 drivers/gpu/drm/i915/intel_drv.h     |  27 +++++++
 drivers/gpu/drm/i915/intel_pm.c      | 144 +++++++++++++++++++++++++++++++++++
 5 files changed, 183 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index a2302a7..c90d5ac 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1541,6 +1541,8 @@ struct drm_i915_private {
                 * state as well as the actual hardware registers
                 */
                struct mutex mutex;
+
+               struct work_struct work;
        } wm;
 
        struct i915_runtime_pm pm;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 304f86a..c680020 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -2067,8 +2067,10 @@ static void ilk_display_irq_handler(struct drm_device 
*dev, u32 de_iir)
                DRM_ERROR("Poison interrupt\n");
 
        for_each_pipe(pipe) {
-               if (de_iir & DE_PIPE_VBLANK(pipe))
+               if (de_iir & DE_PIPE_VBLANK(pipe)) {
                        intel_pipe_handle_vblank(dev, pipe);
+                       ilk_update_pipe_wm(dev, pipe);
+               }
 
                if (de_iir & DE_PIPE_FIFO_UNDERRUN(pipe))
                        if (intel_set_cpu_fifo_underrun_reporting(dev, pipe, 
false))
@@ -2117,8 +2119,10 @@ static void ivb_display_irq_handler(struct drm_device 
*dev, u32 de_iir)
                intel_opregion_asle_intr(dev);
 
        for_each_pipe(pipe) {
-               if (de_iir & (DE_PIPE_VBLANK_IVB(pipe)))
+               if (de_iir & (DE_PIPE_VBLANK_IVB(pipe))) {
                        intel_pipe_handle_vblank(dev, pipe);
+                       ilk_update_pipe_wm(dev, pipe);
+               }
 
                /* plane/pipes map 1:1 on ilk+ */
                if (de_iir & DE_PLANE_FLIP_DONE_IVB(pipe)) {
@@ -2260,8 +2264,10 @@ static irqreturn_t gen8_irq_handler(int irq, void *arg)
                        continue;
 
                pipe_iir = I915_READ(GEN8_DE_PIPE_IIR(pipe));
-               if (pipe_iir & GEN8_PIPE_VBLANK)
+               if (pipe_iir & GEN8_PIPE_VBLANK) {
                        intel_pipe_handle_vblank(dev, pipe);
+                       ilk_update_pipe_wm(dev, pipe);
+               }
 
                if (pipe_iir & GEN8_PIPE_PRIMARY_FLIP_DONE) {
                        intel_prepare_page_flip(dev, pipe);
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index a11bd78..408b238 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10961,6 +10961,7 @@ static void intel_crtc_init(struct drm_device *dev, int 
pipe)
                intel_crtc->plane = !pipe;
        }
 
+       spin_lock_init(&intel_crtc->wm.lock);
        init_waitqueue_head(&intel_crtc->vbl_wait);
 
        BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d75cc2b..72f01b1 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -409,6 +409,32 @@ struct intel_crtc {
                 * protected by dev_priv->wm.mutex
                 */
                struct intel_pipe_wm active;
+               /*
+                * watermarks queued for next vblank
+                * protected by dev_priv->wm.mutex
+                */
+               struct intel_pipe_wm pending;
+
+               /*
+                * the vblank count after which we can switch over to 'pending'
+                * protected by intel_crtc->wm.lock
+                */
+               u32 pending_vbl_count;
+               /*
+                * indicates that 'pending' contains changed watermarks
+                * protected by intel_crtc->wm.lock
+                */
+               bool dirty;
+               /*
+                * watermark update has a vblank reference?
+                * protected by intel_crtc->wm.lock
+                */
+               bool vblank;
+
+               /*
+                * protects some intel_crtc->wm state
+                */
+               spinlock_t lock;
        } wm;
 
        wait_queue_head_t vbl_wait;
@@ -974,6 +1000,7 @@ void intel_runtime_pm_put(struct drm_i915_private 
*dev_priv);
 void intel_init_runtime_pm(struct drm_i915_private *dev_priv);
 void intel_fini_runtime_pm(struct drm_i915_private *dev_priv);
 void ilk_wm_get_hw_state(struct drm_device *dev);
+void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe);
 
 
 /* intel_sdvo.c */
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 2a63418..6fc6416 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2752,6 +2752,65 @@ static bool ilk_disable_lp_wm(struct drm_device *dev)
        return changed;
 }
 
+static bool vbl_count_after_eq(struct drm_device *dev, u32 a, u32 b)
+{
+       u32 mask = dev->max_vblank_count;
+
+       /* just the msb please */
+       mask &= ~(mask >> 1);
+
+       return !((a - b) & mask);
+}
+
+static bool ilk_pending_watermarks_ready(struct intel_crtc *crtc)
+{
+       struct drm_device *dev = crtc->base.dev;
+       u32 vbl_count;
+
+       assert_spin_locked(&crtc->wm.lock);
+
+       if (!crtc->wm.dirty)
+               return false;
+
+       vbl_count = dev->driver->get_vblank_counter(dev, crtc->pipe);
+
+       if (!vbl_count_after_eq(dev, vbl_count, crtc->wm.pending_vbl_count))
+               return false;
+
+       if (crtc->wm.vblank) {
+               drm_vblank_put(dev, crtc->pipe);
+               crtc->wm.vblank = false;
+       }
+
+       return true;
+}
+
+static bool ilk_refresh_pending_watermarks(struct drm_device *dev)
+{
+       struct intel_crtc *crtc;
+       bool changed = false;
+
+       for_each_intel_crtc(dev, crtc) {
+               bool ready;
+
+               spin_lock_irq(&crtc->wm.lock);
+
+               ready = ilk_pending_watermarks_ready(crtc);
+               if (ready)
+                       crtc->wm.dirty = false;
+
+               spin_unlock_irq(&crtc->wm.lock);
+
+               if (!ready)
+                       continue;
+
+               crtc->wm.active = crtc->wm.pending;
+               changed = true;
+       }
+
+       return changed;
+}
+
 static void ilk_program_watermarks(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2810,6 +2869,87 @@ static void ilk_update_wm(struct drm_crtc *crtc)
        mutex_unlock(&dev_priv->wm.mutex);
 }
 
+static void ilk_update_watermarks(struct drm_device *dev)
+{
+       bool changed;
+
+       changed = ilk_refresh_pending_watermarks(dev);
+
+       if (changed)
+               ilk_program_watermarks(dev);
+}
+
+static void ilk_setup_pending_watermarks(struct intel_crtc *crtc,
+                                        const struct intel_pipe_wm *pipe_wm,
+                                        u32 vbl_count)
+{
+       struct drm_device *dev = crtc->base.dev;
+       enum pipe pipe = crtc->pipe;
+
+       WARN(!crtc->active, "pipe %c should be enabled\n",
+            pipe_name(pipe));
+
+       /* do the watermarks actually need changing? */
+       if (!memcmp(&crtc->wm.pending, pipe_wm, sizeof(*pipe_wm)))
+               return;
+
+       crtc->wm.pending = *pipe_wm;
+
+       spin_lock_irq(&crtc->wm.lock);
+       crtc->wm.pending_vbl_count = (vbl_count + 1) & dev->max_vblank_count;
+       crtc->wm.dirty = true;
+       spin_unlock_irq(&crtc->wm.lock);
+
+       /* try to update immediately */
+       ilk_update_watermarks(dev);
+
+       spin_lock_irq(&crtc->wm.lock);
+
+       /* did the immediate update succeed? */
+       if (!crtc->wm.dirty)
+               goto unlock;
+
+       /*
+        * We might already have a pending watermark update, in
+        * which case we shouldn't grab another vblank reference.
+        */
+       if (!crtc->wm.vblank && drm_vblank_get(dev, pipe) == 0)
+               crtc->wm.vblank = true;
+
+       WARN(!crtc->wm.vblank,
+            "unable to set up watermarks for pipe %c\n", pipe_name(pipe));
+
+ unlock:
+       spin_unlock_irq(&crtc->wm.lock);
+}
+
+static void ilk_watermark_work(struct work_struct *work)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(work, struct drm_i915_private, wm.work);
+
+       mutex_lock(&dev_priv->wm.mutex);
+
+       ilk_update_watermarks(dev_priv->dev);
+
+       mutex_unlock(&dev_priv->wm.mutex);
+}
+
+/* Called from vblank irq */
+void ilk_update_pipe_wm(struct drm_device *dev, enum pipe pipe)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *crtc =
+               to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
+
+       spin_lock(&crtc->wm.lock);
+
+       if (ilk_pending_watermarks_ready(crtc))
+               schedule_work(&dev_priv->wm.work);
+
+       spin_unlock(&crtc->wm.lock);
+}
+
 static void ilk_update_sprite_wm(struct drm_plane *plane,
                                     struct drm_crtc *crtc,
                                     uint32_t sprite_width, int pixel_size,
@@ -2872,6 +3012,9 @@ static void _ilk_pipe_wm_hw_to_sw(struct drm_crtc *crtc)
                for (level = 0; level <= max_level; level++)
                        active->wm[level].enable = true;
        }
+
+       /* no update pending */
+       intel_crtc->wm.pending = intel_crtc->wm.active;
 }
 
 void ilk_wm_get_hw_state(struct drm_device *dev)
@@ -6385,6 +6528,7 @@ void intel_init_pm(struct drm_device *dev)
        struct drm_i915_private *dev_priv = dev->dev_private;
 
        mutex_init(&dev_priv->wm.mutex);
+       INIT_WORK(&dev_priv->wm.work, ilk_watermark_work);
 
        if (HAS_FBC(dev)) {
                if (INTEL_INFO(dev)->gen >= 7) {
-- 
1.8.5.5

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to