[Intel-gfx] [PATCH] drm/i915: render p-state support for Sandybridge

Jesse Barnes jbarnes at virtuousgeek.org
Fri Dec 10 00:00:46 CET 2010


Note: this patch is just for comments/testing, it should not be
integrated at this time.

This seems to be enough to initialize render p-state support and get
up/down interrupts (though I've only confirmed down interrupts so
far).  There's code in there for requesting a frequency change, but it
seems to be ignored (at least according to GT_PERF_STATUS; the debugfs
file never indicates that a frequency change has occurred).

Still working with the hw guys on resolving that, but I thought I'd
post this for reference so that others with Sandybridge systems could
try it out and report their results.

Thanks,
-- 
Jesse Barnes, Intel Open Source Technology Center

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index 1f4f3ce..f25e960 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -658,15 +658,42 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
 	struct drm_info_node *node = (struct drm_info_node *) m->private;
 	struct drm_device *dev = node->minor->dev;
 	drm_i915_private_t *dev_priv = dev->dev_private;
-	u16 rgvswctl = I915_READ16(MEMSWCTL);
-	u16 rgvstat = I915_READ16(MEMSTAT_ILK);
-
-	seq_printf(m, "Requested P-state: %d\n", (rgvswctl >> 8) & 0xf);
-	seq_printf(m, "Requested VID: %d\n", rgvswctl & 0x3f);
-	seq_printf(m, "Current VID: %d\n", (rgvstat & MEMSTAT_VID_MASK) >>
-		   MEMSTAT_VID_SHIFT);
-	seq_printf(m, "Current P-state: %d\n",
-		   (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
+
+	if (IS_GEN5(dev)) {
+		u16 rgvswctl = I915_READ16(MEMSWCTL);
+		u16 rgvstat = I915_READ16(MEMSTAT_ILK);
+
+		seq_printf(m, "Requested P-state: %d\n", (rgvswctl >> 8) & 0xf);
+		seq_printf(m, "Requested VID: %d\n", rgvswctl & 0x3f);
+		seq_printf(m, "Current VID: %d\n", (rgvstat & MEMSTAT_VID_MASK) >>
+			   MEMSTAT_VID_SHIFT);
+		seq_printf(m, "Current P-state: %d\n",
+			   (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
+	} else if (IS_GEN6(dev)) {
+		u32 gt_perf_status = I915_READ(0x145948);
+		u32 rp_state_limits = I915_READ(0x145994);
+		u32 rp_state_cap = I915_READ(0x145998);
+		int max_freq;
+
+		seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
+		seq_printf(m, "Render p-state ratio: %d\n",
+			   (gt_perf_status & 0xff00) >> 8);
+		seq_printf(m, "Render p-state VID: %d\n",
+			   gt_perf_status & 0xff);
+		seq_printf(m, "Render p-state limit: %d\n",
+			   rp_state_limits & 0xff);
+
+		max_freq = (rp_state_cap & 0xff0000) >> 16;
+		seq_printf(m, "Max RPN frequency: %dMHz\n", max_freq * 100);
+
+		max_freq = (rp_state_cap & 0xff00) >> 8;
+		seq_printf(m, "Max RP1 frequency: %dMHz\n", max_freq * 100);
+
+		max_freq = rp_state_cap & 0xff;
+		seq_printf(m, "Max RP0 frequency: %dMHz\n", max_freq * 100);
+	} else {
+		seq_printf(m, "no P-state info available\n");
+	}
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 80745f8..d0eeaae 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -509,6 +509,7 @@ i915_pci_remove(struct pci_dev *pdev)
 	struct drm_device *dev = pci_get_drvdata(pdev);
 
 	drm_put_dev(dev);
+	pci_disable_device(pdev);
 }
 
 static int i915_pm_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 90414ae..6c5a1c4 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -186,6 +186,7 @@ struct drm_i915_display_funcs {
 	void (*update_wm)(struct drm_device *dev, int planea_clock,
 			  int planeb_clock, int sr_hdisplay, int sr_htotal,
 			  int pixel_size);
+	void (*handle_rps_change)(struct drm_device *dev);
 	/* clock updates for mode set */
 	/* cursor updates */
 	/* render clock increase/decrease */
@@ -1161,6 +1162,7 @@ extern void intel_disable_fbc(struct drm_device *dev);
 extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
 extern bool intel_fbc_enabled(struct drm_device *dev);
 extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
+extern void sandybridge_set_drps(struct drm_device *dev, u8 val);
 extern void intel_detect_pch (struct drm_device *dev);
 extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
 
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 62aa93a..9d828ef 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -306,6 +306,38 @@ static void notify_ring(struct drm_device *dev,
 		  jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
 }
 
+static void sandybridge_pm_irq_handler(struct drm_device *dev)
+{
+	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+	u8 new_delay = dev_priv->cur_delay;
+	u32 pm_iir;
+
+	pm_iir = I915_READ(PMIIR);
+	if (!pm_iir)
+		return;
+
+	DRM_ERROR("received PM interrupt: 0x%08x\n", pm_iir);
+	DRM_ERROR("  ISR: 0x%08x, IMR: 0x%08x, IER: 0x%08x\n",
+		  I915_READ(PMISR), I915_READ(PMIMR), I915_READ(PMIER));
+
+	if (pm_iir & PM_RP_UP) {
+		if (dev_priv->cur_delay != dev_priv->max_delay)
+			new_delay = dev_priv->cur_delay + 1;
+		if (new_delay > dev_priv->max_delay)
+			new_delay = dev_priv->max_delay;
+	} else if (pm_iir & PM_RP_DOWN) {
+		if (dev_priv->cur_delay != dev_priv->min_delay)
+			new_delay = dev_priv->cur_delay - 1;
+		if (new_delay < dev_priv->min_delay)
+			new_delay = dev_priv->min_delay;
+	}
+
+	sandybridge_set_drps(dev, new_delay);
+	dev_priv->cur_delay = new_delay;
+
+	I915_WRITE(PMIIR, pm_iir);
+}
+
 static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
 {
 	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -379,6 +411,9 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
 		i915_handle_rps_change(dev);
 	}
 
+	if (IS_GEN6(dev))
+		sandybridge_pm_irq_handler(dev);
+
 	/* should clear PCH hotplug event before clear CPU irq */
 	I915_WRITE(SDEIIR, pch_iir);
 	I915_WRITE(GTIIR, gt_iir);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8415950..6bc0c21 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -888,6 +888,48 @@
 #define PALETTE_A		0x0a000
 #define PALETTE_B		0x0a800
 
+/*
+ * Sandybridge PM regs
+ */
+#define RPNSWREQ		0x0a008
+#define RPVSWREQ		0x0a00c
+#define RPDNTIMOUT		0x0a010 /* in usec */
+#define RPINTLIM		0x0a014
+#define RPSTAT1			0x0a01c
+#define RPMODECTL1		0x0a024
+#define RPINCLIMIT		0x0a02c
+#define RPDECLIMIT		0x0a030
+#define RPCURUPEI		0x0a050
+#define RPCURUP			0x0a054
+#define RPPREVUP		0x0a058
+#define RPCURDNEI		0x0a05c
+#define RPCURDN			0x0a060
+#define RPPREVDN		0x0a064
+#define RPUPEI			0x0a068
+#define RPDNEI			0x0a06c
+#define GPMPIHYST		0x0a070
+#define RPDEUCSW		0x0a088
+#define RCCTL1			0x0a090
+#define RCCTL2			0x0a094
+#define RC1WRL			0x0a098
+#define RCxWRL			0x0a09c
+#define RCDPSTRC6WRL		0x0a0a0
+#define RCWC			0x0a0a4
+#define RCI			0x0a0a8
+#define RCIHYST			0x0a0ac
+#define RCUBMABDTMR		0x0a0b0
+#define RC1TIMER		0x0a0b4
+#define RC6TIMER		0x0a0b8
+#define RCDEEPRC6TIMER		0x0a0bc
+#define RCDEEPESTRC6TIMER	0x0a0c0
+#define PMINTRMSK		0x0a168
+#define  PMINTR_TOINTRMSK	(1<<6)
+#define  PMINTR_UPINTRMSK	(1<<5)
+#define  PMINTR_DOWNINTRMSK	(1<<4)
+#define  PMINTR_TEMPMSK		(1<<3)
+#define  PMINTR_EIUPINTRMSK	(1<<2)
+#define  PMINTR_EIDNINTRMSK	(1<<1)
+
 /* MCH MMIO space */
 
 /*
@@ -2433,6 +2475,14 @@
 # define VGA_2X_MODE				(1 << 30)
 # define VGA_PIPE_B_SELECT			(1 << 29)
 
+/* Sandybridge PM */
+#define PMISR		0x44020
+#define PMIMR		0x44024
+#define   PM_RP_UP	(1<<5)
+#define   PM_RP_DOWN	(1<<4)
+#define PMIIR		0x44028
+#define PMIER		0x4402c
+
 /* Ironlake */
 
 #define CPU_VGACNTRL	0x41000
diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
index 454c064..39641fd 100644
--- a/drivers/gpu/drm/i915/i915_suspend.c
+++ b/drivers/gpu/drm/i915/i915_suspend.c
@@ -771,8 +771,10 @@ int i915_save_state(struct drm_device *dev)
 		dev_priv->saveIMR = I915_READ(IMR);
 	}
 
-	if (HAS_PCH_SPLIT(dev))
+	if (IS_IRONLAKE_M(dev))
 		ironlake_disable_drps(dev);
+	if (IS_GEN6(dev))
+		sandybridge_disable_drps(dev);
 
 	/* Cache mode state */
 	dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
@@ -862,11 +864,14 @@ int i915_restore_state(struct drm_device *dev)
 	/* Clock gating state */
 	intel_init_clock_gating(dev);
 
-	if (HAS_PCH_SPLIT(dev)) {
+	if (IS_IRONLAKE_M(dev)) {
 		ironlake_enable_drps(dev);
 		intel_init_emon(dev);
 	}
 
+	if (IS_GEN6(dev))
+		sandybridge_enable_drps(dev);
+
 	/* Cache mode state */
 	I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
 
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 48d8fd6..0a6750a 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -5654,6 +5654,92 @@ void ironlake_disable_drps(struct drm_device *dev)
 
 }
 
+void sandybridge_set_drps(struct drm_device *dev, u8 val)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 swreq;
+
+	DRM_ERROR("setting render p-state to %d\n", val);
+
+	swreq = (val & 0x3ff) << 25;
+	I915_WRITE(0xa18c, 1); /* force wake */
+	wait_for_atomic((I915_READ(0x130090) & 1) == 1, 100); /* wait for wake */
+	I915_WRITE(0xa008, swreq);
+	I915_WRITE(0xa18c, 0); /* clear force wake */
+	wait_for_atomic((I915_READ(0x130090) & 1) == 0, 100); /* wait for sleep */
+
+	DRM_ERROR("RPNSWREQ: 0x%08x\n", I915_READ(0xa008));
+}
+
+void sandybridge_enable_drps(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 gt_perf_status = I915_READ(0x145948);
+	u32 rp_state_cap = I915_READ(0x145998);
+	u64 perf_ctl;
+
+	rdmsrl(0x199, perf_ctl);
+	DRM_ERROR("IA32_PERF_CTL: 0x%016llx\n", (unsigned long long)perf_ctl);
+	DRM_ERROR("RPNSWREQ: 0x%08x\n", I915_READ(0xa008));
+
+	I915_WRITE(0xa094, 0); /* set rc0 */
+	I915_WRITE(0xa18c, 1); /* force wake */
+	wait_for((I915_READ(0x130090) & 1) == 1, 100); /* wait for wake */
+	I915_WRITE(0xa090, 0); /* disable hw rc */
+	I915_WRITE(0xa098, 0x3e80000); /* rc1e wake rate limit */
+	I915_WRITE(0xa09c, 0x28001e); /* rc6/6p wake rate limit */
+	I915_WRITE(0xa0a0, 0x1e); /* rc6pp wake rate limit */
+	I915_WRITE(0xa0ac, 0x64); /* idle hysteresis */
+	I915_WRITE(0x2054, 0xa); /* render idle max count */
+	I915_WRITE(0x12054, 0xa); /* video idle max count */
+	I915_WRITE(0x22054, 0xa); /* blitter idle max count */
+	I915_WRITE(0xa0b0, 0); /* unblock ack to busy */
+	I915_WRITE(0xa0b4, 0x3e8); /* rc1e threshold 1ms/EI */
+	I915_WRITE(0xa0b8, 0xc350); /* rc6 threshold 40% */
+	I915_WRITE(0xa0bc, 0x186a0); /* rc6p threshold */
+	I915_WRITE(0xa0c0, 0xfa00); /* rc6pp threshold */
+	I915_WRITE(0xa090, 0x88040000); /* enable rc etc */
+	I915_WRITE(0xa008, 0x14000000); /* enable turbo, request 1GHz .. */
+	I915_WRITE(0xa00c, 0x18000000); /* 1.2GHz video freq */
+	I915_WRITE(0xa010, 0xf4240); /* rp down timeout 1s */
+	I915_WRITE(0xa014, 0x12060000); /* interrupt limits */
+	I915_WRITE(0xa02c, 0x15f90); /* rp up threshold */
+	I915_WRITE(0xa030, 0x186a0); /* rp down threshold */
+	I915_WRITE(0xa068, 0x186a0); /* rp up ei 100ms */
+	I915_WRITE(0xa06c, 0x493e0); /* rp down ei 300ms */
+	I915_WRITE(0xa070, 0xa); /* idle hysteresis 0us */
+	I915_WRITE(0xa024, 0xb92); /* rp control */
+	wait_for((I915_READ(0x138124) & 0x80000000) == 0, 100);
+	I915_WRITE(0x138128, 0);
+	I915_WRITE(0x138124, 0x80000004);
+	wait_for((I915_READ(0x138124) & 0x80000000) == 0, 100);
+	I915_WRITE(0x4402c, 0x3000076); /* enable PM interrupts */
+	I915_WRITE(0xa18c, 0); /* clear force wake */
+	wait_for((I915_READ(0x130090) & 1) == 0, 100); /* wait for sleep */
+
+	dev_priv->max_delay = rp_state_cap & 0xff;
+	dev_priv->min_delay = 0;
+	dev_priv->cur_delay = (gt_perf_status & 0xff00) >> 8;
+
+	DRM_ERROR("DRPS initialized, max freq %dMHz, current %dMHz\n",
+		  dev_priv->max_delay * 100, dev_priv->cur_delay * 100);
+
+	/* Enable the interrupts */
+	I915_WRITE(PMIER, ~0);
+	I915_WRITE(PMIMR, 0);
+	I915_WRITE(PMINTRMSK, 0);
+//	I915_WRITE(PMINTRMSK, PMINTR_TOINTRMSK | PMINTR_UPINTRMSK |
+//		   PMINTR_DOWNINTRMSK | PMINTR_TEMPMSK | PMINTR_EIUPINTRMSK |
+//		   PMINTR_EIDNINTRMSK);
+}
+
+void sandybridge_disable_drps(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	I915_WRITE(0xa008, 1 << 31);
+}
+
 static unsigned long intel_pxfreq(u32 vidfreq)
 {
 	unsigned long freq;
@@ -6137,6 +6223,9 @@ void intel_modeset_init(struct drm_device *dev)
 		intel_init_emon(dev);
 	}
 
+	if (IS_GEN6(dev))
+		sandybridge_enable_drps(dev);
+
 	INIT_WORK(&dev_priv->idle_work, intel_idle_update);
 	setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
 		    (unsigned long)dev);
@@ -6190,6 +6279,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
 	if (IS_IRONLAKE_M(dev))
 		ironlake_disable_drps(dev);
+	if (IS_GEN6(dev))
+		sandybridge_disable_drps(dev);
 
 	mutex_unlock(&dev->struct_mutex);
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 21551fe..c7d86cb 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -296,6 +296,8 @@ extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 extern void intel_init_clock_gating(struct drm_device *dev);
 extern void ironlake_enable_drps(struct drm_device *dev);
 extern void ironlake_disable_drps(struct drm_device *dev);
+extern void sandybridge_enable_drps(struct drm_device *dev);
+extern void sandybridge_disable_drps(struct drm_device *dev);
 extern void intel_init_emon(struct drm_device *dev);
 
 extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,



More information about the Intel-gfx mailing list