Commit d4906093 authored by Ma Ling's avatar Ma Ling Committed by Eric Anholt

drm/i915: Use a different PLL timing search function on G4X.

This improves the PLL timings according to the suggestion of the hardware
engineers.  This results in some outputs being able to sync that weren't
able to before.

This is part of fixing fd.o bug #17508.
Signed-off-by: default avatarMa Ling <ling.ma@intel.com>
[anholt: cleaned up a couple of redundant comments]
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 044c7c41
...@@ -56,11 +56,13 @@ typedef struct { ...@@ -56,11 +56,13 @@ typedef struct {
} intel_p2_t; } intel_p2_t;
#define INTEL_P2_NUM 2 #define INTEL_P2_NUM 2
typedef struct intel_limit intel_limit_t;
typedef struct { struct intel_limit {
intel_range_t dot, vco, n, m, m1, m2, p, p1; intel_range_t dot, vco, n, m, m1, m2, p, p1;
intel_p2_t p2; intel_p2_t p2;
} intel_limit_t; bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
int, int, intel_clock_t *);
};
#define I8XX_DOT_MIN 25000 #define I8XX_DOT_MIN 25000
#define I8XX_DOT_MAX 350000 #define I8XX_DOT_MAX 350000
...@@ -198,6 +200,12 @@ typedef struct { ...@@ -198,6 +200,12 @@ typedef struct {
#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 #define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
static bool
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
static bool
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock);
static const intel_limit_t intel_limits[] = { static const intel_limit_t intel_limits[] = {
{ /* INTEL_LIMIT_I8XX_DVO_DAC */ { /* INTEL_LIMIT_I8XX_DVO_DAC */
...@@ -211,6 +219,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -211,6 +219,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
.p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
.find_pll = intel_find_best_PLL,
}, },
{ /* INTEL_LIMIT_I8XX_LVDS */ { /* INTEL_LIMIT_I8XX_LVDS */
.dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
...@@ -223,6 +232,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -223,6 +232,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
.p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
.p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
.find_pll = intel_find_best_PLL,
}, },
{ /* INTEL_LIMIT_I9XX_SDVO_DAC */ { /* INTEL_LIMIT_I9XX_SDVO_DAC */
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
...@@ -235,6 +245,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -235,6 +245,7 @@ static const intel_limit_t intel_limits[] = {
.p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
.p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
.p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
.find_pll = intel_find_best_PLL,
}, },
{ /* INTEL_LIMIT_I9XX_LVDS */ { /* INTEL_LIMIT_I9XX_LVDS */
.dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
...@@ -250,6 +261,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -250,6 +261,7 @@ static const intel_limit_t intel_limits[] = {
*/ */
.p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
.p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
.find_pll = intel_find_best_PLL,
}, },
/* below parameter and function is for G4X Chipset Family*/ /* below parameter and function is for G4X Chipset Family*/
{ /* INTEL_LIMIT_G4X_SDVO */ { /* INTEL_LIMIT_G4X_SDVO */
...@@ -265,6 +277,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -265,6 +277,7 @@ static const intel_limit_t intel_limits[] = {
.p2_slow = G4X_P2_SDVO_SLOW, .p2_slow = G4X_P2_SDVO_SLOW,
.p2_fast = G4X_P2_SDVO_FAST .p2_fast = G4X_P2_SDVO_FAST
}, },
.find_pll = intel_g4x_find_best_PLL,
}, },
{ /* INTEL_LIMIT_G4X_HDMI_DAC */ { /* INTEL_LIMIT_G4X_HDMI_DAC */
.dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX }, .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX },
...@@ -279,6 +292,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -279,6 +292,7 @@ static const intel_limit_t intel_limits[] = {
.p2_slow = G4X_P2_HDMI_DAC_SLOW, .p2_slow = G4X_P2_HDMI_DAC_SLOW,
.p2_fast = G4X_P2_HDMI_DAC_FAST .p2_fast = G4X_P2_HDMI_DAC_FAST
}, },
.find_pll = intel_g4x_find_best_PLL,
}, },
{ /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */ { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */
.dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN, .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
...@@ -301,6 +315,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -301,6 +315,7 @@ static const intel_limit_t intel_limits[] = {
.p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW, .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
.p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
}, },
.find_pll = intel_g4x_find_best_PLL,
}, },
{ /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */ { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */
.dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN, .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
...@@ -323,6 +338,7 @@ static const intel_limit_t intel_limits[] = { ...@@ -323,6 +338,7 @@ static const intel_limit_t intel_limits[] = {
.p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW, .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
.p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
}, },
.find_pll = intel_g4x_find_best_PLL,
}, },
}; };
...@@ -437,18 +453,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock) ...@@ -437,18 +453,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
return true; return true;
} }
/** static bool
* Returns a set of divisors for the desired target clock with the given intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
* refclk, or FALSE. The returned values represent the clock equation: int target, int refclk, intel_clock_t *best_clock)
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
*/
static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
int refclk, intel_clock_t *best_clock)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
intel_clock_t clock; intel_clock_t clock;
const intel_limit_t *limit = intel_limit(crtc);
int err = target; int err = target;
if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
...@@ -500,6 +512,63 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target, ...@@ -500,6 +512,63 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
return (err != target); return (err != target);
} }
static bool
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *best_clock)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
intel_clock_t clock;
int max_n;
bool found;
/* approximately equals target * 0.00488 */
int err_most = (target >> 8) + (target >> 10);
found = false;
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
LVDS_CLKB_POWER_UP)
clock.p2 = limit->p2.p2_fast;
else
clock.p2 = limit->p2.p2_slow;
} else {
if (target < limit->p2.dot_limit)
clock.p2 = limit->p2.p2_slow;
else
clock.p2 = limit->p2.p2_fast;
}
memset(best_clock, 0, sizeof(*best_clock));
max_n = limit->n.max;
/* based on hardware requriment prefer smaller n to precision */
for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
/* based on hardware requirment prefere larger m1,m2, p1 */
for (clock.m1 = limit->m1.max;
clock.m1 >= limit->m1.min; clock.m1--) {
for (clock.m2 = limit->m2.max;
clock.m2 >= limit->m2.min; clock.m2--) {
for (clock.p1 = limit->p1.max;
clock.p1 >= limit->p1.min; clock.p1--) {
int this_err;
intel_clock(refclk, &clock);
if (!intel_PLL_is_valid(crtc, &clock))
continue;
this_err = abs(clock.dot - target) ;
if (this_err < err_most) {
*best_clock = clock;
err_most = this_err;
max_n = clock.n;
found = true;
}
}
}
}
}
return found;
}
void void
intel_wait_for_vblank(struct drm_device *dev) intel_wait_for_vblank(struct drm_device *dev)
{ {
...@@ -918,6 +987,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, ...@@ -918,6 +987,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
bool is_crt = false, is_lvds = false, is_tv = false; bool is_crt = false, is_lvds = false, is_tv = false;
struct drm_mode_config *mode_config = &dev->mode_config; struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector; struct drm_connector *connector;
const intel_limit_t *limit;
int ret; int ret;
drm_vblank_pre_modeset(dev, pipe); drm_vblank_pre_modeset(dev, pipe);
...@@ -961,7 +1031,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, ...@@ -961,7 +1031,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
refclk = 48000; refclk = 48000;
} }
ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); /*
* 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);
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
if (!ok) { if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n"); DRM_ERROR("Couldn't find PLL settings for mode!\n");
return -EINVAL; return -EINVAL;
......
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