[Intel-gfx] [PATCH] add LVDS downclocking support

Jesse Barnes jbarnes at virtuousgeek.org
Fri Feb 27 21:12:44 CET 2009


This is a refreshed patch from Matthew Garrett to enable LVDS downclocking.
965+ hardware can do this automatically, but other 9xx chips need some help. 
So this patch adds some damage code to track idle time.  If we're idle for
more than 500ms we downclock the display based on the value calculated at
mode set time.

Comments & testing appreciated.  I've only tested on my GM45 so far, and I
think to fully realize the benefits we'll need to enable self refresh, but it
should help on many platforms as-is.

-- 
Jesse Barnes, Intel Open Source Technology Center

diff --git a/configure.ac b/configure.ac
index 38d373e..27431d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -118,6 +118,11 @@ if test x$DRI = xauto; then
 fi
 AC_MSG_RESULT([$DRI])
 
+AC_CHECK_FILE([${sdkdir}/damage.h], [have_damage_h="yes"], 
[have_damage_h="no"])
+if test "$have_damage_h" = yes; then
+   AC_DEFINE(DAMAGE,1,[Use Damage extension])
+fi
+
 AM_CONDITIONAL(BUILD_UXA, test $BUILD_UXA = yes)
 if test "$BUILD_UXA" = yes; then
 	AC_DEFINE(I830_USE_UXA, 1, [UMA Acceleration Architecture support])
diff --git a/src/i810_reg.h b/src/i810_reg.h
index e2ffba1..29f635e 100644
--- a/src/i810_reg.h
+++ b/src/i810_reg.h
@@ -2144,6 +2144,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #define PIPECONF_PROGRESSIVE	(0 << 21)
 #define PIPECONF_INTERLACE_W_FIELD_INDICATION	(6 << 21)
 #define PIPECONF_INTERLACE_FIELD_0_ONLY		(7 << 21)
+#define PIPECONF_REFRESH_MASK	(0xfffcffff) /* 965+ only */
+#define PIPECONF_REFRESH_EN	(1<<16)
 
 #define PIPEAGCMAXRED		0x70010
 #define PIPEAGCMAXGREEN		0x70014
diff --git a/src/i830.h b/src/i830.h
index 7904b9f..7487b04 100644
--- a/src/i830.h
+++ b/src/i830.h
@@ -476,6 +476,12 @@ typedef struct _I830Rec {
 
    Bool need_mi_flush;
 
+#ifdef DAMAGE
+   DamagePtr pPMDamage;
+   Bool idle;
+   TimeStamp idleTime;
+#endif
+
    Bool NeedRingBufferLow;
    Bool tiling;
    Bool fb_compression;
@@ -902,6 +908,7 @@ void i830_hdmi_init(ScrnInfoPtr pScrn, int output_reg);
 
 /* i830_lvds.c */
 void i830_lvds_init(ScrnInfoPtr pScrn);
+void i830_lvds_reclock(I830Ptr pI830);
 
 extern void i830MarkSync(ScrnInfoPtr pScrn);
 extern void i830WaitSync(ScrnInfoPtr pScrn);
diff --git a/src/i830_cursor.c b/src/i830_cursor.c
index 43a65cb..3df2779 100644
--- a/src/i830_cursor.c
+++ b/src/i830_cursor.c
@@ -172,6 +172,12 @@ i830_crtc_set_cursor_position (xf86CrtcPtr crtc, int x, 
int y)
     I830CrtcPrivatePtr	intel_crtc = I830CrtcPrivate(crtc);
     uint32_t		temp;
 
+    if (pI830->idle) {
+	pI830->idle=0;
+	i830_lvds_reclock(pI830);
+	pI830->idleTime = currentTime;
+    }
+
     temp = 0;
     if (x < 0) {
 	temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
diff --git a/src/i830_display.c b/src/i830_display.c
index 8a5cf24..d2b5010 100644
--- a/src/i830_display.c
+++ b/src/i830_display.c
@@ -1237,7 +1237,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr 
mode,
     int i, num_outputs = 0;
     int refclk;
     intel_clock_t clock;
-    uint32_t dpll = 0, fp = 0, dspcntr, pipeconf, lvds_bits = 0;
+    uint32_t dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf, lvds_bits = 0;
     Bool ok, is_sdvo = FALSE, is_dvo = FALSE;
     Bool is_crt = FALSE, is_lvds = FALSE, is_tv = FALSE;
 
@@ -1325,6 +1325,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr 
mode,
     }
 
     fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+    fp2 = (((clock.n+2)*2)-2) << 16 | clock.m1 << 8 | clock.m2;
 
     dpll = DPLL_VGA_MODE_DIS;
     if (IS_I9XX(pI830)) {
@@ -1427,6 +1428,15 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr 
mode,
 	else
 	    pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
     }
+
+    /* Enable automatic refresh rate adjustment on 965 */
+    if (is_lvds && IS_I965G(pI830)) {
+	xf86DrvMsg(pScrn->scrnIndex, X_INFO,
+		   "Enabling automatic refresh rate control on pipe %d\n",
+		   pipe);
+	pipeconf = (pipeconf & PIPECONF_REFRESH_MASK) | PIPECONF_REFRESH_EN;
+    }
+
     /*
      * This "shouldn't" be needed as the dpms on code
      * will be run after the mode is set. On 9xx, it helps.
@@ -1458,6 +1468,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr 
mode,
     if (dpll & DPLL_VCO_ENABLE)
     {
 	OUTREG(fp_reg, fp);
+	OUTREG(fp_reg+4, fp2);
 	OUTREG(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
 	POSTING_READ(dpll_reg);
 	usleep(150);
@@ -1512,6 +1523,7 @@ i830_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr 
mode,
     }
 
     OUTREG(fp_reg, fp);
+    OUTREG(fp_reg+4, fp2);
     OUTREG(dpll_reg, dpll);
     POSTING_READ(dpll_reg);
     /* Wait for the clocks to stabilize. */
diff --git a/src/i830_driver.c b/src/i830_driver.c
index 571f4c2..4d36774 100644
--- a/src/i830_driver.c
+++ b/src/i830_driver.c
@@ -870,6 +870,12 @@ i830CreateScreenResources(ScreenPtr pScreen)
    if (pI830->accel == ACCEL_UXA)
       i830_uxa_create_screen_resources(pScreen);
 #endif
+
+#ifdef DAMAGE
+   DamageRegister(&pScrn->pScreen->GetScreenPixmap(pScrn->pScreen)->drawable,
+		  pI830->pPMDamage);
+#endif
+
    return TRUE;
 }
 
@@ -1194,6 +1200,73 @@ static const xf86CrtcConfigFuncsRec 
i830_xf86crtc_config_funcs = {
     i830_xf86crtc_resize
 };
 
+/*
+ * Idle detection for LVDS downclocking
+ *
+ * At block time, set a wakeup handler so we can check whether we've been
+ * idle for WAIT_MILLIS milliseconds.  If so, we downclock the LVDS PLL
+ * to save power.  The next damage event will clock it back up.
+ */
+#define WAIT_MILLIS 500
+
+static void
+I830PMBlockHandler(void *closure,
+		   struct timeval **wt,
+		   void *selectmask)
+{
+    I830Ptr pI830 = closure;
+
+    if (pI830->idle)
+	return;
+
+    if (pI830->pPMDamage)
+	DamageEmpty(pI830->pPMDamage);
+
+    AdjustWaitForDelay(wt, WAIT_MILLIS);
+}
+
+static void
+I830WakeupHandler(void *closure,
+		  int rc,
+		  void *selectmask)
+{
+    I830Ptr pI830 = closure;
+
+    if (pI830->idle)
+	return;
+
+    if (currentTime.milliseconds - pI830->idleTime.milliseconds > 
WAIT_MILLIS) {
+	pI830->idle = 1;
+	i830_lvds_reclock(pI830);
+    } else {
+	UpdateCurrentTime();
+    }
+}
+
+static void
+I830DamageReport(DamagePtr pDamage, RegionPtr pRegion, void *closure)
+{
+    I830Ptr pI830 = closure;
+
+    if (pI830->idle) {
+	i830_lvds_reclock(pI830);
+	pI830->idle = 0;
+    }
+    pI830->idleTime = currentTime;
+}
+
+static void
+I830DamageDestroy(DamagePtr pDamage, void *closure)
+{
+    I830Ptr pI830 = closure;
+
+    if (pI830->idle) {
+	i830_lvds_reclock(pI830);
+	pI830->idle = 0;
+    }
+    pI830->idleTime = currentTime;
+}
+
 #define HOTKEY_BIOS_SWITCH	0
 #define HOTKEY_DRIVER_NOTIFY	1
 
@@ -2070,6 +2143,15 @@ I830PreInit(ScrnInfoPtr pScrn, int flags)
    }
 #endif
 
+#if defined(DAMAGE)
+   RegisterBlockAndWakeupHandlers(I830PMBlockHandler,
+				  I830WakeupHandler,
+				  pI830);
+   pI830->pPMDamage = DamageCreate(I830DamageReport, I830DamageDestroy,
+				   DamageReportNonEmpty, TRUE, pScrn->pScreen,
+				   pI830);
+#endif
+
    pI830->preinit = FALSE;
 
    return TRUE;
diff --git a/src/i830_lvds.c b/src/i830_lvds.c
index 027bb5d..2c9ce11 100644
--- a/src/i830_lvds.c
+++ b/src/i830_lvds.c
@@ -608,6 +608,24 @@ i830_lvds_mode_valid(xf86OutputPtr output, DisplayModePtr 
pMode)
     return MODE_OK;
 }
 
+void
+i830_lvds_reclock(I830Ptr pI830)
+{
+    /* Don't do it on non-mobile or i830 class chips, and 965 does it in hw 
*/
+    if (!IS_MOBILE(pI830) || IS_I830(pI830) || IS_I965G(pI830))
+	return;
+
+    OUTREG(PP_CONTROL, INREG(PP_CONTROL) | (0xabcd << 16));
+    /* We don't get damage events for the hardware overlay, so force high-
speed
+       if we're using it */
+    if (pI830->idle && !(*pI830->overlayOn)) {
+	OUTREG(DPLL_B, INREG(DPLL_B) | DISPLAY_RATE_SELECT_FPA1);
+    } else {
+	OUTREG(DPLL_B, INREG(DPLL_B) & ~DISPLAY_RATE_SELECT_FPA1);
+    }
+    OUTREG(PP_CONTROL, INREG(PP_CONTROL) & 0x3);
+}
+
 static Bool
 i830_lvds_mode_fixup(xf86OutputPtr output, DisplayModePtr mode,
 		     DisplayModePtr adjusted_mode)
diff --git a/src/i830_video.c b/src/i830_video.c
index 8a3718d..b4d82ae 100644
--- a/src/i830_video.c
+++ b/src/i830_video.c
@@ -449,6 +449,12 @@ i830_overlay_on(ScrnInfoPtr pScrn)
     if (*pI830->overlayOn)
 	return;
 
+    /* We won't get damage events while the overlay is enabled, so force the
+       screen to full refresh rate */
+
+    pI830->idle = 0;
+    i830_lvds_reclock(pI830);
+
     /*
      * On I830, if pipe A is off when the overlayis enabled, it will fail to
      * turn on and blank the entire screen or lock up the ring. Light up pipe
@@ -558,6 +564,9 @@ i830_overlay_off(ScrnInfoPtr pScrn)
     }
     *pI830->overlayOn = FALSE;
     OVERLAY_DEBUG("overlay_off\n");
+
+    pI830->idle = 1;
+    i830_lvds_reclock(pI830);
 }
 
 void




More information about the Intel-gfx mailing list