]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
drm/i915: Use a different PLL timing search function on G4X.
authorMa Ling <ling.ma@intel.com>
Wed, 18 Mar 2009 12:13:27 +0000 (20:13 +0800)
committerEric Anholt <eric@anholt.net>
Fri, 27 Mar 2009 21:45:12 +0000 (14:45 -0700)
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>
drivers/gpu/drm/i915/intel_display.c

index 89f7af0bdb12d8023ba9b8e05e17df4060925c31..8e29545273b6799e720faf43831d094b55e679e6 100644 (file)
@@ -56,11 +56,13 @@ typedef struct {
 } intel_p2_t;
 
 #define INTEL_P2_NUM                 2
-
-typedef struct {
+typedef struct intel_limit intel_limit_t;
+struct intel_limit {
     intel_range_t   dot, vco, n, m, m1, m2, p, p1;
     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_MAX            350000
@@ -198,6 +200,12 @@ typedef struct {
 #define G4X_P2_DUAL_CHANNEL_LVDS_FAST           7
 #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[] = {
     { /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -211,6 +219,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I8XX_P1_MIN,           .max = I8XX_P1_MAX },
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_SLOW,       .p2_fast = I8XX_P2_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I8XX_LVDS */
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
@@ -223,6 +232,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I8XX_P1_LVDS_MIN,      .max = I8XX_P1_LVDS_MAX },
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_LVDS_SLOW,  .p2_fast = I8XX_P2_LVDS_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I9XX_SDVO_DAC */
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
@@ -235,6 +245,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
        .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I9XX_LVDS */
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
@@ -250,6 +261,7 @@ static const intel_limit_t intel_limits[] = {
         */
        .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
                 .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*/
     { /* INTEL_LIMIT_G4X_SDVO */
@@ -265,6 +277,7 @@ static const intel_limit_t intel_limits[] = {
                 .p2_slow = G4X_P2_SDVO_SLOW,
                 .p2_fast = G4X_P2_SDVO_FAST
        },
+       .find_pll = intel_g4x_find_best_PLL,
     },
     { /* INTEL_LIMIT_G4X_HDMI_DAC */
        .dot = { .min = G4X_DOT_HDMI_DAC_MIN,   .max = G4X_DOT_HDMI_DAC_MAX },
@@ -279,6 +292,7 @@ static const intel_limit_t intel_limits[] = {
                 .p2_slow = G4X_P2_HDMI_DAC_SLOW,
                 .p2_fast = G4X_P2_HDMI_DAC_FAST
        },
+       .find_pll = intel_g4x_find_best_PLL,
     },
     { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */
        .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
@@ -301,6 +315,7 @@ static const intel_limit_t intel_limits[] = {
                 .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
                 .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
        },
+       .find_pll = intel_g4x_find_best_PLL,
     },
     { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */
        .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
@@ -323,6 +338,7 @@ static const intel_limit_t intel_limits[] = {
                 .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
                 .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)
        return true;
 }
 
-/**
- * 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.
- */
-static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
-                               int refclk, intel_clock_t *best_clock)
+static bool
+intel_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;
-       const intel_limit_t *limit = intel_limit(crtc);
        int err = target;
 
        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,
        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
 intel_wait_for_vblank(struct drm_device *dev)
 {
@@ -918,6 +987,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        bool is_crt = false, is_lvds = false, is_tv = false;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct drm_connector *connector;
+       const intel_limit_t *limit;
        int ret;
 
        drm_vblank_pre_modeset(dev, pipe);
@@ -961,7 +1031,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                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) {
                DRM_ERROR("Couldn't find PLL settings for mode!\n");
                return -EINVAL;