We were missing a few bits around power well handling and Gunit save/restore. The code added should be sufficient for runtime D3 as well (though that requires additional changes to how we handle save/restore of state). Signed-off-by: Jesse Barnes <jbarnes@xxxxxxxxxxxxxxxx> --- drivers/gpu/drm/i915/i915_drv.c | 31 +++++++++ drivers/gpu/drm/i915/i915_drv.h | 11 +++ drivers/gpu/drm/i915/i915_gem.c | 5 +- drivers/gpu/drm/i915/i915_reg.h | 8 +++ drivers/gpu/drm/i915/intel_uncore.c | 133 ++++++++++++++++++++++++++++++++---- 5 files changed, 174 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 82a1d53..01ff272 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -459,6 +459,21 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) return 1; } +/* + * On VLV/BYT, transitioning from D0 to a Dx state requires the following + * steps: + * 1) force enable gfx clocks with GTLC survivability reg + * 2) take force wake ref for register saving (really necessary?) + * 3) save Gunit regs + * 4) drop gfx freq to minimum + * 5) drop force wake ref (not needed if we don't need #2) + * 6) clear allow wake bit (prevents further forcewake requests) + * 7) power gate render, media, and display power wells + * 8) release gfx clocks + * Along with the usual steps of idling the GPU, dealing with display state, + * etc. + */ + static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -471,6 +486,7 @@ static int i915_drm_freeze(struct drm_device *dev) /* We do a lot of poking in a lot of registers, make sure they work * properly. */ + intel_uncore_prepare_suspend(dev); hsw_disable_package_c8(dev_priv); intel_set_power_well(dev, true); @@ -515,6 +531,8 @@ static int i915_drm_freeze(struct drm_device *dev) intel_fbdev_set_suspend(dev, FBINFO_STATE_SUSPENDED); console_unlock(); + intel_uncore_suspend(dev); + return 0; } @@ -578,6 +596,17 @@ static void intel_resume_hotplug(struct drm_device *dev) drm_helper_hpd_irq_event(dev); } +/* + * On VLV/BYT, the resume steps from a Dx state are as follows: + * 1) force gfx clocks on + * 2) ungate render, media, display power wells + * 3) restore gunit regs + * 4) set allow wake bit in wake control + * 5) take force wake ref on render & media + * 6) re-enable RC6 + * 7) drop force wake ref + * 8) release gfx clocks + */ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -587,6 +616,8 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings) intel_uncore_sanitize(dev); + intel_uncore_resume(dev); + if (drm_core_check_feature(dev, DRIVER_MODESET) && restore_gtt_mappings) { mutex_lock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4e97840..85cd5be 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -409,6 +409,9 @@ struct intel_uncore_funcs { enum intel_display_power_domain domain); void (*display_power_put)(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); + void (*prepare_suspend)(struct drm_i915_private *dev_priv); + void (*suspend)(struct drm_i915_private *dev_priv); + void (*resume)(struct drm_i915_private *dev_priv); }; struct intel_uncore { @@ -842,6 +845,11 @@ struct i915_suspend_saved_registers { u32 savePIPEB_LINK_N1; u32 saveMCHBAR_RENDER_STANDBY; u32 savePCH_PORT_HOTPLUG; + u32 saveGUNIT_Control; + u32 saveGUNIT_Control2; + u32 saveGUNIT_CZClockGatingDisable1; + u32 saveGUNIT_CZClockGatingDisable2; + u32 saveDPIO_CFG_DATA; }; struct intel_gen6_power_mgmt { @@ -1824,6 +1832,9 @@ extern void intel_pm_init(struct drm_device *dev); extern void intel_hpd_init(struct drm_device *dev); extern void intel_pm_init(struct drm_device *dev); +extern void intel_uncore_prepare_suspend(struct drm_device *dev); +extern void intel_uncore_suspend(struct drm_device *dev); +extern void intel_uncore_resume(struct drm_device *dev); extern void intel_uncore_sanitize(struct drm_device *dev); extern void intel_uncore_early_sanitize(struct drm_device *dev); extern void intel_uncore_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index eb29a23..bd189d6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4470,8 +4470,9 @@ int i915_gem_init(struct drm_device *dev) if (IS_VALLEYVIEW(dev)) { /* VLVA0 (potential hack), BIOS isn't actually waking us */ - I915_WRITE(VLV_GTLC_WAKE_CTRL, 1); - if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & 1) == 1, 10)) + I915_WRITE(VLV_GTLC_WAKE_CTRL, VLV_ALLOW_WAKE_REQ); + if (wait_for((I915_READ(VLV_GTLC_PW_STATUS) & + VLV_ALLOW_WAKE_REQ), 10)) DRM_DEBUG_DRIVER("allow wake ack timed out\n"); } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b64b1a6..cbf3e48 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -791,8 +791,11 @@ #define IIR 0x020a4 #define IMR 0x020a8 #define ISR 0x020ac +#define VLV_GUNIT_CONTROL (VLV_DISPLAY_BASE + 0x2030) +#define VLV_GUNIT_CONTROL2 (VLV_DISPLAY_BASE + 0x2034) #define VLV_GUNIT_CLOCK_GATE (VLV_DISPLAY_BASE + 0x2060) #define GCFG_DIS (1<<8) +#define VLV_GUNIT_CLOCK_GATE2 (VLV_DISPLAY_BASE + 0x2064) #define VLV_IIR_RW (VLV_DISPLAY_BASE + 0x2084) #define VLV_IER (VLV_DISPLAY_BASE + 0x20a0) #define VLV_IIR (VLV_DISPLAY_BASE + 0x20a4) @@ -4644,7 +4647,12 @@ #define FORCEWAKE_ACK_HSW 0x130044 #define FORCEWAKE_ACK 0x130090 #define VLV_GTLC_WAKE_CTRL 0x130090 +#define VLV_ALLOW_WAKE_REQ (1<<0) #define VLV_GTLC_PW_STATUS 0x130094 +#define VLV_ALLOW_WAKE_ACK (1<<0) +#define VLV_GTLC_SURVIVABILITY_REG 0x130098 +#define VLV_GFX_CLK_STATUS (1<<3) +#define VLV_GFX_CLK_FORCE_ON (1<<2) #define FORCEWAKE_MT 0xa188 /* multi-threaded */ #define FORCEWAKE_KERNEL 0x1 #define FORCEWAKE_USER 0x2 diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index ef5d7fd..b126f5a 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -419,10 +419,110 @@ static void vlv_set_media_power(struct drm_i915_private *dev_priv, bool enable) __vlv_set_power_well(dev_priv, MEDIA_PWRGT, enable); } +static void vlv_set_gfx_clock(struct drm_i915_private *dev_priv, bool enable) +{ + u32 reg; + + if (!IS_VALLEYVIEW(dev_priv->dev)) + return; + + reg = I915_READ(VLV_GTLC_SURVIVABILITY_REG); + if (enable) + reg |= VLV_GFX_CLK_FORCE_ON; + else + reg &= ~VLV_GFX_CLK_FORCE_ON; + I915_WRITE(VLV_GTLC_SURVIVABILITY_REG, reg); + if (wait_for_atomic(((VLV_GFX_CLK_STATUS & + I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0), + 100)) { + dev_err(&dev_priv->dev->pdev->dev, + "GFX_CLK_ON timed out, suspend might fail\n"); + } +} + +static void vlv_gunit_save(struct drm_i915_private *dev_priv) +{ + dev_priv->regfile.saveGUNIT_Control = I915_READ(VLV_GUNIT_CONTROL); + dev_priv->regfile.saveGUNIT_Control2 = I915_READ(VLV_GUNIT_CONTROL2); + dev_priv->regfile.saveGUNIT_CZClockGatingDisable1 = + I915_READ(VLV_GUNIT_CLOCK_GATE); + dev_priv->regfile.saveGUNIT_CZClockGatingDisable2 = + I915_READ(VLV_GUNIT_CLOCK_GATE2); + dev_priv->regfile.saveDPIO_CFG_DATA = I915_READ(DPIO_CTL); +} + +static void vlv_gunit_restore(struct drm_i915_private *dev_priv) +{ + I915_WRITE(VLV_GUNIT_CONTROL, dev_priv->regfile.saveGUNIT_Control); + I915_WRITE(VLV_GUNIT_CONTROL2, dev_priv->regfile.saveGUNIT_Control2); + I915_WRITE(VLV_GUNIT_CLOCK_GATE, + dev_priv->regfile.saveGUNIT_CZClockGatingDisable1); + I915_WRITE(VLV_GUNIT_CLOCK_GATE2, + dev_priv->regfile.saveGUNIT_CZClockGatingDisable2); + I915_WRITE(DPIO_CTL, dev_priv->regfile.saveDPIO_CFG_DATA); +} + +static void vlv_uncore_prepare_suspend(struct drm_i915_private *dev_priv) +{ + vlv_set_gfx_clock(dev_priv, true); + vlv_gunit_save(dev_priv); +} + +static void vlv_uncore_suspend(struct drm_i915_private *dev_priv) +{ + u32 reg; + + mutex_lock(&dev_priv->rps.hw_lock); + valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_delay); + mutex_unlock(&dev_priv->rps.hw_lock); + + reg = I915_READ(VLV_GTLC_WAKE_CTRL); + reg &= ~VLV_ALLOW_WAKE_REQ; + I915_WRITE(VLV_GTLC_WAKE_CTRL, reg); + if (wait_for_atomic(!(I915_READ(VLV_GTLC_PW_STATUS) & + VLV_ALLOW_WAKE_ACK), 100)) { + dev_err(&dev_priv->dev->pdev->dev, + "ALLOW_WAKE_SET timed out, suspend might fail\n"); + } + + intel_set_power_well(dev_priv->dev, false); + + vlv_set_display_power(dev_priv, false); + vlv_set_render_power(dev_priv, false); + vlv_set_media_power(dev_priv, false); + + vlv_set_gfx_clock(dev_priv, false); +} + +static void vlv_uncore_resume(struct drm_i915_private *dev_priv) +{ + u32 reg; + + vlv_gunit_restore(dev_priv); + + reg = I915_READ(VLV_GTLC_WAKE_CTRL); + reg |= VLV_ALLOW_WAKE_REQ; + I915_WRITE(VLV_GTLC_WAKE_CTRL, reg); + if (wait_for_atomic((0 != (I915_READ(VLV_GTLC_PW_STATUS) & + VLV_ALLOW_WAKE_ACK)), 100)) { + dev_err(&dev_priv->dev->pdev->dev, + "ALLOW_WAKE_SET timed out, resume might fail\n"); + } + + vlv_set_gfx_clock(dev_priv, false); +} + void intel_uncore_early_sanitize(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (IS_VALLEYVIEW(dev)) { + vlv_set_gfx_clock(dev_priv, true); + vlv_set_display_power(dev_priv, true); + vlv_set_render_power(dev_priv, true); + vlv_set_media_power(dev_priv, true); + } + if (HAS_FPGA_DBG_UNCLAIMED(dev)) __raw_i915_write32(dev_priv, FPGA_DBG, FPGA_DBG_RM_NOCLAIM); } @@ -441,6 +541,9 @@ void intel_uncore_init(struct drm_device *dev) dev_priv->uncore.funcs.display_power_enabled = vlv_display_power_enabled; dev_priv->uncore.funcs.display_power_get = vlv_display_power_get; dev_priv->uncore.funcs.display_power_put = vlv_display_power_put; + dev_priv->uncore.funcs.prepare_suspend = vlv_uncore_prepare_suspend; + dev_priv->uncore.funcs.suspend = vlv_uncore_suspend; + dev_priv->uncore.funcs.resume = vlv_uncore_resume; } else if (IS_HASWELL(dev)) { dev_priv->uncore.funcs.force_wake_get = __gen6_gt_force_wake_mt_get; dev_priv->uncore.funcs.force_wake_put = __gen6_gt_force_wake_mt_put; @@ -512,26 +615,32 @@ static void intel_uncore_forcewake_reset(struct drm_device *dev) void intel_uncore_sanitize(struct drm_device *dev) { - struct drm_i915_private *dev_priv = dev->dev_private; - u32 reg_val; - intel_uncore_forcewake_reset(dev); /* BIOS often leaves RC6 enabled, but disable it for hw init */ intel_disable_gt_powersave(dev); +} - /* Turn off power gate, require especially for the BIOS less system */ - if (IS_VALLEYVIEW(dev)) { - - mutex_lock(&dev_priv->rps.hw_lock); - reg_val = vlv_punit_read(dev_priv, PUNIT_REG_PWRGT_STATUS); - if (reg_val & (RENDER_PWRGT | MEDIA_PWRGT | DISP2D_PWRGT)) - vlv_punit_write(dev_priv, PUNIT_REG_PWRGT_CTRL, 0x0); +void intel_uncore_prepare_suspend(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + if (dev_priv->uncore.funcs.prepare_suspend) + dev_priv->uncore.funcs.prepare_suspend(dev_priv); +} - mutex_unlock(&dev_priv->rps.hw_lock); +void intel_uncore_suspend(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + if (dev_priv->uncore.funcs.suspend) + dev_priv->uncore.funcs.suspend(dev_priv); +} - } +void intel_uncore_resume(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + if (dev_priv->uncore.funcs.resume) + dev_priv->uncore.funcs.resume(dev_priv); } /* -- 1.8.3.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx