From f1f644dc66cbaf5a4c7dcde683361536b41885b9 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 27 Jun 2013 00:39:25 +0300 Subject: [PATCH] drm/i915: get mode clock when reading the pipe config v9 We need this for comparing modes between configuration changes. The tricky part is to allow us to reuse the new get_clock stuff to recover the lvds clock on gen2/3 when neither the vbt has an lvds mode nor the panel a (useful) EDID. v2: try harder to calulate non-simple pixel clocks (Daniel) call get_clock after getting the encoder config, needed for pixel multiply (Jesse) v3: drop get_clock now that the pixel_multiply has been moved into get_pipe_config v4: re-add get_clock; we need to get the pixel multiplier in the encoder, so need to calculate the clock value after the encoder's get_config is called v5: drop hsw clock_get, still needs to be written v6: add fuzzy clock check (Daniel) v7: wrap fuzzy clock check under !IS_HASWELL use port_clock field rather than a new CPU eDP clock field in crtc_config v8: remove stale pixel_multiplier sets (Daniel) multiply by pixel_multiplier in 9xx clock get too (Daniel) v9: make sure we set pixel_multiplier before calling clock_get from mode_get for LVDS (Daniel) Signed-off-by: Jesse Barnes [danvet: Add some explanation to the commit message about why we have to jump through a few hoops. Also remove the rebase-fail hunk from intel_sdvo.c] [danvet: Squash in the fixup from Jesse to also call ->get_clock in the modeset state checker.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_display.c | 126 ++++++++++++++++++++++++++++++++--- drivers/gpu/drm/i915/intel_dp.c | 7 ++ 3 files changed, 123 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4a23e957b85c..810c9fab525e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -367,6 +367,7 @@ struct drm_i915_display_funcs { * fills out the pipe-config with the hw state. */ bool (*get_pipe_config)(struct intel_crtc *, struct intel_crtc_config *); + void (*get_clock)(struct intel_crtc *, struct intel_crtc_config *); int (*crtc_mode_set)(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 65e8f5e512d2..9c05e57fb7ae 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -45,6 +45,11 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type); static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); +static void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config); +static void ironlake_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config); + typedef struct { int min, max; } intel_range_t; @@ -6853,11 +6858,12 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, } /* Returns the clock of the currently programmed mode of the given pipe. */ -static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) +static void i9xx_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) { + struct drm_device *dev = crtc->base.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 pipe = pipe_config->cpu_transcoder; u32 dpll = I915_READ(DPLL(pipe)); u32 fp; intel_clock_t clock; @@ -6896,7 +6902,8 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) default: DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed " "mode\n", (int)(dpll & DPLL_MODE_MASK)); - return 0; + pipe_config->adjusted_mode.clock = 0; + return; } if (IS_PINEVIEW(dev)) @@ -6933,12 +6940,55 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) } } - /* XXX: It would be nice to validate the clocks, but we can't reuse - * i830PllIsValid() because it relies on the xf86_config connector - * configuration being accurate, which it isn't necessarily. + pipe_config->adjusted_mode.clock = clock.dot * + pipe_config->pixel_multiplier; +} + +static void ironlake_crtc_clock_get(struct intel_crtc *crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + enum transcoder cpu_transcoder = pipe_config->cpu_transcoder; + int link_freq, repeat; + u64 clock; + u32 link_m, link_n; + + repeat = pipe_config->pixel_multiplier; + + /* + * The calculation for the data clock is: + * pixel_clock = ((m/n)*(link_clock * nr_lanes * repeat))/bpp + * But we want to avoid losing precison if possible, so: + * pixel_clock = ((m * link_clock * nr_lanes * repeat)/(n*bpp)) + * + * and the link clock is simpler: + * link_clock = (m * link_clock * repeat) / n + */ + + /* + * We need to get the FDI or DP link clock here to derive + * the M/N dividers. + * + * For FDI, we read it from the BIOS or use a fixed 2.7GHz. + * For DP, it's either 1.62GHz or 2.7GHz. + * We do our calculations in 10*MHz since we don't need much precison. */ + if (pipe_config->has_pch_encoder) + link_freq = intel_fdi_link_freq(dev) * 10000; + else + link_freq = pipe_config->port_clock; + + link_m = I915_READ(PIPE_LINK_M1(cpu_transcoder)); + link_n = I915_READ(PIPE_LINK_N1(cpu_transcoder)); + + if (!link_m || !link_n) + return; - return clock.dot; + clock = ((u64)link_m * (u64)link_freq * (u64)repeat); + do_div(clock, link_n); + + pipe_config->adjusted_mode.clock = clock; } /** Returns the currently programmed mode of the given pipe. */ @@ -6949,6 +6999,7 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder; struct drm_display_mode *mode; + struct intel_crtc_config pipe_config; int htot = I915_READ(HTOTAL(cpu_transcoder)); int hsync = I915_READ(HSYNC(cpu_transcoder)); int vtot = I915_READ(VTOTAL(cpu_transcoder)); @@ -6958,7 +7009,18 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, if (!mode) return NULL; - mode->clock = intel_crtc_clock_get(dev, crtc); + /* + * Construct a pipe_config sufficient for getting the clock info + * back out of crtc_clock_get. + * + * Note, if LVDS ever uses a non-1 pixel multiplier, we'll need + * to use a real value here instead. + */ + pipe_config.cpu_transcoder = intel_crtc->pipe; + pipe_config.pixel_multiplier = 1; + i9xx_crtc_clock_get(intel_crtc, &pipe_config); + + mode->clock = pipe_config.adjusted_mode.clock; mode->hdisplay = (htot & 0xffff) + 1; mode->htotal = ((htot & 0xffff0000) >> 16) + 1; mode->hsync_start = (hsync & 0xffff) + 1; @@ -8019,6 +8081,28 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) } +static bool intel_fuzzy_clock_check(struct intel_crtc_config *cur, + struct intel_crtc_config *new) +{ + int clock1, clock2, diff; + + clock1 = cur->adjusted_mode.clock; + clock2 = new->adjusted_mode.clock; + + if (clock1 == clock2) + return true; + + if (!clock1 || !clock2) + return false; + + diff = abs(clock1 - clock2); + + if (((((diff + clock1 + clock2) * 100)) / (clock1 + clock2)) < 105) + return true; + + return false; +} + #define for_each_intel_crtc_masked(dev, mask, intel_crtc) \ list_for_each_entry((intel_crtc), \ &(dev)->mode_config.crtc_list, \ @@ -8124,6 +8208,15 @@ intel_pipe_config_compare(struct drm_device *dev, #undef PIPE_CONF_CHECK_FLAGS #undef PIPE_CONF_QUIRK + if (!IS_HASWELL(dev)) { + if (!intel_fuzzy_clock_check(current_config, pipe_config)) { + DRM_ERROR("mismatch in clock (expected %d, found %d\n", + current_config->adjusted_mode.clock, + pipe_config->adjusted_mode.clock); + return false; + } + } + return true; } @@ -8249,8 +8342,12 @@ check_crtc_state(struct drm_device *dev) base.head) { if (encoder->base.crtc != &crtc->base) continue; - if (encoder->get_config) + if (encoder->get_config && + dev_priv->display.get_clock) { encoder->get_config(encoder, &pipe_config); + dev_priv->display.get_clock(crtc, + &pipe_config); + } } WARN(crtc->active != active, @@ -9253,6 +9350,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = ironlake_update_plane; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.get_pipe_config = ironlake_get_pipe_config; + dev_priv->display.get_clock = ironlake_crtc_clock_get; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; dev_priv->display.crtc_enable = ironlake_crtc_enable; dev_priv->display.crtc_disable = ironlake_crtc_disable; @@ -9260,6 +9358,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = ironlake_update_plane; } else if (IS_VALLEYVIEW(dev)) { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.get_clock = i9xx_crtc_clock_get; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = valleyview_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; @@ -9267,6 +9366,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.update_plane = i9xx_update_plane; } else { dev_priv->display.get_pipe_config = i9xx_get_pipe_config; + dev_priv->display.get_clock = i9xx_crtc_clock_get; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; @@ -9813,8 +9913,12 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) if (encoder->get_hw_state(encoder, &pipe)) { crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); encoder->base.crtc = &crtc->base; - if (encoder->get_config) + if (encoder->get_config && + dev_priv->display.get_clock) { encoder->get_config(encoder, &crtc->config); + dev_priv->display.get_clock(crtc, + &crtc->config); + } } else { encoder->base.crtc = NULL; } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b73971234013..11eb697dec01 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1355,6 +1355,13 @@ static void intel_dp_get_config(struct intel_encoder *encoder, } pipe_config->adjusted_mode.flags |= flags; + + if (dp_to_dig_port(intel_dp)->port == PORT_A) { + if ((I915_READ(DP_A) & DP_PLL_FREQ_MASK) == DP_PLL_FREQ_160MHZ) + pipe_config->port_clock = 162000; + else + pipe_config->port_clock = 270000; + } } static void intel_disable_dp(struct intel_encoder *encoder) -- 2.11.0