drm/i915: Make the LPT iclkip 20MHz case more generic
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Wed, 17 Feb 2016 19:41:11 +0000 (21:41 +0200)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 1 Mar 2016 11:04:46 +0000 (13:04 +0200)
The reason for spcial casing 20MHz in the iclkip calculations is that
it would overflow the 7 bit divisor value. Let's rewrite the special
case to check for just that, and bump up auxdiv when needed. This makes
the code work for freqeuencies close to but not exactly 20MHz. The real
lower limit for auxdiv=0 is actually:
172800000/(0x7f+2)*64)=~20930 kHz, and below that we must resort to
auxdiv=1.

Actually this is all very theoretical since we limit the dotclock to
min 25MHz with CRT on all platforms. 25Mhz is actually the documented
limit in Bspec, so it seems we ought to never need to worry about the
auxdiv=1 case. But no harm in having it.

Cc: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1455738073-14502-5-git-send-email-ville.syrjala@linux.intel.com
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
drivers/gpu/drm/i915/intel_display.c

index 36c470f3c6e5335d8a801b92def931c8492a4b81..1f35523976898d534d404f3a4a57dbb1752040ec 100644 (file)
@@ -4030,37 +4030,35 @@ static void lpt_disable_iclkip(struct drm_i915_private *dev_priv)
 /* Program iCLKIP clock to the desired frequency */
 static void lpt_program_iclkip(struct drm_crtc *crtc)
 {
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_i915_private *dev_priv = to_i915(crtc->dev);
        int clock = to_intel_crtc(crtc)->config->base.adjusted_mode.crtc_clock;
        u32 divsel, phaseinc, auxdiv, phasedir = 0;
        u32 temp;
 
        lpt_disable_iclkip(dev_priv);
 
-       /* 20MHz is a corner case which is out of range for the 7-bit divisor */
-       if (clock == 20000) {
-               auxdiv = 1;
-               divsel = 0x41;
-               phaseinc = 0x20;
-       } else {
-               /* The iCLK virtual clock root frequency is in MHz,
-                * but the adjusted_mode->crtc_clock in in KHz. To get the
-                * divisors, it is necessary to divide one by another, so we
-                * convert the virtual clock precision to KHz here for higher
-                * precision.
-                */
+       /* The iCLK virtual clock root frequency is in MHz,
+        * but the adjusted_mode->crtc_clock in in KHz. To get the
+        * divisors, it is necessary to divide one by another, so we
+        * convert the virtual clock precision to KHz here for higher
+        * precision.
+        */
+       for (auxdiv = 0; auxdiv < 2; auxdiv++) {
                u32 iclk_virtual_root_freq = 172800 * 1000;
                u32 iclk_pi_range = 64;
-               u32 desired_divisor, msb_divisor_value, pi_value;
+               u32 desired_divisor;
 
-               desired_divisor = DIV_ROUND_CLOSEST(iclk_virtual_root_freq, clock);
-               msb_divisor_value = desired_divisor / iclk_pi_range;
-               pi_value = desired_divisor % iclk_pi_range;
+               desired_divisor = DIV_ROUND_CLOSEST(iclk_virtual_root_freq,
+                                                   clock << auxdiv);
+               divsel = (desired_divisor / iclk_pi_range) - 2;
+               phaseinc = desired_divisor % iclk_pi_range;
 
-               auxdiv = 0;
-               divsel = msb_divisor_value - 2;
-               phaseinc = pi_value;
+               /*
+                * Near 20MHz is a corner case which is
+                * out of range for the 7-bit divisor
+                */
+               if (divsel <= 0x7f)
+                       break;
        }
 
        /* This should not happen with any sane values */