[Intel-gfx] [PATCH 2/2] drm/i915: add support for reduced pixel clocks on ILK+
Jesse Barnes
jbarnes at virtuousgeek.org
Tue Jan 11 00:41:26 CET 2011
From 24a5d325ff9970ec10c45d084b8d703408d72961 Mon Sep 17 00:00:00 2001
From: Jesse Barnes <jbarnes at virtuousgeek.org>
Date: Mon, 10 Jan 2011 15:34:50 -0800
Subject: [PATCH] drm/i915: add support for reduced pixel clocks on ILK+
As long as we calculate and program secondary PLL values, we can enable
our reduced refresh code to automatically downclock the display when
it's not active.
Signed-off-by: Jesse Barnes <jbarnes at virtuousgeek.org>
---
drivers/gpu/drm/i915/i915_reg.h | 10 +
drivers/gpu/drm/i915/intel_display.c | 471 +++++++++++++++++++---------------
drivers/gpu/drm/i915/intel_dp.c | 54 +++--
drivers/gpu/drm/i915/intel_drv.h | 3 +-
4 files changed, 306 insertions(+), 232 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2fceb96..f327a70 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -2251,6 +2251,7 @@
#define PIPECONF_PROGRESSIVE (0 << 21)
#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21)
#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21)
+#define PIPECONF_POWERSAVE (1<<20)
#define PIPECONF_CXSR_DOWNCLOCK (1<<16)
#define PIPECONF_BPP_MASK (0x000000e0)
#define PIPECONF_BPP_8 (0<<5)
@@ -2958,6 +2959,15 @@
#define TRANSA_DP_LINK_M2 0xe0048
#define TRANSA_DP_LINK_N2 0xe004c
+#define TRANS_DATA_M1(pipe) _PIPE(pipe, TRANSA_DATA_M1, TRANSB_DATA_M1)
+#define TRANS_DATA_N1(pipe) _PIPE(pipe, TRANSA_DATA_N1, TRANSB_DATA_N1)
+#define TRANS_DATA_M2(pipe) _PIPE(pipe, TRANSA_DATA_M2, TRANSB_DATA_M2)
+#define TRANS_DATA_N2(pipe) _PIPE(pipe, TRANSA_DATA_N2, TRANSB_DATA_N2)
+#define TRANS_DP_LINK_M1(pipe) _PIPE(pipe, TRANSA_DP_LINK_M1, TRANSB_DP_LINK_M1)
+#define TRANS_DP_LINK_N1(pipe) _PIPE(pipe, TRANSA_DP_LINK_N1, TRANSB_DP_LINK_N1)
+#define TRANS_DP_LINK_M2(pipe) _PIPE(pipe, TRANSA_DP_LINK_M2, TRANSB_DP_LINK_M2)
+#define TRANS_DP_LINK_N2(pipe) _PIPE(pipe, TRANSA_DP_LINK_N2, TRANSB_DP_LINK_N2)
+
#define TRANS_HTOTAL_B 0xe1000
#define TRANS_HBLANK_B 0xe1004
#define TRANS_HSYNC_B 0xe1008
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index aa1579b..2fcd17e 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -39,7 +39,13 @@
#include "drm_crtc_helper.h"
-#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+#define HAS_eDP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_EDP))
+#define HAS_DP(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_DISPLAYPORT))
+#define HAS_LVDS(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_LVDS))
+#define HAS_TVOUT(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_TVOUT))
+#define HAS_SDVO(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_SDVO))
+#define HAS_HDMI(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_HDMI))
+#define HAS_ANALOG(crtc) (intel_pipe_has_type((crtc), INTEL_OUTPUT_ANALOG))
bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev);
@@ -649,7 +655,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
struct drm_i915_private *dev_priv = dev->dev_private;
const intel_limit_t *limit;
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if (HAS_LVDS(crtc)) {
if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP) {
/* LVDS dual channel */
@@ -663,8 +669,7 @@ static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
else
limit = &intel_limits_ironlake_single_lvds;
}
- } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
- HAS_eDP)
+ } else if (HAS_DP(crtc) || HAS_eDP(crtc))
limit = &intel_limits_ironlake_display_port;
else
limit = &intel_limits_ironlake_dac;
@@ -678,7 +683,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private;
const intel_limit_t *limit;
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if (HAS_LVDS(crtc)) {
if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP)
/* LVDS with dual channel */
@@ -686,12 +691,11 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
else
/* LVDS with dual channel */
limit = &intel_limits_g4x_single_channel_lvds;
- } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) ||
- intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
+ } else if (HAS_HDMI(crtc) || HAS_ANALOG(crtc)) {
limit = &intel_limits_g4x_hdmi;
- } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+ } else if (HAS_SDVO(crtc)) {
limit = &intel_limits_g4x_sdvo;
- } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ } else if (HAS_DP(crtc)) {
limit = &intel_limits_g4x_display_port;
} else /* The option is for other outputs */
limit = &intel_limits_i9xx_sdvo;
@@ -709,17 +713,17 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
else if (IS_G4X(dev)) {
limit = intel_g4x_limit(crtc);
} else if (IS_PINEVIEW(dev)) {
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ if (HAS_LVDS(crtc))
limit = &intel_limits_pineview_lvds;
else
limit = &intel_limits_pineview_sdvo;
} else if (!IS_GEN2(dev)) {
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ if (HAS_LVDS(crtc))
limit = &intel_limits_i9xx_lvds;
else
limit = &intel_limits_i9xx_sdvo;
} else {
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ if (HAS_LVDS(crtc))
limit = &intel_limits_i8xx_lvds;
else
limit = &intel_limits_i8xx_dvo;
@@ -809,8 +813,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
intel_clock_t clock;
int err = target;
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
- (I915_READ(LVDS)) != 0) {
+ if (HAS_LVDS(crtc) && (I915_READ(LVDS)) != 0) {
/*
* For LVDS, if the panel is on, just rely on its current
* settings for dual-channel. We haven't figured out how to
@@ -875,7 +878,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int err_most = (target >> 8) + (target >> 9);
found = false;
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if (HAS_LVDS(crtc)) {
int lvds_reg;
if (HAS_PCH_SPLIT(dev))
@@ -2039,7 +2042,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_crtc->active = true;
intel_update_watermarks(dev);
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if (HAS_LVDS(crtc)) {
temp = I915_READ(PCH_LVDS);
if ((temp & LVDS_PORT_EN) == 0)
I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
@@ -2049,7 +2052,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
/* Enable panel fitting for LVDS */
if (dev_priv->pch_pf_size &&
- (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) || HAS_eDP)) {
+ (HAS_LVDS(crtc) || HAS_eDP(crtc))) {
/* Force use of hard-coded filter coefficients
* as some pre-programmed values are broken,
* e.g. x201.
@@ -2116,8 +2119,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_fdi_normal_train(crtc);
/* For PCH DP, enable TRANS_DP_CTL */
- if (HAS_PCH_CPT(dev) &&
- intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+ if (HAS_PCH_CPT(dev) && HAS_DP(crtc)) {
reg = TRANS_DP_CTL(pipe);
temp = I915_READ(reg);
temp &= ~(TRANS_DP_PORT_SEL_MASK |
@@ -2256,7 +2258,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
POSTING_READ(reg);
udelay(100);
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ if (HAS_LVDS(crtc)) {
temp = I915_READ(PCH_LVDS);
if (temp & LVDS_PORT_EN) {
I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
@@ -3815,6 +3817,144 @@ static void intel_update_watermarks(struct drm_device *dev)
sr_hdisplay, sr_htotal, pixel_size);
}
+static int intel_find_clock(struct drm_crtc *crtc, int refclk, int target_clock,
+ intel_clock_t *result)
+{
+ struct drm_device *dev = crtc->dev;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ const intel_limit_t *limit;
+ bool ok;
+
+ /*
+ * Returns a set of divisors for the desired target clock with the given
+ * refclk, or FALSE. The returned values represent the clock equation:
+ * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+ */
+ limit = intel_limit(crtc, refclk);
+ ok = limit->find_pll(limit, crtc, target_clock, refclk, result);
+ if (!ok) {
+ drm_vblank_post_modeset(dev, pipe);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * SDVO TV has fixed PLL values depend on its clock range,
+ * this mirrors vbios setting.
+ */
+static void intel_sdvo_tv_adjust(struct drm_display_mode *mode, intel_clock_t *clock)
+{
+ if (mode->clock >= 100000 && mode->clock < 140500) {
+ clock->p1 = 2;
+ clock->p2 = 10;
+ clock->n = 3;
+ clock->m1 = 16;
+ clock->m2 = 8;
+ } else if (mode->clock >= 140500 && mode->clock <= 200000) {
+ clock->p1 = 1;
+ clock->p2 = 10;
+ clock->n = 6;
+ clock->m1 = 12;
+ clock->m2 = 8;
+ }
+}
+
+static int intel_calc_fdi_m_n(struct drm_crtc *crtc,
+ int target_clock,
+ int pixel_multiplier,
+ struct intel_encoder *edp_encoder,
+ struct fdi_m_n *m_n)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ int lane = 0, link_bw, bpp;
+ u32 temp;
+
+ /* CPU eDP doesn't require FDI link, so just set DP M/N
+ according to current link config */
+ if (edp_encoder && !intel_encoder_is_pch_edp(&edp_encoder->base)) {
+ intel_edp_link_config(edp_encoder, &lane, &link_bw);
+ } else {
+ /* FDI is a binary signal running at ~2.7GHz, encoding
+ * each output octet as 10 bits. The actual frequency
+ * is stored as a divider into a 100MHz clock, and the
+ * mode pixel clock is stored in units of 1KHz.
+ * Hence the bw of each lane in terms of the mode signal
+ * is:
+ */
+ link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
+ }
+
+ /* determine panel color depth */
+ temp = I915_READ(PIPECONF(pipe));
+ temp &= ~PIPE_BPC_MASK;
+ if (HAS_LVDS(crtc)) {
+ /* the BPC will be 6 if it is 18-bit LVDS panel */
+ if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+ temp |= PIPE_8BPC;
+ else
+ temp |= PIPE_6BPC;
+ } else if (edp_encoder) {
+ switch (dev_priv->edp.bpp/3) {
+ case 8:
+ temp |= PIPE_8BPC;
+ break;
+ case 10:
+ temp |= PIPE_10BPC;
+ break;
+ case 6:
+ temp |= PIPE_6BPC;
+ break;
+ case 12:
+ temp |= PIPE_12BPC;
+ break;
+ }
+ } else
+ temp |= PIPE_8BPC;
+ I915_WRITE(PIPECONF(pipe), temp);
+
+ switch (temp & PIPE_BPC_MASK) {
+ case PIPE_8BPC:
+ bpp = 24;
+ break;
+ case PIPE_10BPC:
+ bpp = 30;
+ break;
+ case PIPE_6BPC:
+ bpp = 18;
+ break;
+ case PIPE_12BPC:
+ bpp = 36;
+ break;
+ default:
+ DRM_ERROR("unknown pipe bpc value\n");
+ bpp = 24;
+ }
+
+ if (!lane) {
+ /*
+ * Account for spread spectrum to avoid
+ * oversubscribing the link. Max center spread
+ * is 2.5%; use 5% for safety's sake.
+ */
+ u32 bps = target_clock * bpp * 21 / 20;
+ lane = bps / (link_bw * 8) + 1;
+ }
+
+ intel_crtc->fdi_lanes = lane;
+
+ if (pixel_multiplier > 1)
+ link_bw *= pixel_multiplier;
+ ironlake_compute_m_n(bpp, lane, target_clock, link_bw, m_n);
+
+ return 0;
+}
+
static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -3830,54 +3970,29 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
int refclk, num_connectors = 0;
intel_clock_t clock, reduced_clock;
u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
- bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
- bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+ bool has_reduced_clock = false;
struct intel_encoder *has_edp_encoder = NULL;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *encoder;
- const intel_limit_t *limit;
int ret;
- struct fdi_m_n m_n = {0};
+ struct fdi_m_n m_n = {0}, reduced_m_n = {0};
u32 reg, temp;
- int target_clock;
+ int target_clock, pixel_multiplier;
drm_vblank_pre_modeset(dev, pipe);
- list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
- if (encoder->base.crtc != crtc)
- continue;
-
- switch (encoder->type) {
- case INTEL_OUTPUT_LVDS:
- is_lvds = true;
- break;
- case INTEL_OUTPUT_SDVO:
- case INTEL_OUTPUT_HDMI:
- is_sdvo = true;
- if (encoder->needs_tv_clock)
- is_tv = true;
- break;
- case INTEL_OUTPUT_DVO:
- is_dvo = true;
- break;
- case INTEL_OUTPUT_TVOUT:
- is_tv = true;
- break;
- case INTEL_OUTPUT_ANALOG:
- is_crt = true;
- break;
- case INTEL_OUTPUT_DISPLAYPORT:
- is_dp = true;
- break;
- case INTEL_OUTPUT_EDP:
- has_edp_encoder = encoder;
- break;
+ /*
+ * We need to do things differently if this crtc is driving more than
+ * one output
+ */
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+ if (encoder->base.crtc == crtc) {
+ num_connectors++;
+ if (encoder->type == INTEL_OUTPUT_EDP)
+ has_edp_encoder = encoder;
}
- num_connectors++;
- }
-
- if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) {
+ if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2) {
refclk = dev_priv->lvds_ssc_freq * 1000;
DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
refclk / 1000);
@@ -3890,28 +4005,32 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
refclk = 48000;
}
- /*
- * Returns a set of divisors for the desired target clock with the given
- * refclk, or FALSE. The returned values represent the clock equation:
- * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
- */
- limit = intel_limit(crtc, refclk);
- ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
- if (!ok) {
- DRM_ERROR("Couldn't find PLL settings for mode!\n");
- drm_vblank_post_modeset(dev, pipe);
- return -EINVAL;
- }
/* Ensure that the cursor is valid for the new mode before changing... */
intel_crtc_update_cursor(crtc, true);
- if ((is_lvds || has_edp_encoder) && dev_priv->panel_downclock_avail) {
- has_reduced_clock = limit->find_pll(limit, crtc,
- dev_priv->panel_downclock,
- refclk,
- &reduced_clock);
- if (has_reduced_clock && (clock.p != reduced_clock.p)) {
+ /* [e]DP over FDI requires target mode clock
+ instead of link clock */
+ if (HAS_DP(crtc) || HAS_eDP(crtc))
+ target_clock = mode->clock;
+ else
+ target_clock = adjusted_mode->clock;
+ pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+
+ ret = intel_find_clock(crtc, refclk, target_clock, &clock);
+ if (ret) {
+ DRM_ERROR("Couldn't find PLL settings for mode: refclk %d, target %d\n", refclk, target_clock);
+ return ret;
+ }
+
+ /* Find a PLL values for a reduced refresh if possible */
+ if ((HAS_LVDS(crtc) || has_edp_encoder) &&
+ dev_priv->panel_downclock_avail) {
+ ret = intel_find_clock(crtc, refclk, dev_priv->panel_downclock,
+ &reduced_clock);
+ if (ret)
+ DRM_ERROR("Couldn't find PLL settings for reduced refresh mode: refclk %d, target %d\n", refclk, adjusted_mode->clock);
+ else if (clock.p != reduced_clock.p) {
/*
* If the different P is found, it means that we can't
* switch the display clock by using the FP0/FP1.
@@ -3920,118 +4039,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
*/
DRM_DEBUG_KMS("Different P is found for "
"LVDS clock/downclock\n");
- has_reduced_clock = 0;
- }
- }
- /* SDVO TV has fixed PLL values depend on its clock range,
- this mirrors vbios setting. */
- if (is_sdvo && is_tv) {
- if (adjusted_mode->clock >= 100000
- && adjusted_mode->clock < 140500) {
- clock.p1 = 2;
- clock.p2 = 10;
- clock.n = 3;
- clock.m1 = 16;
- clock.m2 = 8;
- } else if (adjusted_mode->clock >= 140500
- && adjusted_mode->clock <= 200000) {
- clock.p1 = 1;
- clock.p2 = 10;
- clock.n = 6;
- clock.m1 = 12;
- clock.m2 = 8;
- }
- }
-
- /* FDI link */
- if (HAS_PCH_SPLIT(dev)) {
- int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
- int lane = 0, link_bw, bpp;
- /* CPU eDP doesn't require FDI link, so just set DP M/N
- according to current link config */
- if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- target_clock = mode->clock;
- intel_edp_link_config(has_edp_encoder,
- &lane, &link_bw);
- } else {
- /* [e]DP over FDI requires target mode clock
- instead of link clock */
- if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
- target_clock = mode->clock;
- else
- target_clock = adjusted_mode->clock;
-
- /* FDI is a binary signal running at ~2.7GHz, encoding
- * each output octet as 10 bits. The actual frequency
- * is stored as a divider into a 100MHz clock, and the
- * mode pixel clock is stored in units of 1KHz.
- * Hence the bw of each lane in terms of the mode signal
- * is:
- */
- link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
- }
-
- /* determine panel color depth */
- temp = I915_READ(PIPECONF(pipe));
- temp &= ~PIPE_BPC_MASK;
- if (is_lvds) {
- /* the BPC will be 6 if it is 18-bit LVDS panel */
- if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
- temp |= PIPE_8BPC;
- else
- temp |= PIPE_6BPC;
- } else if (has_edp_encoder) {
- switch (dev_priv->edp.bpp/3) {
- case 8:
- temp |= PIPE_8BPC;
- break;
- case 10:
- temp |= PIPE_10BPC;
- break;
- case 6:
- temp |= PIPE_6BPC;
- break;
- case 12:
- temp |= PIPE_12BPC;
- break;
- }
+ has_reduced_clock = false;
} else
- temp |= PIPE_8BPC;
- I915_WRITE(PIPECONF(pipe), temp);
-
- switch (temp & PIPE_BPC_MASK) {
- case PIPE_8BPC:
- bpp = 24;
- break;
- case PIPE_10BPC:
- bpp = 30;
- break;
- case PIPE_6BPC:
- bpp = 18;
- break;
- case PIPE_12BPC:
- bpp = 36;
- break;
- default:
- DRM_ERROR("unknown pipe bpc value\n");
- bpp = 24;
- }
-
- if (!lane) {
- /*
- * Account for spread spectrum to avoid
- * oversubscribing the link. Max center spread
- * is 2.5%; use 5% for safety's sake.
- */
- u32 bps = target_clock * bpp * 21 / 20;
- lane = bps / (link_bw * 8) + 1;
- }
+ has_reduced_clock = true;
+ }
- intel_crtc->fdi_lanes = lane;
+ if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
+ intel_sdvo_tv_adjust(adjusted_mode, &clock);
- if (pixel_multiplier > 1)
- link_bw *= pixel_multiplier;
- ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+ if (HAS_PCH_SPLIT(dev)) {
+ intel_calc_fdi_m_n(crtc, target_clock, pixel_multiplier,
+ has_edp_encoder, &m_n);
+ if (has_reduced_clock)
+ intel_calc_fdi_m_n(crtc, dev_priv->panel_downclock,
+ pixel_multiplier, has_edp_encoder,
+ &reduced_m_n);
}
/* Ironlake: try to setup display ref clock before DPLL
@@ -4096,12 +4118,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (HAS_PCH_SPLIT(dev)) {
int factor = 21;
- if (is_lvds) {
+ if (HAS_LVDS(crtc)) {
if ((dev_priv->lvds_use_ssc &&
dev_priv->lvds_ssc_freq == 100) ||
(I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
factor = 25;
- } else if (is_sdvo && is_tv)
+ } else if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
factor = 20;
if (clock.m1 < factor * clock.n)
@@ -4113,11 +4135,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
dpll = DPLL_VGA_MODE_DIS;
if (!IS_GEN2(dev)) {
- if (is_lvds)
+ if (HAS_LVDS(crtc))
dpll |= DPLLB_MODE_LVDS;
else
dpll |= DPLLB_MODE_DAC_SERIAL;
- if (is_sdvo) {
+ if (HAS_SDVO(crtc)) {
int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
if (pixel_multiplier > 1) {
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
@@ -4127,7 +4149,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
dpll |= DPLL_DVO_HIGH_SPEED;
}
- if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base))
+ if (HAS_DP(crtc) || intel_encoder_is_pch_edp(&has_edp_encoder->base))
dpll |= DPLL_DVO_HIGH_SPEED;
/* compute bitmask from p1 value */
@@ -4158,7 +4180,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev))
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
} else {
- if (is_lvds) {
+ if (HAS_LVDS(crtc)) {
dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
} else {
if (clock.p1 == 2)
@@ -4170,13 +4192,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
}
- if (is_sdvo && is_tv)
+ if (HAS_SDVO(crtc) && HAS_TVOUT(crtc))
dpll |= PLL_REF_INPUT_TVCLKINBC;
- else if (is_tv)
+ else if (HAS_TVOUT(crtc))
/* XXX: just matching BIOS for now */
/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
dpll |= 3;
- else if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2)
+ else if (HAS_LVDS(crtc) && dev_priv->lvds_use_ssc && num_connectors < 2)
dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
else
dpll |= PLL_REF_INPUT_DREFCLK;
@@ -4252,7 +4274,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
* This is an exception to the general rule that mode_set doesn't turn
* things on.
*/
- if (is_lvds) {
+ if (HAS_LVDS(crtc)) {
reg = LVDS;
if (HAS_PCH_SPLIT(dev))
reg = PCH_LVDS;
@@ -4298,14 +4320,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (HAS_PCH_SPLIT(dev)) {
pipeconf &= ~PIPECONF_DITHER_EN;
pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
- if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+ if (dev_priv->lvds_dither && (HAS_LVDS(crtc) || has_edp_encoder)) {
pipeconf |= PIPECONF_DITHER_EN;
pipeconf |= PIPECONF_DITHER_TYPE_ST1;
}
}
- if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
- intel_dp_set_m_n(crtc, mode, adjusted_mode);
+ if (HAS_DP(crtc) || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
+ intel_dp_set_m_n(crtc, mode, adjusted_mode,
+ has_reduced_clock ? dev_priv->panel_downclock : 0);
} else if (HAS_PCH_SPLIT(dev)) {
/* For non-DP output, clear any trans DP clock recovery setting.*/
if (pipe == 0) {
@@ -4330,7 +4353,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
temp = 0;
- if (is_sdvo) {
+ if (HAS_SDVO(crtc)) {
temp = intel_mode_get_pixel_multiplier(adjusted_mode);
if (temp > 1)
temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
@@ -4349,7 +4372,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
intel_crtc->lowfreq_avail = false;
- if ((is_lvds || has_edp_encoder) && has_reduced_clock && i915_powersave) {
+ if ((HAS_LVDS(crtc) || has_edp_encoder) && has_reduced_clock && i915_powersave) {
I915_WRITE(fp_reg + 4, fp2);
intel_crtc->lowfreq_avail = true;
if (HAS_PIPE_CXSR(dev)) {
@@ -4414,11 +4437,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(PIPE_LINK_M1(pipe), m_n.link_m);
I915_WRITE(PIPE_LINK_N1(pipe), m_n.link_n);
- I915_WRITE(PIPE_DATA_M2(pipe), TU_SIZE(m_n.tu) | m_n.gmch_m);
- I915_WRITE(PIPE_DATA_N2(pipe), m_n.gmch_n);
- I915_WRITE(PIPE_LINK_M2(pipe), m_n.link_m);
- I915_WRITE(PIPE_LINK_N2(pipe), m_n.link_n);
-
+ if (has_reduced_clock) {
+ I915_WRITE(PIPE_DATA_M2(pipe),
+ TU_SIZE(reduced_m_n.tu) |
+ reduced_m_n.gmch_m);
+ I915_WRITE(PIPE_DATA_N2(pipe), reduced_m_n.gmch_n);
+ I915_WRITE(PIPE_LINK_M2(pipe), reduced_m_n.link_m);
+ I915_WRITE(PIPE_LINK_N2(pipe), reduced_m_n.link_n);
+ }
if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
ironlake_set_pll_edp(crtc, adjusted_mode->clock);
}
@@ -5047,12 +5073,21 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
if (!dev_priv->panel_downclock_avail)
return;
- if (HAS_PCH_SPLIT(dev))
- dpll_reg = PCH_DPLL(pipe);
+ if (HAS_PCH_SPLIT(dev)) {
+ int pipeconf_reg = PIPECONF(pipe);
+ u32 pipeconf;
+
+ DRM_DEBUG_DRIVER("upclocking panel\n");
+ pipeconf = I915_READ(pipeconf_reg);
+ pipeconf &= ~PIPECONF_POWERSAVE;
+ I915_WRITE(pipeconf_reg, pipeconf);
+ POSTING_READ(pipeconf_reg);
+ goto out;
+ }
dpll = I915_READ(dpll_reg);
if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
- DRM_ERROR("upclocking LVDS\n");
+ DRM_DEBUG_DRIVER("upclocking panel\n");
dpll &= ~DISPLAY_RATE_SELECT_FPA1;
I915_WRITE(dpll_reg, dpll);
@@ -5061,9 +5096,10 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
dpll = I915_READ(dpll_reg);
if (dpll & DISPLAY_RATE_SELECT_FPA1)
- DRM_ERROR("failed to upclock LVDS!\n");
+ DRM_ERROR("failed to upclock panel!\n");
}
+out:
/* Schedule downclock */
mod_timer(&intel_crtc->idle_timer, jiffies +
msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
@@ -5081,15 +5117,24 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
if (!dev_priv->panel_downclock_avail)
return;
- if (HAS_PCH_SPLIT(dev))
- dpll_reg = PCH_DPLL(pipe);
+ if (HAS_PCH_SPLIT(dev)) {
+ int pipeconf_reg = PIPECONF(pipe);
+ u32 pipeconf;
+
+ DRM_DEBUG_DRIVER("downclocking panel\n");
+ pipeconf = I915_READ(pipeconf_reg);
+ pipeconf |= PIPECONF_POWERSAVE;
+ I915_WRITE(pipeconf_reg, pipeconf);
+ POSTING_READ(pipeconf_reg);
+ return;
+ }
/*
* Since this is called by a timer, we should never get here in
* the manual case.
*/
if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
- DRM_ERROR("downclocking LVDS\n");
+ DRM_DEBUG_DRIVER("downclocking panel\n");
dpll |= DISPLAY_RATE_SELECT_FPA1;
I915_WRITE(dpll_reg, dpll);
@@ -5097,7 +5142,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
intel_wait_for_vblank(dev, pipe);
dpll = I915_READ(dpll_reg);
if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
- DRM_ERROR("failed to downclock LVDS!\n");
+ DRM_ERROR("failed to downclock panel!\n");
}
}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index feb0c23..d1cbad5 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -676,7 +676,8 @@ intel_dp_compute_m_n(int bpp,
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_display_mode *adjusted_mode,
+ int reduced_pixel_clock)
{
struct drm_device *dev = crtc->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
@@ -684,7 +685,8 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int lane_count = 4, bpp = 24;
- struct intel_dp_m_n m_n;
+ struct intel_dp_m_n m_n, reduced_m_n;
+ int pipe = intel_crtc->pipe;
/*
* Find the lane count in the intel_encoder private
@@ -711,24 +713,40 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
* the number of bytes_per_pixel post-LUT, which we always
* set up for 8-bits of R/G/B, or 3 bytes total.
*/
- intel_dp_compute_m_n(bpp, lane_count,
- mode->clock, adjusted_mode->clock, &m_n);
+ intel_dp_compute_m_n(bpp, lane_count, mode->clock, adjusted_mode->clock,
+ &m_n);
+ if (reduced_pixel_clock)
+ intel_dp_compute_m_n(bpp, lane_count, reduced_pixel_clock,
+ adjusted_mode->clock, &reduced_m_n);
if (HAS_PCH_SPLIT(dev)) {
- if (intel_crtc->pipe == 0) {
- I915_WRITE(TRANSA_DATA_M1,
- ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
- m_n.gmch_m);
- I915_WRITE(TRANSA_DATA_N1, m_n.gmch_n);
- I915_WRITE(TRANSA_DP_LINK_M1, m_n.link_m);
- I915_WRITE(TRANSA_DP_LINK_N1, m_n.link_n);
- } else {
- I915_WRITE(TRANSB_DATA_M1,
- ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
- m_n.gmch_m);
- I915_WRITE(TRANSB_DATA_N1, m_n.gmch_n);
- I915_WRITE(TRANSB_DP_LINK_M1, m_n.link_m);
- I915_WRITE(TRANSB_DP_LINK_N1, m_n.link_n);
+ int trans_m1, trans_n1, trans_m2, trans_n2;
+ int trans_dp_m1, trans_dp_n1, trans_dp_m2, trans_dp_n2;
+
+ trans_m1 = TRANS_DATA_M1(pipe);
+ trans_n1 = TRANS_DATA_N1(pipe);
+ trans_m2 = TRANS_DATA_M2(pipe);
+ trans_n2 = TRANS_DATA_N2(pipe);
+
+ trans_dp_m1 = TRANS_DP_LINK_M1(pipe);
+ trans_dp_n1 = TRANS_DP_LINK_N1(pipe);
+ trans_dp_m2 = TRANS_DP_LINK_M2(pipe);
+ trans_dp_n2 = TRANS_DP_LINK_N2(pipe);
+
+ I915_WRITE(trans_m1,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ m_n.gmch_m);
+ I915_WRITE(trans_n1, m_n.gmch_n);
+ I915_WRITE(trans_dp_m1, m_n.link_m);
+ I915_WRITE(trans_dp_n1, m_n.link_n);
+
+ if (reduced_pixel_clock) {
+ I915_WRITE(trans_m2,
+ ((reduced_m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+ reduced_m_n.gmch_m);
+ I915_WRITE(trans_n2, reduced_m_n.gmch_n);
+ I915_WRITE(trans_dp_m2, reduced_m_n.link_m);
+ I915_WRITE(trans_dp_n2, reduced_m_n.link_n);
}
} else {
if (intel_crtc->pipe == 0) {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d782ad9..a8313d2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -242,7 +242,8 @@ extern bool intel_lvds_init(struct drm_device *dev);
extern void intel_dp_init(struct drm_device *dev, int dp_reg);
void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode);
+ struct drm_display_mode *adjusted_mode,
+ int reduced_pixel_clock);
extern bool intel_dpd_is_edp(struct drm_device *dev);
extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
--
1.7.0.4
More information about the Intel-gfx
mailing list