aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2010-12-21 16:10:23 -0500
committerChris Wilson <chris@chris-wilson.co.uk>2011-01-11 15:36:14 -0500
commitb79d4990226defc3789f9ba492b27e9e56790857 (patch)
tree8d1dd1b5b417057868aa89e9fa63325deeace5f4 /drivers/gpu
parentc97689d8860f086125e7ff9cd730027a0057ca4f (diff)
drm/i915: support low power watermarks on Ironlake
This patch actually makes the watermark code even uglier (if that's possible), but has the advantage of sharing code between SNB and ILK at least. Longer term we should refactor the watermark stuff into its own file and clean it up now that we know how it's supposed to work. Supporting WM2 on my Vaio reduced power consumption by around 0.5W, so this patch is definitely worthwhile (though it also needs lots of test coverage). Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> [ickle: pass the watermark structs arounds] Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h5
-rw-r--r--drivers/gpu/drm/i915/intel_display.c258
2 files changed, 133 insertions, 130 deletions
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 1bc816f3934b..ecfb0023f60d 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2345,8 +2345,13 @@
2345 2345
2346/* Memory latency timer register */ 2346/* Memory latency timer register */
2347#define MLTR_ILK 0x11222 2347#define MLTR_ILK 0x11222
2348#define MLTR_WM1_SHIFT 0
2349#define MLTR_WM2_SHIFT 8
2348/* the unit of memory self-refresh latency time is 0.5us */ 2350/* the unit of memory self-refresh latency time is 0.5us */
2349#define ILK_SRLT_MASK 0x3f 2351#define ILK_SRLT_MASK 0x3f
2352#define ILK_LATENCY(shift) (I915_READ(MLTR_ILK) >> (shift) & ILK_SRLT_MASK)
2353#define ILK_READ_WM1_LATENCY() ILK_LATENCY(MLTR_WM1_SHIFT)
2354#define ILK_READ_WM2_LATENCY() ILK_LATENCY(MLTR_WM2_SHIFT)
2350 2355
2351/* define the fifo size on Ironlake */ 2356/* define the fifo size on Ironlake */
2352#define ILK_DISPLAY_FIFO 128 2357#define ILK_DISPLAY_FIFO 128
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 9d97fd4e5558..79753b8ac797 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3456,113 +3456,17 @@ static bool ironlake_compute_wm0(struct drm_device *dev,
3456 return true; 3456 return true;
3457} 3457}
3458 3458
3459static void ironlake_update_wm(struct drm_device *dev,
3460 int planea_clock, int planeb_clock,
3461 int sr_hdisplay, int sr_htotal,
3462 int pixel_size)
3463{
3464 struct drm_i915_private *dev_priv = dev->dev_private;
3465 int plane_wm, cursor_wm, enabled;
3466 int tmp;
3467
3468 enabled = 0;
3469 if (ironlake_compute_wm0(dev, 0,
3470 &ironlake_display_wm_info,
3471 ILK_LP0_PLANE_LATENCY,
3472 &ironlake_cursor_wm_info,
3473 ILK_LP0_CURSOR_LATENCY,
3474 &plane_wm, &cursor_wm)) {
3475 I915_WRITE(WM0_PIPEA_ILK,
3476 (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
3477 DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
3478 " plane %d, " "cursor: %d\n",
3479 plane_wm, cursor_wm);
3480 enabled++;
3481 }
3482
3483 if (ironlake_compute_wm0(dev, 1,
3484 &ironlake_display_wm_info,
3485 ILK_LP0_PLANE_LATENCY,
3486 &ironlake_cursor_wm_info,
3487 ILK_LP0_CURSOR_LATENCY,
3488 &plane_wm, &cursor_wm)) {
3489 I915_WRITE(WM0_PIPEB_ILK,
3490 (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
3491 DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
3492 " plane %d, cursor: %d\n",
3493 plane_wm, cursor_wm);
3494 enabled++;
3495 }
3496
3497 /*
3498 * Calculate and update the self-refresh watermark only when one
3499 * display plane is used.
3500 */
3501 tmp = 0;
3502 if (enabled == 1) {
3503 unsigned long line_time_us;
3504 int small, large, plane_fbc;
3505 int sr_clock, entries;
3506 int line_count, line_size;
3507 /* Read the self-refresh latency. The unit is 0.5us */
3508 int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
3509
3510 sr_clock = planea_clock ? planea_clock : planeb_clock;
3511 line_time_us = (sr_htotal * 1000) / sr_clock;
3512
3513 /* Use ns/us then divide to preserve precision */
3514 line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
3515 / 1000;
3516 line_size = sr_hdisplay * pixel_size;
3517
3518 /* Use the minimum of the small and large buffer method for primary */
3519 small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000;
3520 large = line_count * line_size;
3521
3522 entries = DIV_ROUND_UP(min(small, large),
3523 ironlake_display_srwm_info.cacheline_size);
3524
3525 plane_fbc = entries * 64;
3526 plane_fbc = DIV_ROUND_UP(plane_fbc, line_size);
3527
3528 plane_wm = entries + ironlake_display_srwm_info.guard_size;
3529 if (plane_wm > (int)ironlake_display_srwm_info.max_wm)
3530 plane_wm = ironlake_display_srwm_info.max_wm;
3531
3532 /* calculate the self-refresh watermark for display cursor */
3533 entries = line_count * pixel_size * 64;
3534 entries = DIV_ROUND_UP(entries,
3535 ironlake_cursor_srwm_info.cacheline_size);
3536
3537 cursor_wm = entries + ironlake_cursor_srwm_info.guard_size;
3538 if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm)
3539 cursor_wm = ironlake_cursor_srwm_info.max_wm;
3540
3541 /* configure watermark and enable self-refresh */
3542 tmp = (WM1_LP_SR_EN |
3543 (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
3544 (plane_fbc << WM1_LP_FBC_SHIFT) |
3545 (plane_wm << WM1_LP_SR_SHIFT) |
3546 cursor_wm);
3547 DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d,"
3548 " cursor %d\n", plane_wm, plane_fbc, cursor_wm);
3549 }
3550 I915_WRITE(WM1_LP_ILK, tmp);
3551 /* XXX setup WM2 and WM3 */
3552}
3553
3554/* 3459/*
3555 * Check the wm result. 3460 * Check the wm result.
3556 * 3461 *
3557 * If any calculated watermark values is larger than the maximum value that 3462 * If any calculated watermark values is larger than the maximum value that
3558 * can be programmed into the associated watermark register, that watermark 3463 * can be programmed into the associated watermark register, that watermark
3559 * must be disabled. 3464 * must be disabled.
3560 *
3561 * Also return true if all of those watermark values is 0, which is set by
3562 * sandybridge_compute_srwm, to indicate the latency is ZERO.
3563 */ 3465 */
3564static bool sandybridge_check_srwm(struct drm_device *dev, int level, 3466static bool ironlake_check_srwm(struct drm_device *dev, int level,
3565 int fbc_wm, int display_wm, int cursor_wm) 3467 int fbc_wm, int display_wm, int cursor_wm,
3468 const struct intel_watermark_params *display,
3469 const struct intel_watermark_params *cursor)
3566{ 3470{
3567 struct drm_i915_private *dev_priv = dev->dev_private; 3471 struct drm_i915_private *dev_priv = dev->dev_private;
3568 3472
@@ -3571,7 +3475,7 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level,
3571 3475
3572 if (fbc_wm > SNB_FBC_MAX_SRWM) { 3476 if (fbc_wm > SNB_FBC_MAX_SRWM) {
3573 DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n", 3477 DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
3574 fbc_wm, SNB_FBC_MAX_SRWM, level); 3478 fbc_wm, SNB_FBC_MAX_SRWM, level);
3575 3479
3576 /* fbc has it's own way to disable FBC WM */ 3480 /* fbc has it's own way to disable FBC WM */
3577 I915_WRITE(DISP_ARB_CTL, 3481 I915_WRITE(DISP_ARB_CTL,
@@ -3579,15 +3483,15 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level,
3579 return false; 3483 return false;
3580 } 3484 }
3581 3485
3582 if (display_wm > SNB_DISPLAY_MAX_SRWM) { 3486 if (display_wm > display->max_wm) {
3583 DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n", 3487 DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
3584 display_wm, SNB_DISPLAY_MAX_SRWM, level); 3488 display_wm, SNB_DISPLAY_MAX_SRWM, level);
3585 return false; 3489 return false;
3586 } 3490 }
3587 3491
3588 if (cursor_wm > SNB_CURSOR_MAX_SRWM) { 3492 if (cursor_wm > cursor->max_wm) {
3589 DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n", 3493 DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
3590 cursor_wm, SNB_CURSOR_MAX_SRWM, level); 3494 cursor_wm, SNB_CURSOR_MAX_SRWM, level);
3591 return false; 3495 return false;
3592 } 3496 }
3593 3497
@@ -3602,16 +3506,18 @@ static bool sandybridge_check_srwm(struct drm_device *dev, int level,
3602/* 3506/*
3603 * Compute watermark values of WM[1-3], 3507 * Compute watermark values of WM[1-3],
3604 */ 3508 */
3605static bool sandybridge_compute_srwm(struct drm_device *dev, int level, 3509static bool ironlake_compute_srwm(struct drm_device *dev, int level,
3606 int hdisplay, int htotal, int pixel_size, 3510 int hdisplay, int htotal,
3607 int clock, int latency_ns, int *fbc_wm, 3511 int pixel_size, int clock, int latency_ns,
3608 int *display_wm, int *cursor_wm) 3512 const struct intel_watermark_params *display,
3513 const struct intel_watermark_params *cursor,
3514 int *fbc_wm, int *display_wm, int *cursor_wm)
3609{ 3515{
3610 3516
3611 unsigned long line_time_us; 3517 unsigned long line_time_us;
3518 int line_count, line_size;
3612 int small, large; 3519 int small, large;
3613 int entries; 3520 int entries;
3614 int line_count, line_size;
3615 3521
3616 if (!latency_ns) { 3522 if (!latency_ns) {
3617 *fbc_wm = *display_wm = *cursor_wm = 0; 3523 *fbc_wm = *display_wm = *cursor_wm = 0;
@@ -3626,24 +3532,110 @@ static bool sandybridge_compute_srwm(struct drm_device *dev, int level,
3626 small = ((clock * pixel_size / 1000) * latency_ns) / 1000; 3532 small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
3627 large = line_count * line_size; 3533 large = line_count * line_size;
3628 3534
3629 entries = DIV_ROUND_UP(min(small, large), 3535 entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
3630 sandybridge_display_srwm_info.cacheline_size); 3536 *display_wm = entries + display->guard_size;
3631 *display_wm = entries + sandybridge_display_srwm_info.guard_size;
3632 3537
3633 /* 3538 /*
3634 * Spec said: 3539 * Spec says:
3635 * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2 3540 * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
3636 */ 3541 */
3637 *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2; 3542 *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
3638 3543
3639 /* calculate the self-refresh watermark for display cursor */ 3544 /* calculate the self-refresh watermark for display cursor */
3640 entries = line_count * pixel_size * 64; 3545 entries = line_count * pixel_size * 64;
3641 entries = DIV_ROUND_UP(entries, 3546 entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
3642 sandybridge_cursor_srwm_info.cacheline_size); 3547 *cursor_wm = entries + cursor->guard_size;
3643 *cursor_wm = entries + sandybridge_cursor_srwm_info.guard_size;
3644 3548
3645 return sandybridge_check_srwm(dev, level, 3549 return ironlake_check_srwm(dev, level,
3646 *fbc_wm, *display_wm, *cursor_wm); 3550 *fbc_wm, *display_wm, *cursor_wm,
3551 display, cursor);
3552}
3553
3554static void ironlake_update_wm(struct drm_device *dev,
3555 int planea_clock, int planeb_clock,
3556 int hdisplay, int htotal,
3557 int pixel_size)
3558{
3559 struct drm_i915_private *dev_priv = dev->dev_private;
3560 int fbc_wm, plane_wm, cursor_wm, enabled;
3561 int clock;
3562
3563 enabled = 0;
3564 if (ironlake_compute_wm0(dev, 0,
3565 &ironlake_display_wm_info,
3566 ILK_LP0_PLANE_LATENCY,
3567 &ironlake_cursor_wm_info,
3568 ILK_LP0_CURSOR_LATENCY,
3569 &plane_wm, &cursor_wm)) {
3570 I915_WRITE(WM0_PIPEA_ILK,
3571 (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
3572 DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
3573 " plane %d, " "cursor: %d\n",
3574 plane_wm, cursor_wm);
3575 enabled++;
3576 }
3577
3578 if (ironlake_compute_wm0(dev, 1,
3579 &ironlake_display_wm_info,
3580 ILK_LP0_PLANE_LATENCY,
3581 &ironlake_cursor_wm_info,
3582 ILK_LP0_CURSOR_LATENCY,
3583 &plane_wm, &cursor_wm)) {
3584 I915_WRITE(WM0_PIPEB_ILK,
3585 (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
3586 DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
3587 " plane %d, cursor: %d\n",
3588 plane_wm, cursor_wm);
3589 enabled++;
3590 }
3591
3592 /*
3593 * Calculate and update the self-refresh watermark only when one
3594 * display plane is used.
3595 */
3596 I915_WRITE(WM3_LP_ILK, 0);
3597 I915_WRITE(WM2_LP_ILK, 0);
3598 I915_WRITE(WM1_LP_ILK, 0);
3599
3600 if (enabled != 1)
3601 return;
3602
3603 clock = planea_clock ? planea_clock : planeb_clock;
3604
3605 /* WM1 */
3606 if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
3607 clock, ILK_READ_WM1_LATENCY() * 500,
3608 &ironlake_display_srwm_info,
3609 &ironlake_cursor_srwm_info,
3610 &fbc_wm, &plane_wm, &cursor_wm))
3611 return;
3612
3613 I915_WRITE(WM1_LP_ILK,
3614 WM1_LP_SR_EN |
3615 (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
3616 (fbc_wm << WM1_LP_FBC_SHIFT) |
3617 (plane_wm << WM1_LP_SR_SHIFT) |
3618 cursor_wm);
3619
3620 /* WM2 */
3621 if (!ironlake_compute_srwm(dev, 2, hdisplay, htotal, pixel_size,
3622 clock, ILK_READ_WM2_LATENCY() * 500,
3623 &ironlake_display_srwm_info,
3624 &ironlake_cursor_srwm_info,
3625 &fbc_wm, &plane_wm, &cursor_wm))
3626 return;
3627
3628 I915_WRITE(WM2_LP_ILK,
3629 WM2_LP_EN |
3630 (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
3631 (fbc_wm << WM1_LP_FBC_SHIFT) |
3632 (plane_wm << WM1_LP_SR_SHIFT) |
3633 cursor_wm);
3634
3635 /*
3636 * WM3 is unsupported on ILK, probably because we don't have latency
3637 * data for that power state
3638 */
3647} 3639}
3648 3640
3649static void sandybridge_update_wm(struct drm_device *dev, 3641static void sandybridge_update_wm(struct drm_device *dev,
@@ -3701,9 +3693,11 @@ static void sandybridge_update_wm(struct drm_device *dev,
3701 clock = planea_clock ? planea_clock : planeb_clock; 3693 clock = planea_clock ? planea_clock : planeb_clock;
3702 3694
3703 /* WM1 */ 3695 /* WM1 */
3704 if (!sandybridge_compute_srwm(dev, 1, hdisplay, htotal, pixel_size, 3696 if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
3705 clock, SNB_READ_WM1_LATENCY() * 500, 3697 clock, SNB_READ_WM1_LATENCY() * 500,
3706 &fbc_wm, &plane_wm, &cursor_wm)) 3698 &sandybridge_display_srwm_info,
3699 &sandybridge_cursor_srwm_info,
3700 &fbc_wm, &plane_wm, &cursor_wm))
3707 return; 3701 return;
3708 3702
3709 I915_WRITE(WM1_LP_ILK, 3703 I915_WRITE(WM1_LP_ILK,
@@ -3714,10 +3708,12 @@ static void sandybridge_update_wm(struct drm_device *dev,
3714 cursor_wm); 3708 cursor_wm);
3715 3709
3716 /* WM2 */ 3710 /* WM2 */
3717 if (!sandybridge_compute_srwm(dev, 2, 3711 if (!ironlake_compute_srwm(dev, 2,
3718 hdisplay, htotal, pixel_size, 3712 hdisplay, htotal, pixel_size,
3719 clock, SNB_READ_WM2_LATENCY() * 500, 3713 clock, SNB_READ_WM2_LATENCY() * 500,
3720 &fbc_wm, &plane_wm, &cursor_wm)) 3714 &sandybridge_display_srwm_info,
3715 &sandybridge_cursor_srwm_info,
3716 &fbc_wm, &plane_wm, &cursor_wm))
3721 return; 3717 return;
3722 3718
3723 I915_WRITE(WM2_LP_ILK, 3719 I915_WRITE(WM2_LP_ILK,
@@ -3728,10 +3724,12 @@ static void sandybridge_update_wm(struct drm_device *dev,
3728 cursor_wm); 3724 cursor_wm);
3729 3725
3730 /* WM3 */ 3726 /* WM3 */
3731 if (!sandybridge_compute_srwm(dev, 3, 3727 if (!ironlake_compute_srwm(dev, 3,
3732 hdisplay, htotal, pixel_size, 3728 hdisplay, htotal, pixel_size,
3733 clock, SNB_READ_WM3_LATENCY() * 500, 3729 clock, SNB_READ_WM3_LATENCY() * 500,
3734 &fbc_wm, &plane_wm, &cursor_wm)) 3730 &sandybridge_display_srwm_info,
3731 &sandybridge_cursor_srwm_info,
3732 &fbc_wm, &plane_wm, &cursor_wm))
3735 return; 3733 return;
3736 3734
3737 I915_WRITE(WM3_LP_ILK, 3735 I915_WRITE(WM3_LP_ILK,