From cd1d4b398ec91d551bdaaa26c769e5e6a9442df1 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 28 Feb 2007 09:47:38 -0800 Subject: [PATCH] Many fixes to mode_get, mode_set, clock limits, and register dumps on i855. This should fix a number of issues with i855s, particularly with integrated LVDS panels. --- src/i810_reg.h | 25 ++++++++- src/i830_debug.c | 85 ++++++++++++++++++++++------ src/i830_display.c | 135 +++++++++++++++++++++++++++++++-------------- 3 files changed, 186 insertions(+), 59 deletions(-) diff --git a/src/i810_reg.h b/src/i810_reg.h index 46d473d7..6a9c11e4 100644 --- a/src/i810_reg.h +++ b/src/i810_reg.h @@ -299,6 +299,20 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define VCLK_DIVISOR_VGA0 0x6000 #define VCLK_DIVISOR_VGA1 0x6004 #define VCLK_POST_DIV 0x6010 +/** Selects a post divisor of 4 instead of 2. */ +# define VGA1_PD_P2_DIV_4 (1 << 15) +/** Overrides the p2 post divisor field */ +# define VGA1_PD_P1_DIV_2 (1 << 13) +# define VGA1_PD_P1_SHIFT 8 +/** P1 value is 2 greater than this field */ +# define VGA1_PD_P1_MASK (0x1f << 8) +/** Selects a post divisor of 4 instead of 2. */ +# define VGA0_PD_P2_DIV_4 (1 << 7) +/** Overrides the p2 post divisor field */ +# define VGA0_PD_P1_DIV_2 (1 << 5) +# define VGA0_PD_P1_SHIFT 0 +/** P1 value is 2 greater than this field */ +# define VGA0_PD_P1_MASK (0x1f << 0) #define POST_DIV_SELECT 0x70 #define POST_DIV_1 0x00 @@ -847,9 +861,18 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ # define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ # define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ +/** + * The i830 generation, in DAC/serial mode, defines p1 as two plus this + * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set. + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830 0x001f0000 +/** + * The i830 generation, in LVDS mode, defines P1 as the bit number set within + * this field (only one bit may be set). + */ +# define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000 # define DPLL_FPA01_P1_POST_DIV_SHIFT 16 # define PLL_P2_DIVIDE_BY_4 (1 << 23) /* i830, required in DVO non-gang */ -# define DPLL_FPA01_P1_POS_DIV_MASK_I830 0x001f0000 /* i830 */ # define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */ # define PLL_REF_INPUT_DREFCLK (0 << 13) # define PLL_REF_INPUT_TVCLKINA (1 << 13) /* i830 */ diff --git a/src/i830_debug.c b/src/i830_debug.c index 6716eaab..de36a045 100644 --- a/src/i830_debug.c +++ b/src/i830_debug.c @@ -102,6 +102,28 @@ DEBUGSTRING(i830_debug_fp) ((val & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT)); } +DEBUGSTRING(i830_debug_vga_pd) +{ + int vga0_p1, vga0_p2, vga1_p1, vga1_p2; + + /* XXX: i9xx version */ + + if (val & VGA0_PD_P1_DIV_2) + vga0_p1 = 2; + else + vga0_p1 = ((val & VGA0_PD_P1_MASK) >> VGA0_PD_P1_SHIFT) + 2; + vga0_p2 = (val & VGA0_PD_P2_DIV_4) ? 4 : 2; + + if (val & VGA1_PD_P1_DIV_2) + vga1_p1 = 2; + else + vga1_p1 = ((val & VGA1_PD_P1_MASK) >> VGA1_PD_P1_SHIFT) + 2; + vga1_p2 = (val & VGA1_PD_P2_DIV_4) ? 4 : 2; + + return XNFprintf("vga0 p1 = %d, p2 = %d, vga1 p1 = %d, p2 = %d", + vga0_p1, vga0_p2, vga1_p1, vga1_p2); +} + DEBUGSTRING(i830_debug_pp_status) { char *status = val & PP_ON ? "on" : "off"; @@ -140,18 +162,44 @@ DEBUGSTRING(i830_debug_dpll) char sdvoextra[20]; int p1, p2 = 0; - p1 = ffs((val & DPLL_FPA01_P1_POST_DIV_MASK) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - switch (val & DPLL_MODE_MASK) { - case DPLLB_MODE_DAC_SERIAL: - mode = "dac/serial"; - p2 = val & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? 5 : 10; - break; - case DPLLB_MODE_LVDS: - mode = "LVDS"; - p2 = val & DPLLB_LVDS_P2_CLOCK_DIV_7 ? 7 : 14; - break; + if (IS_I9XX(pI830)) { + p1 = ffs((val & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + switch (val & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + mode = "DAC/serial"; + p2 = val & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? 5 : 10; + break; + case DPLLB_MODE_LVDS: + mode = "LVDS"; + p2 = val & DPLLB_LVDS_P2_CLOCK_DIV_7 ? 7 : 14; + break; + } + } else { + Bool is_lvds = (INREG(LVDS) & LVDS_PORT_EN) && (reg == DPLL_B); + + if (val & PLL_P2_DIVIDE_BY_4) + p2 = 4; + else + p2 = 2; + + if (is_lvds) { + mode = "LVDS"; + /* Map the bit number set from (1, 6) to (-1, 4). */ + p1 = ffs((val & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + } else { + mode = "DAC/serial"; + if (val & PLL_P1_DIVIDE_BY_TWO) { + p1 = 2; + } else { + /* Map the number in the field to (1, 31) */ + p1 = ((val & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; + } + } } + switch (val & PLL_REF_INPUT_MASK) { case PLL_REF_INPUT_DREFCLK: clock = "default"; @@ -162,7 +210,12 @@ DEBUGSTRING(i830_debug_dpll) case PLL_REF_INPUT_TVCLKINBC: clock = "TV B/C"; break; + case PLLB_REF_INPUT_SPREADSPECTRUMIN: + if (reg == DPLL_B) + clock = "spread spectrum"; + break; } + if (IS_I945G(pI830) || IS_I945GM(pI830)) { sprintf(sdvoextra, ", SDVO mult %d", (int)((val & SDVO_MULTIPLIER_MASK) >> @@ -171,9 +224,9 @@ DEBUGSTRING(i830_debug_dpll) sdvoextra[0] = '\0'; } - return XNFprintf("%s, %s%s, %s mode, %s clock, p1 = %d, " + return XNFprintf("%s, %s%s, %s clock, %s mode, p1 = %d, " "p2 = %d%s%s", - enabled, dvomode, vgamode, mode, clock, p1, p2, + enabled, dvomode, vgamode, clock, mode, p1, p2, fpextra, sdvoextra); } @@ -233,9 +286,9 @@ static struct i830SnapshotRec { char *(*debug_output)(I830Ptr pI830, int reg, CARD32 val); CARD32 val; } i830_snapshot[] = { - DEFINEREG(VCLK_DIVISOR_VGA0), - DEFINEREG(VCLK_DIVISOR_VGA1), - DEFINEREG(VCLK_POST_DIV), + DEFINEREG2(VCLK_DIVISOR_VGA0, i830_debug_fp), + DEFINEREG2(VCLK_DIVISOR_VGA1, i830_debug_fp), + DEFINEREG2(VCLK_POST_DIV, i830_debug_vga_pd), DEFINEREG2(DPLL_TEST, i830_debug_dpll_test), DEFINEREG(D_STATE), DEFINEREG(DSPCLK_GATE_D), diff --git a/src/i830_display.c b/src/i830_display.c index a1660e35..e9666de0 100644 --- a/src/i830_display.c +++ b/src/i830_display.c @@ -85,10 +85,15 @@ typedef struct { #define I8XX_M2_MAX 16 #define I8XX_P_MIN 4 #define I8XX_P_MAX 128 +/* LVDS p1 value can go from 1 to 6, while DAC goes from 2 to 33. These + * values below get 2 added in the clock calculations. + */ #define I8XX_P1_MIN 0 -#define I8XX_P1_MAX 30 -#define I8XX_P2_SLOW 1 -#define I8XX_P2_FAST 0 +#define I8XX_P1_MAX 31 +#define I8XX_P1_LVDS_MIN -1 +#define I8XX_P1_LVDS_MAX 4 +#define I8XX_P2_SLOW 1 /* this is a bit shift amount */ +#define I8XX_P2_FAST 0 /* this is a bit shift amount */ #define I8XX_P2_SLOW_LIMIT 165000 #define I9XX_DOT_MIN 20000 @@ -116,12 +121,13 @@ typedef struct { #define I9XX_P2_LVDS_FAST 7 #define I9XX_P2_LVDS_SLOW_LIMIT 112000 -#define INTEL_LIMIT_I8XX 0 -#define INTEL_LIMIT_I9XX_SDVO_DAC 1 -#define INTEL_LIMIT_I9XX_LVDS 2 +#define INTEL_LIMIT_I8XX_DVO_DAC 0 +#define INTEL_LIMIT_I8XX_LVDS 1 +#define INTEL_LIMIT_I9XX_SDVO_DAC 2 +#define INTEL_LIMIT_I9XX_LVDS 3 static const intel_limit_t intel_limits[] = { - { + { /* INTEL_LIMIT_I8XX_DVO_DAC */ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, @@ -133,7 +139,19 @@ static const intel_limit_t intel_limits[] = { .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST }, }, - { + { /* INTEL_LIMIT_I8XX_LVDS */ + .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX }, + .vco = { .min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX }, + .n = { .min = I8XX_N_MIN, .max = I8XX_N_MAX }, + .m = { .min = I8XX_M_MIN, .max = I8XX_M_MAX }, + .m1 = { .min = I8XX_M1_MIN, .max = I8XX_M1_MAX }, + .m2 = { .min = I8XX_M2_MIN, .max = I8XX_M2_MAX }, + .p = { .min = I8XX_P_MIN, .max = I8XX_P_MAX }, + .p1 = { .min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX }, + .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT, + .p2_slow = I8XX_P2_FAST, .p2_fast = I8XX_P2_FAST }, + }, + { /* INTEL_LIMIT_I9XX_SDVO_DAC */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, @@ -145,7 +163,7 @@ static const intel_limit_t intel_limits[] = { .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST }, }, - { + { /* INTEL_LIMIT_I9XX_LVDS */ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX }, .vco = { .min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX }, .n = { .min = I9XX_N_MIN, .max = I9XX_N_MAX }, @@ -168,15 +186,18 @@ static const intel_limit_t *intel_limit (xf86CrtcPtr crtc) I830Ptr pI830 = I830PTR(pScrn); const intel_limit_t *limit; - if (IS_I9XX(pI830)) - { + if (IS_I9XX(pI830)) { if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS]; else limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; + } else { + if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) + limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS]; + else + limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC]; } - else - limit = &intel_limits[INTEL_LIMIT_I8XX]; + return limit; } @@ -792,7 +813,15 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, if (IS_I965G(pI830)) dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT); } else { - dpll |= clock.p1 << 16; + if (is_lvds) { + /* map (-1 to 4) to ((1 << 0) to (1 << 5)). */ + dpll |= (1 << (clock.p1 + 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT; + } else { + if (clock.p1 == 0) + dpll |= PLL_P1_DIVIDE_BY_TWO; + else + dpll |= clock.p1 << DPLL_FPA01_P1_POST_DIV_SHIFT; + } dpll |= clock.p2 << 23; } @@ -1250,35 +1279,59 @@ i830_crtc_clock_get(ScrnInfoPtr pScrn, xf86CrtcPtr crtc) clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; - clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> - DPLL_FPA01_P1_POST_DIV_SHIFT); - switch (dpll & DPLL_MODE_MASK) { - case DPLLB_MODE_DAC_SERIAL: - clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? 5 : 10; - break; - case DPLLB_MODE_LVDS: - clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? 7 : 14; - break; - default: - xf86DrvMsg(pScrn->scrnIndex, X_WARNING, - "Unknown DPLL mode %08x in programmed mode\n", - (int)(dpll & DPLL_MODE_MASK)); - return 0; - } - - /* XXX: Handle the 100Mhz refclk */ - if (IS_I9XX(pI830)) - i9xx_clock(96000, &clock); - else - i9xx_clock(48000, &clock); + if (IS_I9XX(pI830)) { + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); - if (!i830PllIsValid(crtc, &clock)) { - xf86DrvMsg(pScrn->scrnIndex, X_WARNING, - "Bad clock found programmed in pipe %c\n", - pipe == 0 ? 'A' : 'B'); - i830PrintPll("", &clock); + switch (dpll & DPLL_MODE_MASK) { + case DPLLB_MODE_DAC_SERIAL: + clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ? 5 : 10; + break; + case DPLLB_MODE_LVDS: + clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ? 7 : 14; + break; + default: + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "Unknown DPLL mode %08x in programmed mode\n", + (int)(dpll & DPLL_MODE_MASK)); + return 0; + } + + /* XXX: Handle the 100Mhz refclk */ + i9xx_clock(96000, &clock); + } else { + Bool is_lvds = (pipe == 1) && (INREG(LVDS) & LVDS_PORT_EN); + + if (is_lvds) { + /* Map the bit number set from (1, 6) to (-1, 4). */ + clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> + DPLL_FPA01_P1_POST_DIV_SHIFT) - 2; + clock.p2 = 0; + } else { + if (dpll & PLL_P1_DIVIDE_BY_TWO) { + clock.p1 = 0; + } else { + /* Map the number in the field to (1, 31) */ + clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >> + DPLL_FPA01_P1_POST_DIV_SHIFT); + } + if (dpll & PLL_P2_DIVIDE_BY_4) + clock.p2 = 1; + else + clock.p2 = 0; + } + + /* XXX: Deal with other refclocks */ + i8xx_clock(48000, &clock); } + /* XXX: It would be nice to validate the clocks, but we can't reuse + * i830PllIsValid() because it relies on the xf86_config output + * configuration being accurate, which it isn't necessarily. + */ + if (0) + i830PrintPll("probed", &clock); + return clock.dot; } @@ -1299,8 +1352,6 @@ i830_crtc_mode_get(ScrnInfoPtr pScrn, xf86CrtcPtr crtc) if (mode == NULL) return NULL; - memset(mode, 0, sizeof(*mode)); - mode->Clock = i830_crtc_clock_get(pScrn, crtc); mode->HDisplay = (htot & 0xffff) + 1; mode->HTotal = ((htot & 0xffff0000) >> 16) + 1;