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.
This commit is contained in:
Eric Anholt 2007-02-28 09:47:38 -08:00
parent 3e8e75e5d8
commit cd1d4b398e
3 changed files with 186 additions and 59 deletions

View File

@ -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 */

View File

@ -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),

View File

@ -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;