Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7648fa99 authored by Jesse Barnes's avatar Jesse Barnes Committed by Eric Anholt
Browse files

drm/i915: add power monitoring support



Add power monitoring support to the i915 driver for use by the IPS
driver.  Export the available power info to the IPS driver through a few
new inter-driver hooks.  When used together, the IPS driver and this
patch can significantly increase graphics performance on Ironlake class
chips.

Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[anholt: Fixed 32-bit compile.  stupid obfuscating div_u64()]
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 7a772c49
Loading
Loading
Loading
Loading
+49 −7
Original line number Diff line number Diff line
@@ -491,11 +491,14 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
	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, "Last command: 0x%01x\n", (rgvswctl >> 13) & 0x3);
	seq_printf(m, "Command status: %d\n", (rgvswctl >> 12) & 1);
	seq_printf(m, "P%d DELAY 0x%02x\n", (rgvswctl >> 8) & 0xf,
		   rgvswctl & 0x3f);
	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);

	return 0;
}
@@ -510,7 +513,8 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)

	for (i = 0; i < 16; i++) {
		delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
		seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq);
		seq_printf(m, "P%02dVIDFREQ: 0x%08x (VID: %d)\n", i, delayfreq,
			   (delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT);
	}

	return 0;
@@ -543,6 +547,8 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
	struct drm_device *dev = node->minor->dev;
	drm_i915_private_t *dev_priv = dev->dev_private;
	u32 rgvmodectl = I915_READ(MEMMODECTL);
	u32 rstdbyctl = I915_READ(MCHBAR_RENDER_STANDBY);
	u16 crstandvid = I915_READ16(CRSTANDVID);

	seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
		   "yes" : "no");
@@ -557,9 +563,13 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
		   rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no");
	seq_printf(m, "Starting frequency: P%d\n",
		   (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT);
	seq_printf(m, "Max frequency: P%d\n",
	seq_printf(m, "Max P-state: P%d\n",
		   (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT);
	seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
	seq_printf(m, "Min P-state: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
	seq_printf(m, "RS1 VID: %d\n", (crstandvid & 0x3f));
	seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f));
	seq_printf(m, "Render standby enabled: %s\n",
		   (rstdbyctl & RCX_SW_EXIT) ? "no" : "yes");

	return 0;
}
@@ -623,6 +633,36 @@ static int i915_sr_status(struct seq_file *m, void *unused)
	return 0;
}

static int i915_emon_status(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;
	unsigned long temp, chipset, gfx;

	temp = i915_mch_val(dev_priv);
	chipset = i915_chipset_val(dev_priv);
	gfx = i915_gfx_val(dev_priv);

	seq_printf(m, "GMCH temp: %ld\n", temp);
	seq_printf(m, "Chipset power: %ld\n", chipset);
	seq_printf(m, "GFX power: %ld\n", gfx);
	seq_printf(m, "Total power: %ld\n", chipset + gfx);

	return 0;
}

static int i915_gfxec(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;

	seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4));

	return 0;
}

static int
i915_wedged_open(struct inode *inode,
		 struct file *filp)
@@ -745,6 +785,8 @@ static struct drm_info_list i915_debugfs_list[] = {
	{"i915_delayfreq_table", i915_delayfreq_table, 0},
	{"i915_inttoext_table", i915_inttoext_table, 0},
	{"i915_drpc_info", i915_drpc_info, 0},
	{"i915_emon_status", i915_emon_status, 0},
	{"i915_gfxec", i915_gfxec, 0},
	{"i915_fbc_status", i915_fbc_status, 0},
	{"i915_sr_status", i915_sr_status, 0},
};
+528 −5
Original line number Diff line number Diff line
@@ -1458,14 +1458,11 @@ 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)
static void i915_pineview_get_mem_freq(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	u32 tmp;

	if (!IS_PINEVIEW(dev))
		return;

	tmp = I915_READ(CLKCFG);

	switch (tmp & CLKCFG_FSB_MASK) {
@@ -1496,6 +1493,519 @@ static void i915_get_mem_freq(struct drm_device *dev)
	}
}

static void i915_ironlake_get_mem_freq(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	u16 ddrpll, csipll;

	ddrpll = I915_READ16(DDRMPLL1);
	csipll = I915_READ16(CSIPLL0);

	switch (ddrpll & 0xff) {
	case 0xc:
		dev_priv->mem_freq = 800;
		break;
	case 0x10:
		dev_priv->mem_freq = 1066;
		break;
	case 0x14:
		dev_priv->mem_freq = 1333;
		break;
	case 0x18:
		dev_priv->mem_freq = 1600;
		break;
	default:
		DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n",
				 ddrpll & 0xff);
		dev_priv->mem_freq = 0;
		break;
	}

	dev_priv->r_t = dev_priv->mem_freq;

	switch (csipll & 0x3ff) {
	case 0x00c:
		dev_priv->fsb_freq = 3200;
		break;
	case 0x00e:
		dev_priv->fsb_freq = 3733;
		break;
	case 0x010:
		dev_priv->fsb_freq = 4266;
		break;
	case 0x012:
		dev_priv->fsb_freq = 4800;
		break;
	case 0x014:
		dev_priv->fsb_freq = 5333;
		break;
	case 0x016:
		dev_priv->fsb_freq = 5866;
		break;
	case 0x018:
		dev_priv->fsb_freq = 6400;
		break;
	default:
		DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n",
				 csipll & 0x3ff);
		dev_priv->fsb_freq = 0;
		break;
	}

	if (dev_priv->fsb_freq == 3200) {
		dev_priv->c_m = 0;
	} else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) {
		dev_priv->c_m = 1;
	} else {
		dev_priv->c_m = 2;
	}
}

struct v_table {
	u8 vid;
	unsigned long vd; /* in .1 mil */
	unsigned long vm; /* in .1 mil */
	u8 pvid;
};

static struct v_table v_table[] = {
	{ 0, 16125, 15000, 0x7f, },
	{ 1, 16000, 14875, 0x7e, },
	{ 2, 15875, 14750, 0x7d, },
	{ 3, 15750, 14625, 0x7c, },
	{ 4, 15625, 14500, 0x7b, },
	{ 5, 15500, 14375, 0x7a, },
	{ 6, 15375, 14250, 0x79, },
	{ 7, 15250, 14125, 0x78, },
	{ 8, 15125, 14000, 0x77, },
	{ 9, 15000, 13875, 0x76, },
	{ 10, 14875, 13750, 0x75, },
	{ 11, 14750, 13625, 0x74, },
	{ 12, 14625, 13500, 0x73, },
	{ 13, 14500, 13375, 0x72, },
	{ 14, 14375, 13250, 0x71, },
	{ 15, 14250, 13125, 0x70, },
	{ 16, 14125, 13000, 0x6f, },
	{ 17, 14000, 12875, 0x6e, },
	{ 18, 13875, 12750, 0x6d, },
	{ 19, 13750, 12625, 0x6c, },
	{ 20, 13625, 12500, 0x6b, },
	{ 21, 13500, 12375, 0x6a, },
	{ 22, 13375, 12250, 0x69, },
	{ 23, 13250, 12125, 0x68, },
	{ 24, 13125, 12000, 0x67, },
	{ 25, 13000, 11875, 0x66, },
	{ 26, 12875, 11750, 0x65, },
	{ 27, 12750, 11625, 0x64, },
	{ 28, 12625, 11500, 0x63, },
	{ 29, 12500, 11375, 0x62, },
	{ 30, 12375, 11250, 0x61, },
	{ 31, 12250, 11125, 0x60, },
	{ 32, 12125, 11000, 0x5f, },
	{ 33, 12000, 10875, 0x5e, },
	{ 34, 11875, 10750, 0x5d, },
	{ 35, 11750, 10625, 0x5c, },
	{ 36, 11625, 10500, 0x5b, },
	{ 37, 11500, 10375, 0x5a, },
	{ 38, 11375, 10250, 0x59, },
	{ 39, 11250, 10125, 0x58, },
	{ 40, 11125, 10000, 0x57, },
	{ 41, 11000, 9875, 0x56, },
	{ 42, 10875, 9750, 0x55, },
	{ 43, 10750, 9625, 0x54, },
	{ 44, 10625, 9500, 0x53, },
	{ 45, 10500, 9375, 0x52, },
	{ 46, 10375, 9250, 0x51, },
	{ 47, 10250, 9125, 0x50, },
	{ 48, 10125, 9000, 0x4f, },
	{ 49, 10000, 8875, 0x4e, },
	{ 50, 9875, 8750, 0x4d, },
	{ 51, 9750, 8625, 0x4c, },
	{ 52, 9625, 8500, 0x4b, },
	{ 53, 9500, 8375, 0x4a, },
	{ 54, 9375, 8250, 0x49, },
	{ 55, 9250, 8125, 0x48, },
	{ 56, 9125, 8000, 0x47, },
	{ 57, 9000, 7875, 0x46, },
	{ 58, 8875, 7750, 0x45, },
	{ 59, 8750, 7625, 0x44, },
	{ 60, 8625, 7500, 0x43, },
	{ 61, 8500, 7375, 0x42, },
	{ 62, 8375, 7250, 0x41, },
	{ 63, 8250, 7125, 0x40, },
	{ 64, 8125, 7000, 0x3f, },
	{ 65, 8000, 6875, 0x3e, },
	{ 66, 7875, 6750, 0x3d, },
	{ 67, 7750, 6625, 0x3c, },
	{ 68, 7625, 6500, 0x3b, },
	{ 69, 7500, 6375, 0x3a, },
	{ 70, 7375, 6250, 0x39, },
	{ 71, 7250, 6125, 0x38, },
	{ 72, 7125, 6000, 0x37, },
	{ 73, 7000, 5875, 0x36, },
	{ 74, 6875, 5750, 0x35, },
	{ 75, 6750, 5625, 0x34, },
	{ 76, 6625, 5500, 0x33, },
	{ 77, 6500, 5375, 0x32, },
	{ 78, 6375, 5250, 0x31, },
	{ 79, 6250, 5125, 0x30, },
	{ 80, 6125, 5000, 0x2f, },
	{ 81, 6000, 4875, 0x2e, },
	{ 82, 5875, 4750, 0x2d, },
	{ 83, 5750, 4625, 0x2c, },
	{ 84, 5625, 4500, 0x2b, },
	{ 85, 5500, 4375, 0x2a, },
	{ 86, 5375, 4250, 0x29, },
	{ 87, 5250, 4125, 0x28, },
	{ 88, 5125, 4000, 0x27, },
	{ 89, 5000, 3875, 0x26, },
	{ 90, 4875, 3750, 0x25, },
	{ 91, 4750, 3625, 0x24, },
	{ 92, 4625, 3500, 0x23, },
	{ 93, 4500, 3375, 0x22, },
	{ 94, 4375, 3250, 0x21, },
	{ 95, 4250, 3125, 0x20, },
	{ 96, 4125, 3000, 0x1f, },
	{ 97, 4125, 3000, 0x1e, },
	{ 98, 4125, 3000, 0x1d, },
	{ 99, 4125, 3000, 0x1c, },
	{ 100, 4125, 3000, 0x1b, },
	{ 101, 4125, 3000, 0x1a, },
	{ 102, 4125, 3000, 0x19, },
	{ 103, 4125, 3000, 0x18, },
	{ 104, 4125, 3000, 0x17, },
	{ 105, 4125, 3000, 0x16, },
	{ 106, 4125, 3000, 0x15, },
	{ 107, 4125, 3000, 0x14, },
	{ 108, 4125, 3000, 0x13, },
	{ 109, 4125, 3000, 0x12, },
	{ 110, 4125, 3000, 0x11, },
	{ 111, 4125, 3000, 0x10, },
	{ 112, 4125, 3000, 0x0f, },
	{ 113, 4125, 3000, 0x0e, },
	{ 114, 4125, 3000, 0x0d, },
	{ 115, 4125, 3000, 0x0c, },
	{ 116, 4125, 3000, 0x0b, },
	{ 117, 4125, 3000, 0x0a, },
	{ 118, 4125, 3000, 0x09, },
	{ 119, 4125, 3000, 0x08, },
	{ 120, 1125, 0, 0x07, },
	{ 121, 1000, 0, 0x06, },
	{ 122, 875, 0, 0x05, },
	{ 123, 750, 0, 0x04, },
	{ 124, 625, 0, 0x03, },
	{ 125, 500, 0, 0x02, },
	{ 126, 375, 0, 0x01, },
	{ 127, 0, 0, 0x00, },
};

struct cparams {
	int i;
	int t;
	int m;
	int c;
};

static struct cparams cparams[] = {
	{ 1, 1333, 301, 28664 },
	{ 1, 1066, 294, 24460 },
	{ 1, 800, 294, 25192 },
	{ 0, 1333, 276, 27605 },
	{ 0, 1066, 276, 27605 },
	{ 0, 800, 231, 23784 },
};

unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
{
	u64 total_count, diff, ret;
	u32 count1, count2, count3, m = 0, c = 0;
	unsigned long now = jiffies_to_msecs(jiffies), diff1;
	int i;

	diff1 = now - dev_priv->last_time1;

	count1 = I915_READ(DMIEC);
	count2 = I915_READ(DDREC);
	count3 = I915_READ(CSIEC);

	total_count = count1 + count2 + count3;

	/* FIXME: handle per-counter overflow */
	if (total_count < dev_priv->last_count1) {
		diff = ~0UL - dev_priv->last_count1;
		diff += total_count;
	} else {
		diff = total_count - dev_priv->last_count1;
	}

	for (i = 0; i < ARRAY_SIZE(cparams); i++) {
		if (cparams[i].i == dev_priv->c_m &&
		    cparams[i].t == dev_priv->r_t) {
			m = cparams[i].m;
			c = cparams[i].c;
			break;
		}
	}

	div_u64(diff, diff1);
	ret = ((m * diff) + c);
	div_u64(ret, 10);

	dev_priv->last_count1 = total_count;
	dev_priv->last_time1 = now;

	return ret;
}

unsigned long i915_mch_val(struct drm_i915_private *dev_priv)
{
	unsigned long m, x, b;
	u32 tsfs;

	tsfs = I915_READ(TSFS);

	m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT);
	x = I915_READ8(TR1);

	b = tsfs & TSFS_INTR_MASK;

	return ((m * x) / 127) - b;
}

static unsigned long pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
{
	unsigned long val = 0;
	int i;

	for (i = 0; i < ARRAY_SIZE(v_table); i++) {
		if (v_table[i].pvid == pxvid) {
			if (IS_MOBILE(dev_priv->dev))
				val = v_table[i].vm;
			else
				val = v_table[i].vd;
		}
	}

	return val;
}

void i915_update_gfx_val(struct drm_i915_private *dev_priv)
{
	struct timespec now, diff1;
	u64 diff;
	unsigned long diffms;
	u32 count;

	getrawmonotonic(&now);
	diff1 = timespec_sub(now, dev_priv->last_time2);

	/* Don't divide by 0 */
	diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000;
	if (!diffms)
		return;

	count = I915_READ(GFXEC);

	if (count < dev_priv->last_count2) {
		diff = ~0UL - dev_priv->last_count2;
		diff += count;
	} else {
		diff = count - dev_priv->last_count2;
	}

	dev_priv->last_count2 = count;
	dev_priv->last_time2 = now;

	/* More magic constants... */
	diff = diff * 1181;
	div_u64(diff, diffms * 10);
	dev_priv->gfx_power = diff;
}

unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
{
	unsigned long t, corr, state1, corr2, state2;
	u32 pxvid, ext_v;

	pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4));
	pxvid = (pxvid >> 24) & 0x7f;
	ext_v = pvid_to_extvid(dev_priv, pxvid);

	state1 = ext_v;

	t = i915_mch_val(dev_priv);

	/* Revel in the empirically derived constants */

	/* Correction factor in 1/100000 units */
	if (t > 80)
		corr = ((t * 2349) + 135940);
	else if (t >= 50)
		corr = ((t * 964) + 29317);
	else /* < 50 */
		corr = ((t * 301) + 1004);

	corr = corr * ((150142 * state1) / 10000 - 78642);
	corr /= 100000;
	corr2 = (corr * dev_priv->corr);

	state2 = (corr2 * state1) / 10000;
	state2 /= 100; /* convert to mW */

	i915_update_gfx_val(dev_priv);

	return dev_priv->gfx_power + state2;
}

/* Global for IPS driver to get at the current i915 device */
static struct drm_i915_private *i915_mch_dev;
/*
 * Lock protecting IPS related data structures
 *   - i915_mch_dev
 *   - dev_priv->max_delay
 *   - dev_priv->min_delay
 *   - dev_priv->fmax
 *   - dev_priv->gpu_busy
 */
DEFINE_SPINLOCK(mchdev_lock);

/**
 * i915_read_mch_val - return value for IPS use
 *
 * Calculate and return a value for the IPS driver to use when deciding whether
 * we have thermal and power headroom to increase CPU or GPU power budget.
 */
unsigned long i915_read_mch_val(void)
{
  	struct drm_i915_private *dev_priv;
	unsigned long chipset_val, graphics_val, ret = 0;

  	spin_lock(&mchdev_lock);
	if (!i915_mch_dev)
		goto out_unlock;
	dev_priv = i915_mch_dev;

	chipset_val = i915_chipset_val(dev_priv);
	graphics_val = i915_gfx_val(dev_priv);

	ret = chipset_val + graphics_val;

out_unlock:
  	spin_unlock(&mchdev_lock);

  	return ret;
}
EXPORT_SYMBOL_GPL(i915_read_mch_val);

/**
 * i915_gpu_raise - raise GPU frequency limit
 *
 * Raise the limit; IPS indicates we have thermal headroom.
 */
bool i915_gpu_raise(void)
{
  	struct drm_i915_private *dev_priv;
	bool ret = true;

  	spin_lock(&mchdev_lock);
	if (!i915_mch_dev) {
		ret = false;
		goto out_unlock;
	}
	dev_priv = i915_mch_dev;

	if (dev_priv->max_delay > dev_priv->fmax)
		dev_priv->max_delay--;

out_unlock:
  	spin_unlock(&mchdev_lock);

  	return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_raise);

/**
 * i915_gpu_lower - lower GPU frequency limit
 *
 * IPS indicates we're close to a thermal limit, so throttle back the GPU
 * frequency maximum.
 */
bool i915_gpu_lower(void)
{
  	struct drm_i915_private *dev_priv;
	bool ret = true;

  	spin_lock(&mchdev_lock);
	if (!i915_mch_dev) {
		ret = false;
		goto out_unlock;
	}
	dev_priv = i915_mch_dev;

	if (dev_priv->max_delay < dev_priv->min_delay)
		dev_priv->max_delay++;

out_unlock:
  	spin_unlock(&mchdev_lock);

  	return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_lower);

/**
 * i915_gpu_busy - indicate GPU business to IPS
 *
 * Tell the IPS driver whether or not the GPU is busy.
 */
bool i915_gpu_busy(void)
{
  	struct drm_i915_private *dev_priv;
	bool ret = false;

  	spin_lock(&mchdev_lock);
	if (!i915_mch_dev)
		goto out_unlock;
	dev_priv = i915_mch_dev;

	ret = dev_priv->busy;

out_unlock:
  	spin_unlock(&mchdev_lock);

  	return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_busy);

/**
 * i915_gpu_turbo_disable - disable graphics turbo
 *
 * Disable graphics turbo by resetting the max frequency and setting the
 * current frequency to the default.
 */
bool i915_gpu_turbo_disable(void)
{
  	struct drm_i915_private *dev_priv;
	bool ret = true;

  	spin_lock(&mchdev_lock);
	if (!i915_mch_dev) {
		ret = false;
		goto out_unlock;
	}
	dev_priv = i915_mch_dev;

	dev_priv->max_delay = dev_priv->fstart;

	if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart))
		ret = false;

out_unlock:
  	spin_unlock(&mchdev_lock);

  	return ret;
}
EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable);

/**
 * i915_driver_load - setup chip and create an initial config
 * @dev: DRM device
@@ -1616,7 +2126,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
			goto out_workqueue_free;
	}

	i915_get_mem_freq(dev);
	if (IS_PINEVIEW(dev))
		i915_pineview_get_mem_freq(dev);
	else if (IS_IRONLAKE(dev))
		i915_ironlake_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
@@ -1662,6 +2175,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)

	setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
		    (unsigned long) dev);

	spin_lock(&mchdev_lock);
	i915_mch_dev = dev_priv;
	dev_priv->mchdev_lock = &mchdev_lock;
	spin_unlock(&mchdev_lock);

	return 0;

out_workqueue_free:
@@ -1683,6 +2202,10 @@ int i915_driver_unload(struct drm_device *dev)

	i915_destroy_error_state(dev);

	spin_lock(&mchdev_lock);
	i915_mch_dev = NULL;
	spin_unlock(&mchdev_lock);

	destroy_workqueue(dev_priv->wq);
	del_timer_sync(&dev_priv->hangcheck_timer);

+19 −1
Original line number Diff line number Diff line
@@ -619,6 +619,18 @@ typedef struct drm_i915_private {
	u8 cur_delay;
	u8 min_delay;
	u8 max_delay;
	u8 fmax;
	u8 fstart;

 	u64 last_count1;
 	unsigned long last_time1;
 	u64 last_count2;
 	struct timespec last_time2;
 	unsigned long gfx_power;
 	int c_m;
 	int r_t;
 	u8 corr;
	spinlock_t *mchdev_lock;

	enum no_fbc_reason no_fbc_reason;

@@ -802,6 +814,11 @@ extern int i915_emit_box(struct drm_device *dev,
			 struct drm_clip_rect *boxes,
			 int i, int DR1, int DR4);
extern int i965_reset(struct drm_device *dev, u8 flags);
extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);


/* i915_irq.c */
void i915_hangcheck_elapsed(unsigned long data);
@@ -1005,7 +1022,7 @@ extern void g4x_disable_fbc(struct drm_device *dev);
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 intel_detect_pch (struct drm_device *dev);
extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);

@@ -1030,6 +1047,7 @@ extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
#define I915_WRITE64(reg, val)	writeq(val, dev_priv->regs + (reg))
#define I915_READ64(reg)	readq(dev_priv->regs + (reg))
#define POSTING_READ(reg)	(void)I915_READ(reg)
#define POSTING_READ16(reg)	(void)I915_READ16(reg)

#define I915_VERBOSE 0

+4 −24
Original line number Diff line number Diff line
@@ -278,10 +278,9 @@ static void i915_handle_rps_change(struct drm_device *dev)
{
	drm_i915_private_t *dev_priv = dev->dev_private;
	u32 busy_up, busy_down, max_avg, min_avg;
	u16 rgvswctl;
	u8 new_delay = dev_priv->cur_delay;

	I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG);
	I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
	busy_up = I915_READ(RCPREVBSYTUPAVG);
	busy_down = I915_READ(RCPREVBSYTDNAVG);
	max_avg = I915_READ(RCBMAXAVG);
@@ -300,28 +299,9 @@ static void i915_handle_rps_change(struct drm_device *dev)
			new_delay = dev_priv->min_delay;
	}

	DRM_DEBUG("rps change requested: %d -> %d\n",
		  dev_priv->cur_delay, new_delay);

	rgvswctl = I915_READ(MEMSWCTL);
	if (rgvswctl & MEMCTL_CMD_STS) {
		DRM_ERROR("gpu busy, RCS change rejected\n");
		return; /* still busy with another command */
	}

	/* Program the new state */
	rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
		(new_delay << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
	I915_WRITE(MEMSWCTL, rgvswctl);
	POSTING_READ(MEMSWCTL);

	rgvswctl |= MEMCTL_CMD_STS;
	I915_WRITE(MEMSWCTL, rgvswctl);

	if (ironlake_set_drps(dev, new_delay))
		dev_priv->cur_delay = new_delay;

	DRM_DEBUG("rps changed\n");

	return;
}

@@ -392,7 +372,7 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
	}

	if (de_iir & DE_PCU_EVENT) {
		I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS));
		I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
		i915_handle_rps_change(dev);
	}

+41 −0
Original line number Diff line number Diff line
@@ -838,6 +838,12 @@
#define CLKCFG_MEM_800					(3 << 4)
#define CLKCFG_MEM_MASK					(7 << 4)

#define TR1			0x11006
#define TSFS			0x11020
#define   TSFS_SLOPE_MASK	0x0000ff00
#define   TSFS_SLOPE_SHIFT	8
#define   TSFS_INTR_MASK	0x000000ff

#define CRSTANDVID		0x11100
#define PXVFREQ_BASE		0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */
#define   PXVFREQ_PX_MASK	0x7f000000
@@ -976,6 +982,41 @@
#define   MEMSTAT_SRC_CTL_STDBY 3
#define RCPREVBSYTUPAVG		0x113b8
#define RCPREVBSYTDNAVG		0x113bc
#define SDEW			0x1124c
#define CSIEW0			0x11250
#define CSIEW1			0x11254
#define CSIEW2			0x11258
#define PEW			0x1125c
#define DEW			0x11270
#define MCHAFE			0x112c0
#define CSIEC			0x112e0
#define DMIEC			0x112e4
#define DDREC			0x112e8
#define PEG0EC			0x112ec
#define PEG1EC			0x112f0
#define GFXEC			0x112f4
#define RPPREVBSYTUPAVG		0x113b8
#define RPPREVBSYTDNAVG		0x113bc
#define ECR			0x11600
#define   ECR_GPFE		(1<<31)
#define   ECR_IMONE		(1<<30)
#define   ECR_CAP_MASK		0x0000001f /* Event range, 0-31 */
#define OGW0			0x11608
#define OGW1			0x1160c
#define EG0			0x11610
#define EG1			0x11614
#define EG2			0x11618
#define EG3			0x1161c
#define EG4			0x11620
#define EG5			0x11624
#define EG6			0x11628
#define EG7			0x1162c
#define PXW			0x11664
#define PXWL			0x11680
#define LCFUSE02		0x116c0
#define   LCFUSE_HIV_MASK	0x000000ff
#define CSIPLL0			0x12c10
#define DDRMPLL1		0X12c20
#define PEG_BAND_GAP_DATA	0x14d68

/*
Loading