]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/commitdiff
drm/i915: Add i830 "pipes power well"
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 1 Jun 2017 14:36:16 +0000 (17:36 +0300)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 15 Jun 2017 12:35:38 +0000 (15:35 +0300)
830 more or less requires both pipes and DPLLs to remain on as long
as either pipe is needed. However, when neither pipe is actually needed,
we can save a bit of power by turning everything off. To do that we add
a new "power well" that turns both pipes and DPLLs on and off in the
right order. Seems to save ~50mW on my Fujitsu-Siemens Lifebook S6010.

This also avoids having to abuse the load detection to force pipe A on
at init time. That was never very robust, and it only worked for one
pipe, whereas 830 really needs both pipes enabled. As a bonus the 830
pipe quirk is now a bit more isolated from the rest of the mode setting
infrastructure, which should mean that it's much less likely someone
will accidentally break it in the future. The extra cost is of course
slight code duplication, but that seems like a worthwile tradeoff here.

v2; s/BIT/BIT_ULL/

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170601143619.27840-5-ville.syrjala@linux.intel.com
Acked-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_runtime_pm.c

index 7318005421593b879e9b07c90b091eeb613ebd99..379679eb4366ae3475fd0ac9ee795ac5ea7a69a6 100644 (file)
@@ -5836,6 +5836,10 @@ static void i9xx_crtc_disable(struct intel_crtc_state *old_crtc_state,
 
        if (!dev_priv->display.initial_watermarks)
                intel_update_watermarks(intel_crtc);
+
+       /* clock the pipe down to 640x480@60 to potentially save power */
+       if (IS_I830(dev_priv))
+               i830_enable_pipe(dev_priv, pipe);
 }
 
 static void intel_crtc_disable_noatomic(struct drm_crtc *crtc,
@@ -15145,6 +15149,91 @@ int intel_modeset_init(struct drm_device *dev)
        return 0;
 }
 
+void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+       /* 640x480@60Hz, ~25175 kHz */
+       struct dpll clock = {
+               .m1 = 18,
+               .m2 = 7,
+               .p1 = 13,
+               .p2 = 4,
+               .n = 2,
+       };
+       u32 dpll, fp;
+       int i;
+
+       WARN_ON(i9xx_calc_dpll_params(48000, &clock) != 25154);
+
+       DRM_DEBUG_KMS("enabling pipe %c due to force quirk (vco=%d dot=%d)\n",
+                     pipe_name(pipe), clock.vco, clock.dot);
+
+       fp = i9xx_dpll_compute_fp(&clock);
+       dpll = (I915_READ(DPLL(pipe)) & DPLL_DVO_2X_MODE) |
+               DPLL_VGA_MODE_DIS |
+               ((clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT) |
+               PLL_P2_DIVIDE_BY_4 |
+               PLL_REF_INPUT_DREFCLK |
+               DPLL_VCO_ENABLE;
+
+       I915_WRITE(FP0(pipe), fp);
+       I915_WRITE(FP1(pipe), fp);
+
+       I915_WRITE(HTOTAL(pipe), (640 - 1) | ((800 - 1) << 16));
+       I915_WRITE(HBLANK(pipe), (640 - 1) | ((800 - 1) << 16));
+       I915_WRITE(HSYNC(pipe), (656 - 1) | ((752 - 1) << 16));
+       I915_WRITE(VTOTAL(pipe), (480 - 1) | ((525 - 1) << 16));
+       I915_WRITE(VBLANK(pipe), (480 - 1) | ((525 - 1) << 16));
+       I915_WRITE(VSYNC(pipe), (490 - 1) | ((492 - 1) << 16));
+       I915_WRITE(PIPESRC(pipe), ((640 - 1) << 16) | (480 - 1));
+
+       /*
+        * Apparently we need to have VGA mode enabled prior to changing
+        * the P1/P2 dividers. Otherwise the DPLL will keep using the old
+        * dividers, even though the register value does change.
+        */
+       I915_WRITE(DPLL(pipe), dpll & ~DPLL_VGA_MODE_DIS);
+       I915_WRITE(DPLL(pipe), dpll);
+
+       /* Wait for the clocks to stabilize. */
+       POSTING_READ(DPLL(pipe));
+       udelay(150);
+
+       /* The pixel multiplier can only be updated once the
+        * DPLL is enabled and the clocks are stable.
+        *
+        * So write it again.
+        */
+       I915_WRITE(DPLL(pipe), dpll);
+
+       /* We do this three times for luck */
+       for (i = 0; i < 3 ; i++) {
+               I915_WRITE(DPLL(pipe), dpll);
+               POSTING_READ(DPLL(pipe));
+               udelay(150); /* wait for warmup */
+       }
+
+       I915_WRITE(PIPECONF(pipe), PIPECONF_ENABLE | PIPECONF_PROGRESSIVE);
+       POSTING_READ(PIPECONF(pipe));
+}
+
+void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe)
+{
+       DRM_DEBUG_KMS("disabling pipe %c due to force quirk\n",
+                     pipe_name(pipe));
+
+       assert_plane_disabled(dev_priv, PLANE_A);
+       assert_plane_disabled(dev_priv, PLANE_B);
+
+       I915_WRITE(PIPECONF(pipe), 0);
+       POSTING_READ(PIPECONF(pipe));
+
+       if (wait_for(pipe_dsl_stopped(dev_priv, pipe), 100))
+               DRM_ERROR("pipe %c off wait timed out\n", pipe_name(pipe));
+
+       I915_WRITE(DPLL(pipe), DPLL_VGA_MODE_DIS);
+       POSTING_READ(DPLL(pipe));
+}
+
 static void intel_enable_pipe_a(struct drm_device *dev,
                                struct drm_modeset_acquire_ctx *ctx)
 {
@@ -15274,7 +15363,8 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
                crtc->plane = plane;
        }
 
-       if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
+       if (!IS_I830(dev_priv) &&
+           dev_priv->quirks & QUIRK_PIPEA_FORCE &&
            crtc->pipe == PIPE_A && !crtc->active) {
                /* BIOS forgot to enable pipe A, this mostly happens after
                 * resume. Force-enable the pipe to fix this, the update_dpms
index 2bc3326f40680ddc888d56d845745ecf7786b506..4d3982bb596a380814c2b3595b1f81ca03a759d3 100644 (file)
@@ -1332,6 +1332,8 @@ void intel_set_cdclk(struct drm_i915_private *dev_priv,
                     const struct intel_cdclk_state *cdclk_state);
 
 /* intel_display.c */
+void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
+void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
 enum transcoder intel_crtc_pch_transcoder(struct intel_crtc *crtc);
 void intel_update_rawclk(struct drm_i915_private *dev_priv);
 int vlv_get_hpll_vco(struct drm_i915_private *dev_priv);
index 436ec7a7b8431bf4de56d146d2bfe6343efa4fb4..efe80ed5fd4d062f1999bfafc19670293d5a3dea 100644 (file)
@@ -1041,6 +1041,38 @@ static bool i9xx_always_on_power_well_enabled(struct drm_i915_private *dev_priv,
        return true;
 }
 
+static void i830_pipes_power_well_enable(struct drm_i915_private *dev_priv,
+                                        struct i915_power_well *power_well)
+{
+       if ((I915_READ(PIPECONF(PIPE_A)) & PIPECONF_ENABLE) == 0)
+               i830_enable_pipe(dev_priv, PIPE_A);
+       if ((I915_READ(PIPECONF(PIPE_B)) & PIPECONF_ENABLE) == 0)
+               i830_enable_pipe(dev_priv, PIPE_B);
+}
+
+static void i830_pipes_power_well_disable(struct drm_i915_private *dev_priv,
+                                         struct i915_power_well *power_well)
+{
+       i830_disable_pipe(dev_priv, PIPE_B);
+       i830_disable_pipe(dev_priv, PIPE_A);
+}
+
+static bool i830_pipes_power_well_enabled(struct drm_i915_private *dev_priv,
+                                         struct i915_power_well *power_well)
+{
+       return I915_READ(PIPECONF(PIPE_A)) & PIPECONF_ENABLE &&
+               I915_READ(PIPECONF(PIPE_B)) & PIPECONF_ENABLE;
+}
+
+static void i830_pipes_power_well_sync_hw(struct drm_i915_private *dev_priv,
+                                         struct i915_power_well *power_well)
+{
+       if (power_well->count > 0)
+               i830_pipes_power_well_enable(dev_priv, power_well);
+       else
+               i830_pipes_power_well_disable(dev_priv, power_well);
+}
+
 static void vlv_set_power_well(struct drm_i915_private *dev_priv,
                               struct i915_power_well *power_well, bool enable)
 {
@@ -1929,6 +1961,15 @@ void intel_display_power_put(struct drm_i915_private *dev_priv,
        BIT_ULL(POWER_DOMAIN_AUX_D) |           \
        BIT_ULL(POWER_DOMAIN_INIT))
 
+#define I830_PIPES_POWER_DOMAINS (             \
+       BIT_ULL(POWER_DOMAIN_PIPE_A) |          \
+       BIT_ULL(POWER_DOMAIN_PIPE_B) |          \
+       BIT_ULL(POWER_DOMAIN_PIPE_A_PANEL_FITTER) |     \
+       BIT_ULL(POWER_DOMAIN_PIPE_B_PANEL_FITTER) |     \
+       BIT_ULL(POWER_DOMAIN_TRANSCODER_A) |    \
+       BIT_ULL(POWER_DOMAIN_TRANSCODER_B) |    \
+       BIT_ULL(POWER_DOMAIN_INIT))
+
 static const struct i915_power_well_ops i9xx_always_on_power_well_ops = {
        .sync_hw = i9xx_power_well_sync_hw_noop,
        .enable = i9xx_always_on_power_well_noop,
@@ -1959,6 +2000,27 @@ static struct i915_power_well i9xx_always_on_power_well[] = {
        },
 };
 
+static const struct i915_power_well_ops i830_pipes_power_well_ops = {
+       .sync_hw = i830_pipes_power_well_sync_hw,
+       .enable = i830_pipes_power_well_enable,
+       .disable = i830_pipes_power_well_disable,
+       .is_enabled = i830_pipes_power_well_enabled,
+};
+
+static struct i915_power_well i830_power_wells[] = {
+       {
+               .name = "always-on",
+               .always_on = 1,
+               .domains = POWER_DOMAIN_MASK,
+               .ops = &i9xx_always_on_power_well_ops,
+       },
+       {
+               .name = "pipes",
+               .domains = I830_PIPES_POWER_DOMAINS,
+               .ops = &i830_pipes_power_well_ops,
+       },
+};
+
 static const struct i915_power_well_ops hsw_power_well_ops = {
        .sync_hw = hsw_power_well_sync_hw,
        .enable = hsw_power_well_enable,
@@ -2504,6 +2566,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
                set_power_wells(power_domains, chv_power_wells);
        } else if (IS_VALLEYVIEW(dev_priv)) {
                set_power_wells(power_domains, vlv_power_wells);
+       } else if (IS_I830(dev_priv)) {
+               set_power_wells(power_domains, i830_power_wells);
        } else {
                set_power_wells(power_domains, i9xx_always_on_power_well);
        }