From 4212a3cf2b168e771b2b719fc2fd808641b6454d Mon Sep 17 00:00:00 2001 From: root Date: Wed, 4 Mar 2009 14:22:36 +0800 Subject: [PATCH] dpll --- src/i830_display.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 files changed, 259 insertions(+), 22 deletions(-) diff --git a/src/i830_display.c b/src/i830_display.c index e243bfd..717f22a 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -67,9 +67,11 @@ typedef struct { #define INTEL_P2_NUM 2 -typedef struct { +typedef struct intel_limit { intel_range_t dot, vco, n, m, m1, m2, p, p1; intel_p2_t p2; + Bool (* cal_fn)(struct intel_limit *, xf86CrtcPtr, + int, int, intel_clock_t *); } intel_limit_t; #define I8XX_DOT_MIN 25000 @@ -153,6 +155,99 @@ typedef struct { #define INTEL_LIMIT_I9XX_LVDS 3 #define INTEL_LIMIT_IGD_SDVO_DAC 4 #define INTEL_LIMIT_IGD_LVDS 5 +#define INTEL_LIMIT_IG4X_SDVO 6 +#define INTEL_LIMIT_IG4X_HDMI_DAC 7 +#define INTEL_LIMIT_IG4X_SIN_LVDS 8 +#define INTEL_LIMIT_IG4X_DUA_LVDS 9 + +/*The parameter is for SDVO on G4x platform*/ +#define IG4X_DOT_SDVO_MIN 25000 +#define IG4X_DOT_SDVO_MAX 270000 +#define IG4X_VCO_SDVO_MIN 1760000 +#define IG4X_VCO_SDVO_MAX 3500000 +#define IG4X_N_SDVO_MIN 1 +#define IG4X_N_SDVO_MAX 4 +#define IG4X_M_SDVO_MIN 104 +#define IG4X_M_SDVO_MAX 138 +#define IG4X_M1_SDVO_MIN 17 +#define IG4X_M1_SDVO_MAX 23 +#define IG4X_M2_SDVO_MIN 5 +#define IG4X_M2_SDVO_MAX 11 +#define IG4X_P_SDVO_MIN 10 +#define IG4X_P_SDVO_MAX 30 +#define IG4X_P1_SDVO_MIN 1 +#define IG4X_P1_SDVO_MAX 3 +#define IG4X_P2_SDVO_SLOW 10 +#define IG4X_P2_SDVO_FAST 10 +#define IG4X_P2_SDVO_LIMIT 270000 + +/*The parameter is for HDMI_DAC on G4x platform*/ +#define IG4X_DOT_HDMI_DAC_MIN 20000 +#define IG4X_DOT_HDMI_DAC_MAX 400000 +#define IG4X_VCO_HDMI_DAC_MIN 1750000 +#define IG4X_VCO_HDMI_DAC_MAX 3500000 +#define IG4X_N_HDMI_DAC_MIN 1 +#define IG4X_N_HDMI_DAC_MAX 4 +#define IG4X_M_HDMI_DAC_MIN 104 +#define IG4X_M_HDMI_DAC_MAX 138 +#define IG4X_M1_HDMI_DAC_MIN 16 +#define IG4X_M1_HDMI_DAC_MAX 23 +#define IG4X_M2_HDMI_DAC_MIN 5 +#define IG4X_M2_HDMI_DAC_MAX 11 +#define IG4X_P_HDMI_DAC_MIN 5 +#define IG4X_P_HDMI_DAC_MAX 80 +#define IG4X_P1_HDMI_DAC_MIN 1 +#define IG4X_P1_HDMI_DAC_MAX 8 +#define IG4X_P2_HDMI_DAC_SLOW 10 +#define IG4X_P2_HDMI_DAC_FAST 5 +#define IG4X_P2_HDMI_DAC_LIMIT 165000 + +/*The parameter is for SIN_LVDS on G4x platform*/ +#define IG4X_DOT_SIN_LVDS_MIN 20000 +#define IG4X_DOT_SIN_LVDS_MAX 115000 +#define IG4X_VCO_SIN_LVDS_MIN 2054000 +#define IG4X_VCO_SIN_LVDS_MAX 3488000 +#define IG4X_N_SIN_LVDS_MIN 1 +#define IG4X_N_SIN_LVDS_MAX 3 +#define IG4X_M_SIN_LVDS_MIN 104 +#define IG4X_M_SIN_LVDS_MAX 138 +#define IG4X_M1_SIN_LVDS_MIN 17 +#define IG4X_M1_SIN_LVDS_MAX 23 +#define IG4X_M2_SIN_LVDS_MIN 5 +#define IG4X_M2_SIN_LVDS_MAX 11 +#define IG4X_P_SIN_LVDS_MIN 28 +#define IG4X_P_SIN_LVDS_MAX 112 +#define IG4X_P1_SIN_LVDS_MIN 2 +#define IG4X_P1_SIN_LVDS_MAX 8 +#define IG4X_P2_SIN_LVDS_SLOW 14 +#define IG4X_P2_SIN_LVDS_FAST 14 +#define IG4X_P2_SIN_LVDS_LIMIT 0 + +/*The parameter is for DUA_LVDS on G4x platform*/ +#define IG4X_DOT_DUA_LVDS_MIN 80000 +#define IG4X_DOT_DUA_LVDS_MAX 224000 +#define IG4X_VCO_DUA_LVDS_MIN 2342000 +#define IG4X_VCO_DUA_LVDS_MAX 3488000 +#define IG4X_N_DUA_LVDS_MIN 1 +#define IG4X_N_DUA_LVDS_MAX 3 +#define IG4X_M_DUA_LVDS_MIN 104 +#define IG4X_M_DUA_LVDS_MAX 138 +#define IG4X_M1_DUA_LVDS_MIN 17 +#define IG4X_M1_DUA_LVDS_MAX 23 +#define IG4X_M2_DUA_LVDS_MIN 5 +#define IG4X_M2_DUA_LVDS_MAX 11 +#define IG4X_P_DUA_LVDS_MIN 14 +#define IG4X_P_DUA_LVDS_MAX 42 +#define IG4X_P1_DUA_LVDS_MIN 2 +#define IG4X_P1_DUA_LVDS_MAX 6 +#define IG4X_P2_DUA_LVDS_SLOW 7 +#define IG4X_P2_DUA_LVDS_FAST 7 +#define IG4X_P2_DUA_LVDS_LIMIT 0 + +static Bool I8xx_and_I9xx_calculate_pll(intel_limit_t * limit, xf86CrtcPtr crtc, + int target, int refclk, intel_clock_t *best_clock); +static Bool IG4x_calculate_pll(intel_limit_t * limit, xf86CrtcPtr crtc, + int target, int refclk, intel_clock_t *best_clock); static const intel_limit_t intel_limits[] = { { /* INTEL_LIMIT_I8XX_DVO_DAC */ @@ -166,6 +261,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 }, + .cal_fn = I8xx_and_I9xx_calculate_pll, }, { /* INTEL_LIMIT_I8XX_LVDS */ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, @@ -178,6 +274,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 }, + .cal_fn = I8xx_and_I9xx_calculate_pll, }, { /* INTEL_LIMIT_I9XX_SDVO_DAC */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, @@ -190,6 +287,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 }, + .cal_fn = I8xx_and_I9xx_calculate_pll, }, { /* INTEL_LIMIT_I9XX_LVDS */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, @@ -205,6 +303,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 }, + .cal_fn = I8xx_and_I9xx_calculate_pll, }, { /* INTEL_LIMIT_IGD_SDVO */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, @@ -217,6 +316,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 }, + .cal_fn = I8xx_and_I9xx_calculate_pll, }, { /* INTEL_LIMIT_IGD_LVDS */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, @@ -230,7 +330,73 @@ static const intel_limit_t intel_limits[] = { /* IGD only supports single-channel mode. */ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW }, + .cal_fn = I8xx_and_I9xx_calculate_pll, + }, + + /* below parameter and function is for EagleLake and Cantig Chipset Family*/ + { /* INTEL_LIMIT_EL_AND_CTG_SDVO */ + .dot = { .min = IG4X_DOT_SDVO_MIN, .max = IG4X_DOT_SDVO_MAX }, + .vco = { .min = IG4X_VCO_SDVO_MIN, .max = IG4X_VCO_SDVO_MAX}, + .n = { .min = IG4X_N_SDVO_MIN, .max = IG4X_N_SDVO_MAX }, + .m = { .min = IG4X_M_SDVO_MIN, .max = IG4X_M_SDVO_MAX }, + .m1 = { .min = IG4X_M1_SDVO_MIN, .max = IG4X_M1_SDVO_MAX }, + .m2 = { .min = IG4X_M2_SDVO_MIN, .max = IG4X_M2_SDVO_MAX }, + .p = { .min = IG4X_P_SDVO_MIN, .max = IG4X_P_SDVO_MAX }, + .p1 = { .min = IG4X_P1_SDVO_MIN, .max = IG4X_P1_SDVO_MAX}, + .p2 = { .dot_limit = IG4X_P2_SDVO_LIMIT, + .p2_slow = IG4X_P2_SDVO_SLOW, + .p2_fast = IG4X_P2_SDVO_FAST + }, + .cal_fn = IG4x_calculate_pll, + }, + + { /* INTEL_LIMIT_EL_AND_CTG_HDMI_DAC */ + .dot = { .min = IG4X_DOT_HDMI_DAC_MIN, .max = IG4X_DOT_HDMI_DAC_MAX }, + .vco = { .min = IG4X_VCO_HDMI_DAC_MIN, .max = IG4X_VCO_HDMI_DAC_MAX}, + .n = { .min = IG4X_N_HDMI_DAC_MIN, .max = IG4X_N_HDMI_DAC_MAX }, + .m = { .min = IG4X_M_HDMI_DAC_MIN, .max = IG4X_M_HDMI_DAC_MAX }, + .m1 = { .min = IG4X_M1_HDMI_DAC_MIN, .max = IG4X_M1_HDMI_DAC_MAX }, + .m2 = { .min = IG4X_M2_HDMI_DAC_MIN, .max = IG4X_M2_HDMI_DAC_MAX }, + .p = { .min = IG4X_P_HDMI_DAC_MIN, .max = IG4X_P_HDMI_DAC_MAX }, + .p1 = { .min = IG4X_P1_HDMI_DAC_MIN, .max = IG4X_P1_HDMI_DAC_MAX}, + .p2 = { .dot_limit = IG4X_P2_HDMI_DAC_LIMIT, + .p2_slow = IG4X_P2_HDMI_DAC_SLOW, + .p2_fast = IG4X_P2_HDMI_DAC_FAST + }, + .cal_fn = IG4x_calculate_pll, + }, + + { /* INTEL_LIMIT_EL_AND_CTG_SIN_LVDS */ + .dot = { .min = IG4X_DOT_SIN_LVDS_MIN, .max = IG4X_DOT_SIN_LVDS_MAX }, + .vco = { .min = IG4X_VCO_SIN_LVDS_MIN, .max = IG4X_VCO_SIN_LVDS_MAX}, + .n = { .min = IG4X_N_SIN_LVDS_MIN, .max = IG4X_N_SIN_LVDS_MAX }, + .m = { .min = IG4X_M_SIN_LVDS_MIN, .max = IG4X_M_SIN_LVDS_MAX }, + .m1 = { .min = IG4X_M1_SIN_LVDS_MIN, .max = IG4X_M1_SIN_LVDS_MAX }, + .m2 = { .min = IG4X_M2_SIN_LVDS_MIN, .max = IG4X_M2_SIN_LVDS_MAX }, + .p = { .min = IG4X_P_SIN_LVDS_MIN, .max = IG4X_P_SIN_LVDS_MAX }, + .p1 = { .min = IG4X_P1_SIN_LVDS_MIN, .max = IG4X_P1_SIN_LVDS_MAX}, + .p2 = { .dot_limit = IG4X_P2_SIN_LVDS_LIMIT, + .p2_slow = IG4X_P2_SIN_LVDS_SLOW, + .p2_fast = IG4X_P2_SIN_LVDS_FAST + }, + .cal_fn = IG4x_calculate_pll, }, + + { /* INTEL_LIMIT_EL_AND_CTG_DUA_LVDS */ + .dot = { .min = IG4X_DOT_DUA_LVDS_MIN, .max = IG4X_DOT_DUA_LVDS_MAX }, + .vco = { .min = IG4X_VCO_DUA_LVDS_MIN, .max = IG4X_VCO_DUA_LVDS_MAX}, + .n = { .min = IG4X_N_DUA_LVDS_MIN, .max = IG4X_N_DUA_LVDS_MAX }, + .m = { .min = IG4X_M_DUA_LVDS_MIN, .max = IG4X_M_DUA_LVDS_MAX }, + .m1 = { .min = IG4X_M1_DUA_LVDS_MIN, .max = IG4X_M1_DUA_LVDS_MAX }, + .m2 = { .min = IG4X_M2_DUA_LVDS_MIN, .max = IG4X_M2_DUA_LVDS_MAX }, + .p = { .min = IG4X_P_DUA_LVDS_MIN, .max = IG4X_P_DUA_LVDS_MAX }, + .p1 = { .min = IG4X_P1_DUA_LVDS_MIN, .max = IG4X_P1_DUA_LVDS_MAX}, + .p2 = { .dot_limit = IG4X_P2_DUA_LVDS_LIMIT, + .p2_slow = IG4X_P2_DUA_LVDS_SLOW, + .p2_fast = IG4X_P2_DUA_LVDS_FAST + }, + .cal_fn = IG4x_calculate_pll, + }, }; static const intel_limit_t *intel_limit (xf86CrtcPtr crtc) @@ -239,7 +405,22 @@ static const intel_limit_t *intel_limit (xf86CrtcPtr crtc) I830Ptr pI830 = I830PTR(pScrn); const intel_limit_t *limit; - if (IS_I9XX(pI830) && !IS_IGD(pI830)) { + if (IS_G4X(pI830)){ + if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) { + if ((INREG(LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP) { + /*LVDS with dual channel*/ + limit = &intel_limits[INTEL_LIMIT_IG4X_DUA_LVDS]; + } else /*LVDS with single channel*/ + limit = &intel_limits[INTEL_LIMIT_IG4X_SIN_LVDS]; + } else if (i830PipeHasType (crtc, I830_OUTPUT_HDMI) || + i830PipeHasType (crtc, I830_OUTPUT_ANALOG)) { + limit = &intel_limits[INTEL_LIMIT_IG4X_HDMI_DAC]; + } else if (i830PipeHasType (crtc, I830_OUTPUT_SDVO)) { + limit = &intel_limits[INTEL_LIMIT_IG4X_SDVO]; + } else /*The option is for other outputs*/ + limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; + + } else if (IS_I9XX(pI830) && !IS_IGD(pI830)) { if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; else @@ -372,18 +553,70 @@ i830PllIsValid(xf86CrtcPtr 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 -i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk, intel_clock_t *best_clock) +static Bool IG4x_calculate_pll(intel_limit_t * limit, xf86CrtcPtr crtc, + int target, int refclk, intel_clock_t *best_clock) { ScrnInfoPtr pScrn = crtc->scrn; I830Ptr pI830 = I830PTR(pScrn); - intel_clock_t clock; - const intel_limit_t *limit = intel_limit (crtc); + intel_clock_t clock; + int max_n; + Bool found = FALSE; + float err_most = 0.0048; + + if (i830PipeHasType(crtc, I830_OUTPUT_LVDS)) + { + /* For LVDS, if the panel is on, 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 ((INREG(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; + } + + 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 bigger 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--) { + float this_err; + + intel_clock (pI830, refclk, &clock); + if (!i830PllIsValid(crtc, &clock)) + continue; + this_err = abs(clock.dot - target) ; + this_err = this_err/clock.dot; + if (this_err < err_most && clock.n <= max_n) { + memcpy(best_clock, &clock, sizeof(intel_clock_t)); + err_most = this_err; + /*chose smaller n*/ + max_n = clock.n; + found = TRUE; + } + } + } + } + } + return found; +} + +static Bool I8xx_and_I9xx_calculate_pll(intel_limit_t * limit, xf86CrtcPtr crtc, + int target, int refclk, intel_clock_t *best_clock) +{ + ScrnInfoPtr pScrn = crtc->scrn; + I830Ptr pI830 = I830PTR(pScrn); + intel_clock_t clock; int err = target; if (i830PipeHasType(crtc, I830_OUTPUT_LVDS)) @@ -404,22 +637,20 @@ i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk, intel_clock_t *best_cl } memset (best_clock, 0, sizeof (*best_clock)); - - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) + 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++) + for (clock.m2 = limit->m2.min; + clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; clock.m2++) { - /* m1 is always 0 in IGD */ - if (clock.m2 >= clock.m1 && !IS_IGD(pI830)) - break; - for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) + 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++) + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { int this_err; intel_clock (pI830, refclk, &clock); - + if (!i830PllIsValid(crtc, &clock)) continue; @@ -1303,6 +1534,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, uint32_t dpll = 0, fp = 0, dspcntr, pipeconf, lvds_bits = 0; Bool ok, is_sdvo = FALSE, is_dvo = FALSE; Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE; + intel_limit_t *limit; /* Set up some convenient bools for what outputs are connected to * our pipe, used in DPLL setup. @@ -1355,7 +1587,13 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, refclk = 48000; } - ok = i830FindBestPLL(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->cal_fn(limit, crtc, adjusted_mode->Clock, refclk, &clock); if (!ok) FatalError("Couldn't find PLL settings for mode!\n"); @@ -2142,4 +2380,3 @@ i830_crtc_init(ScrnInfoPtr pScrn, int pipe) } crtc->driver_private = intel_crtc; } - -- 1.5.4.4