On Fri, May 24, 2013 at 4:19 PM, Jani Nikula <jani.nikula at intel.com> wrote: > This was a quick patch to see if it would be easy to extract the limit > and pll stuff from intel_display.c to a separate file. Turns out it's > trivial, and reduces the size of overbloated intel_display.c nicely: I know, but I'd like to do that as part of the general pipe config refactoring. I.e. your patch here will conflict madly with my outstanding pll patches :( -Daniel > > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/intel_display.c | 645 +-------------------------------- > drivers/gpu/drm/i915/intel_drv.h | 7 + > drivers/gpu/drm/i915/intel_limits.c | 648 ++++++++++++++++++++++++++++++++++ > 4 files changed, 670 insertions(+), 631 deletions(-) > > Opinions, bikesheds? > > Signed-off-by: Jani Nikula <jani.nikula at intel.com> > --- > drivers/gpu/drm/i915/Makefile | 1 + > drivers/gpu/drm/i915/intel_display.c | 645 +-------------------------------- > drivers/gpu/drm/i915/intel_drv.h | 7 + > drivers/gpu/drm/i915/intel_limits.c | 648 ++++++++++++++++++++++++++++++++++ > 4 files changed, 670 insertions(+), 631 deletions(-) > create mode 100644 drivers/gpu/drm/i915/intel_limits.c > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile > index 40034ec..b9d7426 100644 > --- a/drivers/gpu/drm/i915/Makefile > +++ b/drivers/gpu/drm/i915/Makefile > @@ -21,6 +21,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ > intel_crt.o \ > intel_lvds.o \ > intel_bios.o \ > + intel_limits.o \ > intel_ddi.o \ > intel_dp.o \ > intel_hdmi.o \ > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c > index d63bb3f..721d461 100644 > --- a/drivers/gpu/drm/i915/intel_display.c > +++ b/drivers/gpu/drm/i915/intel_display.c > @@ -45,40 +45,6 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type); > static void intel_increase_pllclock(struct drm_crtc *crtc); > static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); > > -typedef struct { > - int min, max; > -} intel_range_t; > - > -typedef struct { > - int dot_limit; > - int p2_slow, p2_fast; > -} intel_p2_t; > - > -#define INTEL_P2_NUM 2 > -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; > - /** > - * find_pll() - Find the best values for the PLL > - * @limit: limits for the PLL > - * @crtc: current CRTC > - * @target: target frequency in kHz > - * @refclk: reference clock frequency in kHz > - * @match_clock: if provided, @best_clock P divider must > - * match the P divider from @match_clock > - * used for LVDS downclocking > - * @best_clock: best PLL values found > - * > - * Returns true on success, false on failure. > - */ > - bool (*find_pll)(const intel_limit_t *limit, > - struct drm_crtc *crtc, > - int target, int refclk, > - intel_clock_t *match_clock, > - intel_clock_t *best_clock); > -}; > - > /* FDI */ > #define IRONLAKE_FDI_FREQ 2700000 /* in kHz for mode->clock */ > > @@ -92,20 +58,6 @@ intel_pch_rawclk(struct drm_device *dev) > return I915_READ(PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK; > } > > -static bool > -intel_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 bool > -intel_g4x_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 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) > { > @@ -116,351 +68,6 @@ intel_fdi_link_freq(struct drm_device *dev) > return 27; > } > > -static const intel_limit_t intel_limits_i8xx_dvo = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 930000, .max = 1400000 }, > - .n = { .min = 3, .max = 16 }, > - .m = { .min = 96, .max = 140 }, > - .m1 = { .min = 18, .max = 26 }, > - .m2 = { .min = 6, .max = 16 }, > - .p = { .min = 4, .max = 128 }, > - .p1 = { .min = 2, .max = 33 }, > - .p2 = { .dot_limit = 165000, > - .p2_slow = 4, .p2_fast = 2 }, > - .find_pll = intel_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_i8xx_lvds = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 930000, .max = 1400000 }, > - .n = { .min = 3, .max = 16 }, > - .m = { .min = 96, .max = 140 }, > - .m1 = { .min = 18, .max = 26 }, > - .m2 = { .min = 6, .max = 16 }, > - .p = { .min = 4, .max = 128 }, > - .p1 = { .min = 1, .max = 6 }, > - .p2 = { .dot_limit = 165000, > - .p2_slow = 14, .p2_fast = 7 }, > - .find_pll = intel_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_i9xx_sdvo = { > - .dot = { .min = 20000, .max = 400000 }, > - .vco = { .min = 1400000, .max = 2800000 }, > - .n = { .min = 1, .max = 6 }, > - .m = { .min = 70, .max = 120 }, > - .m1 = { .min = 8, .max = 18 }, > - .m2 = { .min = 3, .max = 7 }, > - .p = { .min = 5, .max = 80 }, > - .p1 = { .min = 1, .max = 8 }, > - .p2 = { .dot_limit = 200000, > - .p2_slow = 10, .p2_fast = 5 }, > - .find_pll = intel_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_i9xx_lvds = { > - .dot = { .min = 20000, .max = 400000 }, > - .vco = { .min = 1400000, .max = 2800000 }, > - .n = { .min = 1, .max = 6 }, > - .m = { .min = 70, .max = 120 }, > - .m1 = { .min = 8, .max = 18 }, > - .m2 = { .min = 3, .max = 7 }, > - .p = { .min = 7, .max = 98 }, > - .p1 = { .min = 1, .max = 8 }, > - .p2 = { .dot_limit = 112000, > - .p2_slow = 14, .p2_fast = 7 }, > - .find_pll = intel_find_best_PLL, > -}; > - > - > -static const intel_limit_t intel_limits_g4x_sdvo = { > - .dot = { .min = 25000, .max = 270000 }, > - .vco = { .min = 1750000, .max = 3500000}, > - .n = { .min = 1, .max = 4 }, > - .m = { .min = 104, .max = 138 }, > - .m1 = { .min = 17, .max = 23 }, > - .m2 = { .min = 5, .max = 11 }, > - .p = { .min = 10, .max = 30 }, > - .p1 = { .min = 1, .max = 3}, > - .p2 = { .dot_limit = 270000, > - .p2_slow = 10, > - .p2_fast = 10 > - }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_g4x_hdmi = { > - .dot = { .min = 22000, .max = 400000 }, > - .vco = { .min = 1750000, .max = 3500000}, > - .n = { .min = 1, .max = 4 }, > - .m = { .min = 104, .max = 138 }, > - .m1 = { .min = 16, .max = 23 }, > - .m2 = { .min = 5, .max = 11 }, > - .p = { .min = 5, .max = 80 }, > - .p1 = { .min = 1, .max = 8}, > - .p2 = { .dot_limit = 165000, > - .p2_slow = 10, .p2_fast = 5 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_g4x_single_channel_lvds = { > - .dot = { .min = 20000, .max = 115000 }, > - .vco = { .min = 1750000, .max = 3500000 }, > - .n = { .min = 1, .max = 3 }, > - .m = { .min = 104, .max = 138 }, > - .m1 = { .min = 17, .max = 23 }, > - .m2 = { .min = 5, .max = 11 }, > - .p = { .min = 28, .max = 112 }, > - .p1 = { .min = 2, .max = 8 }, > - .p2 = { .dot_limit = 0, > - .p2_slow = 14, .p2_fast = 14 > - }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_g4x_dual_channel_lvds = { > - .dot = { .min = 80000, .max = 224000 }, > - .vco = { .min = 1750000, .max = 3500000 }, > - .n = { .min = 1, .max = 3 }, > - .m = { .min = 104, .max = 138 }, > - .m1 = { .min = 17, .max = 23 }, > - .m2 = { .min = 5, .max = 11 }, > - .p = { .min = 14, .max = 42 }, > - .p1 = { .min = 2, .max = 6 }, > - .p2 = { .dot_limit = 0, > - .p2_slow = 7, .p2_fast = 7 > - }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_pineview_sdvo = { > - .dot = { .min = 20000, .max = 400000}, > - .vco = { .min = 1700000, .max = 3500000 }, > - /* Pineview's Ncounter is a ring counter */ > - .n = { .min = 3, .max = 6 }, > - .m = { .min = 2, .max = 256 }, > - /* Pineview only has one combined m divider, which we treat as m2. */ > - .m1 = { .min = 0, .max = 0 }, > - .m2 = { .min = 0, .max = 254 }, > - .p = { .min = 5, .max = 80 }, > - .p1 = { .min = 1, .max = 8 }, > - .p2 = { .dot_limit = 200000, > - .p2_slow = 10, .p2_fast = 5 }, > - .find_pll = intel_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_pineview_lvds = { > - .dot = { .min = 20000, .max = 400000 }, > - .vco = { .min = 1700000, .max = 3500000 }, > - .n = { .min = 3, .max = 6 }, > - .m = { .min = 2, .max = 256 }, > - .m1 = { .min = 0, .max = 0 }, > - .m2 = { .min = 0, .max = 254 }, > - .p = { .min = 7, .max = 112 }, > - .p1 = { .min = 1, .max = 8 }, > - .p2 = { .dot_limit = 112000, > - .p2_slow = 14, .p2_fast = 14 }, > - .find_pll = intel_find_best_PLL, > -}; > - > -/* Ironlake / Sandybridge > - * > - * We calculate clock using (register_value + 2) for N/M1/M2, so here > - * the range value for them is (actual_value - 2). > - */ > -static const intel_limit_t intel_limits_ironlake_dac = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 1760000, .max = 3510000 }, > - .n = { .min = 1, .max = 5 }, > - .m = { .min = 79, .max = 127 }, > - .m1 = { .min = 12, .max = 22 }, > - .m2 = { .min = 5, .max = 9 }, > - .p = { .min = 5, .max = 80 }, > - .p1 = { .min = 1, .max = 8 }, > - .p2 = { .dot_limit = 225000, > - .p2_slow = 10, .p2_fast = 5 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_ironlake_single_lvds = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 1760000, .max = 3510000 }, > - .n = { .min = 1, .max = 3 }, > - .m = { .min = 79, .max = 118 }, > - .m1 = { .min = 12, .max = 22 }, > - .m2 = { .min = 5, .max = 9 }, > - .p = { .min = 28, .max = 112 }, > - .p1 = { .min = 2, .max = 8 }, > - .p2 = { .dot_limit = 225000, > - .p2_slow = 14, .p2_fast = 14 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_ironlake_dual_lvds = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 1760000, .max = 3510000 }, > - .n = { .min = 1, .max = 3 }, > - .m = { .min = 79, .max = 127 }, > - .m1 = { .min = 12, .max = 22 }, > - .m2 = { .min = 5, .max = 9 }, > - .p = { .min = 14, .max = 56 }, > - .p1 = { .min = 2, .max = 8 }, > - .p2 = { .dot_limit = 225000, > - .p2_slow = 7, .p2_fast = 7 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -/* LVDS 100mhz refclk limits. */ > -static const intel_limit_t intel_limits_ironlake_single_lvds_100m = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 1760000, .max = 3510000 }, > - .n = { .min = 1, .max = 2 }, > - .m = { .min = 79, .max = 126 }, > - .m1 = { .min = 12, .max = 22 }, > - .m2 = { .min = 5, .max = 9 }, > - .p = { .min = 28, .max = 112 }, > - .p1 = { .min = 2, .max = 8 }, > - .p2 = { .dot_limit = 225000, > - .p2_slow = 14, .p2_fast = 14 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = { > - .dot = { .min = 25000, .max = 350000 }, > - .vco = { .min = 1760000, .max = 3510000 }, > - .n = { .min = 1, .max = 3 }, > - .m = { .min = 79, .max = 126 }, > - .m1 = { .min = 12, .max = 22 }, > - .m2 = { .min = 5, .max = 9 }, > - .p = { .min = 14, .max = 42 }, > - .p1 = { .min = 2, .max = 6 }, > - .p2 = { .dot_limit = 225000, > - .p2_slow = 7, .p2_fast = 7 }, > - .find_pll = intel_g4x_find_best_PLL, > -}; > - > -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 = 1, .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 = 25000, .max = 270000 }, > - .vco = { .min = 4000000, .max = 6000000 }, > - .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 = 25000, .max = 270000 }, > - .vco = { .min = 4000000, .max = 6000000 }, > - .n = { .min = 1, .max = 7 }, > - .m = { .min = 22, .max = 450 }, > - .m1 = { .min = 2, .max = 3 }, > - .m2 = { .min = 11, .max = 156 }, > - .p = { .min = 10, .max = 30 }, > - .p1 = { .min = 1, .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_ironlake_limit(struct drm_crtc *crtc, > - int refclk) > -{ > - struct drm_device *dev = crtc->dev; > - const intel_limit_t *limit; > - > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > - if (intel_is_dual_link_lvds(dev)) { > - if (refclk == 100000) > - limit = &intel_limits_ironlake_dual_lvds_100m; > - else > - limit = &intel_limits_ironlake_dual_lvds; > - } else { > - if (refclk == 100000) > - limit = &intel_limits_ironlake_single_lvds_100m; > - else > - limit = &intel_limits_ironlake_single_lvds; > - } > - } else > - limit = &intel_limits_ironlake_dac; > - > - return limit; > -} > - > -static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc) > -{ > - struct drm_device *dev = crtc->dev; > - const intel_limit_t *limit; > - > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > - if (intel_is_dual_link_lvds(dev)) > - limit = &intel_limits_g4x_dual_channel_lvds; > - else > - limit = &intel_limits_g4x_single_channel_lvds; > - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) || > - intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { > - limit = &intel_limits_g4x_hdmi; > - } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { > - limit = &intel_limits_g4x_sdvo; > - } else /* The option is for other outputs */ > - limit = &intel_limits_i9xx_sdvo; > - > - return limit; > -} > - > -static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk) > -{ > - struct drm_device *dev = crtc->dev; > - const intel_limit_t *limit; > - > - if (HAS_PCH_SPLIT(dev)) > - limit = intel_ironlake_limit(crtc, refclk); > - else if (IS_G4X(dev)) { > - limit = intel_g4x_limit(crtc); > - } else if (IS_PINEVIEW(dev)) { > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) > - 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; > - else > - limit = &intel_limits_i9xx_sdvo; > - } else { > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) > - limit = &intel_limits_i8xx_lvds; > - else > - limit = &intel_limits_i8xx_dvo; > - } > - return limit; > -} > - > /* m1 is reserved as 0 in Pineview, n is a ring counter */ > static void pineview_clock(int refclk, intel_clock_t *clock) > { > @@ -475,7 +82,7 @@ static uint32_t i9xx_dpll_compute_m(struct dpll *dpll) > return 5 * (dpll->m1 + 2) + (dpll->m2 + 2); > } > > -static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock) > +void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock) > { > if (IS_PINEVIEW(dev)) { > pineview_clock(refclk, clock); > @@ -502,230 +109,6 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type) > return false; > } > > -#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0) > -/** > - * Returns whether the given set of divisors are valid for a given refclk with > - * the given connectors. > - */ > - > -static bool intel_PLL_is_valid(struct drm_device *dev, > - const intel_limit_t *limit, > - const intel_clock_t *clock) > -{ > - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) > - INTELPllInvalid("p1 out of range\n"); > - if (clock->p < limit->p.min || limit->p.max < clock->p) > - INTELPllInvalid("p out of range\n"); > - if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) > - INTELPllInvalid("m2 out of range\n"); > - if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) > - INTELPllInvalid("m1 out of range\n"); > - if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev)) > - INTELPllInvalid("m1 <= m2\n"); > - if (clock->m < limit->m.min || limit->m.max < clock->m) > - INTELPllInvalid("m out of range\n"); > - if (clock->n < limit->n.min || limit->n.max < clock->n) > - INTELPllInvalid("n out of range\n"); > - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) > - INTELPllInvalid("vco out of range\n"); > - /* XXX: We may need to be checking "Dot clock" depending on the multiplier, > - * connector, etc., rather than just a single range. > - */ > - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) > - INTELPllInvalid("dot out of range\n"); > - > - return true; > -} > - > -static bool > -intel_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) > - > -{ > - struct drm_device *dev = crtc->dev; > - intel_clock_t clock; > - int err = target; > - > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > - /* > - * For LVDS just rely on its current settings for dual-channel. > - * We haven't figured out how to reliably set up different > - * single/dual channel state, if we even can. > - */ > - if (intel_is_dual_link_lvds(dev)) > - 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)); > - > - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; > - clock.m1++) { > - for (clock.m2 = limit->m2.min; > - clock.m2 <= limit->m2.max; clock.m2++) { > - /* m1 is always 0 in Pineview */ > - if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev)) > - break; > - for (clock.n = limit->n.min; > - clock.n <= limit->n.max; clock.n++) { > - for (clock.p1 = limit->p1.min; > - clock.p1 <= limit->p1.max; clock.p1++) { > - int this_err; > - > - intel_clock(dev, refclk, &clock); > - if (!intel_PLL_is_valid(dev, limit, > - &clock)) > - continue; > - if (match_clock && > - clock.p != match_clock->p) > - continue; > - > - this_err = abs(clock.dot - target); > - if (this_err < err) { > - *best_clock = clock; > - err = this_err; > - } > - } > - } > - } > - } > - > - 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 *match_clock, > - intel_clock_t *best_clock) > -{ > - struct drm_device *dev = crtc->dev; > - intel_clock_t clock; > - int max_n; > - bool found; > - /* approximately equals target * 0.00585 */ > - int err_most = (target >> 8) + (target >> 9); > - found = false; > - > - if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > - if (intel_is_dual_link_lvds(dev)) > - 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 requirement, prefer smaller n to precision */ > - for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { > - /* based on hardware requirement, prefere larger m1,m2 */ > - 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(dev, refclk, &clock); > - if (!intel_PLL_is_valid(dev, limit, > - &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; > -} > - > -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; > - > - flag = 0; > - 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; > -} > - > enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv, > enum pipe pipe) > { > @@ -4815,7 +4198,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, > * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. > */ > limit = intel_limit(crtc, refclk); > - ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, > + ok = intel_find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, > &clock); > if (!ok) { > DRM_ERROR("Couldn't find PLL settings for mode!\n"); > @@ -4832,11 +4215,11 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, > * by using the FP0/FP1. In such case we will disable the LVDS > * downclock feature. > */ > - has_reduced_clock = limit->find_pll(limit, crtc, > - dev_priv->lvds_downclock, > - refclk, > - &clock, > - &reduced_clock); > + has_reduced_clock = intel_find_pll(limit, crtc, > + dev_priv->lvds_downclock, > + refclk, > + &clock, > + &reduced_clock); > } > /* Compat-code for transition, will disappear. */ > if (!intel_crtc->config.clock_set) { > @@ -5452,8 +4835,8 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, > * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. > */ > limit = intel_limit(crtc, refclk); > - ret = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, > - clock); > + ret = intel_find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, > + clock); > if (!ret) > return false; > > @@ -5464,11 +4847,11 @@ static bool ironlake_compute_clocks(struct drm_crtc *crtc, > * by using the FP0/FP1. In such case we will disable the LVDS > * downclock feature. > */ > - *has_reduced_clock = limit->find_pll(limit, crtc, > - dev_priv->lvds_downclock, > - refclk, > - clock, > - reduced_clock); > + *has_reduced_clock = intel_find_pll(limit, crtc, > + dev_priv->lvds_downclock, > + refclk, > + clock, > + reduced_clock); > } > > return true; > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h > index 83b4fe4..fcd4570 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -191,6 +191,13 @@ typedef struct dpll { > int m; > int p; > } intel_clock_t; > +void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock); > + > +typedef struct intel_limit intel_limit_t; > +const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk); > +bool intel_find_pll(const intel_limit_t *limit, struct drm_crtc *crtc, > + int target, int refclk, intel_clock_t *match_clock, > + intel_clock_t *best_clock); > > struct intel_crtc_config { > struct drm_display_mode requested_mode; > diff --git a/drivers/gpu/drm/i915/intel_limits.c b/drivers/gpu/drm/i915/intel_limits.c > new file mode 100644 > index 0000000..2865b70f > --- /dev/null > +++ b/drivers/gpu/drm/i915/intel_limits.c > @@ -0,0 +1,648 @@ > +/* > + * Copyright ? 2013 Intel Corporation > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER > + * DEALINGS IN THE SOFTWARE. > + * > + */ > + > +#include <linux/dmi.h> > +#include <linux/module.h> > +#include <linux/input.h> > +#include <linux/i2c.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/vgaarb.h> > +#include <drm/drm_edid.h> > +#include <drm/drmP.h> > +#include "intel_drv.h" > +#include <drm/i915_drm.h> > +#include "i915_drv.h" > +#include "i915_trace.h" > +#include <drm/drm_dp_helper.h> > +#include <drm/drm_crtc_helper.h> > +#include <linux/dma_remapping.h> > + > +typedef struct { > + int min, max; > +} intel_range_t; > + > +typedef struct { > + int dot_limit; > + int p2_slow, p2_fast; > +} intel_p2_t; > + > +struct intel_limit { > + intel_range_t dot, vco, n, m, m1, m2, p, p1; > + intel_p2_t p2; > + /** > + * find_pll() - Find the best values for the PLL > + * @limit: limits for the PLL > + * @crtc: current CRTC > + * @target: target frequency in kHz > + * @refclk: reference clock frequency in kHz > + * @match_clock: if provided, @best_clock P divider must > + * match the P divider from @match_clock > + * used for LVDS downclocking > + * @best_clock: best PLL values found > + * > + * Returns true on success, false on failure. > + */ > + bool (*find_pll)(const struct intel_limit *limit, > + struct drm_crtc *crtc, > + int target, int refclk, > + intel_clock_t *match_clock, > + intel_clock_t *best_clock); > +}; > + > +#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0) > +/** > + * Returns whether the given set of divisors are valid for a given refclk with > + * the given connectors. > + */ > + > +static bool intel_PLL_is_valid(struct drm_device *dev, > + const struct intel_limit *limit, > + const intel_clock_t *clock) > +{ > + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) > + INTELPllInvalid("p1 out of range\n"); > + if (clock->p < limit->p.min || limit->p.max < clock->p) > + INTELPllInvalid("p out of range\n"); > + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) > + INTELPllInvalid("m2 out of range\n"); > + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) > + INTELPllInvalid("m1 out of range\n"); > + if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev)) > + INTELPllInvalid("m1 <= m2\n"); > + if (clock->m < limit->m.min || limit->m.max < clock->m) > + INTELPllInvalid("m out of range\n"); > + if (clock->n < limit->n.min || limit->n.max < clock->n) > + INTELPllInvalid("n out of range\n"); > + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) > + INTELPllInvalid("vco out of range\n"); > + /* XXX: We may need to be checking "Dot clock" depending on the multiplier, > + * connector, etc., rather than just a single range. > + */ > + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) > + INTELPllInvalid("dot out of range\n"); > + > + return true; > +} > + > +static bool > +intel_find_best_PLL(const struct intel_limit *limit, struct drm_crtc *crtc, > + int target, int refclk, intel_clock_t *match_clock, > + intel_clock_t *best_clock) > + > +{ > + struct drm_device *dev = crtc->dev; > + intel_clock_t clock; > + int err = target; > + > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > + /* > + * For LVDS just rely on its current settings for dual-channel. > + * We haven't figured out how to reliably set up different > + * single/dual channel state, if we even can. > + */ > + if (intel_is_dual_link_lvds(dev)) > + 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)); > + > + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; > + clock.m1++) { > + for (clock.m2 = limit->m2.min; > + clock.m2 <= limit->m2.max; clock.m2++) { > + /* m1 is always 0 in Pineview */ > + if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev)) > + break; > + for (clock.n = limit->n.min; > + clock.n <= limit->n.max; clock.n++) { > + for (clock.p1 = limit->p1.min; > + clock.p1 <= limit->p1.max; clock.p1++) { > + int this_err; > + > + intel_clock(dev, refclk, &clock); > + if (!intel_PLL_is_valid(dev, limit, > + &clock)) > + continue; > + if (match_clock && > + clock.p != match_clock->p) > + continue; > + > + this_err = abs(clock.dot - target); > + if (this_err < err) { > + *best_clock = clock; > + err = this_err; > + } > + } > + } > + } > + } > + > + return (err != target); > +} > + > +static bool > +intel_g4x_find_best_PLL(const struct intel_limit *limit, struct drm_crtc *crtc, > + int target, int refclk, intel_clock_t *match_clock, > + intel_clock_t *best_clock) > +{ > + struct drm_device *dev = crtc->dev; > + intel_clock_t clock; > + int max_n; > + bool found; > + /* approximately equals target * 0.00585 */ > + int err_most = (target >> 8) + (target >> 9); > + found = false; > + > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > + if (intel_is_dual_link_lvds(dev)) > + 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 requirement, prefer smaller n to precision */ > + for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) { > + /* based on hardware requirement, prefere larger m1,m2 */ > + 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(dev, refclk, &clock); > + if (!intel_PLL_is_valid(dev, limit, > + &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; > +} > + > +static bool > +intel_vlv_find_best_pll(const struct intel_limit *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; > + > + flag = 0; > + 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 const struct intel_limit intel_limits_i8xx_dvo = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 930000, .max = 1400000 }, > + .n = { .min = 3, .max = 16 }, > + .m = { .min = 96, .max = 140 }, > + .m1 = { .min = 18, .max = 26 }, > + .m2 = { .min = 6, .max = 16 }, > + .p = { .min = 4, .max = 128 }, > + .p1 = { .min = 2, .max = 33 }, > + .p2 = { .dot_limit = 165000, > + .p2_slow = 4, .p2_fast = 2 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_i8xx_lvds = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 930000, .max = 1400000 }, > + .n = { .min = 3, .max = 16 }, > + .m = { .min = 96, .max = 140 }, > + .m1 = { .min = 18, .max = 26 }, > + .m2 = { .min = 6, .max = 16 }, > + .p = { .min = 4, .max = 128 }, > + .p1 = { .min = 1, .max = 6 }, > + .p2 = { .dot_limit = 165000, > + .p2_slow = 14, .p2_fast = 7 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_i9xx_sdvo = { > + .dot = { .min = 20000, .max = 400000 }, > + .vco = { .min = 1400000, .max = 2800000 }, > + .n = { .min = 1, .max = 6 }, > + .m = { .min = 70, .max = 120 }, > + .m1 = { .min = 8, .max = 18 }, > + .m2 = { .min = 3, .max = 7 }, > + .p = { .min = 5, .max = 80 }, > + .p1 = { .min = 1, .max = 8 }, > + .p2 = { .dot_limit = 200000, > + .p2_slow = 10, .p2_fast = 5 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_i9xx_lvds = { > + .dot = { .min = 20000, .max = 400000 }, > + .vco = { .min = 1400000, .max = 2800000 }, > + .n = { .min = 1, .max = 6 }, > + .m = { .min = 70, .max = 120 }, > + .m1 = { .min = 8, .max = 18 }, > + .m2 = { .min = 3, .max = 7 }, > + .p = { .min = 7, .max = 98 }, > + .p1 = { .min = 1, .max = 8 }, > + .p2 = { .dot_limit = 112000, > + .p2_slow = 14, .p2_fast = 7 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_g4x_sdvo = { > + .dot = { .min = 25000, .max = 270000 }, > + .vco = { .min = 1750000, .max = 3500000}, > + .n = { .min = 1, .max = 4 }, > + .m = { .min = 104, .max = 138 }, > + .m1 = { .min = 17, .max = 23 }, > + .m2 = { .min = 5, .max = 11 }, > + .p = { .min = 10, .max = 30 }, > + .p1 = { .min = 1, .max = 3}, > + .p2 = { .dot_limit = 270000, > + .p2_slow = 10, > + .p2_fast = 10 > + }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_g4x_hdmi = { > + .dot = { .min = 22000, .max = 400000 }, > + .vco = { .min = 1750000, .max = 3500000}, > + .n = { .min = 1, .max = 4 }, > + .m = { .min = 104, .max = 138 }, > + .m1 = { .min = 16, .max = 23 }, > + .m2 = { .min = 5, .max = 11 }, > + .p = { .min = 5, .max = 80 }, > + .p1 = { .min = 1, .max = 8}, > + .p2 = { .dot_limit = 165000, > + .p2_slow = 10, .p2_fast = 5 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_g4x_single_channel_lvds = { > + .dot = { .min = 20000, .max = 115000 }, > + .vco = { .min = 1750000, .max = 3500000 }, > + .n = { .min = 1, .max = 3 }, > + .m = { .min = 104, .max = 138 }, > + .m1 = { .min = 17, .max = 23 }, > + .m2 = { .min = 5, .max = 11 }, > + .p = { .min = 28, .max = 112 }, > + .p1 = { .min = 2, .max = 8 }, > + .p2 = { .dot_limit = 0, > + .p2_slow = 14, .p2_fast = 14 > + }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_g4x_dual_channel_lvds = { > + .dot = { .min = 80000, .max = 224000 }, > + .vco = { .min = 1750000, .max = 3500000 }, > + .n = { .min = 1, .max = 3 }, > + .m = { .min = 104, .max = 138 }, > + .m1 = { .min = 17, .max = 23 }, > + .m2 = { .min = 5, .max = 11 }, > + .p = { .min = 14, .max = 42 }, > + .p1 = { .min = 2, .max = 6 }, > + .p2 = { .dot_limit = 0, > + .p2_slow = 7, .p2_fast = 7 > + }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_pineview_sdvo = { > + .dot = { .min = 20000, .max = 400000}, > + .vco = { .min = 1700000, .max = 3500000 }, > + /* Pineview's Ncounter is a ring counter */ > + .n = { .min = 3, .max = 6 }, > + .m = { .min = 2, .max = 256 }, > + /* Pineview only has one combined m divider, which we treat as m2. */ > + .m1 = { .min = 0, .max = 0 }, > + .m2 = { .min = 0, .max = 254 }, > + .p = { .min = 5, .max = 80 }, > + .p1 = { .min = 1, .max = 8 }, > + .p2 = { .dot_limit = 200000, > + .p2_slow = 10, .p2_fast = 5 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_pineview_lvds = { > + .dot = { .min = 20000, .max = 400000 }, > + .vco = { .min = 1700000, .max = 3500000 }, > + .n = { .min = 3, .max = 6 }, > + .m = { .min = 2, .max = 256 }, > + .m1 = { .min = 0, .max = 0 }, > + .m2 = { .min = 0, .max = 254 }, > + .p = { .min = 7, .max = 112 }, > + .p1 = { .min = 1, .max = 8 }, > + .p2 = { .dot_limit = 112000, > + .p2_slow = 14, .p2_fast = 14 }, > + .find_pll = intel_find_best_PLL, > +}; > + > +/* Ironlake / Sandybridge > + * > + * We calculate clock using (register_value + 2) for N/M1/M2, so here > + * the range value for them is (actual_value - 2). > + */ > +static const struct intel_limit intel_limits_ironlake_dac = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 1760000, .max = 3510000 }, > + .n = { .min = 1, .max = 5 }, > + .m = { .min = 79, .max = 127 }, > + .m1 = { .min = 12, .max = 22 }, > + .m2 = { .min = 5, .max = 9 }, > + .p = { .min = 5, .max = 80 }, > + .p1 = { .min = 1, .max = 8 }, > + .p2 = { .dot_limit = 225000, > + .p2_slow = 10, .p2_fast = 5 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_ironlake_single_lvds = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 1760000, .max = 3510000 }, > + .n = { .min = 1, .max = 3 }, > + .m = { .min = 79, .max = 118 }, > + .m1 = { .min = 12, .max = 22 }, > + .m2 = { .min = 5, .max = 9 }, > + .p = { .min = 28, .max = 112 }, > + .p1 = { .min = 2, .max = 8 }, > + .p2 = { .dot_limit = 225000, > + .p2_slow = 14, .p2_fast = 14 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_ironlake_dual_lvds = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 1760000, .max = 3510000 }, > + .n = { .min = 1, .max = 3 }, > + .m = { .min = 79, .max = 127 }, > + .m1 = { .min = 12, .max = 22 }, > + .m2 = { .min = 5, .max = 9 }, > + .p = { .min = 14, .max = 56 }, > + .p1 = { .min = 2, .max = 8 }, > + .p2 = { .dot_limit = 225000, > + .p2_slow = 7, .p2_fast = 7 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +/* LVDS 100mhz refclk limits. */ > +static const struct intel_limit intel_limits_ironlake_single_lvds_100m = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 1760000, .max = 3510000 }, > + .n = { .min = 1, .max = 2 }, > + .m = { .min = 79, .max = 126 }, > + .m1 = { .min = 12, .max = 22 }, > + .m2 = { .min = 5, .max = 9 }, > + .p = { .min = 28, .max = 112 }, > + .p1 = { .min = 2, .max = 8 }, > + .p2 = { .dot_limit = 225000, > + .p2_slow = 14, .p2_fast = 14 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit intel_limits_ironlake_dual_lvds_100m = { > + .dot = { .min = 25000, .max = 350000 }, > + .vco = { .min = 1760000, .max = 3510000 }, > + .n = { .min = 1, .max = 3 }, > + .m = { .min = 79, .max = 126 }, > + .m1 = { .min = 12, .max = 22 }, > + .m2 = { .min = 5, .max = 9 }, > + .p = { .min = 14, .max = 42 }, > + .p1 = { .min = 2, .max = 6 }, > + .p2 = { .dot_limit = 225000, > + .p2_slow = 7, .p2_fast = 7 }, > + .find_pll = intel_g4x_find_best_PLL, > +}; > + > +static const struct intel_limit 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 = 1, .max = 3 }, > + .p2 = { .dot_limit = 270000, > + .p2_slow = 2, .p2_fast = 20 }, > + .find_pll = intel_vlv_find_best_pll, > +}; > + > +static const struct intel_limit intel_limits_vlv_hdmi = { > + .dot = { .min = 25000, .max = 270000 }, > + .vco = { .min = 4000000, .max = 6000000 }, > + .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 struct intel_limit intel_limits_vlv_dp = { > + .dot = { .min = 25000, .max = 270000 }, > + .vco = { .min = 4000000, .max = 6000000 }, > + .n = { .min = 1, .max = 7 }, > + .m = { .min = 22, .max = 450 }, > + .m1 = { .min = 2, .max = 3 }, > + .m2 = { .min = 11, .max = 156 }, > + .p = { .min = 10, .max = 30 }, > + .p1 = { .min = 1, .max = 3 }, > + .p2 = { .dot_limit = 270000, > + .p2_slow = 2, .p2_fast = 20 }, > + .find_pll = intel_vlv_find_best_pll, > +}; > + > +static const struct intel_limit *intel_ironlake_limit(struct drm_crtc *crtc, > + int refclk) > +{ > + struct drm_device *dev = crtc->dev; > + const struct intel_limit *limit; > + > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > + if (intel_is_dual_link_lvds(dev)) { > + if (refclk == 100000) > + limit = &intel_limits_ironlake_dual_lvds_100m; > + else > + limit = &intel_limits_ironlake_dual_lvds; > + } else { > + if (refclk == 100000) > + limit = &intel_limits_ironlake_single_lvds_100m; > + else > + limit = &intel_limits_ironlake_single_lvds; > + } > + } else > + limit = &intel_limits_ironlake_dac; > + > + return limit; > +} > + > +static const struct intel_limit *intel_g4x_limit(struct drm_crtc *crtc) > +{ > + struct drm_device *dev = crtc->dev; > + const struct intel_limit *limit; > + > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { > + if (intel_is_dual_link_lvds(dev)) > + limit = &intel_limits_g4x_dual_channel_lvds; > + else > + limit = &intel_limits_g4x_single_channel_lvds; > + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) || > + intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) { > + limit = &intel_limits_g4x_hdmi; > + } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { > + limit = &intel_limits_g4x_sdvo; > + } else /* The option is for other outputs */ > + limit = &intel_limits_i9xx_sdvo; > + > + return limit; > +} > + > +const struct intel_limit *intel_limit(struct drm_crtc *crtc, int refclk) > +{ > + struct drm_device *dev = crtc->dev; > + const struct intel_limit *limit; > + > + if (HAS_PCH_SPLIT(dev)) > + limit = intel_ironlake_limit(crtc, refclk); > + else if (IS_G4X(dev)) { > + limit = intel_g4x_limit(crtc); > + } else if (IS_PINEVIEW(dev)) { > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) > + 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; > + else > + limit = &intel_limits_i9xx_sdvo; > + } else { > + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) > + limit = &intel_limits_i8xx_lvds; > + else > + limit = &intel_limits_i8xx_dvo; > + } > + return limit; > +} > + > +bool intel_find_pll(const struct intel_limit *limit, struct drm_crtc *crtc, > + int target, int refclk, intel_clock_t *match_clock, > + intel_clock_t *best_clock) > +{ > + return limit->find_pll(limit, crtc, target, refclk, > + match_clock, best_clock); > +} > -- > 1.7.9.5 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx at lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/intel-gfx -- Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch