Commit f1f644dc authored by Jesse Barnes's avatar Jesse Barnes Committed by Daniel Vetter

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: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[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: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent 2385bdf0
......@@ -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);
......
......@@ -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
*/
return clock.dot;
/*
* 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;
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;
}
......
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment