summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2007-02-13 10:21:12 -0800
committerEric Anholt <eric@anholt.net>2007-02-13 10:21:12 -0800
commit6641aec0a1cbc869fba1956c556cdd204631545a (patch)
tree80706e6785fc773324aa6867bf234a7aa1b7f634
parent991439d4c78cf5b2a8f6bb8f5b36fffbfcc4e4fc (diff)
Attempt to detect panel fixed mode from EDID or current programmed mode.
These two sources are placed in higher priority to the BIOS data when available, since the BIOS data has proven unreliable. The BIOS data is still read, and warnings printed if it doesn't match what we probe. The BIOS data remains useful for the situation where we want to turn on LVDS but there is no EDID available and no current mode programmed (i.e. booting with VGA or TV connected).
-rw-r--r--src/i830.h3
-rw-r--r--src/i830_bios.c26
-rw-r--r--src/i830_bios.h3
-rw-r--r--src/i830_display.c85
-rw-r--r--src/i830_display.h1
-rw-r--r--src/i830_driver.c2
-rw-r--r--src/i830_lvds.c115
7 files changed, 196 insertions, 39 deletions
diff --git a/src/i830.h b/src/i830.h
index 57d8da18..96972eb5 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -590,6 +590,9 @@ extern Bool I830I2CInit(ScrnInfoPtr pScrn, I2CBusPtr *bus_ptr, int i2c_reg,
/* return a mask of output indices matching outputs against type_mask */
int i830_output_clones (ScrnInfoPtr pScrn, int type_mask);
+/* i830_bios.c */
+DisplayModePtr i830_bios_get_panel_mode(ScrnInfoPtr pScrn);
+
/* i830_display.c */
Bool
i830PipeHasType (xf86CrtcPtr crtc, int type);
diff --git a/src/i830_bios.c b/src/i830_bios.c
index a9ef474d..cb886b54 100644
--- a/src/i830_bios.c
+++ b/src/i830_bios.c
@@ -122,8 +122,16 @@ i830GetBIOS(ScrnInfoPtr pScrn)
return bios;
}
-Bool
-i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
+/**
+ * Returns the BIOS's fixed panel mode.
+ *
+ * Note that many BIOSes will have the appropriate tables for a panel even when
+ * a panel is not attached. Additionally, many BIOSes adjust table sizes or
+ * offsets, such that this parsing fails. Thus, almost any other method for
+ * detecting the panel mode is preferable.
+ */
+DisplayModePtr
+i830_bios_get_panel_mode(ScrnInfoPtr pScrn)
{
I830Ptr pI830 = I830PTR(pScrn);
struct vbt_header *vbt;
@@ -131,12 +139,11 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
int vbt_off, bdb_off, bdb_block_off, block_size;
int panel_type = -1;
unsigned char *bios;
- Bool found_panel_info = FALSE;
bios = i830GetBIOS(pScrn);
if (bios == NULL)
- return FALSE;
+ return NULL;
vbt_off = INTEL_BIOS_16(0x1a);
vbt = (struct vbt_header *)(bios + vbt_off);
@@ -146,7 +153,7 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
if (memcmp(bdb->signature, "BIOS_DATA_BLOCK ", 16) != 0) {
xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Bad BDB signature\n");
xfree(bios);
- return FALSE;
+ return NULL;
}
for (bdb_block_off = bdb->header_size; bdb_block_off < bdb->bdb_size;
@@ -163,7 +170,6 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
id = INTEL_BIOS_8(start);
block_size = INTEL_BIOS_16(start + 1) + 3;
- xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Found BDB block type %d\n", id);
switch (id) {
case 40:
lvds1 = (struct lvds_bdb_1 *)(bios + start);
@@ -227,13 +233,11 @@ i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn)
"Found panel mode in BIOS VBT tables:\n");
xf86PrintModeline(pScrn->scrnIndex, fixed_mode);
- pI830->panel_fixed_mode = fixed_mode;
-
- found_panel_info = TRUE;
- break;
+ xfree(bios);
+ return fixed_mode;
}
}
xfree(bios);
- return found_panel_info;
+ return NULL;
}
diff --git a/src/i830_bios.h b/src/i830_bios.h
index 9bd0db8a..881d5c86 100644
--- a/src/i830_bios.h
+++ b/src/i830_bios.h
@@ -114,6 +114,3 @@ struct lvds_bdb_2 {
CARD8 table_size; /* not sure on this one */
struct lvds_bdb_2_entry panels[16];
} __attribute__((packed));
-
-Bool
-i830GetLVDSInfoFromBIOS(ScrnInfoPtr pScrn);
diff --git a/src/i830_display.c b/src/i830_display.c
index 82029850..345eea9f 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -1132,6 +1132,91 @@ i830ReleaseLoadDetectPipe(xf86OutputPtr output)
}
}
+/* Returns the clock of the currently programmed mode of the given pipe. */
+static int
+i830_crtc_clock_get(ScrnInfoPtr pScrn, xf86CrtcPtr crtc)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ I830CrtcPrivatePtr intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ CARD32 dpll = INREG((pipe == 0) ? DPLL_A : DPLL_B);
+ CARD32 fp;
+ intel_clock_t clock;
+
+ if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+ fp = INREG((pipe == 0) ? FPA0 : FPB0);
+ else
+ fp = INREG((pipe == 0) ? FPA1 : FPB1);
+
+ 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 (!i830PllIsValid(crtc, &clock)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Bad clock found programmed in pipe %c\n",
+ pipe == 0 ? 'A' : 'B');
+ i830PrintPll("", &clock);
+ }
+
+ return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+DisplayModePtr
+i830_crtc_mode_get(ScrnInfoPtr pScrn, xf86CrtcPtr crtc)
+{
+ I830Ptr pI830 = I830PTR(pScrn);
+ I830CrtcPrivatePtr intel_crtc = crtc->driver_private;
+ int pipe = intel_crtc->pipe;
+ DisplayModePtr mode;
+ int htot = INREG((pipe == 0) ? HTOTAL_A : HTOTAL_B);
+ int hsync = INREG((pipe == 0) ? HSYNC_A : HSYNC_B);
+ int vtot = INREG((pipe == 0) ? VTOTAL_A : VTOTAL_B);
+ int vsync = INREG((pipe == 0) ? VSYNC_A : VSYNC_B);
+
+ mode = xcalloc(1, sizeof(DisplayModeRec));
+ 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;
+ mode->HSyncStart = (hsync & 0xffff) + 1;
+ mode->HSyncEnd = ((hsync & 0xffff0000) >> 16) + 1;
+ mode->VDisplay = (vtot & 0xffff) + 1;
+ mode->VTotal = ((vtot & 0xffff0000) >> 16) + 1;
+ mode->VSyncStart = (vsync & 0xffff) + 1;
+ mode->VSyncEnd = ((vsync & 0xffff0000) >> 16) + 1;
+ xf86SetModeDefaultName(mode);
+ xf86SetModeCrtc(mode, 0);
+
+ return mode;
+}
+
static const xf86CrtcFuncsRec i830_crtc_funcs = {
.dpms = i830_crtc_dpms,
.save = NULL, /* XXX */
diff --git a/src/i830_display.h b/src/i830_display.h
index dc800553..dbd1ea8e 100644
--- a/src/i830_display.h
+++ b/src/i830_display.h
@@ -39,3 +39,4 @@ xf86CrtcPtr i830GetLoadDetectPipe(xf86OutputPtr output);
void i830ReleaseLoadDetectPipe(xf86OutputPtr output);
void i830_crtc_init(ScrnInfoPtr pScrn, int pipe);
void i830_crtc_load_lut(xf86CrtcPtr crtc);
+DisplayModePtr i830_crtc_mode_get(ScrnInfoPtr pScrn, xf86CrtcPtr crtc);
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 0f663115..cb3dd878 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -1332,10 +1332,10 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
}
I830PreInitDDC(pScrn);
- I830SetupOutputs(pScrn);
for (i = 0; i < num_pipe; i++) {
i830_crtc_init(pScrn, i);
}
+ I830SetupOutputs(pScrn);
SaveHWState(pScrn);
/* Do an initial detection of the outputs while none are configured on yet.
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index 59af13b9..4c1afb05 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -32,6 +32,7 @@
#include "xf86.h"
#include "i830.h"
#include "i830_bios.h"
+#include "i830_display.h"
#include "X11/Xatom.h"
/**
@@ -406,21 +407,99 @@ i830_lvds_init(ScrnInfoPtr pScrn)
I830Ptr pI830 = I830PTR(pScrn);
xf86OutputPtr output;
I830OutputPrivatePtr intel_output;
+ DisplayModePtr modes, scan, bios_mode;
+ output = xf86OutputCreate (pScrn, &i830_lvds_output_funcs, "LVDS");
+ if (!output)
+ return;
+ intel_output = xnfcalloc (sizeof (I830OutputPrivateRec), 1);
+ if (!intel_output)
+ {
+ xf86OutputDestroy (output);
+ return;
+ }
+ intel_output->type = I830_OUTPUT_LVDS;
+ output->driver_private = intel_output;
+ output->subpixel_order = SubPixelHorizontalRGB;
+ output->interlaceAllowed = FALSE;
+ output->doubleScanAllowed = FALSE;
+
+ /* Set up the LVDS DDC channel. Most panels won't support it, but it can
+ * be useful if available.
+ */
+ I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOC, "LVDSDDC_C");
+
+ /* Attempt to get the fixed panel mode from DDC. Assume that the preferred
+ * mode is the right one.
+ */
+ modes = i830_ddc_get_modes(output);
+ for (scan = modes; scan != NULL; scan = scan->next) {
+ if (scan->type & M_T_PREFERRED)
+ break;
+ }
+ if (scan != NULL) {
+ /* Pull our chosen mode out and make it the fixed mode */
+ if (modes == scan)
+ modes = modes->next;
+ if (scan->prev != NULL)
+ scan->prev = scan->next;
+ if (scan->next != NULL)
+ scan->next = scan->prev;
+ pI830->panel_fixed_mode = scan;
+ }
+ /* Delete the mode list */
+ while (modes != NULL)
+ xf86DeleteMode(&modes, modes);
+
+ /* If we didn't get EDID, try checking if the panel is already turned on.
+ * If so, assume that whatever is currently programmed is the correct mode.
+ */
+ if (pI830->panel_fixed_mode == NULL) {
+ CARD32 lvds = INREG(LVDS);
+ int pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+ xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
+ xf86CrtcPtr crtc = xf86_config->crtc[pipe];
+
+ if (lvds & LVDS_PORT_EN) {
+ pI830->panel_fixed_mode = i830_crtc_mode_get(pScrn, crtc);
+ if (pI830->panel_fixed_mode != NULL)
+ pI830->panel_fixed_mode->type |= M_T_PREFERRED;
+ }
+ }
/* Get the LVDS fixed mode out of the BIOS. We should support LVDS with
* the BIOS being unavailable or broken, but lack the configuration options
* for now.
*/
- if (!i830GetLVDSInfoFromBIOS(pScrn))
- return;
+ bios_mode = i830_bios_get_panel_mode(pScrn);
+ if (bios_mode != NULL) {
+ if (pI830->panel_fixed_mode != NULL) {
+ if (!xf86ModesEqual(pI830->panel_fixed_mode, bios_mode)) {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "BIOS panel mode data doesn't match probed data, "
+ "continuing with probed.\n");
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "BIOS mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, bios_mode);
+ xf86DrvMsg(pScrn->scrnIndex, X_INFO, "probed mode:\n");
+ xf86PrintModeline(pScrn->scrnIndex, pI830->panel_fixed_mode);
+ xfree(bios_mode->name);
+ xfree(bios_mode);
+ }
+ } else {
+ pI830->panel_fixed_mode = bios_mode;
+ }
+ } else {
+ xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
+ "Couldn't detect panel mode. Disabling panel\n");
+ goto disable_exit;
+ }
/* Blacklist machines with BIOSes that list an LVDS panel without actually
* having one.
*/
if (pI830->PciInfo->chipType == PCI_CHIP_I945_GM) {
if (pI830->PciInfo->subsysVendor == 0xa0a0) /* aopen mini pc */
- return;
+ goto disable_exit;
if ((pI830->PciInfo->subsysVendor == 0x8086) &&
(pI830->PciInfo->subsysCard == 0x7270)) {
@@ -435,31 +514,19 @@ i830_lvds_init(ScrnInfoPtr pScrn)
if (pI830->panel_fixed_mode != NULL &&
pI830->panel_fixed_mode->HDisplay == 800 &&
- pI830->panel_fixed_mode->VDisplay == 600) {
+ pI830->panel_fixed_mode->VDisplay == 600)
+ {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"Suspected Mac Mini, ignoring the LVDS\n");
- return;
+ goto disable_exit;
}
}
- }
-
- output = xf86OutputCreate (pScrn, &i830_lvds_output_funcs, "LVDS");
- if (!output)
- return;
- intel_output = xnfcalloc (sizeof (I830OutputPrivateRec), 1);
- if (!intel_output)
- {
- xf86OutputDestroy (output);
- return;
}
- intel_output->type = I830_OUTPUT_LVDS;
- output->driver_private = intel_output;
- output->subpixel_order = SubPixelHorizontalRGB;
- output->interlaceAllowed = FALSE;
- output->doubleScanAllowed = FALSE;
- /* Set up the LVDS DDC channel. Most panels won't support it, but it can
- * be useful if available.
- */
- I830I2CInit(pScrn, &intel_output->pDDCBus, GPIOC, "LVDSDDC_C");
+ return;
+
+disable_exit:
+ xf86DestroyI2CBusRec(intel_output->pDDCBus, TRUE, TRUE);
+ xfree(intel_output);
+ xf86OutputDestroy(output);
}