aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorMa Ling <ling.ma@intel.com>2009-03-18 08:13:27 -0400
committerEric Anholt <eric@anholt.net>2009-03-27 17:45:12 -0400
commitd490609321828c62e8dfa6220f0acd82e5cb3756 (patch)
treea15e45265673aa20784dfd693fd9903395dcb536 /drivers/gpu
parent044c7c415a68077b7c444c753aa03a35149e881a (diff)
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: Ma Ling <ling.ma@intel.com> [anholt: cleaned up a couple of redundant comments] Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/intel_display.c100
1 files changed, 88 insertions, 12 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 89f7af0bdb12..8e29545273b6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -56,11 +56,13 @@ typedef struct {
56} intel_p2_t; 56} intel_p2_t;
57 57
58#define INTEL_P2_NUM 2 58#define INTEL_P2_NUM 2
59 59typedef struct intel_limit intel_limit_t;
60typedef struct { 60struct intel_limit {
61 intel_range_t dot, vco, n, m, m1, m2, p, p1; 61 intel_range_t dot, vco, n, m, m1, m2, p, p1;
62 intel_p2_t p2; 62 intel_p2_t p2;
63} intel_limit_t; 63 bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
64 int, int, intel_clock_t *);
65};
64 66
65#define I8XX_DOT_MIN 25000 67#define I8XX_DOT_MIN 25000
66#define I8XX_DOT_MAX 350000 68#define I8XX_DOT_MAX 350000
@@ -198,6 +200,12 @@ typedef struct {
198#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7 200#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
199#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0 201#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
200 202
203static bool
204intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
205 int target, int refclk, intel_clock_t *best_clock);
206static bool
207intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
208 int target, int refclk, intel_clock_t *best_clock);
201 209
202static const intel_limit_t intel_limits[] = { 210static const intel_limit_t intel_limits[] = {
203 { /* INTEL_LIMIT_I8XX_DVO_DAC */ 211 { /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -211,6 +219,7 @@ static const intel_limit_t intel_limits[] = {
211 .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX }, 219 .p1 = { .min = I8XX_P1_MIN, .max = I8XX_P1_MAX },
212 .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, 220 .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
213 .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, 221 .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
222 .find_pll = intel_find_best_PLL,
214 }, 223 },
215 { /* INTEL_LIMIT_I8XX_LVDS */ 224 { /* INTEL_LIMIT_I8XX_LVDS */
216 .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, 225 .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
@@ -223,6 +232,7 @@ static const intel_limit_t intel_limits[] = {
223 .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, 232 .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX },
224 .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, 233 .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
225 .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST }, 234 .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
235 .find_pll = intel_find_best_PLL,
226 }, 236 },
227 { /* INTEL_LIMIT_I9XX_SDVO_DAC */ 237 { /* INTEL_LIMIT_I9XX_SDVO_DAC */
228 .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, 238 .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
@@ -235,6 +245,7 @@ static const intel_limit_t intel_limits[] = {
235 .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX }, 245 .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
236 .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, 246 .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
237 .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, 247 .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
248 .find_pll = intel_find_best_PLL,
238 }, 249 },
239 { /* INTEL_LIMIT_I9XX_LVDS */ 250 { /* INTEL_LIMIT_I9XX_LVDS */
240 .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, 251 .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
@@ -250,6 +261,7 @@ static const intel_limit_t intel_limits[] = {
250 */ 261 */
251 .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, 262 .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
252 .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST }, 263 .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
264 .find_pll = intel_find_best_PLL,
253 }, 265 },
254 /* below parameter and function is for G4X Chipset Family*/ 266 /* below parameter and function is for G4X Chipset Family*/
255 { /* INTEL_LIMIT_G4X_SDVO */ 267 { /* INTEL_LIMIT_G4X_SDVO */
@@ -265,6 +277,7 @@ static const intel_limit_t intel_limits[] = {
265 .p2_slow = G4X_P2_SDVO_SLOW, 277 .p2_slow = G4X_P2_SDVO_SLOW,
266 .p2_fast = G4X_P2_SDVO_FAST 278 .p2_fast = G4X_P2_SDVO_FAST
267 }, 279 },
280 .find_pll = intel_g4x_find_best_PLL,
268 }, 281 },
269 { /* INTEL_LIMIT_G4X_HDMI_DAC */ 282 { /* INTEL_LIMIT_G4X_HDMI_DAC */
270 .dot = { .min = G4X_DOT_HDMI_DAC_MIN, .max = G4X_DOT_HDMI_DAC_MAX }, 283 .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 .p2_slow = G4X_P2_HDMI_DAC_SLOW, 292 .p2_slow = G4X_P2_HDMI_DAC_SLOW,
280 .p2_fast = G4X_P2_HDMI_DAC_FAST 293 .p2_fast = G4X_P2_HDMI_DAC_FAST
281 }, 294 },
295 .find_pll = intel_g4x_find_best_PLL,
282 }, 296 },
283 { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */ 297 { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */
284 .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN, 298 .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
@@ -301,6 +315,7 @@ static const intel_limit_t intel_limits[] = {
301 .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW, 315 .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
302 .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST 316 .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
303 }, 317 },
318 .find_pll = intel_g4x_find_best_PLL,
304 }, 319 },
305 { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */ 320 { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */
306 .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN, 321 .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
@@ -323,6 +338,7 @@ static const intel_limit_t intel_limits[] = {
323 .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW, 338 .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
324 .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST 339 .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
325 }, 340 },
341 .find_pll = intel_g4x_find_best_PLL,
326 }, 342 },
327}; 343};
328 344
@@ -437,18 +453,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
437 return true; 453 return true;
438} 454}
439 455
440/** 456static bool
441 * Returns a set of divisors for the desired target clock with the given 457intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
442 * refclk, or FALSE. The returned values represent the clock equation: 458 int target, int refclk, intel_clock_t *best_clock)
443 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. 459
444 */
445static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
446 int refclk, intel_clock_t *best_clock)
447{ 460{
448 struct drm_device *dev = crtc->dev; 461 struct drm_device *dev = crtc->dev;
449 struct drm_i915_private *dev_priv = dev->dev_private; 462 struct drm_i915_private *dev_priv = dev->dev_private;
450 intel_clock_t clock; 463 intel_clock_t clock;
451 const intel_limit_t *limit = intel_limit(crtc);
452 int err = target; 464 int err = target;
453 465
454 if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && 466 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 return (err != target); 512 return (err != target);
501} 513}
502 514
515static bool
516intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
517 int target, int refclk, intel_clock_t *best_clock)
518{
519 struct drm_device *dev = crtc->dev;
520 struct drm_i915_private *dev_priv = dev->dev_private;
521 intel_clock_t clock;
522 int max_n;
523 bool found;
524 /* approximately equals target * 0.00488 */
525 int err_most = (target >> 8) + (target >> 10);
526 found = false;
527
528 if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
529 if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
530 LVDS_CLKB_POWER_UP)
531 clock.p2 = limit->p2.p2_fast;
532 else
533 clock.p2 = limit->p2.p2_slow;
534 } else {
535 if (target < limit->p2.dot_limit)
536 clock.p2 = limit->p2.p2_slow;
537 else
538 clock.p2 = limit->p2.p2_fast;
539 }
540
541 memset(best_clock, 0, sizeof(*best_clock));
542 max_n = limit->n.max;
543 /* based on hardware requriment prefer smaller n to precision */
544 for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
545 /* based on hardware requirment prefere larger m1,m2, p1 */
546 for (clock.m1 = limit->m1.max;
547 clock.m1 >= limit->m1.min; clock.m1--) {
548 for (clock.m2 = limit->m2.max;
549 clock.m2 >= limit->m2.min; clock.m2--) {
550 for (clock.p1 = limit->p1.max;
551 clock.p1 >= limit->p1.min; clock.p1--) {
552 int this_err;
553
554 intel_clock(refclk, &clock);
555 if (!intel_PLL_is_valid(crtc, &clock))
556 continue;
557 this_err = abs(clock.dot - target) ;
558 if (this_err < err_most) {
559 *best_clock = clock;
560 err_most = this_err;
561 max_n = clock.n;
562 found = true;
563 }
564 }
565 }
566 }
567 }
568
569 return found;
570}
571
503void 572void
504intel_wait_for_vblank(struct drm_device *dev) 573intel_wait_for_vblank(struct drm_device *dev)
505{ 574{
@@ -918,6 +987,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
918 bool is_crt = false, is_lvds = false, is_tv = false; 987 bool is_crt = false, is_lvds = false, is_tv = false;
919 struct drm_mode_config *mode_config = &dev->mode_config; 988 struct drm_mode_config *mode_config = &dev->mode_config;
920 struct drm_connector *connector; 989 struct drm_connector *connector;
990 const intel_limit_t *limit;
921 int ret; 991 int ret;
922 992
923 drm_vblank_pre_modeset(dev, pipe); 993 drm_vblank_pre_modeset(dev, pipe);
@@ -961,7 +1031,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
961 refclk = 48000; 1031 refclk = 48000;
962 } 1032 }
963 1033
964 ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock); 1034 /*
1035 * Returns a set of divisors for the desired target clock with the given
1036 * refclk, or FALSE. The returned values represent the clock equation:
1037 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
1038 */
1039 limit = intel_limit(crtc);
1040 ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
965 if (!ok) { 1041 if (!ok) {
966 DRM_ERROR("Couldn't find PLL settings for mode!\n"); 1042 DRM_ERROR("Couldn't find PLL settings for mode!\n");
967 return -EINVAL; 1043 return -EINVAL;