diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2010-05-20 17:28:11 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2010-05-26 17:10:01 -0400 |
commit | 7648fa99eb77a2e1a90b7beaa420e07d819b9c11 (patch) | |
tree | 29991eba782a22922441813dcc3a5cbde8377119 /drivers/gpu/drm/i915/intel_display.c | |
parent | 7a772c492fcfffae812ffca78a628e76fa57fe58 (diff) |
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: Jesse Barnes <jbarnes@virtuousgeek.org>
[anholt: Fixed 32-bit compile. stupid obfuscating div_u64()]
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 147 |
1 files changed, 131 insertions, 16 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4c7c151114f7..e94d1db35364 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -4459,6 +4459,8 @@ static void intel_idle_update(struct work_struct *work) | |||
4459 | 4459 | ||
4460 | mutex_lock(&dev->struct_mutex); | 4460 | mutex_lock(&dev->struct_mutex); |
4461 | 4461 | ||
4462 | i915_update_gfx_val(dev_priv); | ||
4463 | |||
4462 | if (IS_I945G(dev) || IS_I945GM(dev)) { | 4464 | if (IS_I945G(dev) || IS_I945GM(dev)) { |
4463 | DRM_DEBUG_DRIVER("enable memory self refresh on 945\n"); | 4465 | DRM_DEBUG_DRIVER("enable memory self refresh on 945\n"); |
4464 | I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN); | 4466 | I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN); |
@@ -5045,10 +5047,32 @@ err_unref: | |||
5045 | return NULL; | 5047 | return NULL; |
5046 | } | 5048 | } |
5047 | 5049 | ||
5050 | bool ironlake_set_drps(struct drm_device *dev, u8 val) | ||
5051 | { | ||
5052 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
5053 | u16 rgvswctl; | ||
5054 | |||
5055 | rgvswctl = I915_READ16(MEMSWCTL); | ||
5056 | if (rgvswctl & MEMCTL_CMD_STS) { | ||
5057 | DRM_DEBUG("gpu busy, RCS change rejected\n"); | ||
5058 | return false; /* still busy with another command */ | ||
5059 | } | ||
5060 | |||
5061 | rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | | ||
5062 | (val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; | ||
5063 | I915_WRITE16(MEMSWCTL, rgvswctl); | ||
5064 | POSTING_READ16(MEMSWCTL); | ||
5065 | |||
5066 | rgvswctl |= MEMCTL_CMD_STS; | ||
5067 | I915_WRITE16(MEMSWCTL, rgvswctl); | ||
5068 | |||
5069 | return true; | ||
5070 | } | ||
5071 | |||
5048 | void ironlake_enable_drps(struct drm_device *dev) | 5072 | void ironlake_enable_drps(struct drm_device *dev) |
5049 | { | 5073 | { |
5050 | struct drm_i915_private *dev_priv = dev->dev_private; | 5074 | struct drm_i915_private *dev_priv = dev->dev_private; |
5051 | u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl; | 5075 | u32 rgvmodectl = I915_READ(MEMMODECTL); |
5052 | u8 fmax, fmin, fstart, vstart; | 5076 | u8 fmax, fmin, fstart, vstart; |
5053 | int i = 0; | 5077 | int i = 0; |
5054 | 5078 | ||
@@ -5067,13 +5091,21 @@ void ironlake_enable_drps(struct drm_device *dev) | |||
5067 | fmin = (rgvmodectl & MEMMODE_FMIN_MASK); | 5091 | fmin = (rgvmodectl & MEMMODE_FMIN_MASK); |
5068 | fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> | 5092 | fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >> |
5069 | MEMMODE_FSTART_SHIFT; | 5093 | MEMMODE_FSTART_SHIFT; |
5094 | fstart = fmax; | ||
5095 | |||
5070 | vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> | 5096 | vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> |
5071 | PXVFREQ_PX_SHIFT; | 5097 | PXVFREQ_PX_SHIFT; |
5072 | 5098 | ||
5073 | dev_priv->max_delay = fstart; /* can't go to fmax w/o IPS */ | 5099 | dev_priv->fmax = fstart; /* IPS callback will increase this */ |
5100 | dev_priv->fstart = fstart; | ||
5101 | |||
5102 | dev_priv->max_delay = fmax; | ||
5074 | dev_priv->min_delay = fmin; | 5103 | dev_priv->min_delay = fmin; |
5075 | dev_priv->cur_delay = fstart; | 5104 | dev_priv->cur_delay = fstart; |
5076 | 5105 | ||
5106 | DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin, | ||
5107 | fstart); | ||
5108 | |||
5077 | I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN); | 5109 | I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN); |
5078 | 5110 | ||
5079 | /* | 5111 | /* |
@@ -5095,20 +5127,19 @@ void ironlake_enable_drps(struct drm_device *dev) | |||
5095 | } | 5127 | } |
5096 | msleep(1); | 5128 | msleep(1); |
5097 | 5129 | ||
5098 | rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | | 5130 | ironlake_set_drps(dev, fstart); |
5099 | (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; | ||
5100 | I915_WRITE(MEMSWCTL, rgvswctl); | ||
5101 | POSTING_READ(MEMSWCTL); | ||
5102 | 5131 | ||
5103 | rgvswctl |= MEMCTL_CMD_STS; | 5132 | dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) + |
5104 | I915_WRITE(MEMSWCTL, rgvswctl); | 5133 | I915_READ(0x112e0); |
5134 | dev_priv->last_time1 = jiffies_to_msecs(jiffies); | ||
5135 | dev_priv->last_count2 = I915_READ(0x112f4); | ||
5136 | getrawmonotonic(&dev_priv->last_time2); | ||
5105 | } | 5137 | } |
5106 | 5138 | ||
5107 | void ironlake_disable_drps(struct drm_device *dev) | 5139 | void ironlake_disable_drps(struct drm_device *dev) |
5108 | { | 5140 | { |
5109 | struct drm_i915_private *dev_priv = dev->dev_private; | 5141 | struct drm_i915_private *dev_priv = dev->dev_private; |
5110 | u32 rgvswctl; | 5142 | u16 rgvswctl = I915_READ16(MEMSWCTL); |
5111 | u8 fstart; | ||
5112 | 5143 | ||
5113 | /* Ack interrupts, disable EFC interrupt */ | 5144 | /* Ack interrupts, disable EFC interrupt */ |
5114 | I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN); | 5145 | I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN); |
@@ -5118,11 +5149,7 @@ void ironlake_disable_drps(struct drm_device *dev) | |||
5118 | I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT); | 5149 | I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT); |
5119 | 5150 | ||
5120 | /* Go back to the starting frequency */ | 5151 | /* Go back to the starting frequency */ |
5121 | fstart = (I915_READ(MEMMODECTL) & MEMMODE_FSTART_MASK) >> | 5152 | ironlake_set_drps(dev, dev_priv->fstart); |
5122 | MEMMODE_FSTART_SHIFT; | ||
5123 | rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) | | ||
5124 | (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM; | ||
5125 | I915_WRITE(MEMSWCTL, rgvswctl); | ||
5126 | msleep(1); | 5153 | msleep(1); |
5127 | rgvswctl |= MEMCTL_CMD_STS; | 5154 | rgvswctl |= MEMCTL_CMD_STS; |
5128 | I915_WRITE(MEMSWCTL, rgvswctl); | 5155 | I915_WRITE(MEMSWCTL, rgvswctl); |
@@ -5130,6 +5157,92 @@ void ironlake_disable_drps(struct drm_device *dev) | |||
5130 | 5157 | ||
5131 | } | 5158 | } |
5132 | 5159 | ||
5160 | static unsigned long intel_pxfreq(u32 vidfreq) | ||
5161 | { | ||
5162 | unsigned long freq; | ||
5163 | int div = (vidfreq & 0x3f0000) >> 16; | ||
5164 | int post = (vidfreq & 0x3000) >> 12; | ||
5165 | int pre = (vidfreq & 0x7); | ||
5166 | |||
5167 | if (!pre) | ||
5168 | return 0; | ||
5169 | |||
5170 | freq = ((div * 133333) / ((1<<post) * pre)); | ||
5171 | |||
5172 | return freq; | ||
5173 | } | ||
5174 | |||
5175 | void intel_init_emon(struct drm_device *dev) | ||
5176 | { | ||
5177 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
5178 | u32 lcfuse; | ||
5179 | u8 pxw[16]; | ||
5180 | int i; | ||
5181 | |||
5182 | /* Disable to program */ | ||
5183 | I915_WRITE(ECR, 0); | ||
5184 | POSTING_READ(ECR); | ||
5185 | |||
5186 | /* Program energy weights for various events */ | ||
5187 | I915_WRITE(SDEW, 0x15040d00); | ||
5188 | I915_WRITE(CSIEW0, 0x007f0000); | ||
5189 | I915_WRITE(CSIEW1, 0x1e220004); | ||
5190 | I915_WRITE(CSIEW2, 0x04000004); | ||
5191 | |||
5192 | for (i = 0; i < 5; i++) | ||
5193 | I915_WRITE(PEW + (i * 4), 0); | ||
5194 | for (i = 0; i < 3; i++) | ||
5195 | I915_WRITE(DEW + (i * 4), 0); | ||
5196 | |||
5197 | /* Program P-state weights to account for frequency power adjustment */ | ||
5198 | for (i = 0; i < 16; i++) { | ||
5199 | u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4)); | ||
5200 | unsigned long freq = intel_pxfreq(pxvidfreq); | ||
5201 | unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >> | ||
5202 | PXVFREQ_PX_SHIFT; | ||
5203 | unsigned long val; | ||
5204 | |||
5205 | val = vid * vid; | ||
5206 | val *= (freq / 1000); | ||
5207 | val *= 255; | ||
5208 | val /= (127*127*900); | ||
5209 | if (val > 0xff) | ||
5210 | DRM_ERROR("bad pxval: %ld\n", val); | ||
5211 | pxw[i] = val; | ||
5212 | } | ||
5213 | /* Render standby states get 0 weight */ | ||
5214 | pxw[14] = 0; | ||
5215 | pxw[15] = 0; | ||
5216 | |||
5217 | for (i = 0; i < 4; i++) { | ||
5218 | u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) | | ||
5219 | (pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]); | ||
5220 | I915_WRITE(PXW + (i * 4), val); | ||
5221 | } | ||
5222 | |||
5223 | /* Adjust magic regs to magic values (more experimental results) */ | ||
5224 | I915_WRITE(OGW0, 0); | ||
5225 | I915_WRITE(OGW1, 0); | ||
5226 | I915_WRITE(EG0, 0x00007f00); | ||
5227 | I915_WRITE(EG1, 0x0000000e); | ||
5228 | I915_WRITE(EG2, 0x000e0000); | ||
5229 | I915_WRITE(EG3, 0x68000300); | ||
5230 | I915_WRITE(EG4, 0x42000000); | ||
5231 | I915_WRITE(EG5, 0x00140031); | ||
5232 | I915_WRITE(EG6, 0); | ||
5233 | I915_WRITE(EG7, 0); | ||
5234 | |||
5235 | for (i = 0; i < 8; i++) | ||
5236 | I915_WRITE(PXWL + (i * 4), 0); | ||
5237 | |||
5238 | /* Enable PMON + select events */ | ||
5239 | I915_WRITE(ECR, 0x80000019); | ||
5240 | |||
5241 | lcfuse = I915_READ(LCFUSE02); | ||
5242 | |||
5243 | dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK); | ||
5244 | } | ||
5245 | |||
5133 | void intel_init_clock_gating(struct drm_device *dev) | 5246 | void intel_init_clock_gating(struct drm_device *dev) |
5134 | { | 5247 | { |
5135 | struct drm_i915_private *dev_priv = dev->dev_private; | 5248 | struct drm_i915_private *dev_priv = dev->dev_private; |
@@ -5376,8 +5489,10 @@ void intel_modeset_init(struct drm_device *dev) | |||
5376 | 5489 | ||
5377 | intel_init_clock_gating(dev); | 5490 | intel_init_clock_gating(dev); |
5378 | 5491 | ||
5379 | if (IS_IRONLAKE_M(dev)) | 5492 | if (IS_IRONLAKE_M(dev)) { |
5380 | ironlake_enable_drps(dev); | 5493 | ironlake_enable_drps(dev); |
5494 | intel_init_emon(dev); | ||
5495 | } | ||
5381 | 5496 | ||
5382 | INIT_WORK(&dev_priv->idle_work, intel_idle_update); | 5497 | INIT_WORK(&dev_priv->idle_work, intel_idle_update); |
5383 | setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, | 5498 | setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, |