summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/i810_reg.h53
-rw-r--r--src/i830_debug.c15
-rw-r--r--src/i830_display.c53
-rw-r--r--src/i830_driver.c5
-rw-r--r--src/i830_lvds.c9
5 files changed, 105 insertions, 30 deletions
diff --git a/src/i810_reg.h b/src/i810_reg.h
index f50646bd..e4bebafa 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -1128,8 +1128,22 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define DVO_SRCDIM_HORIZONTAL_SHIFT 12
#define DVO_SRCDIM_VERTICAL_SHIFT 0
+/** @defgroup LVDS
+ * @{
+ */
+/**
+ * This register controls the LVDS output enable, pipe selection, and data
+ * format selection.
+ *
+ * All of the clock/data pairs are force powered down by power sequencing.
+ */
#define LVDS 0x61180
+/**
+ * Enables the LVDS port. This bit must be set before DPLLs are enabled, as
+ * the DPLL semantics change when the LVDS is assigned to that pipe.
+ */
# define LVDS_PORT_EN (1 << 31)
+/** Selects pipe B for LVDS data. Must be set on pre-965. */
# define LVDS_PIPEB_SELECT (1 << 30)
/* on 965, dithering is enabled in this register, not PFIT_CONTROL */
@@ -1189,18 +1203,39 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
# define LVDS_POWER_DOWN_TRI_STATE (1 << 10)
-/*
- * Clock A control; overridden by LVDS power sequencing
+/**
+ * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per
+ * pixel.
*/
+# define LVDS_A0A2_CLKA_POWER_MASK (3 << 8)
+# define LVDS_A0A2_CLKA_POWER_DOWN (0 << 8)
+# define LVDS_A0A2_CLKA_POWER_UP (3 << 8)
+/**
+ * Controls the A3 data pair, which contains the additional LSBs for 24 bit
+ * mode. Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be
+ * on.
+ */
+# define LVDS_A3_POWER_MASK (3 << 6)
+# define LVDS_A3_POWER_DOWN (0 << 6)
+# define LVDS_A3_POWER_UP (3 << 6)
+/**
+ * Controls the CLKB pair. This should only be set when LVDS_B0B3_POWER_UP
+ * is set.
+ */
+# define LVDS_CLKB_POWER_MASK (3 << 4)
+# define LVDS_CLKB_POWER_DOWN (0 << 4)
+# define LVDS_CLKB_POWER_UP (3 << 4)
-/* power down everything including A3 (0V) */
-# define LVDS_CLKA_POWER_DOWN (0 << 8)
-
-/* Partially active. A0, A1, A2 set to 0, timing active, clock active */
-# define LVDS_CLKA_POWER_PARTIAL (1 << 8)
+/**
+ * Controls the B0-B3 data pairs. This must be set to match the DPLL p2
+ * setting for whether we are in dual-channel mode. The B3 pair will
+ * additionally only be powered up when LVDS_A3_POWER_UP is set.
+ */
+# define LVDS_B0B3_POWER_MASK (3 << 2)
+# define LVDS_B0B3_POWER_DOWN (0 << 2)
+# define LVDS_B0B3_POWER_UP (3 << 2)
-/* running, data and clock active */
-# define LVDS_CLKA_POWER_UP (3 << 8)
+/** @} */
/*
* Two channel clock control. Turn this on if you need clkb for two channel mode
diff --git a/src/i830_debug.c b/src/i830_debug.c
index c746d35d..c0261a6c 100644
--- a/src/i830_debug.c
+++ b/src/i830_debug.c
@@ -269,8 +269,21 @@ DEBUGSTRING(i830_debug_lvds)
{
char pipe = val & LVDS_PIPEB_SELECT ? 'B' : 'A';
char *enable = val & LVDS_PORT_EN ? "enabled" : "disabled";
+ int depth;
+ char *channels;
- return XNFprintf("%s, pipe %c", enable, pipe);
+ if ((val & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
+ depth = 24;
+ else
+ depth = 18;
+ if ((val & LVDS_B0B3_POWER_MASK) == LVDS_B0B3_POWER_UP)
+ channels = "2 channels";
+ else
+ channels = "1 channel";
+
+
+ return XNFprintf("%s, pipe %c, %d bit, %s",
+ enable, pipe, depth, channels);
}
DEBUGSTRING(i830_debug_sdvo)
diff --git a/src/i830_display.c b/src/i830_display.c
index 2810a8be..5cedd778 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -298,8 +298,9 @@ i830PllIsValid(xf86CrtcPtr crtc, intel_clock_t *clock)
}
/**
- * Returns a set of divisors for the desired target clock with the given refclk,
- * or FALSE. Divisor values are the actual divisors for
+ * 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)
@@ -310,10 +311,23 @@ i830FindBestPLL(xf86CrtcPtr crtc, int target, int refclk, intel_clock_t *best_cl
const intel_limit_t *limit = intel_limit (crtc);
int err = target;
- if (target < limit->p2.dot_limit)
- clock.p2 = limit->p2.p2_slow;
- else
- clock.p2 = limit->p2.p2_fast;
+ if (IS_I9XX(pI830) && i830PipeHasType(crtc, I830_OUTPUT_LVDS) &&
+ (INREG(LVDS) & LVDS_PORT_EN) != 0)
+ {
+ /* 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;
+ }
memset (best_clock, 0, sizeof (*best_clock));
@@ -890,22 +904,37 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode,
usleep(150);
}
+ /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+ * This is an exception to the general rule that mode_set doesn't turn
+ * things on.
+ */
if (is_lvds)
{
- CARD32 lvds = INREG(LVDS);
+ CARD32 lvds = INREG(LVDS);
+
+ lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
+ /* Set the B0-B3 data pairs corresponding to whether we're going to
+ * set the DPLLs for dual-channel mode or not.
+ */
+ if (adjusted_mode->Clock >= I9XX_P2_LVDS_SLOW_LIMIT)
+ lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+ else
+ lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
- /* The LVDS pin pair needs to be on before the DPLLs are enabled.
- * This is an exception to the general rule that mode_set doesn't turn
- * things on.
+ /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+ * appropriately here, but we need to look more thoroughly into how
+ * panels behave in the two modes.
*/
- lvds |= LVDS_PORT_EN | LVDS_PIPEB_SELECT;
+
+ /* Enable dithering if we're in 18-bit mode. */
if (IS_I965G(pI830))
{
- if (pI830->panel_wants_dither)
+ if ((lvds & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
lvds |= LVDS_DITHER_ENABLE;
else
lvds &= ~LVDS_DITHER_ENABLE;
}
+
OUTREG(LVDS, lvds);
POSTING_READ(LVDS);
}
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 513b2065..20217538 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -1734,6 +1734,8 @@ SaveHWState(ScrnInfoPtr pScrn)
pI830->saveSWF[15] = INREG(SWF31);
pI830->saveSWF[16] = INREG(SWF32);
+ if (IS_MOBILE(pI830) && !IS_I830(pI830))
+ pI830->saveLVDS = INREG(LVDS);
pI830->savePFIT_CONTROL = INREG(PFIT_CONTROL);
for (i = 0; i < xf86_config->num_output; i++) {
@@ -1776,6 +1778,9 @@ RestoreHWState(ScrnInfoPtr pScrn)
}
i830WaitForVblank(pScrn);
+ if (IS_MOBILE(pI830) && !IS_I830(pI830))
+ OUTREG(LVDS, pI830->saveLVDS);
+
if (!IS_I830(pI830) && !IS_845G(pI830))
OUTREG(PFIT_CONTROL, pI830->savePFIT_CONTROL);
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index 23b61f28..7e5ce67a 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -98,7 +98,7 @@ i830_lvds_dpms (xf86OutputPtr output, int mode)
else
i830SetLVDSPanelPower(pScrn, FALSE);
- /* XXX: We never power down the LVDS pair. */
+ /* XXX: We never power down the LVDS pairs. */
}
static void
@@ -109,7 +109,6 @@ i830_lvds_save (xf86OutputPtr output)
pI830->savePP_ON = INREG(LVDSPP_ON);
pI830->savePP_OFF = INREG(LVDSPP_OFF);
- pI830->saveLVDS = INREG(LVDS);
pI830->savePP_CONTROL = INREG(PP_CONTROL);
pI830->savePP_CYCLE = INREG(PP_CYCLE);
pI830->saveBLC_PWM_CTL = INREG(BLC_PWM_CTL);
@@ -133,7 +132,6 @@ i830_lvds_restore(xf86OutputPtr output)
OUTREG(LVDSPP_ON, pI830->savePP_ON);
OUTREG(LVDSPP_OFF, pI830->savePP_OFF);
OUTREG(PP_CYCLE, pI830->savePP_CYCLE);
- OUTREG(LVDS, pI830->saveLVDS);
OUTREG(PP_CONTROL, pI830->savePP_CONTROL);
if (pI830->savePP_CONTROL & POWER_TARGET_ON)
i830SetLVDSPanelPower(pScrn, TRUE);
@@ -204,11 +202,6 @@ i830_lvds_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
xf86SetModeCrtc(adjusted_mode, INTERLACE_HALVE_V);
}
- /* XXX: if we don't have BIOS fixed timings (or we have
- * a preferred mode from DDC, probably), we should use the
- * DDC mode as the fixed timing.
- */
-
/* XXX: It would be nice to support lower refresh rates on the
* panels to reduce power consumption, and perhaps match the
* user's requested refresh rate.