@@ -923,6 +923,8 @@ struct intel_gen6_power_mgmt {
bool enabled;
struct delayed_work delayed_resume_work;
+ bool gfx_clk_down;
+
/*
* Protects RPS/RC6 register access and PCU communication.
* Must be taken after struct_mutex if nested.
@@ -4974,6 +4974,10 @@
GEN6_PM_RP_DOWN_THRESHOLD | \
GEN6_PM_RP_DOWN_TIMEOUT)
+#define VLV_GTLC_SURVIVABILITY_REG 0x130098
+#define VLV_GFX_CLK_STATUS_BIT (1<<3)
+#define VLV_GFX_CLK_FORCE_ON_BIT (1<<2)
+
#define GEN6_GT_GFX_RC6_LOCKED 0x138104
#define VLV_COUNTER_CONTROL 0x138104
#define VLV_COUNT_RANGE_HIGH (1<<15)
@@ -3581,12 +3581,70 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
trace_intel_gpu_freq_change(val * 50);
}
+/* vlv_set_rps_idle: Set the frequency to Rpe if Gfx clocks are down
+ *
+ * If Gfx clock is UP, then reset the timer as there is a possibility
+ * that normal Turbo logic can bring down the freq to Rpe.
+ * If Gfx clock is Down, then
+ * 1. Mask Turbo interrupts
+ * 2. Bring up Gfx clock
+ * 3. Change the freq to Rpe and wait till P-Unit updates freq
+ * 4. Clear the Force GFX CLK ON bit so that Gfx can down
+ * 5. Unmask Turbo interrupts
+*/
+static void vlv_set_rps_idle(struct drm_i915_private *dev_priv)
+{
+
+ /*
+ * When we are idle. Drop to min voltage state.
+ * Note: we use RPe here since it should match the
+ * Vmin we were shooting for. That should give us better
+ * perf when we come back out of RC6 than if we used the
+ * min freq available.
+ */
+
+ if (dev_priv->rps.cur_delay <= dev_priv->rps.rpe_delay)
+ return;
+
+ if (!(I915_READ(VLV_GTLC_SURVIVABILITY_REG) & VLV_GFX_CLK_STATUS_BIT))
+ dev_priv->rps.gfx_clk_down = true;
+
+ /* Mask turbo interrupt so that they will not come in between */
+ if (dev_priv->rps.gfx_clk_down)
+ I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
+
+ /* Bring up the Gfx clock */
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG) |
+ VLV_GFX_CLK_FORCE_ON_BIT);
+
+ if (wait_for_atomic(((VLV_GFX_CLK_STATUS_BIT &
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG)) != 0), 500)) {
+ DRM_ERROR("GFX_CLK_ON request timed out\n");
+ return;
+ }
+
+ valleyview_set_rps(dev_priv->dev, dev_priv->rps.rpe_delay);
+
+ /* Release the Gfx clock */
+ I915_WRITE(VLV_GTLC_SURVIVABILITY_REG,
+ I915_READ(VLV_GTLC_SURVIVABILITY_REG) &
+ ~VLV_GFX_CLK_FORCE_ON_BIT);
+
+ /* Unmask Turbo interrupts */
+ if (dev_priv->rps.gfx_clk_down)
+ I915_WRITE(GEN6_PMINTRMSK, ~GEN6_PM_RPS_EVENTS);
+
+ dev_priv->rps.gfx_clk_down = false;
+}
+
+
void gen6_rps_idle(struct drm_i915_private *dev_priv)
{
mutex_lock(&dev_priv->rps.hw_lock);
if (dev_priv->rps.enabled) {
if (dev_priv->info->is_valleyview)
- valleyview_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
+ vlv_set_rps_idle(dev_priv);
else
gen6_set_rps(dev_priv->dev, dev_priv->rps.min_delay);
dev_priv->rps.last_adj = 0;
@@ -4826,6 +4884,7 @@ void intel_gpu_ips_teardown(void)
i915_mch_dev = NULL;
spin_unlock_irq(&mchdev_lock);
}
+
static void intel_init_emon(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;