From: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> 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. Signed-off-by: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> --- 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 0b19723..f79f1b4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1606,6 +1606,8 @@ typedef struct drm_i915_private { * state as well as the actual hardware registers */ struct mutex mutex; + + struct work_struct work; } wm; struct i915_package_c8 pc8; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index bd79224..052da8c 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1909,8 +1909,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)) @@ -1959,8 +1961,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)) { @@ -2102,8 +2106,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_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 c028b5c..aa9acc5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10283,6 +10283,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 8e32d69..c9d2603 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -389,6 +389,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; @@ -904,6 +930,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 d8adcb3..de8fb65 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2701,6 +2701,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 *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + u32 vbl_count; + + assert_spin_locked(&intel_crtc->wm.lock); + + if (!intel_crtc->wm.dirty) + return false; + + vbl_count = dev->driver->get_vblank_counter(dev, intel_crtc->pipe); + + if (!vbl_count_after_eq(dev, vbl_count, intel_crtc->wm.pending_vbl_count)) + return false; + + if (intel_crtc->wm.vblank) { + drm_vblank_put(dev, intel_crtc->pipe); + intel_crtc->wm.vblank = false; + } + + return true; +} + +static bool ilk_refresh_pending_watermarks(struct drm_device *dev) +{ + struct intel_crtc *intel_crtc; + bool changed = false; + + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { + bool ready; + + spin_lock_irq(&intel_crtc->wm.lock); + + ready = ilk_pending_watermarks_ready(intel_crtc); + if (ready) + intel_crtc->wm.dirty = false; + + spin_unlock_irq(&intel_crtc->wm.lock); + + if (!ready) + continue; + + intel_crtc->wm.active = intel_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; @@ -2759,6 +2818,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 *intel_crtc, + const struct intel_pipe_wm *pipe_wm, + u32 vbl_count) +{ + struct drm_device *dev = intel_crtc->base.dev; + enum pipe pipe = intel_crtc->pipe; + + WARN(!intel_crtc->active, "pipe %c should be enabled\n", + pipe_name(pipe)); + + /* do the watermarks actually need changing? */ + if (!memcmp(&intel_crtc->wm.pending, pipe_wm, sizeof(*pipe_wm))) + return; + + intel_crtc->wm.pending = *pipe_wm; + + spin_lock_irq(&intel_crtc->wm.lock); + intel_crtc->wm.pending_vbl_count = (vbl_count + 1) & dev->max_vblank_count; + intel_crtc->wm.dirty = true; + spin_unlock_irq(&intel_crtc->wm.lock); + + /* try to update immediately */ + ilk_update_watermarks(dev); + + spin_lock_irq(&intel_crtc->wm.lock); + + /* did the immediate update succeed? */ + if (!intel_crtc->wm.dirty) + goto unlock; + + /* + * We might already have a pending watermark update, in + * which case we shouldn't grab another vblank reference. + */ + if (!intel_crtc->wm.vblank && drm_vblank_get(dev, pipe) == 0) + intel_crtc->wm.vblank = true; + + WARN(!intel_crtc->wm.vblank, + "unable to set up watermarks for pipe %c\n", pipe_name(pipe)); + + unlock: + spin_unlock_irq(&intel_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 *intel_crtc = + to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + spin_lock(&intel_crtc->wm.lock); + + if (ilk_pending_watermarks_ready(intel_crtc)) + schedule_work(&dev_priv->wm.work); + + spin_unlock(&intel_crtc->wm.lock); +} + static void ilk_update_sprite_wm(struct drm_plane *plane, struct drm_crtc *crtc, uint32_t sprite_width, int pixel_size, @@ -2821,6 +2961,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) @@ -5776,6 +5919,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.3.2 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx