Skip to content

Commit

Permalink
drm/i915: get mode clock when reading the pipe config v9
Browse files Browse the repository at this point in the history
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 <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: Daniel Vetter <daniel.vetter@ffwll.ch>
  • Loading branch information
Jesse Barnes authored and Daniel Vetter committed Jul 1, 2013
1 parent 2385bdf commit f1f644d
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 11 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
126 changes: 115 additions & 11 deletions drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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. */
Expand All @@ -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));
Expand All @@ -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;
Expand Down Expand Up @@ -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, \
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -9253,20 +9350,23 @@ 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;
dev_priv->display.off = ironlake_crtc_off;
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;
dev_priv->display.off = i9xx_crtc_off;
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;
Expand Down Expand Up @@ -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;
}
Expand Down
7 changes: 7 additions & 0 deletions drivers/gpu/drm/i915/intel_dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit f1f644d

Please sign in to comment.