drm/i915: Add i830 "pipes power well"

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>
This commit is contained in:
Ville Syrjälä 2017-06-01 17:36:16 +03:00
parent bb408dd2b2
commit 2ee0da1631
3 changed files with 157 additions and 1 deletions

View 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

View 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);

View 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);
}