Skip to content

Commit

Permalink
drm/i915: dynamic render p-state support for Sandy Bridge
Browse files Browse the repository at this point in the history
Add an interrupt handler for switching graphics frequencies and handling
PM interrupts.  This should allow for increased performance when busy
and lower power consumption when idle.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
  • Loading branch information
Jesse Barnes authored and Chris Wilson committed Dec 18, 2010
1 parent 9c3d2f7 commit 3b8d8d9
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 20 deletions.
54 changes: 45 additions & 9 deletions drivers/gpu/drm/i915/i915_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -797,15 +797,51 @@ 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(GEN6_GT_PERF_STATUS);
u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
int max_freq;

/* RPSTAT1 is in the GT power well */
__gen6_force_wake_get(dev_priv);

seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
seq_printf(m, "RPSTAT1: 0x%08x\n", I915_READ(GEN6_RPSTAT1));
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, "Lowest (RPN) frequency: %dMHz\n",
max_freq * 100);

max_freq = (rp_state_cap & 0xff00) >> 8;
seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
max_freq * 100);

max_freq = rp_state_cap & 0xff;
seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
max_freq * 100);

__gen6_force_wake_put(dev_priv);
} else {
seq_printf(m, "no P-state info available\n");
}

return 0;
}
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,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 gen6_set_rps(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);

Expand Down
47 changes: 45 additions & 2 deletions drivers/gpu/drm/i915/i915_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,49 @@ static void notify_ring(struct drm_device *dev,
jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
}

static void gen6_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(GEN6_PMIIR);
if (!pm_iir)
return;

if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
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 & (GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT)) {
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;
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
I915_READ(GEN6_RP_INTERRUPT_LIMITS) |
((new_delay << 16) & 0x3f0000));
} else {
/* Make sure we continue to get down interrupts
* until we hit the minimum frequency */
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
I915_READ(GEN6_RP_INTERRUPT_LIMITS) & ~0x3f0000);
}

}

gen6_set_rps(dev, new_delay);
dev_priv->cur_delay = new_delay;

I915_WRITE(GEN6_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;
int ret = IRQ_NONE;
u32 de_iir, gt_iir, de_ier, pch_iir;
u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
u32 hotplug_mask;
struct drm_i915_master_private *master_priv;
u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT;
Expand All @@ -417,8 +455,10 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
de_iir = I915_READ(DEIIR);
gt_iir = I915_READ(GTIIR);
pch_iir = I915_READ(SDEIIR);
pm_iir = I915_READ(GEN6_PMIIR);

if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 &&
(!IS_GEN6(dev) || pm_iir == 0))
goto done;

if (HAS_PCH_CPT(dev))
Expand Down Expand Up @@ -470,6 +510,9 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
i915_handle_rps_change(dev);
}

if (IS_GEN6(dev))
gen6_pm_irq_handler(dev);

/* should clear PCH hotplug event before clear CPU irq */
I915_WRITE(SDEIIR, pch_iir);
I915_WRITE(GTIIR, gt_iir);
Expand Down
8 changes: 7 additions & 1 deletion drivers/gpu/drm/i915/i915_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,10 @@
#define DDRMPLL1 0X12c20
#define PEG_BAND_GAP_DATA 0x14d68

#define GEN6_GT_PERF_STATUS 0x145948
#define GEN6_RP_STATE_LIMITS 0x145994
#define GEN6_RP_STATE_CAP 0x145998

/*
* Logical Context regs
*/
Expand Down Expand Up @@ -3169,7 +3173,7 @@
#define FORCEWAKE 0xA18C
#define FORCEWAKE_ACK 0x130090

#define GEN6_RC_NORMAL_FREQ 0xA008
#define GEN6_RPNSWREQ 0xA008
#define GEN6_TURBO_DISABLE (1<<31)
#define GEN6_FREQUENCY(x) ((x)<<25)
#define GEN6_OFFSET(x) ((x)<<19)
Expand All @@ -3185,6 +3189,7 @@
#define GEN6_RC_CTL_HW_ENABLE (1<<31)
#define GEN6_RP_DOWN_TIMEOUT 0xA010
#define GEN6_RP_INTERRUPT_LIMITS 0xA014
#define GEN6_RPSTAT1 0xA01C
#define GEN6_RP_CONTROL 0xA024
#define GEN6_RP_MEDIA_TURBO (1<<11)
#define GEN6_RP_USE_NORMAL_FREQ (1<<9)
Expand All @@ -3208,6 +3213,7 @@
#define GEN6_RC6_THRESHOLD 0xA0B8
#define GEN6_RC6p_THRESHOLD 0xA0BC
#define GEN6_RC6pp_THRESHOLD 0xA0C0
#define GEN6_PMINTRMSK 0xA168

#define GEN6_PMISR 0x44020
#define GEN6_PMIMR 0x44024
Expand Down
9 changes: 7 additions & 2 deletions drivers/gpu/drm/i915/i915_suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -817,8 +817,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))
gen6_disable_rps(dev);

intel_disable_clock_gating(dev);

Expand Down Expand Up @@ -867,11 +869,14 @@ int i915_restore_state(struct drm_device *dev)
/* Clock gating state */
intel_enable_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))
gen6_enable_rps(dev_priv);

/* Cache mode state */
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);

Expand Down
36 changes: 30 additions & 6 deletions drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -6021,6 +6021,25 @@ void ironlake_disable_drps(struct drm_device *dev)

}

void gen6_set_rps(struct drm_device *dev, u8 val)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 swreq;

swreq = (val & 0x3ff) << 25;
I915_WRITE(GEN6_RPNSWREQ, swreq);
}

void gen6_disable_rps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;

I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
I915_WRITE(GEN6_PMIER, 0);
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
}

static unsigned long intel_pxfreq(u32 vidfreq)
{
unsigned long freq;
Expand Down Expand Up @@ -6107,7 +6126,7 @@ void intel_init_emon(struct drm_device *dev)
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
}

static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
void gen6_enable_rps(struct drm_i915_private *dev_priv)
{
int i;

Expand All @@ -6120,7 +6139,7 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
I915_WRITE(GEN6_RC_STATE, 0);
__gen6_force_wake_get(dev_priv);

/* disable the counters and set determistic thresholds */
/* disable the counters and set deterministic thresholds */
I915_WRITE(GEN6_RC_CONTROL, 0);

I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
Expand All @@ -6144,7 +6163,7 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
GEN6_RC_CTL_EI_MODE(1) |
GEN6_RC_CTL_HW_ENABLE);

I915_WRITE(GEN6_RC_NORMAL_FREQ,
I915_WRITE(GEN6_RPNSWREQ,
GEN6_FREQUENCY(10) |
GEN6_OFFSET(0) |
GEN6_AGGRESSIVE_TURBO);
Expand Down Expand Up @@ -6189,6 +6208,9 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
GEN6_PM_RP_DOWN_THRESHOLD |
GEN6_PM_RP_UP_EI_EXPIRED |
GEN6_PM_RP_DOWN_EI_EXPIRED);
I915_WRITE(GEN6_PMIMR, 0);
/* enable all PM interrupts */
I915_WRITE(GEN6_PMINTRMSK, 0);

__gen6_force_wake_put(dev_priv);
}
Expand Down Expand Up @@ -6381,9 +6403,6 @@ void intel_enable_clock_gating(struct drm_device *dev)
I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
}
}

if (IS_GEN6(dev))
gen6_enable_rc6(dev_priv);
}

void intel_disable_clock_gating(struct drm_device *dev)
Expand Down Expand Up @@ -6657,6 +6676,9 @@ void intel_modeset_init(struct drm_device *dev)
intel_init_emon(dev);
}

if (IS_GEN6(dev))
gen6_enable_rps(dev_priv);

INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
(unsigned long)dev);
Expand Down Expand Up @@ -6690,6 +6712,8 @@ void intel_modeset_cleanup(struct drm_device *dev)

if (IS_IRONLAKE_M(dev))
ironlake_disable_drps(dev);
if (IS_GEN6(dev))
gen6_disable_rps(dev);

intel_disable_clock_gating(dev);

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ extern void intel_enable_clock_gating(struct drm_device *dev);
extern void intel_disable_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_disable_rps(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,
Expand Down

0 comments on commit 3b8d8d9

Please sign in to comment.