[Intel-gfx] [PATCH]DRM i915: IGD big FIFO support

Shaohua Li shaohua.li at intel.com
Mon May 25 04:28:36 CEST 2009


On Sat, May 23, 2009 at 03:39:51AM +0800, Eric Anholt wrote:
> On Mon, 2009-05-18 at 10:44 +0800, Shaohua Li wrote:
> > +	addr = ioremap(mch_bar, HOSTBAR_SIZE);
> > +	if (!addr)
> > +		return -ENOMEM;
> > +	tmp = *(u32 *)(addr + 0xc00);
> > +	iounmap(addr);
> 
> MCHBAR mapping is tricky.  I think we'll need to use the same code here
> as i915_gem_tiling.c, and that's changing in 2.6.31 to successfully
> enable it (safely) more often.
Makes sense, I fixed the issues you pointed out.


Big FIFO is a feature to put memory into self-refresh mode when CPU
enters C3+ state. Gfx has a FIFO to buffer memory access, when the
watermark of the FIFO is under threshold, Gfx doesn't need access
memory, so at that time memory can be put into self-refresh mode.

The watermark calculation is based on CPU C3 state exit latency. If
watermark is wrong, when CPU enters C3+, display will be broken or
flicker.

I had a power measurement about the feature:
environment: 1920x1400 display, Atom CPU with C4 enabled, system FSB 
is 667 and memory is DDR2 667. Launch X, and gives system several
minutes to settle down, then test the power of the whole system in idle time:
without big fifo, idle power is 19.8w
with it, idle power is 18.6w

The patch doesn't enable HPLL off for CxSR. Last time I heard it's broken
in current IGD chip, if it works, then I'll add it later.

Signed-off-by: Shaohua Li <shaohua.li at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |    2 
 drivers/gpu/drm/i915/i915_reg.h      |    5 
 drivers/gpu/drm/i915/intel_display.c |  225 ++++++++++++++++++++++++++++++++++-
 3 files changed, 231 insertions(+), 1 deletion(-)

Index: linux/drivers/gpu/drm/i915/i915_drv.h
===================================================================
--- linux.orig/drivers/gpu/drm/i915/i915_drv.h	2009-05-25 09:47:21.000000000 +0800
+++ linux/drivers/gpu/drm/i915/i915_drv.h	2009-05-25 10:10:45.000000000 +0800
@@ -195,6 +195,8 @@ typedef struct drm_i915_private {
 	int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
 	int num_fence_regs; /* 8 on pre-965, 16 otherwise */
 
+	bool cxsr_initialized;
+
 	/* Register state */
 	u8 saveLBB;
 	u32 saveDSPACNTR;
Index: linux/drivers/gpu/drm/i915/intel_display.c
===================================================================
--- linux.orig/drivers/gpu/drm/i915/intel_display.c	2009-05-25 09:47:21.000000000 +0800
+++ linux/drivers/gpu/drm/i915/intel_display.c	2009-05-25 10:24:58.000000000 +0800
@@ -786,7 +786,7 @@ intel_pipe_set_base(struct drm_crtc *crt
 }
 
 
-
+static void igd_set_cxsr(struct drm_device *dev, bool on);
 /**
  * Sets the power management mode of the pipe and plane.
  *
@@ -848,8 +848,12 @@ static void intel_crtc_dpms(struct drm_c
 
 		/* Give the overlay scaler a chance to enable if it's on this pipe */
 		//intel_crtc_dpms_video(crtc, true); TODO
+		if (crtc->enabled)
+			igd_set_cxsr(dev, true);
 	break;
 	case DRM_MODE_DPMS_OFF:
+		if (crtc->enabled)
+			igd_set_cxsr(dev, false);
 		/* Give the overlay scaler a chance to disable if it's on this pipe */
 		//intel_crtc_dpms_video(crtc, FALSE); TODO
 
@@ -1030,6 +1034,223 @@ static int intel_panel_fitter_pipe (stru
 	return 1;
 }
 
+struct cxsr_latency {
+	int is_desktop;
+	unsigned long fsb;
+	unsigned long mem_freq;
+	unsigned long display_sr;
+	unsigned long display_hpll_disable;
+	unsigned long cursor_sr;
+	unsigned long cursor_hpll_disable;
+};
+
+static struct cxsr_latency igd_cxsr_latency[] = {
+	{1, 800, 400, 3382, 33382, 3983, 33983},    // DDR2-400 SC
+	{1, 800, 667, 3354, 33354, 3807, 33807},    // DDR2-667 SC
+	{1, 800, 800, 3347, 33347, 3763, 33763},    // DDR2-800 SC
+
+	{1, 667, 400, 3400, 33400, 4021, 34021},    // DDR2-400 SC
+	{1, 667, 667, 3372, 33372, 3845, 33845},    // DDR2-667 SC
+	{1, 667, 800, 3386, 33386, 3822, 33822},    // DDR2-800 SC
+
+	{1, 400, 400, 3472, 33472, 4173, 34173},    // DDR2-400 SC
+	{1, 400, 667, 3443, 33443, 3996, 33996},    // DDR2-667 SC
+	{1, 400, 800, 3430, 33430, 3946, 33946},    // DDR2-800 SC
+
+	{0, 800, 400, 3438, 33438, 4065, 34065},    // DDR2-400 SC
+	{0, 800, 667, 3410, 33410, 3889, 33889},    // DDR2-667 SC
+	{0, 800, 800, 3403, 33403, 3845, 33845},    // DDR2-800 SC
+
+	{0, 667, 400, 3456, 33456, 4103, 34106},    // DDR2-400 SC
+	{0, 667, 667, 3428, 33428, 3927, 33927},    // DDR2-667 SC
+	{0, 667, 800, 3443, 33443, 3905, 33905},    // DDR2-800 SC
+
+	{0, 400, 400, 3528, 33528, 4255, 34255},    // DDR2-400 SC
+	{0, 400, 667, 3500, 33500, 4079, 34079},    // DDR2-667 SC
+	{0, 400, 800, 3487, 33487, 4029, 34029},    // DDR2-800 SC
+};
+
+struct watermark_igd_parameter {
+	unsigned long fifo_size;
+	unsigned long max_wm;
+	unsigned long default_wm;
+	unsigned long guard_size;
+};
+
+#define IGD_DISPLAY_FIFO 512 /* in 64byte unit */
+#define IGD_MAX_WM 0x1ff
+#define IGD_DFT_WM 0x3f
+#define IGD_DFT_HPLLOFF_WM 0
+#define IGD_GUARD_WM 10
+#define IGD_CURSOR_FIFO 64
+#define IGD_CURSOR_MAX_WM 0x3f
+#define IGD_CURSOR_DFT_WM 0
+#define IGD_CURSOR_GUARD_WM 5
+static struct watermark_igd_parameter display_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_WM, IGD_GUARD_WM};
+static struct watermark_igd_parameter display_hplloff_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_HPLLOFF_WM, IGD_GUARD_WM};
+static struct watermark_igd_parameter cursor_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+static struct watermark_igd_parameter cursor_hplloff_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+
+static unsigned long cxsr_igd_calculate_wm(unsigned long clock_in_khz,
+		struct watermark_igd_parameter *wm, unsigned long latency)
+{
+	unsigned long bytes_required, wm_size;
+
+	/* always use 32bpp */
+	bytes_required = clock_in_khz * 4 * latency/1000000;
+	bytes_required /= 64;
+	wm_size = wm->fifo_size - bytes_required - wm->guard_size;
+
+	if (wm_size > wm->max_wm)
+		wm_size = wm->max_wm;
+	if (wm_size == 0)
+		wm_size = wm->default_wm;
+	return wm_size;
+}
+
+static void igd_set_cxsr(struct drm_device *dev, bool on)
+{
+	u32 reg;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!IS_IGD(dev) || !dev_priv->cxsr_initialized)
+		return;
+	if (on) {
+		/* activate cxsr */
+		reg = I915_READ(DSPFW3);
+		reg |= 1 << 30;
+		I915_WRITE(DSPFW3, reg);
+	} else {
+		/* deactivate cxsr */
+		reg = I915_READ(DSPFW3);
+		reg &= ~(1 << 30);
+		I915_WRITE(DSPFW3, reg);
+	}
+}
+
+static int igd_get_freq(struct drm_device *dev, unsigned long *fsb,
+	unsigned long *mem)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	u32 tmp;
+
+	tmp = I915_READ(CLKCFG);
+
+	switch (tmp & 0x7) {
+	case 1:
+		*fsb = 533; /* 133*4 */
+		break;
+	case 2:
+		*fsb = 800; /* 200*4 */
+		break;
+	case 3:
+		*fsb = 667; /* 167*4 */
+		break;
+	case 5:
+		*fsb = 400; /* 100*4 */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch ((tmp >> 4) & 0x7) {
+	case 1:
+		*mem = 533;
+		break;
+	case 2:
+		*mem = 667;
+		break;
+	case 3:
+		*mem = 800;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void igd_setup_cxsr(struct drm_device *dev, unsigned long clock)
+{
+	u32 reg;
+	unsigned long wm;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct cxsr_latency *latency;
+	unsigned long fsb, mem;
+	int i;
+	int pipes_enabled = 0;
+	struct drm_crtc *crtc;
+
+	if (!IS_IGD(dev))
+		return;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		if (crtc->enabled)
+			pipes_enabled++;
+	}
+	if (pipes_enabled != 1) {
+		DRM_DEBUG("Two pipes are enabled, disable CxSR\n");
+		goto disable_cxsr;
+	}
+
+	if (igd_get_freq(dev, &fsb, &mem))
+		goto disable_cxsr;
+
+	for (i = 0; i < ARRAY_SIZE(igd_cxsr_latency); i++) {
+		latency = &igd_cxsr_latency[i];
+		if ((IS_IGDG(dev)) == latency->is_desktop &&
+			fsb == latency->fsb && mem == latency->mem_freq)
+			break;
+	}
+	if (i >= ARRAY_SIZE(igd_cxsr_latency)) {
+		DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+		goto disable_cxsr;
+	}
+
+	/* Display SR */
+	wm = cxsr_igd_calculate_wm(clock, &display_wm, latency->display_sr);
+	reg = I915_READ(DSPFW1);
+	reg &= 0x7fffff;
+	reg |= wm << 23;
+	I915_WRITE(DSPFW1, reg);
+	DRM_DEBUG("DSPFW1 register is %x\n", reg);
+
+	/* cursor SR */
+	wm = cxsr_igd_calculate_wm(clock, &cursor_wm, latency->cursor_sr);
+	reg = I915_READ(DSPFW3);
+	reg &= ~(0x3f << 24);
+	reg |= (wm & 0x3f) << 24;
+	I915_WRITE(DSPFW3, reg);
+
+	/* Display HPLL off SR */
+	wm = cxsr_igd_calculate_wm(clock, &display_hplloff_wm,
+			latency->display_hpll_disable);
+	reg = I915_READ(DSPFW3);
+	reg &= 0xfffffe00;
+	reg |= wm & 0x1ff;
+	I915_WRITE(DSPFW3, reg);
+
+	/* cursor HPLL off SR */
+	wm = cxsr_igd_calculate_wm(clock, &cursor_hplloff_wm,
+			latency->cursor_hpll_disable);
+	reg = I915_READ(DSPFW3);
+	reg &= ~(0x3f << 16);
+	reg |= (wm & 0x3f) << 16;
+	I915_WRITE(DSPFW3, reg);
+	DRM_DEBUG("DSPFW3 register is %x\n", reg);
+
+	dev_priv->cxsr_initialized = true;
+
+	DRM_INFO("Big FIFO is enabled\n");
+	return;
+disable_cxsr:
+	dev_priv->cxsr_initialized = false;
+	DRM_INFO("Big FIFO is disabled\n");
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode,
@@ -1318,6 +1539,8 @@ static int intel_crtc_mode_set(struct dr
 	if (ret != 0)
 	    return ret;
 
+	igd_setup_cxsr(dev, clock.dot);
+
 	drm_vblank_post_modeset(dev, pipe);
 
 	return 0;
Index: linux/drivers/gpu/drm/i915/i915_reg.h
===================================================================
--- linux.orig/drivers/gpu/drm/i915/i915_reg.h	2009-05-25 09:47:21.000000000 +0800
+++ linux/drivers/gpu/drm/i915/i915_reg.h	2009-05-25 10:12:19.000000000 +0800
@@ -561,6 +561,7 @@
 #define C0DRB3			0x10206
 #define C1DRB3			0x10606
 
+#define CLKCFG			0x10c00
 /** GM965 GM45 render standby register */
 #define MCHBAR_RENDER_STANDBY	0x111B8
 
@@ -1381,6 +1382,10 @@
 #define   DSPARB_CSTART_SHIFT	7
 #define   DSPARB_BSTART_MASK	(0x7f)
 #define   DSPARB_BSTART_SHIFT	0
+
+#define DSPFW1			0x70034
+#define DSPFW2			0x70038
+#define DSPFW3			0x7003c
 /*
  * The two pipe frame counter registers are not synchronized, so
  * reading a stable value is somewhat tricky. The following code



More information about the Intel-gfx mailing list