aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVille Syrjälä <ville.syrjala@linux.intel.com>2014-04-28 08:44:57 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2014-05-06 04:18:04 -0400
commitd52fea5bede33818def6af692644e5de27bfc1b3 (patch)
tree96acba6af9fc7bca7f3cfc1ab3c20797e019821e
parenta3cb40483acdd9caeb1974e301c3e5d5926a1221 (diff)
drm/i915: Merge LP1+ watermarks in safer way
On ILK when we disable a particular watermark level, we must maintain the actual watermark values for that level for some time (until the next vblank possibly). Otherwise we risk underruns. In order to achieve that result we must merge the LP1+ watermarks a bit differently since we must also merge levels that are to be disabled. We must also make sure we don't overflow the fields in the watermark registers in case the calculated watermarks come out too big to fit. As early as possbile we mark all computed watermark levels as disabled if they would exceed the register maximums. We make sure to leave the actual watermarks for such levels zeroed out. Then during merging, we take the maxium values for every level, regardless if they're disabled or not. That may seem a bit pointless since at the moment all the watermark levels we merge should have their values zeroed if the level is already disabled. However soon we will be dealing with intermediate watermarks that, in addition to the new watermark values, also contain the previous watermark values, and so levels that are disabled may no longer be zeroed out. v2: Split the patch in two (Paulo) Use if() instead of & when merging ->enable (Paulo) Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com> [danvet: Fix commit message as noted by Paulo.] Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r--drivers/gpu/drm/i915/intel_pm.c37
1 files changed, 28 insertions, 9 deletions
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 9d01922e7c0f..834c49c2beb7 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2252,6 +2252,8 @@ static void ilk_merge_wm_level(struct drm_device *dev,
2252{ 2252{
2253 const struct intel_crtc *intel_crtc; 2253 const struct intel_crtc *intel_crtc;
2254 2254
2255 ret_wm->enable = true;
2256
2255 list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { 2257 list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) {
2256 const struct intel_pipe_wm *active = &intel_crtc->wm.active; 2258 const struct intel_pipe_wm *active = &intel_crtc->wm.active;
2257 const struct intel_wm_level *wm = &active->wm[level]; 2259 const struct intel_wm_level *wm = &active->wm[level];
@@ -2259,16 +2261,19 @@ static void ilk_merge_wm_level(struct drm_device *dev,
2259 if (!active->pipe_enabled) 2261 if (!active->pipe_enabled)
2260 continue; 2262 continue;
2261 2263
2264 /*
2265 * The watermark values may have been used in the past,
2266 * so we must maintain them in the registers for some
2267 * time even if the level is now disabled.
2268 */
2262 if (!wm->enable) 2269 if (!wm->enable)
2263 return; 2270 ret_wm->enable = false;
2264 2271
2265 ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val); 2272 ret_wm->pri_val = max(ret_wm->pri_val, wm->pri_val);
2266 ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val); 2273 ret_wm->spr_val = max(ret_wm->spr_val, wm->spr_val);
2267 ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val); 2274 ret_wm->cur_val = max(ret_wm->cur_val, wm->cur_val);
2268 ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val); 2275 ret_wm->fbc_val = max(ret_wm->fbc_val, wm->fbc_val);
2269 } 2276 }
2270
2271 ret_wm->enable = true;
2272} 2277}
2273 2278
2274/* 2279/*
@@ -2280,6 +2285,7 @@ static void ilk_wm_merge(struct drm_device *dev,
2280 struct intel_pipe_wm *merged) 2285 struct intel_pipe_wm *merged)
2281{ 2286{
2282 int level, max_level = ilk_wm_max_level(dev); 2287 int level, max_level = ilk_wm_max_level(dev);
2288 int last_enabled_level = max_level;
2283 2289
2284 /* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */ 2290 /* ILK/SNB/IVB: LP1+ watermarks only w/ single pipe */
2285 if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) && 2291 if ((INTEL_INFO(dev)->gen <= 6 || IS_IVYBRIDGE(dev)) &&
@@ -2295,15 +2301,19 @@ static void ilk_wm_merge(struct drm_device *dev,
2295 2301
2296 ilk_merge_wm_level(dev, level, wm); 2302 ilk_merge_wm_level(dev, level, wm);
2297 2303
2298 if (!ilk_validate_wm_level(level, max, wm)) 2304 if (level > last_enabled_level)
2299 break; 2305 wm->enable = false;
2306 else if (!ilk_validate_wm_level(level, max, wm))
2307 /* make sure all following levels get disabled */
2308 last_enabled_level = level - 1;
2300 2309
2301 /* 2310 /*
2302 * The spec says it is preferred to disable 2311 * The spec says it is preferred to disable
2303 * FBC WMs instead of disabling a WM level. 2312 * FBC WMs instead of disabling a WM level.
2304 */ 2313 */
2305 if (wm->fbc_val > max->fbc) { 2314 if (wm->fbc_val > max->fbc) {
2306 merged->fbc_wm_enabled = false; 2315 if (wm->enable)
2316 merged->fbc_wm_enabled = false;
2307 wm->fbc_val = 0; 2317 wm->fbc_val = 0;
2308 } 2318 }
2309 } 2319 }
@@ -2358,14 +2368,19 @@ static void ilk_compute_wm_results(struct drm_device *dev,
2358 level = ilk_wm_lp_to_level(wm_lp, merged); 2368 level = ilk_wm_lp_to_level(wm_lp, merged);
2359 2369
2360 r = &merged->wm[level]; 2370 r = &merged->wm[level];
2361 if (!r->enable)
2362 break;
2363 2371
2364 results->wm_lp[wm_lp - 1] = WM3_LP_EN | 2372 /*
2373 * Maintain the watermark values even if the level is
2374 * disabled. Doing otherwise could cause underruns.
2375 */
2376 results->wm_lp[wm_lp - 1] =
2365 (ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT) | 2377 (ilk_wm_lp_latency(dev, level) << WM1_LP_LATENCY_SHIFT) |
2366 (r->pri_val << WM1_LP_SR_SHIFT) | 2378 (r->pri_val << WM1_LP_SR_SHIFT) |
2367 r->cur_val; 2379 r->cur_val;
2368 2380
2381 if (r->enable)
2382 results->wm_lp[wm_lp - 1] |= WM1_LP_SR_EN;
2383
2369 if (INTEL_INFO(dev)->gen >= 8) 2384 if (INTEL_INFO(dev)->gen >= 8)
2370 results->wm_lp[wm_lp - 1] |= 2385 results->wm_lp[wm_lp - 1] |=
2371 r->fbc_val << WM1_LP_FBC_SHIFT_BDW; 2386 r->fbc_val << WM1_LP_FBC_SHIFT_BDW;
@@ -2373,6 +2388,10 @@ static void ilk_compute_wm_results(struct drm_device *dev,
2373 results->wm_lp[wm_lp - 1] |= 2388 results->wm_lp[wm_lp - 1] |=
2374 r->fbc_val << WM1_LP_FBC_SHIFT; 2389 r->fbc_val << WM1_LP_FBC_SHIFT;
2375 2390
2391 /*
2392 * Always set WM1S_LP_EN when spr_val != 0, even if the
2393 * level is disabled. Doing otherwise could cause underruns.
2394 */
2376 if (INTEL_INFO(dev)->gen <= 6 && r->spr_val) { 2395 if (INTEL_INFO(dev)->gen <= 6 && r->spr_val) {
2377 WARN_ON(wm_lp != 1); 2396 WARN_ON(wm_lp != 1);
2378 results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val; 2397 results->wm_lp_spr[wm_lp - 1] = WM1S_LP_EN | r->spr_val;