diff options
-rw-r--r-- | drivers/gpu/drm/i915/i915_reg.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 240 |
2 files changed, 239 insertions, 2 deletions
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 782e5d1dc218..b6f5f1040d77 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -904,6 +904,7 @@ #define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ #define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ #define DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */ +#define DPLL_LOCK_VLV (1<<15) #define DPLL_INTEGRATED_CLOCK_VLV (1<<13) #define SRX_INDEX 0x3c4 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 97301621f48a..7794c1a630c0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -98,6 +98,11 @@ intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc, int target, int refclk, intel_clock_t *match_clock, intel_clock_t *best_clock); +static bool +intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock); + static inline u32 /* units of 100MHz */ intel_fdi_link_freq(struct drm_device *dev) { @@ -359,6 +364,48 @@ static const intel_limit_t intel_limits_ironlake_display_port = { .find_pll = intel_find_pll_ironlake_dp, }; +static const intel_limit_t intel_limits_vlv_dac = { + .dot = { .min = 25000, .max = 270000 }, + .vco = { .min = 4000000, .max = 6000000 }, + .n = { .min = 1, .max = 7 }, + .m = { .min = 22, .max = 450 }, /* guess */ + .m1 = { .min = 2, .max = 3 }, + .m2 = { .min = 11, .max = 156 }, + .p = { .min = 10, .max = 30 }, + .p1 = { .min = 2, .max = 3 }, + .p2 = { .dot_limit = 270000, + .p2_slow = 2, .p2_fast = 20 }, + .find_pll = intel_vlv_find_best_pll, +}; + +static const intel_limit_t intel_limits_vlv_hdmi = { + .dot = { .min = 20000, .max = 165000 }, + .vco = { .min = 5994000, .max = 4000000 }, + .n = { .min = 1, .max = 7 }, + .m = { .min = 60, .max = 300 }, /* guess */ + .m1 = { .min = 2, .max = 3 }, + .m2 = { .min = 11, .max = 156 }, + .p = { .min = 10, .max = 30 }, + .p1 = { .min = 2, .max = 3 }, + .p2 = { .dot_limit = 270000, + .p2_slow = 2, .p2_fast = 20 }, + .find_pll = intel_vlv_find_best_pll, +}; + +static const intel_limit_t intel_limits_vlv_dp = { + .dot = { .min = 162000, .max = 270000 }, + .vco = { .min = 5994000, .max = 4000000 }, + .n = { .min = 1, .max = 7 }, + .m = { .min = 60, .max = 300 }, /* guess */ + .m1 = { .min = 2, .max = 3 }, + .m2 = { .min = 11, .max = 156 }, + .p = { .min = 10, .max = 30 }, + .p1 = { .min = 2, .max = 3 }, + .p2 = { .dot_limit = 270000, + .p2_slow = 2, .p2_fast = 20 }, + .find_pll = intel_vlv_find_best_pll, +}; + u32 intel_dpio_read(struct drm_i915_private *dev_priv, int reg) { unsigned long flags; @@ -384,6 +431,28 @@ out_unlock: return val; } +static void intel_dpio_write(struct drm_i915_private *dev_priv, int reg, + u32 val) +{ + unsigned long flags; + + spin_lock_irqsave(&dev_priv->dpio_lock, flags); + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) { + DRM_ERROR("DPIO idle wait timed out\n"); + goto out_unlock; + } + + I915_WRITE(DPIO_DATA, val); + I915_WRITE(DPIO_REG, reg); + I915_WRITE(DPIO_PKT, DPIO_RID | DPIO_OP_WRITE | DPIO_PORTID | + DPIO_BYTE); + if (wait_for_atomic_us((I915_READ(DPIO_PKT) & DPIO_BUSY) == 0, 100)) + DRM_ERROR("DPIO write wait timed out\n"); + +out_unlock: + spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); +} + static void vlv_init_dpio(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -510,6 +579,13 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk) limit = &intel_limits_pineview_lvds; else limit = &intel_limits_pineview_sdvo; + } else if (IS_VALLEYVIEW(dev)) { + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) + limit = &intel_limits_vlv_dac; + else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) + limit = &intel_limits_vlv_hdmi; + else + limit = &intel_limits_vlv_dp; } else if (!IS_GEN2(dev)) { if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &intel_limits_i9xx_lvds; @@ -783,6 +859,73 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, memcpy(best_clock, &clock, sizeof(intel_clock_t)); return true; } +static bool +intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc, + int target, int refclk, intel_clock_t *match_clock, + intel_clock_t *best_clock) +{ + u32 p1, p2, m1, m2, vco, bestn, bestm1, bestm2, bestp1, bestp2; + u32 m, n, fastclk; + u32 updrate, minupdate, fracbits, p; + unsigned long bestppm, ppm, absppm; + int dotclk, flag; + + dotclk = target * 1000; + bestppm = 1000000; + ppm = absppm = 0; + fastclk = dotclk / (2*100); + updrate = 0; + minupdate = 19200; + fracbits = 1; + n = p = p1 = p2 = m = m1 = m2 = vco = bestn = 0; + bestm1 = bestm2 = bestp1 = bestp2 = 0; + + /* based on hardware requirement, prefer smaller n to precision */ + for (n = limit->n.min; n <= ((refclk) / minupdate); n++) { + updrate = refclk / n; + for (p1 = limit->p1.max; p1 > limit->p1.min; p1--) { + for (p2 = limit->p2.p2_fast+1; p2 > 0; p2--) { + if (p2 > 10) + p2 = p2 - 1; + p = p1 * p2; + /* based on hardware requirement, prefer bigger m1,m2 values */ + for (m1 = limit->m1.min; m1 <= limit->m1.max; m1++) { + m2 = (((2*(fastclk * p * n / m1 )) + + refclk) / (2*refclk)); + m = m1 * m2; + vco = updrate * m; + if (vco >= limit->vco.min && vco < limit->vco.max) { + ppm = 1000000 * ((vco / p) - fastclk) / fastclk; + absppm = (ppm > 0) ? ppm : (-ppm); + if (absppm < 100 && ((p1 * p2) > (bestp1 * bestp2))) { + bestppm = 0; + flag = 1; + } + if (absppm < bestppm - 10) { + bestppm = absppm; + flag = 1; + } + if (flag) { + bestn = n; + bestm1 = m1; + bestm2 = m2; + bestp1 = p1; + bestp2 = p2; + flag = 0; + } + } + } + } + } + } + best_clock->n = bestn; + best_clock->m1 = bestm1; + best_clock->m2 = bestm2; + best_clock->p1 = bestp1; + best_clock->p2 = bestp2; + + return true; +} static void ironlake_wait_for_vblank(struct drm_device *dev, int pipe) { @@ -1293,7 +1436,7 @@ static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) u32 val; /* No really, not for ILK+ */ - BUG_ON(dev_priv->info->gen >= 5); + BUG_ON(!IS_VALLEYVIEW(dev_priv->dev) && dev_priv->info->gen >= 5); /* PLL is protected by panel, make sure we can write it */ if (IS_MOBILE(dev_priv->dev) && !IS_I830(dev_priv->dev)) @@ -3672,13 +3815,37 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, return display_bpc != bpc; } +static int vlv_get_refclk(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int refclk = 27000; /* for DP & HDMI */ + + return 100000; /* only one validated so far */ + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { + refclk = 96000; + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + if (intel_panel_use_ssc(dev_priv)) + refclk = 100000; + else + refclk = 96000; + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { + refclk = 100000; + } + + return refclk; +} + static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; int refclk; - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + if (IS_VALLEYVIEW(dev)) { + refclk = vlv_get_refclk(crtc); + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { refclk = dev_priv->lvds_ssc_freq * 1000; DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", @@ -3793,6 +3960,72 @@ static void intel_update_lvds(struct drm_crtc *crtc, intel_clock_t *clock, I915_WRITE(LVDS, temp); } +static void vlv_update_pll(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + intel_clock_t *clock, intel_clock_t *reduced_clock, + int refclk, int num_connectors) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + u32 dpll, mdiv, pdiv; + u32 bestn, bestm1, bestm2, bestp1, bestp2; + bool is_hdmi; + + is_hdmi = intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI); + + bestn = clock->n; + bestm1 = clock->m1; + bestm2 = clock->m2; + bestp1 = clock->p1; + bestp2 = clock->p2; + + /* Enable DPIO clock input */ + dpll = DPLL_EXT_BUFFER_ENABLE_VLV | DPLL_REFA_CLK_ENABLE_VLV | + DPLL_VGA_MODE_DIS | DPLL_INTEGRATED_CLOCK_VLV; + I915_WRITE(DPLL(pipe), dpll); + POSTING_READ(DPLL(pipe)); + + mdiv = ((bestm1 << DPIO_M1DIV_SHIFT) | (bestm2 & DPIO_M2DIV_MASK)); + mdiv |= ((bestp1 << DPIO_P1_SHIFT) | (bestp2 << DPIO_P2_SHIFT)); + mdiv |= ((bestn << DPIO_N_SHIFT)); + mdiv |= (1 << DPIO_POST_DIV_SHIFT); + mdiv |= (1 << DPIO_K_SHIFT); + mdiv |= DPIO_ENABLE_CALIBRATION; + intel_dpio_write(dev_priv, DPIO_DIV(pipe), mdiv); + + intel_dpio_write(dev_priv, DPIO_CORE_CLK(pipe), 0x01000000); + + pdiv = DPIO_REFSEL_OVERRIDE | (5 << DPIO_PLL_MODESEL_SHIFT) | + (3 << DPIO_BIAS_CURRENT_CTL_SHIFT) | (1<<20) | + (8 << DPIO_DRIVER_CTL_SHIFT) | (5 << DPIO_CLK_BIAS_CTL_SHIFT); + intel_dpio_write(dev_priv, DPIO_REFSFR(pipe), pdiv); + + intel_dpio_write(dev_priv, DPIO_LFP_COEFF(pipe), 0x009f0051); + + dpll |= DPLL_VCO_ENABLE; + I915_WRITE(DPLL(pipe), dpll); + POSTING_READ(DPLL(pipe)); + if (wait_for(((I915_READ(DPLL(pipe)) & DPLL_LOCK_VLV) == DPLL_LOCK_VLV), 1)) + DRM_ERROR("DPLL %d failed to lock\n", pipe); + + if (is_hdmi) { + u32 temp = intel_mode_get_pixel_multiplier(adjusted_mode); + + if (temp > 1) + temp = (temp - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT; + else + temp = 0; + + I915_WRITE(DPLL_MD(pipe), temp); + POSTING_READ(DPLL_MD(pipe)); + } + + intel_dpio_write(dev_priv, DPIO_FASTCLK_DISABLE, 0x641); /* ??? */ +} + static void i9xx_update_pll(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4050,6 +4283,9 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, if (IS_GEN2(dev)) i8xx_update_pll(crtc, adjusted_mode, &clock, num_connectors); + else if (IS_VALLEYVIEW(dev)) + vlv_update_pll(crtc, mode,adjusted_mode, &clock, NULL, + refclk, num_connectors); else i9xx_update_pll(crtc, mode, adjusted_mode, &clock, has_reduced_clock ? &reduced_clock : NULL, |