xf86-video-intel: Branch 'modesetting' - src/i830_display.c src/i830_randr.c
Keith Packard
keithp at kemper.freedesktop.org
Sun Dec 3 08:58:45 EET 2006
src/i830_display.c | 417 +++++++++++++++++++++++++++++++++--------------------
src/i830_randr.c | 2
2 files changed, 265 insertions(+), 154 deletions(-)
New commits:
diff-tree 8fcf9a81179ee8577ddab5e904c58fbfd14cf59c (from 81dde11d419c8f9198ab3502d9813d66d0bc6d6d)
Author: Keith Packard <keithp at ukulele.keithp.com>
Date: Sat Dec 2 22:58:31 2006 -0800
DOUBLE_WIDE mode for high pixel clock 8xx. Rewrite PLL search.
High pixel clock modes on pipe A of an 8xx chip require
DOUBLE_WIDE mode. It's supposed to be modes > 180MHz or so,
but the board I have requires DOUBLE_WIDE mode for clocks > 108MHz
or so. The limit is related to the core clock speed of the chip, which
can be found indirectly through PCI config space. None of the possible
values explain why this board needs this mode for these relatively low
clock rates though.
Also, create tables of data for the PLL computation and use them
instead of code. I think it's cleaner looking. It is also untested on
9xx. It'll work. Really.
diff --git a/src/i830_display.c b/src/i830_display.c
index 29b783b..ac56528 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -43,21 +43,179 @@
#include "i830_debug.h"
#include "i830_xf86Modes.h"
-/** Returns the pixel clock for the given refclk and divisors. */
-static int i830_clock(int refclk, int m1, int m2, int n, int p1, int p2)
+typedef struct {
+ /* given values */
+ int n;
+ int m1, m2;
+ int p1, p2;
+ /* derived values */
+ int dot;
+ int vco;
+ int m;
+ int p;
+} intel_clock_t;
+
+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_range_t dot, vco, n, m, m1, m2, p, p1;
+ intel_p2_t p2;
+} intel_limit_t;
+
+#define I8XX_DOT_MIN 25000
+#define I8XX_DOT_MAX 350000
+#define I8XX_VCO_MIN 930000
+#define I8XX_VCO_MAX 1400000
+#define I8XX_N_MIN 3
+#define I8XX_N_MAX 16
+#define I8XX_M_MIN 96
+#define I8XX_M_MAX 140
+#define I8XX_M1_MIN 18
+#define I8XX_M1_MAX 26
+#define I8XX_M2_MIN 6
+#define I8XX_M2_MAX 16
+#define I8XX_P_MIN 4
+#define I8XX_P_MAX 128
+#define I8XX_P1_MIN 0
+#define I8XX_P1_MAX 30
+#define I8XX_P2_SLOW 1
+#define I8XX_P2_FAST 0
+#define I8XX_P2_SLOW_LIMIT 165000
+
+#define I9XX_DOT_MIN 20000
+#define I9XX_DOT_MAX 400000
+#define I9XX_VCO_MIN 1400000
+#define I9XX_VCO_MAX 2800000
+#define I9XX_N_MIN 3
+#define I9XX_N_MAX 8
+#define I9XX_M_MIN 70
+#define I9XX_M_MAX 120
+#define I9XX_M1_MIN 10
+#define I9XX_M1_MAX 20
+#define I9XX_M2_MIN 5
+#define I9XX_M2_MAX 9
+#define I9XX_P_SDVO_DAC_MIN 5
+#define I9XX_P_SDVO_DAC_MAX 80
+#define I9XX_P_LVDS_MIN 7
+#define I9XX_P_LVDS_MAX 98
+#define I9XX_P1_MIN 1
+#define I9XX_P1_MAX 8
+#define I9XX_P2_SDVO_DAC_SLOW 10
+#define I9XX_P2_SDVO_DAC_FAST 5
+#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000
+#define I9XX_P2_LVDS_SLOW 14
+#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
+
+static const intel_limit_t intel_limits[] = {
+ {
+ .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_MIN, .max = I8XX_P1_MAX },
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ },
+ {
+ .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 },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .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 },
+ },
+ {
+ .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 },
+ .m = { .min = I9XX_M_MIN, .max = I9XX_M_MAX },
+ .m1 = { .min = I9XX_M1_MIN, .max = I9XX_M1_MAX },
+ .m2 = { .min = I9XX_M2_MIN, .max = I9XX_M2_MAX },
+ .p = { .min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ /* The single-channel range is 25-112Mhz, and dual-channel
+ * is 80-224Mhz. Prefer single channel as much as possible.
+ */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ },
+};
+
+static const intel_limit_t *intel_limit (xf86CrtcPtr crtc)
{
- return refclk * (5 * m1 + m2) / n / (p1 * p2);
+ ScrnInfoPtr pScrn = crtc->scrn;
+ I830Ptr pI830 = I830PTR(pScrn);
+ const intel_limit_t *limit;
+
+ 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
+ limit = &intel_limits[INTEL_LIMIT_I8XX];
+ return limit;
}
-static void
-i830PrintPll(char *prefix, int refclk, int m1, int m2, int n, int p1, int p2)
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+
+static void i8xx_clock(int refclk, intel_clock_t *clock)
{
- int dotclock;
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = (clock->p1 + 2) << (clock->p2 + 1);
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
- dotclock = i830_clock(refclk, m1, m2, n, p1, p2);
+/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
+
+static void i9xx_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = refclk * clock->m / (clock->n + 2);
+ clock->dot = clock->vco / clock->p;
+}
+
+static void intel_clock(I830Ptr pI830, int refclk, intel_clock_t *clock)
+{
+ if (IS_I9XX(pI830))
+ return i9xx_clock (refclk, clock);
+ else
+ return i8xx_clock (refclk, clock);
+}
- ErrorF("%s: dotclock %d ((%d, %d), %d, (%d, %d))\n", prefix, dotclock,
- m1, m2, n, p1, p2);
+static void
+i830PrintPll(char *prefix, intel_clock_t *clock)
+{
+ ErrorF("%s: dotclock %d vco %d ((m %d, m1 %d, m2 %d), n %d, (p %d, p1 %d, p2 %d))\n",
+ prefix, clock->dot, clock->vco,
+ clock->m, clock->m1, clock->m2,
+ clock->n,
+ clock->p, clock->p1, clock->p2);
}
/**
@@ -83,90 +241,38 @@ i830PipeHasType (xf86CrtcPtr crtc, int t
return FALSE;
}
+#define i830PllInvalid(s) { /* ErrorF (s) */; return FALSE; }
/**
* Returns whether the given set of divisors are valid for a given refclk with
* the given outputs.
- *
- * The equation for these divisors would be:
- * clk = refclk * (5 * m1 + m2) / n / (p1 * p2)
*/
+
static Bool
-i830PllIsValid(xf86CrtcPtr crtc, int refclk, int m1, int m2,
- int n, int p1, int p2)
+i830PllIsValid(xf86CrtcPtr crtc, intel_clock_t *clock)
{
- ScrnInfoPtr pScrn = crtc->scrn;
- I830Ptr pI830 = I830PTR(pScrn);
- int p, m, vco, dotclock;
- int min_m1, max_m1, min_m2, max_m2, min_m, max_m, min_n, max_n;
- int min_p1, max_p1, min_p, max_p, min_vco, max_vco, min_dot, max_dot;
+ const intel_limit_t *limit = intel_limit (crtc);
- if (IS_I9XX(pI830)) {
- min_m1 = 10;
- max_m1 = 20;
- min_m2 = 5;
- max_m2 = 9;
- min_m = 70;
- max_m = 120;
- min_n = 3;
- max_n = 8;
- min_p1 = 1;
- max_p1 = 8;
- if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) {
- min_p = 7;
- max_p = 98;
- } else {
- min_p = 5;
- max_p = 80;
- }
- min_vco = 1400000;
- max_vco = 2800000;
- min_dot = 20000;
- max_dot = 400000;
- } else {
- min_m1 = 18;
- max_m1 = 26;
- min_m2 = 6;
- max_m2 = 16;
- min_m = 96;
- max_m = 140;
- min_n = 3;
- max_n = 16;
- min_p1 = 2;
- max_p1 = 18;
- min_vco = 930000;
- max_vco = 1400000;
- min_dot = 20000;
- max_dot = 350000;
- min_p = 4;
- max_p = 128;
- }
-
- p = p1 * p2;
- m = 5 * m1 + m2;
- vco = refclk * m / n;
- dotclock = i830_clock(refclk, m1, m2, n, p1, p2);
-
- if (p1 < min_p1 || p1 > max_p1)
- return FALSE;
- if (p < min_p || p > max_p)
- return FALSE;
- if (m2 < min_m2 || m2 > max_m2)
- return FALSE;
- if (m1 < min_m1 || m1 > max_m1)
- return FALSE;
- if (m1 <= m2)
- return FALSE;
- if (m < min_m || m > max_m)
- return FALSE;
- if (n < min_n || n > max_n)
- return FALSE;
- if (vco < min_vco || vco > max_vco)
- return FALSE;
+ if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
+ i830PllInvalid ("p1 out of range\n");
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ i830PllInvalid ("p out of range\n");
+ if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
+ i830PllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ i830PllInvalid ("m1 out of range\n");
+ if (clock->m1 <= clock->m2)
+ i830PllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ i830PllInvalid ("m out of range\n");
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ i830PllInvalid ("n out of range\n");
+ if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
+ i830PllInvalid ("vco out of range\n");
/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
* output, etc., rather than just a single range.
*/
- if (dotclock < min_dot || dotclock > max_dot)
- return FALSE;
+ if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
+ i830PllInvalid ("dot out of range\n");
return TRUE;
}
@@ -174,83 +280,47 @@ i830PllIsValid(xf86CrtcPtr crtc, int ref
/**
* Returns a set of divisors for the desired target clock with the given refclk,
* or FALSE. Divisor values are the actual divisors for
- * clk = refclk * (5 * m1 + m2) / n / (p1 * p2)
*/
static Bool
-i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk,
- int *outm1, int *outm2, int *outn, int *outp1, int *outp2)
+i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk, intel_clock_t *best_clock)
{
ScrnInfoPtr pScrn = crtc->scrn;
I830Ptr pI830 = I830PTR(pScrn);
- int m1, m2, n, p1, p2;
+ intel_clock_t clock;
+ const intel_limit_t *limit = intel_limit (crtc);
int err = target;
- int min_m1, max_m1, min_m2, max_m2, min_n, max_n, min_p1, max_p1;
-
- if (IS_I9XX(pI830)) {
- min_m1 = 10;
- max_m1 = 20;
- min_m2 = 5;
- max_m2 = 9;
- min_n = 3;
- max_n = 8;
- min_p1 = 1;
- max_p1 = 8;
- if (i830PipeHasType (crtc, I830_OUTPUT_LVDS)) {
- /* The single-channel range is 25-112Mhz, and dual-channel
- * is 80-224Mhz. Prefer single channel as much as possible.
- */
- if (target < 112000)
- p2 = 14;
- else
- p2 = 7;
- } else {
- if (target < 200000)
- p2 = 10;
- else
- p2 = 5;
- }
- } else {
- min_m1 = 18;
- max_m1 = 26;
- min_m2 = 6;
- max_m2 = 16;
- min_n = 3;
- max_n = 16;
- min_p1 = 2;
- max_p1 = 18;
- if (target < 165000)
- p2 = 4;
- else
- p2 = 2;
- }
+ if (target < limit->p2.dot_limit)
+ clock.p2 = limit->p2.p2_slow;
+ else
+ clock.p2 = limit->p2.p2_fast;
- for (m1 = min_m1; m1 <= max_m1; m1++) {
- for (m2 = min_m2; m2 < max_m2; m2++) {
- for (n = min_n; n <= max_n; n++) {
- for (p1 = min_p1; p1 <= max_p1; p1++) {
- int clock, this_err;
+ memset (best_clock, 0, sizeof (*best_clock));
- if (!i830PllIsValid(crtc, refclk, m1, m2, n,
- p1, p2)) {
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++)
+ {
+ for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 && clock.m2 < limit->m2.max; clock.m2++)
+ {
+ 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 (pI830, refclk, &clock);
+
+ if (!i830PllIsValid(crtc, &clock))
continue;
- }
- clock = i830_clock(refclk, m1, m2, n, p1, p2);
- this_err = abs(clock - target);
+ this_err = abs(clock.dot - target);
if (this_err < err) {
- *outm1 = m1;
- *outm2 = m2;
- *outn = n;
- *outp1 = p1;
- *outp2 = p2;
+ *best_clock = clock;
err = this_err;
}
}
}
}
}
-
return (err != target);
}
@@ -424,7 +494,7 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
I830Ptr pI830 = I830PTR(pScrn);
I830CrtcPrivatePtr intel_crtc = crtc->driver_private;
int pipe = intel_crtc->pipe;
- int m1 = 0, m2 = 0, n = 0, p1 = 0, p2 = 0;
+ intel_clock_t clock;
CARD32 dpll = 0, fp = 0, temp;
CARD32 htot, hblank, hsync, vtot, vblank, vsync, dspcntr;
CARD32 pipesrc, dspsize;
@@ -446,6 +516,7 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
int dspstride_reg = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+ int pipestat_reg = (pipe == 0) ? PIPEASTAT : PIPEBSTAT;
Bool ret = FALSE;
#ifdef XF86DRI
Bool didLock = FALSE;
@@ -572,8 +643,8 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
} else {
refclk = 48000;
}
- ok = i830FindBestPLL(crtc, pixel_clock, refclk, &m1, &m2, &n,
- &p1, &p2);
+
+ ok = i830FindBestPLL(crtc, pixel_clock, refclk, &clock);
if (!ok) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
"Couldn't find PLL settings for mode!\n");
@@ -586,8 +657,9 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
dpll |= DPLLB_MODE_LVDS;
else
dpll |= DPLLB_MODE_DAC_SERIAL;
- dpll |= (1 << (p1 - 1)) << 16;
- switch (p2) {
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 1)) << 16;
+ switch (clock.p2) {
case 5:
dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
break;
@@ -604,9 +676,8 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
if (IS_I965G(pI830))
dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
} else {
- dpll |= (p1 - 2) << 16;
- if (p2 == 4)
- dpll |= PLL_P2_DIVIDE_BY_4;
+ dpll |= clock.p1 << 16;
+ dpll |= clock.p2 << 23;
}
if (is_tv)
@@ -622,7 +693,7 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
else
dpll |= PLL_REF_INPUT_DREFCLK;
- fp = ((n - 2) << 16) | ((m1 - 2) << 8) | (m2 - 2);
+ fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
#if 1
ErrorF("hact: %d htot: %d hbstart: %d hbend: %d hsyncstart: %d hsyncend: %d\n",
@@ -638,7 +709,7 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
(int)(dspsize & 0xffff) + 1, (int)(dspsize >> 16) + 1);
#endif
- i830PrintPll("chosen", refclk, m1, m2, n, p1, p2);
+ i830PrintPll("chosen", &clock);
ErrorF("clock regs: 0x%08x, 0x%08x\n", (int)dpll, (int)fp);
dspcntr = DISPLAY_PLANE_ENABLE;
@@ -714,13 +785,53 @@ i830PipeSetMode(xf86CrtcPtr crtc, Displa
/* Then, turn the pipe on first */
temp = INREG(pipeconf_reg);
- OUTREG(pipeconf_reg, temp | PIPEACONF_ENABLE);
+ temp |= PIPEACONF_ENABLE;
+ if (!IS_I9XX(pI830) && pipe == 0)
+ {
+ /*
+ * The docs say this is needed when the dot clock is > 90% of the
+ * core speed. Core speeds are indicated by bits in the PCI
+ * config space and don't seem to ever be less than 200MHz,
+ * which is a bit confusing.
+ *
+ * However, For one little 855/852 card I have, 135000 requires
+ * double wide mode, but 108000 does not. That makes no sense
+ * but we're used to that. It may be affected by pixel size,
+ * but the BIOS mode setting code doesn't appear to use that.
+ *
+ * It doesn't seem to cause any harm, although it
+ * does restrict some output options.
+ */
+ if (pixel_clock > 108000)
+ temp |= PIPEACONF_DOUBLE_WIDE;
+ else
+ temp &= ~PIPEACONF_DOUBLE_WIDE;
+ }
+ OUTREG(pipeconf_reg, temp);
if (plane_enable) {
/* And then turn the plane on */
OUTREG(dspcntr_reg, dspcntr);
}
+#if 0
+ /*
+ * If the display isn't solid, it may be running out
+ * of memory bandwidth. This code will dump out the
+ * pipe status, if bit 31 is on, the fifo underran
+ */
+ for (i = 0; i < 4; i++) {
+ i830WaitForVblank(pScrn);
+
+ OUTREG(pipestat_reg, INREG(pipestat_reg) | 0x80000000);
+
+ i830WaitForVblank(pScrn);
+
+ temp = INREG(pipestat_reg);
+ ErrorF ("pipe status 0x%x\n", temp);
+ }
+#endif
+
crtc->curMode = *pMode;
ret = TRUE;
diff --git a/src/i830_randr.c b/src/i830_randr.c
index 373403f..da8d746 100644
--- a/src/i830_randr.c
+++ b/src/i830_randr.c
@@ -710,7 +710,7 @@ xf86RandR12SetInfo12 (ScrnInfoPtr pScrn)
subpixel = SubPixelHorizontalRGB;
break;
case I830_OUTPUT_ANALOG:
- crtc_types = ((1 << 0) | (1 << 1));
+ crtc_types = ((1 << 0));
clone_types = ((1 << I830_OUTPUT_ANALOG) |
(1 << I830_OUTPUT_DVO) |
(1 << I830_OUTPUT_SDVO));
More information about the xorg-commit
mailing list