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. TODO: The modification of DPLL requires some guards to prevent concurrent access during vblank and modeset. Flushing the vblank handler before modeset sounds like the safest approach. <Find some testers> --- drivers/gpu/drm/i915/intel_display.c | 102 ++++++++++++++++++---------------- 1 files changed, 55 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6c2101e..2d96394 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2240,7 +2240,6 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, return ret; intel_update_fbc(dev); - intel_increase_pllclock(crtc); return 0; } @@ -2319,6 +2318,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, mutex_unlock(&dev->struct_mutex); + intel_increase_pllclock(crtc); if (!dev->primary->master) return 0; @@ -7065,73 +7065,85 @@ 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) { - 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_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); +} + +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, + __intel_crtc_increase_pllclock)) + __intel_crtc_increase_pllclock(intel_crtc); +} + +static void __intel_crtc_decrease_pllclock(struct intel_crtc *crtc) +{ + 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, + __intel_crtc_decrease_pllclock)) + __intel_crtc_decrease_pllclock(intel_crtc); } /** @@ -9234,7 +9246,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(); @@ -9250,8 +9261,7 @@ void intel_modeset_cleanup(struct drm_device *dev) if (!crtc->fb) continue; - intel_crtc = to_intel_crtc(crtc); - intel_increase_pllclock(crtc); + intel_restore_pllclock(crtc); } intel_disable_fbc(dev); @@ -9276,10 +9286,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.9.1