From patchwork Mon Jan 10 23:41:26 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 469911 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p0B00Bbj013321 for ; Tue, 11 Jan 2011 00:00:37 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 7C3229E9E6 for ; Mon, 10 Jan 2011 15:42:43 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from cpoproxy3-pub.bluehost.com (cpoproxy3-pub.bluehost.com [67.222.54.6]) by gabe.freedesktop.org (Postfix) with SMTP id 0665A9E790 for ; Mon, 10 Jan 2011 15:41:31 -0800 (PST) Received: (qmail 15673 invoked by uid 0); 10 Jan 2011 23:41:31 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by cpoproxy3.bluehost.com with SMTP; 10 Jan 2011 23:41:31 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=virtuousgeek.org; h=Received:Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References:X-Mailer:Mime-Version:Content-Type:Content-Transfer-Encoding:X-Identified-User; b=UvvWH/mX+5CXBhFz1zI0oYQvPvvSz51krO6iknitsq7nlumVt/xCY1xBDR8gtX8T5Jzmnsl3gbtrbjMAXrCqixon2FWDL+OY1BT8vm6zGYQPsj/rC3hMXNzhHOvV9ZlC; Received: from c-67-174-193-198.hsd1.ca.comcast.net ([67.174.193.198] helo=jbarnes-desktop) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1PcRMd-0006hK-Fi; Mon, 10 Jan 2011 16:41:27 -0700 Date: Mon, 10 Jan 2011 15:41:26 -0800 From: Jesse Barnes To: Jesse Barnes Message-ID: <20110110154126.4a4204d4@jbarnes-desktop> In-Reply-To: <1294702736-2790-3-git-send-email-jbarnes@virtuousgeek.org> References: <1294702736-2790-1-git-send-email-jbarnes@virtuousgeek.org> <1294702736-2790-3-git-send-email-jbarnes@virtuousgeek.org> X-Mailer: Claws Mail 3.7.6 (GTK+ 2.18.9; x86_64-redhat-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 67.174.193.198 authed with jbarnes@virtuousgeek.org} Cc: intel-gfx@lists.freedesktop.org Subject: Re: [Intel-gfx] [PATCH 2/2] drm/i915: add support for reduced pixel clocks on ILK+ X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 11 Jan 2011 00:00:37 +0000 (UTC) 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);