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

Jesse Barnes jbarnes at virtuousgeek.org
Thu Jun 18 02:41:04 CEST 2009


On Wed, 10 Jun 2009 14:52:55 +0800
Shaohua Li <shaohua.li at intel.com> wrote:

> On Wed, Jun 10, 2009 at 06:05:19AM +0800, Jesse Barnes wrote:
> > On Tue, 2 Jun 2009 15:49:27 +0800
> > Shaohua Li <shaohua.li at intel.com> wrote:
> > 
> > > On Mon, Jun 01, 2009 at 05:26:01PM +0800, Jesse Barnes wrote:
> > > > On Mon, 25 May 2009 10:28:36 +0800
> > > > Shaohua Li <shaohua.li at intel.com> wrote:
> > > > 
> > > > > 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.
> > > > 
> > > > Shaohua, can you take a look at this patch and see if it makes
> > > > sense to include in yours?  I think we could probably share the
> > > > latency tables & math (haven't checked mine in this patch yet,
> > > > it's untested on platforms needing FIFO adjustment) at the very
> > > > least...
> > > 
> > > the latency talbe might be ok. the math looks different. My patch
> > > is using clock*pixel_size*latency/cachline_size for SR WM, but
> > > looks yours takes a different approach for SR WM. does the math
> > > depends on chipset? The register write is different. Most fields
> > > of FW_BLC and FW_BLC_SELF is reserved in IGD and my patch is
> > > using FW1-FW3.
> > 
> > No, the SR WM should probably be shared; you have both docs right?
> > It's quite possible I have the wrong formula.
> > 
> > A few comments to help integrate things:
> >   - the DPMS callout should be like mine, just call it
> >     update_watermarks or something instead
> >   - the new IGD_* constants should be in i915_reg.h probably
> >   - the read frequency function should use new defines for all the
> >     registers read/written
> >   - see anholt's comment about MCHBAR; we probably want to put this
> > in the tiling.c file and run it at load time rather than mode set
> > time
> >   - the update_watermarks should either be a function pointer to set
> >     the chip specific watermark regs or should handle all cases
> > along the lines of my patch
> > 
> > I'd like to get both bits of code into 2.6.31 though, so we should
> > get them integrated soon.
> I did some cleanup and try to make it (hopefully) generic. please
> check how to integrate yours.

Here's the patch I've been testing...  In looking at integrating the
code I found some bugs:
  - intel_crtc->plane wasn't being set properly
  - the FIFO entry calculation was overflowing
  - the CXSR code assumes only one pipe is on (is this safe?)
  - watermark update needs to be called at dpms on/off time as well,
    since we could go from a one/two pipe config to one/two/zero at
    that time

I think there's room for integration in my update_watermarks function.
I have if (...) blocks for the various chip types, we could probably
put IGD there, and split out the actual register writes into separate
functions.  I've also got an if (...) block for a single pipe
configuration, which is where we'd want to update the self-refresh
anyway, right?  Also, it looks like our entry calculation could be
mostly unified...  I'd just need to set guards & minimums like you have
(which is a good idea).

What do you think?


-- 
Jesse Barnes, Intel Open Source Technology Center

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 6bc716d..8b1c329 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1086,6 +1086,44 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
 	master->driver_priv = NULL;
 }
 
+static void i915_get_mem_freq(struct drm_device *dev)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	u32 tmp;
+
+	if (!IS_IGD(dev))
+		return;
+
+	tmp = I915_READ(CLKCFG);
+
+	switch (tmp & 0x7) {
+	case 1:
+		dev_priv->fsb_freq = 533; /* 133*4 */
+		break;
+	case 2:
+		dev_priv->fsb_freq = 800; /* 200*4 */
+		break;
+	case 3:
+		dev_priv->fsb_freq =  667; /* 167*4 */
+		break;
+	case 5:
+		dev_priv->fsb_freq = 400; /* 100*4 */
+		break;
+	}
+
+	switch ((tmp >> 4) & 0x7) {
+	case 1:
+		dev_priv->mem_freq = 533;
+		break;
+	case 2:
+		dev_priv->mem_freq = 667;
+		break;
+	case 3:
+		dev_priv->mem_freq = 800;
+		break;
+	}
+}
+
 /**
  * i915_driver_load - setup chip and create an initial config
  * @dev: DRM device
@@ -1176,6 +1214,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 			goto out_iomapfree;
 	}
 
+	i915_get_mem_freq(dev);
+
 	/* On the 945G/GM, the chipset reports the MSI capability on the
 	 * integrated graphics even though the support isn't actually there
 	 * according to the published specs.  It doesn't appear to function
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 8ef6bce..c6bc06c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -209,6 +209,9 @@ 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;
+	unsigned int fsb_freq, mem_freq;
+
 	/* Register state */
 	u8 saveLBB;
 	u32 saveDSPACNTR;
@@ -856,7 +859,21 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 						      IS_I915GM(dev)))
 #define SUPPORTS_INTEGRATED_HDMI(dev)	(IS_G4X(dev) || IS_IGDNG(dev))
 #define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
+/* dsparb controlled by hw only */
+#define DSPARB_HWCONTROL(dev) (IS_G4X(dev))
 
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
 
+/* FIFO watermark */
+#define I915_FIFO_LINE_SIZE 64
+#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
+
 #endif
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index b86b7b7..d63264b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -323,11 +323,15 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
 		 * Clear the PIPE(A|B)STAT regs before the IIR
 		 */
 		if (pipea_stats & 0x8000ffff) {
+			if (pipea_stats &  PIPE_FIFO_UNDERRUN_STATUS)
+				DRM_ERROR("pipe a underrun\n");
 			I915_WRITE(PIPEASTAT, pipea_stats);
 			irq_received = 1;
 		}
 
 		if (pipeb_stats & 0x8000ffff) {
+			if (pipeb_stats &  PIPE_FIFO_UNDERRUN_STATUS)
+				DRM_ERROR("pipe b underrun\n");
 			I915_WRITE(PIPEBSTAT, pipeb_stats);
 			irq_received = 1;
 		}
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index f6237a0..5bd18d4 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -261,7 +261,12 @@
 #define INSTPM	        0x020c0
 #define ACTHD	        0x020c8
 #define FW_BLC		0x020d8
+#define FW_BLC2	 	0x020dc
 #define FW_BLC_SELF	0x020e0 /* 915+ only */
+#define MM_BURST_LENGTH     0x00700000
+#define MM_FIFO_WATERMARK   0x0001F000
+#define LM_BURST_LENGTH     0x00000700
+#define LM_FIFO_WATERMARK   0x0000001F
 #define MI_ARB_STATE	0x020e4 /* 915+ only */
 #define CACHE_MODE_0	0x02120 /* 915+ only */
 #define   CM0_MASK_SHIFT          16
@@ -569,6 +574,7 @@
 #define C0DRB3			0x10206
 #define C1DRB3			0x10606
 
+#define CLKCFG			0x10c00
 /** GM965 GM45 render standby register */
 #define MCHBAR_RENDER_STANDBY	0x111B8
 
@@ -1552,6 +1558,12 @@
 #define   DSPARB_CSTART_SHIFT	7
 #define   DSPARB_BSTART_MASK	(0x7f)
 #define   DSPARB_BSTART_SHIFT	0
+#define   DSPARB_BEND_SHIFT	9 /* on 855 */
+#define   DSPARB_AEND_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
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b32a51f..c5736c8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -25,6 +25,7 @@
  */
 
 #include <linux/i2c.h>
+#include <linux/kernel.h>
 #include "drmP.h"
 #include "intel_drv.h"
 #include "i915_drm.h"
@@ -33,6 +34,8 @@
 #include "drm_crtc_helper.h"
 
 bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+static void intel_update_watermarks(struct drm_device *dev,
+				    unsigned long clock);
 
 typedef struct {
     /* given values */
@@ -921,7 +924,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	int pipe = intel_crtc->pipe;
-	int plane = intel_crtc->pipe;
+	int plane = intel_crtc->plane;
 	int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
 	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
 	int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
@@ -1251,8 +1254,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 
 		/* Give the overlay scaler a chance to enable if it's on this pipe */
 		//intel_crtc_dpms_video(crtc, true); TODO
+		intel_update_watermarks(dev, crtc->mode.clock);
 	break;
 	case DRM_MODE_DPMS_OFF:
+		intel_update_watermarks(dev, crtc->mode.clock);
 		/* Give the overlay scaler a chance to disable if it's on this pipe */
 		//intel_crtc_dpms_video(crtc, FALSE); TODO
 
@@ -1431,7 +1436,6 @@ static int intel_get_core_clock_speed(struct drm_device *dev)
 	return 0; /* Silence gcc warning */
 }
 
-
 /**
  * Return the pipe currently connected to the panel fitter,
  * or -1 if the panel fitter is not present or not in use
@@ -1501,6 +1505,383 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes,
 }
 
 
+struct intel_watermark_parameter {
+	unsigned long fifo_size;
+	unsigned long max_wm;
+	unsigned long default_wm;
+	unsigned long guard_size;
+};
+
+static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
+		struct intel_watermark_parameter *wm, unsigned long latency,
+		int line_size)
+{
+	unsigned long bytes_required, wm_size;
+
+	/* always use 32bpp */
+	bytes_required = clock_in_khz * 4 * latency/1000000;
+	bytes_required /= line_size;
+	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;
+}
+
+struct cxsr_latency {
+	int is_desktop;
+	unsigned long fsb_freq;
+	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 cxsr_latency_table[] = {
+	{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 */
+};
+
+static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop,
+	int fsb, int mem)
+{
+	int i;
+	struct cxsr_latency *latency;
+
+	if (fsb == 0 || mem == 0)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) {
+		latency = &cxsr_latency_table[i];
+		if (is_desktop == latency->is_desktop &&
+			fsb == latency->fsb_freq && mem == latency->mem_freq)
+			break;
+	}
+	if (i >= ARRAY_SIZE(cxsr_latency_table)) {
+		DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+		return NULL;
+	}
+	return latency;
+}
+
+static void intel_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 struct intel_watermark_parameter igd_display_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_WM, IGD_GUARD_WM};
+static struct intel_watermark_parameter igd_display_hplloff_wm =
+{IGD_DISPLAY_FIFO, IGD_MAX_WM, IGD_DFT_HPLLOFF_WM, IGD_GUARD_WM};
+static struct intel_watermark_parameter igd_cursor_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+static struct intel_watermark_parameter igd_cursor_hplloff_wm =
+{IGD_CURSOR_FIFO, IGD_CURSOR_MAX_WM, IGD_CURSOR_DFT_WM, IGD_CURSOR_GUARD_WM};
+
+static void igd_update_watermarks(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;
+	int pipes_enabled = 0;
+	struct drm_crtc *crtc;
+
+	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;
+	}
+
+	latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq,
+		dev_priv->mem_freq);
+	if (!latency) {
+		DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+		goto disable_cxsr;
+	}
+
+	/* Display SR */
+	wm = intel_calculate_wm(clock, &igd_display_wm, latency->display_sr,
+		I915_FIFO_LINE_SIZE);
+	reg = I915_READ(DSPFW1);
+	reg &= 0x7fffff;
+	reg |= wm << 23;
+	I915_WRITE(DSPFW1, reg);
+	DRM_DEBUG("DSPFW1 register is %x\n", reg);
+
+	/* cursor SR */
+	wm = intel_calculate_wm(clock, &igd_cursor_wm, latency->cursor_sr,
+		I915_FIFO_LINE_SIZE);
+	reg = I915_READ(DSPFW3);
+	reg &= ~(0x3f << 24);
+	reg |= (wm & 0x3f) << 24;
+	I915_WRITE(DSPFW3, reg);
+
+	/* Display HPLL off SR */
+	wm = intel_calculate_wm(clock, &igd_display_hplloff_wm,
+		latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
+	reg = I915_READ(DSPFW3);
+	reg &= 0xfffffe00;
+	reg |= wm & 0x1ff;
+	I915_WRITE(DSPFW3, reg);
+
+	/* cursor HPLL off SR */
+	wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm,
+		latency->cursor_hpll_disable, I915_FIFO_LINE_SIZE);
+	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");
+}
+
+const static int latency_us = 5; /* not accurate across all platforms */
+
+static int intel_calc_entries(int clock, int pixel_size, int cacheline_size)
+{
+	unsigned long ret = 0;
+
+	DRM_DEBUG("calculating entries: %d, %d, %d, %d\n", clock, pixel_size,
+		  cacheline_size, latency_us);
+	ret = (clock * pixel_size * latency_us) / cacheline_size;
+	/*
+	 * clock is in khz so we only divide by 1000 to account for
+	 * the latency shift (which is usecs)
+	 */
+	ret /= 1000;
+
+	return ret ? ret : 1; /* minimum of one entry */
+}
+
+/**
+ * i830_update_watermarks - update FIFO watermark values based on current modes
+ *
+ * Calculate watermark values for the various WM regs based on current mode
+ * and plane configuration.
+ *
+ * There are several cases to deal with here:
+ *   - normal (i.e. non-self-refresh)
+ *   - self-refresh (SR) mode
+ *   - lines are large relative to FIFO size (buffer can hold up to 2)
+ *   - lines are small relative to FIFO size (buffer can hold more than 2
+ *     lines), so need to account for TLB latency
+ *
+ *   The normal calculation is:
+ *     watermark = dotclock * bytes per pixel * latency
+ *   where latency is platform & configuration dependent (we assume pessimal
+ *   values here).
+ *
+ *   The SR calculation is:
+ *     watermark = (trunc(latency/line time)+1) * surface width *
+ *       bytes per pixel
+ *   where
+ *     line time = htotal / dotclock
+ *   and latency is assumed to be high, as above.
+ *
+ * The final value programmed to the register should always be rounded up,
+ * and include an extra 2 entries to account for clock crossings.
+ *
+ * We don't use the sprite, so we can ignore that.  And on Crestline we have
+ * to set the non-SR watermarks to 8.
+  */
+static void i830_update_watermarks(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct intel_crtc *intel_crtc;
+	int total_hdisplay = 0, planea_hdisplay = 0, planeb_hdisplay = 0,
+		sr_hdisplay = 0;
+	int planea_entries = 0, planeb_entries = 0, sr_entries = 0;
+	unsigned long planea_dotclock = 0, planeb_dotclock = 0, sr_dotclock = 0;
+	int fifo_entries = 0, line_size = 0, enabled = 0;
+	uint32_t dsparb = I915_READ(DSPARB);
+	int pixel_size = 4;
+
+	if (IS_I965GM(dev) || IS_I945GM(dev)) {
+		fifo_entries = 127;
+		line_size = 64;
+	} else if (IS_I9XX(dev)) {
+		fifo_entries = 95;
+		line_size = 64;
+	} else if (IS_MOBILE(dev)) {
+		fifo_entries = 255;
+		line_size = 32;
+	} else {
+		/* The 845/865 only have a AEND field.  Though the field size
+		 * would allow 128 entries, the 865 rendered the cursor wrong
+		 * then. The BIOS set it up for 96.
+		 */
+		line_size = 32;
+		fifo_entries = 95;
+	}
+
+	/* Get the clock config from both planes */
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		intel_crtc = to_intel_crtc(crtc);
+		if (crtc->enabled) {
+			enabled++;
+			total_hdisplay += crtc->mode.hdisplay;
+			if (intel_crtc->plane == 0) {
+				DRM_DEBUG("plane A (pipe %d) clock: %d\n",
+					  intel_crtc->pipe, crtc->mode.clock);
+				planea_hdisplay = crtc->mode.hdisplay;
+				planea_dotclock = crtc->mode.clock;
+			} else {
+				DRM_DEBUG("plane B (pipe %d) clock: %d\n",
+					  intel_crtc->pipe, crtc->mode.clock);
+				planeb_hdisplay = crtc->mode.hdisplay;
+				planeb_dotclock = crtc->mode.clock;
+			}
+			sr_hdisplay = crtc->mode.hdisplay;
+			sr_dotclock = crtc->mode.clock;
+		}
+	}
+
+	planea_entries = intel_calc_entries(planea_dotclock, pixel_size,
+					    line_size);
+	planeb_entries = intel_calc_entries(planeb_dotclock, pixel_size,
+					    line_size);
+
+	DRM_DEBUG("FIFO entries - A: %d, B: %d\n", planea_entries,
+		  planeb_entries);
+
+	/* Set self refresh values */
+	if (enabled == 1) {
+		unsigned long line_time_us = (sr_hdisplay * 1000000) /
+			sr_dotclock;
+
+		sr_entries = (((latency_us / line_time_us) + 1) * pixel_size *
+			      sr_hdisplay) / 1000000;
+		sr_entries = roundup(sr_entries / line_size, 1);
+	}
+
+	if (IS_I965G(dev)) {
+		DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
+
+		/* 965 has limitations... */
+		I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
+		I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
+	} else if (IS_I9XX(dev) || IS_MOBILE(dev)) {
+		uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
+		uint32_t fwater_hi = I915_READ(FW_BLC2) & LM_FIFO_WATERMARK;
+		int bsize, asize, cwm, bwm = 1, awm = 1, srwm = 1;
+
+		if (IS_I9XX(dev)) {
+			asize = dsparb & 0x7f;
+			bsize = (dsparb >> DSPARB_CSTART_SHIFT) & 0x7f;
+		} else {
+			asize = dsparb & 0x1ff;
+			bsize = (dsparb >> DSPARB_BEND_SHIFT) & 0x1ff;
+		}
+		DRM_DEBUG("FIFO size - A: %d, B: %d\n", asize, bsize);
+
+		/* Two extra entries for padding */
+		awm = asize - (planea_entries + 2);
+		bwm = bsize - (planeb_entries + 2);
+
+		/* Sanity check against potentially bad FIFO allocations */
+		if (awm <= 0) {
+			/* pipe is on but has too few FIFO entries */
+			if (planea_entries != 0)
+				DRM_DEBUG("plane A needs more FIFO entries\n");
+			awm = 1;
+		}
+		if (bwm <= 0) {
+			if (planeb_entries != 0)
+				DRM_DEBUG("plane B needs more FIFO entries\n");
+			bwm = 1;
+		}
+
+		/*
+		 * Overlay gets an aggressive default since video jitter is bad.
+		 */
+		cwm = 2;
+		if (sr_entries < fifo_entries)
+			srwm = fifo_entries - sr_entries;
+
+		DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
+			   awm, bwm, cwm, srwm);
+
+		fwater_lo = fwater_lo | ((bwm & 0x1f) << 16) | (awm & 0x1f);
+		fwater_hi = fwater_hi | (cwm & 0x1f);
+
+		I915_WRITE(FW_BLC, fwater_lo);
+		I915_WRITE(FW_BLC2, fwater_hi);
+		if (IS_I9XX(dev))
+			I915_WRITE(FW_BLC_SELF, srwm & 0x3f);
+	} else {
+		uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
+		unsigned int asize, awm;
+
+		asize = dsparb & 0x7f;
+
+		awm = asize - planea_entries;
+
+		fwater_lo = fwater_lo | awm;
+
+		I915_WRITE(FW_BLC, fwater_lo);
+	}
+}
+
+static void intel_update_watermarks(struct drm_device *dev, unsigned long clock)
+{
+	if (IS_IGD(dev))
+		igd_update_watermarks(dev, clock);
+	else if (!DSPARB_HWCONTROL(dev))
+		i830_update_watermarks(dev);
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode,
@@ -1859,6 +2240,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
 	/* Flush the plane changes */
 	ret = intel_pipe_set_base(crtc, x, y, old_fb);
+
+	intel_update_watermarks(dev, clock.dot);
+
 	drm_vblank_post_modeset(dev, pipe);
 
 	return ret;
@@ -2345,6 +2729,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
 	drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
 	intel_crtc->pipe = pipe;
+	intel_crtc->plane = pipe;
 	for (i = 0; i < 256; i++) {
 		intel_crtc->lut_r[i] = i;
 		intel_crtc->lut_g[i] = i;




More information about the Intel-gfx mailing list