Jesse mentioned that we had reports of flickering due to switching clocks for powersaving and that would be a useful task to be run at vblank. <Find some testers> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk> --- drivers/gpu/drm/i915/intel_display.c | 111 ++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 35901eb..6af8d50 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -46,6 +46,7 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type); static void intel_increase_pllclock(struct drm_crtc *crtc); +static void intel_crtc_restore_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); static void __intel_crtc_load_lut(struct intel_crtc *crtc, void *data); @@ -1919,7 +1920,8 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, if (dev_priv->display.disable_fbc) dev_priv->display.disable_fbc(dev); - intel_increase_pllclock(crtc); + + intel_crtc_restore_pllclock(crtc); return dev_priv->display.update_plane(crtc, fb, x, y); } @@ -2028,6 +2030,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex); + intel_increase_pllclock(crtc); if (!dev->primary->master) return 0; @@ -5472,73 +5475,91 @@ static void intel_crtc_idle_timer(unsigned long arg) queue_work(dev_priv->wq, &dev_priv->idle_work); } -static void intel_increase_pllclock(struct drm_crtc *crtc) +static void __intel_crtc_increase_pllclock(struct intel_crtc *crtc, + void *data) { - struct drm_device *dev = crtc->dev; - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - int dpll_reg = DPLL(pipe); + drm_i915_private_t *dev_priv = crtc->base.dev->dev_private; + int dpll_reg = DPLL(crtc->pipe); int dpll; - if (HAS_PCH_SPLIT(dev)) - return; - - if (!dev_priv->lvds_downclock_avail) - return; - dpll = I915_READ(dpll_reg); - if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) { + if (dpll & DISPLAY_RATE_SELECT_FPA1) { DRM_DEBUG_DRIVER("upclocking LVDS\n"); - assert_panel_unlocked(dev_priv, pipe); - - dpll &= ~DISPLAY_RATE_SELECT_FPA1; - I915_WRITE(dpll_reg, dpll); - intel_wait_for_vblank(dev, pipe); - - dpll = I915_READ(dpll_reg); - if (dpll & DISPLAY_RATE_SELECT_FPA1) - DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); + assert_panel_unlocked(dev_priv, crtc->pipe); + I915_WRITE(dpll_reg, dpll & ~DISPLAY_RATE_SELECT_FPA1); } /* Schedule downclock */ - mod_timer(&intel_crtc->idle_timer, jiffies + - msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); + mod_timer(&crtc->idle_timer, + jiffies + msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); } -static void intel_decrease_pllclock(struct drm_crtc *crtc) +static void intel_crtc_restore_pllclock(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + int reg; + + if (HAS_PCH_SPLIT(dev) || HAS_PIPE_CXSR(dev)) + return; + + reg = DPLL(to_intel_crtc(crtc)->pipe); + I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_RATE_SELECT_FPA1); + + del_timer(&to_intel_crtc(crtc)->idle_timer); +} + +static void intel_increase_pllclock(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - int dpll_reg = DPLL(pipe); - int dpll = I915_READ(dpll_reg); - if (HAS_PCH_SPLIT(dev)) + if (HAS_PCH_SPLIT(dev) || HAS_PIPE_CXSR(dev)) return; if (!dev_priv->lvds_downclock_avail) return; + if (intel_crtc_add_vblank_task(intel_crtc, true, + __intel_crtc_increase_pllclock, + NULL)) + __intel_crtc_increase_pllclock(intel_crtc, NULL); +} + +static void __intel_crtc_decrease_pllclock(struct intel_crtc *crtc, + void *data) +{ + drm_i915_private_t *dev_priv = crtc->base.dev->dev_private; + int dpll_reg = DPLL(crtc->pipe); + /* * Since this is called by a timer, we should never get here in * the manual case. */ - if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) { - DRM_DEBUG_DRIVER("downclocking LVDS\n"); + DRM_DEBUG_DRIVER("downclocking LVDS\n"); - assert_panel_unlocked(dev_priv, pipe); + assert_panel_unlocked(dev_priv, crtc->pipe); + I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DISPLAY_RATE_SELECT_FPA1); +} - dpll |= DISPLAY_RATE_SELECT_FPA1; - I915_WRITE(dpll_reg, dpll); - intel_wait_for_vblank(dev, pipe); - dpll = I915_READ(dpll_reg); - if (!(dpll & DISPLAY_RATE_SELECT_FPA1)) - DRM_DEBUG_DRIVER("failed to downclock LVDS!\n"); - } +static void intel_decrease_pllclock(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (HAS_PCH_SPLIT(dev) || HAS_PIPE_CXSR(dev)) + return; + + if (!dev_priv->lvds_downclock_avail || !intel_crtc->lowfreq_avail) + return; + if (intel_crtc_add_vblank_task(intel_crtc, true, + __intel_crtc_decrease_pllclock, + NULL)) + __intel_crtc_decrease_pllclock(intel_crtc, NULL); } /** @@ -7835,7 +7856,6 @@ void intel_modeset_cleanup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; - struct intel_crtc *intel_crtc; /* Clear the vblank worker prior to taking any locks */ flush_scheduled_work(); @@ -7851,8 +7871,7 @@ void intel_modeset_cleanup(struct drm_device *dev) if (!crtc->fb) continue; - intel_crtc = to_intel_crtc(crtc); - intel_increase_pllclock(crtc); + intel_crtc_restore_pllclock(crtc); } intel_disable_fbc(dev); @@ -7880,10 +7899,8 @@ void intel_modeset_cleanup(struct drm_device *dev) flush_scheduled_work(); /* Shut off idle work before the crtcs get freed. */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - intel_crtc = to_intel_crtc(crtc); - del_timer_sync(&intel_crtc->idle_timer); - } + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + del_timer_sync(&to_intel_crtc(crtc)->idle_timer); del_timer_sync(&dev_priv->idle_timer); cancel_work_sync(&dev_priv->idle_work); -- 1.7.10