Skip to content

Commit

Permalink
drm/i915: load a ring frequency scaling table v3
Browse files Browse the repository at this point in the history
The ring frequency scaling table tells the PCU to treat certain GPU
frequencies as if they were a given CPU frequency for purposes of
scaling the ring frequency.  Normally the PCU will scale the ring
frequency based on the CPU P-state, but with the table present, it will
also take the GPU frequency into account.

The main downside of keeping the ring frequency high while the CPU is
at a low frequency (or asleep altogether) is increased power
consumption.  But then if you're keeping your GPU busy, you probably
want the extra performance.

v2:
  - add units to debug table header (from Eric)
  - use tsc_khz as a fallback if the cpufreq driver doesn't give us a freq
    (from Chris)
v3:
  - fix comments & debug output
  - remove unneeded force wake get/put

Reviewed-by: Ben Widawsky <ben@bwidawsk.net>
Tested-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
Jesse Barnes authored and Keith Packard committed Jun 28, 2011
1 parent 3d73710 commit 23b2f8b
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 3 deletions.
39 changes: 39 additions & 0 deletions drivers/gpu/drm/i915/i915_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,44 @@ static int i915_emon_status(struct seq_file *m, void *unused)
return 0;
}

static int i915_ring_freq_table(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;
int ret;
int gpu_freq, ia_freq;

if (!IS_GEN6(dev)) {
seq_printf(m, "unsupported on this chipset\n");
return 0;
}

ret = mutex_lock_interruptible(&dev->struct_mutex);
if (ret)
return ret;

seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");

for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
gpu_freq++) {
I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
GEN6_PCODE_READ_MIN_FREQ_TABLE);
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
GEN6_PCODE_READY) == 0, 10)) {
DRM_ERROR("pcode read of freq table timed out\n");
continue;
}
ia_freq = I915_READ(GEN6_PCODE_DATA);
seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
}

mutex_unlock(&dev->struct_mutex);

return 0;
}

static int i915_gfxec(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
Expand Down Expand Up @@ -1426,6 +1464,7 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_inttoext_table", i915_inttoext_table, 0},
{"i915_drpc_info", i915_drpc_info, 0},
{"i915_emon_status", i915_emon_status, 0},
{"i915_ring_freq_table", i915_ring_freq_table, 0},
{"i915_gfxec", i915_gfxec, 0},
{"i915_fbc_status", i915_fbc_status, 0},
{"i915_sr_status", i915_sr_status, 0},
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/i915/i915_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -3434,7 +3434,9 @@
#define GEN6_PCODE_MAILBOX 0x138124
#define GEN6_PCODE_READY (1<<31)
#define GEN6_READ_OC_PARAMS 0xc
#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x9
#define GEN6_PCODE_WRITE_MIN_FREQ_TABLE 0x8
#define GEN6_PCODE_READ_MIN_FREQ_TABLE 0x9
#define GEN6_PCODE_DATA 0x138128
#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8

#endif /* _I915_REG_H_ */
4 changes: 3 additions & 1 deletion drivers/gpu/drm/i915/i915_suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,10 @@ int i915_restore_state(struct drm_device *dev)
intel_init_emon(dev);
}

if (IS_GEN6(dev))
if (IS_GEN6(dev)) {
gen6_enable_rps(dev_priv);
gen6_update_ring_freq(dev_priv);
}

/* Cache mode state */
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
Expand Down
58 changes: 57 additions & 1 deletion drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Eric Anholt <eric@anholt.net>
*/

#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/i2c.h>
Expand Down Expand Up @@ -7273,6 +7274,59 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
mutex_unlock(&dev_priv->dev->struct_mutex);
}

void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
{
int min_freq = 15;
int gpu_freq, ia_freq, max_ia_freq;
int scaling_factor = 180;

max_ia_freq = cpufreq_quick_get_max(0);
/*
* Default to measured freq if none found, PCU will ensure we don't go
* over
*/
if (!max_ia_freq)
max_ia_freq = tsc_khz;

/* Convert from kHz to MHz */
max_ia_freq /= 1000;

mutex_lock(&dev_priv->dev->struct_mutex);

/*
* For each potential GPU frequency, load a ring frequency we'd like
* to use for memory access. We do this by specifying the IA frequency
* the PCU should use as a reference to determine the ring frequency.
*/
for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
gpu_freq--) {
int diff = dev_priv->max_delay - gpu_freq;

/*
* For GPU frequencies less than 750MHz, just use the lowest
* ring freq.
*/
if (gpu_freq < min_freq)
ia_freq = 800;
else
ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);

I915_WRITE(GEN6_PCODE_DATA,
(ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
gpu_freq);
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
GEN6_PCODE_READY) == 0, 10)) {
DRM_ERROR("pcode write of freq table timed out\n");
continue;
}
}

mutex_unlock(&dev_priv->dev->struct_mutex);
}

static void ironlake_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
Expand Down Expand Up @@ -7916,8 +7970,10 @@ void intel_modeset_init(struct drm_device *dev)
intel_init_emon(dev);
}

if (IS_GEN6(dev))
if (IS_GEN6(dev)) {
gen6_enable_rps(dev_priv);
gen6_update_ring_freq(dev_priv);
}

INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ extern void intel_enable_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 gen6_enable_rps(struct drm_i915_private *dev_priv);
extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv);
extern void gen6_disable_rps(struct drm_device *dev);
extern void intel_init_emon(struct drm_device *dev);

Expand Down

0 comments on commit 23b2f8b

Please sign in to comment.