[PATCH] dualhead support for r128

Alex Deucher <[email protected]>
Newsgroups gmane.linux.redhat.xfree86
Message-ID <[email protected]>
well, I finally got around to finishing my patch.  here it is.  I need
some people to test it because I don't have a laptop with a r128 chip
in it.  Also, someone with r128 mobility docs might want to look over
the code since I coded it from the radeon driver and since I don't have
r128 docs, I have to assume the they share the sames regs for dualhead.
 I would imagine they do though since most of the rest of the stuff is
very similar.

The patch is against xfree86 4.3.0. 

raw files are available here:
http://www.botchco.com/alex/r128/4.3/ati/

diff and binary for redhat 9:
http://www.botchco.com/alex/r128/final/

Thanks,

Alex

__________________________________________________
Do you Yahoo!?
The New Yahoo! Search - Faster. Easier. Bingo
http://search.yahoo.com
r128_dualhead.diff (application/octet-stream, 46.7 KB)
--- ../ati.orig/r128_accel.c	2002-11-14 22:01:35.000000000 -0500
+++ r128_accel.c	2003-04-17 20:03:14.000000000 -0400
@@ -83,6 +83,7 @@
 #include "r128.h"
 #include "r128_reg.h"
 #include "r128_sarea.h"
+#include "r128_probe.h"
 #ifdef XF86DRI
 #include "r128_sarea.h"
 #define _XF86DRI_SERVER_
@@ -117,6 +118,8 @@
     { R128_ROP3_ONE,  R128_ROP3_ONE  }  /* GXset          */
 };
 
+extern int gR128EntityIndex;
+
 /* Flush all dirty data in the Pixel Cache to memory. */
 void R128EngineFlush(ScrnInfoPtr pScrn)
 {
@@ -1641,6 +1644,25 @@
                          &indirect, sizeof(drmR128Indirect));
 }
 
+/* This callback is required for multihead cards using XAA */
+static
+void R128RestoreCCEAccelState(ScrnInfoPtr pScrn)
+{
+    R128InfoPtr info        = R128PTR(pScrn);
+/*    unsigned char *R128MMIO = info->MMIO;  needed for OUTREG below */
+    /*xf86DrvMsg(pScrn->scrnIndex, X_INFO, "===>RestoreCP\n");*/
+
+    R128WaitForFifo(pScrn, 1);
+/* is this needed on r128
+    OUTREG( R128_DEFAULT_OFFSET, info->frontPitchOffset);
+*/
+    R128WaitForIdle(pScrn);
+
+    /* FIXME: May need to restore other things, 
+       like BKGD_CLK FG_CLK...*/
+
+}
+
 static void R128CCEAccelInit(ScrnInfoPtr pScrn, XAAInfoRecPtr a)
 {
     R128InfoPtr info = R128PTR(pScrn);
@@ -1701,9 +1723,47 @@
 					   | HARDWARE_PATTERN_PROGRAMMED_ORIGIN
 					   | HARDWARE_PATTERN_SCREEN_ORIGIN
 					   | BIT_ORDER_IN_BYTE_LSBFIRST);
+
+    if(!info->IsSecondary && xf86IsEntityShared(pScrn->entityList[0]))
+        a->RestoreAccelState           = R128RestoreCCEAccelState;
+
 }
 #endif
 
+/* This callback is required for multihead cards using XAA */
+static
+void R128RestoreAccelState(ScrnInfoPtr pScrn)
+{
+    R128InfoPtr info        = R128PTR(pScrn);
+    unsigned char *R128MMIO = info->MMIO;
+    CARD32 pitch64;
+    R128EntPtr pR128Ent;
+    DevUnion* pPriv;
+
+    pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+            gR128EntityIndex);
+    pR128Ent = pPriv->ptr;
+    if(pR128Ent->IsDRIEnabled)
+    {
+        /*not working yet*/
+        /*
+        RADEONInfoPtr info0 = RADEONPTR(pRADEONEnt->pPrimaryScrn);
+        RADEONCP_TO_MMIO(pRADEONEnt->pPrimaryScrn, info0);
+        */
+    }
+    pitch64 = ((pScrn->displayWidth * (pScrn->bitsPerPixel / 8) + 0x3f)) >> 6;
+
+    OUTREG(R128_DEFAULT_OFFSET, (pScrn->fbOffset>>10) |
+				  (pitch64 << 22));
+
+    /* FIXME: May need to restore other things, 
+       like BKGD_CLK FG_CLK...*/
+
+    R128WaitForIdle(pScrn);
+
+}
+
+
 static void R128MMIOAccelInit(ScrnInfoPtr pScrn, XAAInfoRecPtr a)
 {
     R128InfoPtr info = R128PTR(pScrn);
@@ -1768,6 +1828,22 @@
     a->DashedLineFlags                 = (LINE_PATTERN_LSBFIRST_LSBJUSTIFIED
 					  | LINE_PATTERN_POWER_OF_2_ONLY);
 
+
+    if(xf86IsEntityShared(pScrn->entityList[0]))
+    {
+        DevUnion* pPriv;
+        R128EntPtr pR128Ent;
+        pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+                gR128EntityIndex);
+        pR128Ent = pPriv->ptr;
+        
+        /*if there are more than one devices sharing this entity, we
+          have to assign this call back, otherwise the XAA will be
+          disabled */
+        if(pR128Ent->HasSecondary || pR128Ent->BypassSecondary)
+           a->RestoreAccelState           = R128RestoreAccelState;
+    }
+
 				/* ImageWrite */
     a->NumScanlineImageWriteBuffers    = 1;
     a->ScanlineImageWriteBuffers       = info->scratch_buffer;
--- ../ati.orig/r128_cursor.c	2003-02-13 15:28:40.000000000 -0500
+++ r128_cursor.c	2003-04-17 19:31:11.000000000 -0400
@@ -72,8 +72,16 @@
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
 
-    OUTREG(R128_CUR_CLR0, bg);
-    OUTREG(R128_CUR_CLR1, fg);
+    if(info->IsSecondary)
+    {
+        OUTREG(R128_CUR2_CLR0, bg);
+        OUTREG(R128_CUR2_CLR1, fg);
+    }
+    else
+    {
+    	OUTREG(R128_CUR_CLR0, bg);
+    	OUTREG(R128_CUR_CLR1, fg);
+    }
 }
 
 /* Set cursor position to (x,y) with offset into cursor bitmap at
@@ -94,11 +102,26 @@
     if (xorigin >= cursor->MaxWidth)  xorigin = cursor->MaxWidth - 1;
     if (yorigin >= cursor->MaxHeight) yorigin = cursor->MaxHeight - 1;
 
-    OUTREG(R128_CUR_HORZ_VERT_OFF,  R128_CUR_LOCK | (xorigin << 16) | yorigin);
-    OUTREG(R128_CUR_HORZ_VERT_POSN, (R128_CUR_LOCK
+    if(!info->IsSecondary)
+    {
+    	OUTREG(R128_CUR_HORZ_VERT_OFF,  R128_CUR_LOCK | (xorigin << 16) | yorigin);
+    	OUTREG(R128_CUR_HORZ_VERT_POSN, (R128_CUR_LOCK
 				     | ((xorigin ? 0 : x) << 16)
 				     | (yorigin ? 0 : y)));
-    OUTREG(R128_CUR_OFFSET,         info->cursor_start + yorigin * 16);
+    	OUTREG(R128_CUR_OFFSET,         info->cursor_start + yorigin * 16);
+    } 
+    else 
+    {
+        OUTREG(R128_CUR2_HORZ_VERT_OFF,  (R128_CUR2_LOCK
+				       | (xorigin << 16)
+				       | yorigin));
+        OUTREG(R128_CUR2_HORZ_VERT_POSN, (R128_CUR2_LOCK
+				       | ((xorigin ? 0 : x) << 16)
+				       | (yorigin ? 0 : y)));
+        OUTREG(R128_CUR2_OFFSET,         
+			info->cursor_start + pScrn->fbOffset + yorigin * 16);
+    }
+
 }
 
 /* Copy cursor image from `image' to video memory.  R128SetCursorPosition
@@ -112,8 +135,16 @@
     int           y;
     CARD32        save;
 
-    save = INREG(R128_CRTC_GEN_CNTL);
-    OUTREG(R128_CRTC_GEN_CNTL, save & (CARD32)~R128_CRTC_CUR_EN);
+    if(!info->IsSecondary)
+    {
+    	save = INREG(R128_CRTC_GEN_CNTL);
+    	OUTREG(R128_CRTC_GEN_CNTL, save & (CARD32)~R128_CRTC_CUR_EN);
+    }
+    else
+    {
+        save = INREG(R128_CRTC2_GEN_CNTL);
+        OUTREG(R128_CRTC2_GEN_CNTL, save & (CARD32)~R128_CRTC2_CUR_EN);
+    }
 
 #if X_BYTE_ORDER == X_BIG_ENDIAN
     switch(info->CurrentLayout.pixel_bytes) {
@@ -168,8 +199,10 @@
 	*d++ = 0x00000000;
     }
 
-
-    OUTREG(R128_CRTC_GEN_CNTL, save);
+    if(!info->IsSecondary)
+    	OUTREG(R128_CRTC_GEN_CNTL, save);
+    else
+        OUTREG(R128_CRTC2_GEN_CNTL, save);
 }
 
 /* Hide hardware cursor. */
@@ -178,7 +211,10 @@
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
 
-    OUTREGP(R128_CRTC_GEN_CNTL, 0, ~R128_CRTC_CUR_EN);
+     if(info->IsSecondary)
+        OUTREGP(R128_CRTC2_GEN_CNTL, 0, ~R128_CRTC2_CUR_EN);
+     else
+    	OUTREGP(R128_CRTC_GEN_CNTL, 0, ~R128_CRTC_CUR_EN);
 }
 
 /* Show hardware cursor. */
@@ -187,7 +223,15 @@
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
 
-    OUTREGP(R128_CRTC_GEN_CNTL, R128_CRTC_CUR_EN, ~R128_CRTC_CUR_EN);
+    if(info->IsSecondary)
+    {
+         OUTREGP(R128_CRTC2_GEN_CNTL, R128_CRTC2_CUR_EN,
+               ~R128_CRTC2_CUR_EN);
+    }
+    else
+    {
+    	OUTREGP(R128_CRTC_GEN_CNTL, R128_CRTC_CUR_EN, ~R128_CRTC_CUR_EN);
+    }
 }
 
 /* Determine if hardware cursor is in use. */
--- ../ati.orig/r128_driver.c	2003-02-18 20:19:41.000000000 -0500
+++ r128_driver.c	2003-04-17 20:19:45.000000000 -0400
@@ -358,6 +358,8 @@
 		      NULL);
 }
 
+extern int gR128EntityIndex;
+
 /* Allocate our private R128InfoRec. */
 static Bool R128GetRec(ScrnInfoPtr pScrn)
 {
@@ -500,10 +502,24 @@
 {
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
-    if(info->isDFP)
-        OUTREGP(R128_FP_GEN_CNTL, R128_FP_BLANK_DIS, ~R128_FP_BLANK_DIS);
+
+    if(!info->IsSecondary)
+    {
+    	if(info->isDFP)
+        	OUTREGP(R128_FP_GEN_CNTL, R128_FP_BLANK_DIS, ~R128_FP_BLANK_DIS);
+    	else
+        	OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_DISPLAY_DIS, ~R128_CRTC_DISPLAY_DIS);
+    }
     else
-        OUTREGP(R128_CRTC_EXT_CNTL, R128_CRTC_DISPLAY_DIS, ~R128_CRTC_DISPLAY_DIS);
+    {
+            OUTREGP(R128_CRTC2_GEN_CNTL, 
+                 R128_CRTC2_DISP_DIS |
+	         R128_CRTC2_VSYNC_DIS |
+	         R128_CRTC2_HSYNC_DIS,
+	       ~(R128_CRTC2_DISP_DIS |
+	         R128_CRTC2_VSYNC_DIS |
+	         R128_CRTC2_HSYNC_DIS));
+    }
 }
 
 /* Unblank screen. */
@@ -511,11 +527,21 @@
 {
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
-
-    if(info->isDFP)
-        OUTREGP(R128_FP_GEN_CNTL, 0, ~R128_FP_BLANK_DIS);
+    if(!info->IsSecondary)
+    {
+    	if(info->isDFP)
+        	OUTREGP(R128_FP_GEN_CNTL, 0, ~R128_FP_BLANK_DIS);
+    	else
+        	OUTREGP(R128_CRTC_EXT_CNTL, 0, ~R128_CRTC_DISPLAY_DIS);
+    }
     else
-        OUTREGP(R128_CRTC_EXT_CNTL, 0, ~R128_CRTC_DISPLAY_DIS);
+    {
+        OUTREGP(R128_CRTC2_GEN_CNTL, 0,
+	       ~(R128_CRTC2_DISP_DIS |
+	         R128_CRTC2_VSYNC_DIS |
+	         R128_CRTC2_HSYNC_DIS));
+    }
+
 }
 
 /* Compute log base 2 of val. */
@@ -926,6 +952,7 @@
     } else {
         info->isDFP = FALSE;
         info->isPro2 = FALSE;
+        info->HasCRTC2 = FALSE;
 	switch (info->Chipset) {
 	/* R128 Pro and Pro2 can have DFP, we will deal with it.
 	   No support for dual-head/xinerama yet.
@@ -975,7 +1002,11 @@
 	case PCI_CHIP_RAGE128LE:
 	case PCI_CHIP_RAGE128LF:
 	case PCI_CHIP_RAGE128MF:
-	case PCI_CHIP_RAGE128ML: info->HasPanelRegs = TRUE;  break;
+	case PCI_CHIP_RAGE128ML: 
+			info->HasPanelRegs = TRUE;  
+			/* which chips support dualhead? */
+			info->HasCRTC2 = TRUE;  
+			break;
 	case PCI_CHIP_RAGE128RE:
 	case PCI_CHIP_RAGE128RF:
 	case PCI_CHIP_RAGE128RG:
@@ -1008,6 +1039,23 @@
     else
 	pScrn->videoRam       = INREG(R128_CONFIG_MEMSIZE) / 1024;
 
+    if(info->IsSecondary)
+    {  
+	/*FIXME: For now, split FB into two equal sections. This should
+          be able to be adjusted by user with a config option*/
+        DevUnion* pPriv;
+        R128EntPtr pR128Ent;
+        R128InfoPtr   info1;
+        pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+              gR128EntityIndex);
+        pR128Ent = pPriv->ptr;
+        pScrn->videoRam /= 2;
+        pR128Ent->pPrimaryScrn->videoRam = pScrn->videoRam;
+        info1 = R128PTR(pR128Ent->pPrimaryScrn);
+        info1->FbMapSize  = pScrn->videoRam * 1024;
+        info->LinearAddr += pScrn->videoRam * 1024;
+    }
+
     info->MemCntl             = INREG(R128_MEM_CNTL);
     info->BusCntl             = INREG(R128_BUS_CNTL);
 
@@ -1877,6 +1925,36 @@
     info->pEnt         = xf86GetEntityInfo(pScrn->entityList[0]);
     if (info->pEnt->location.type != BUS_PCI) goto fail;
 
+    if(xf86IsEntityShared(pScrn->entityList[0]))
+    {
+        if(xf86IsPrimInitDone(pScrn->entityList[0]))
+        {
+            DevUnion* pPriv;
+            R128EntPtr pR128Ent;
+            info->IsSecondary = TRUE;
+            pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+                    gR128EntityIndex);
+            pR128Ent = pPriv->ptr;
+            if(pR128Ent->BypassSecondary) return FALSE;
+            pR128Ent->pSecondaryScrn = pScrn;
+        }
+        else
+        {
+            DevUnion* pPriv;
+            R128EntPtr pR128Ent;
+            xf86SetPrimInitDone(pScrn->entityList[0]);
+            pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+                    gR128EntityIndex);
+            pR128Ent = pPriv->ptr;
+            pR128Ent->pPrimaryScrn = pScrn;
+            pR128Ent->IsDRIEnabled = FALSE;
+            pR128Ent->BypassSecondary = FALSE;
+            pR128Ent->HasSecondary = FALSE;
+            pR128Ent->RestorePrimary = FALSE;
+            pR128Ent->IsSecondaryRestored = FALSE;
+        }
+    }
+
     if (flags & PROBE_DETECT) {
 	R128ProbeDDC(pScrn, info->pEnt->index);
 	return TRUE;
@@ -2009,12 +2087,21 @@
 {
     R128InfoPtr   info      = R128PTR(pScrn);
     unsigned char *R128MMIO = info->MMIO;
-    int           i;
+    int           i, j;
     int           idx;
     unsigned char r, g, b;
 
+    /* If the second monitor is connected, we also 
+       need to deal with the secondary palette*/
+    if (info->IsSecondary) j = 1;
+    else j = 0;
+    
+    PAL_SELECT(j);
+
+#if 0
     /* Select palette 0 (main CRTC) if using FP-enabled chip */
     if (info->HasPanelRegs || info->isDFP) PAL_SELECT(0);
+#endif
 
     if (info->CurrentLayout.depth == 15) {
 	/* 15bpp mode.  This sends 32 values. */
@@ -2092,6 +2179,8 @@
 
     if (!R128MapMem(pScrn)) return FALSE;
     pScrn->fbOffset    = 0;
+    if(info->IsSecondary) pScrn->fbOffset = pScrn->videoRam * 1024;
+
 #ifdef XF86DRI
     info->fbX          = 0;
     info->fbY          = 0;
@@ -2145,7 +2234,33 @@
 			info->CurrentLayout.pixel_bytes * 3 + 1023) / 1024);
 	    info->directRenderingEnabled = FALSE;
 	} else {
-	    info->directRenderingEnabled = R128DRIScreenInit(pScreen);
+            if(info->IsSecondary)
+                info->directRenderingEnabled = FALSE;
+            else 
+            {
+                /* Xinerama has sync problem with DRI, disable it for now */
+                if(xf86IsEntityShared(pScrn->entityList[0]))
+                {
+                    info->directRenderingEnabled = FALSE;
+ 	            xf86DrvMsg(scrnIndex, X_WARNING,
+                        "Direct Rendering Disabled -- "
+                        "Dual-head configuration is not working with DRI "
+                        "at present.\nPlease use only one Device/Screen "
+                        "section in your XFConfig file.\n");
+                }
+                else
+                info->directRenderingEnabled =
+                    R128DRIScreenInit(pScreen);
+                if(xf86IsEntityShared(pScrn->entityList[0]))
+                {
+                    DevUnion* pPriv;
+                    R128EntPtr pR128Ent;
+                    pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+                        gR128EntityIndex);
+                    pR128Ent = pPriv->ptr;
+                    pR128Ent->IsDRIEnabled = info->directRenderingEnabled;
+                }
+            }
 	}
     }
 #endif
@@ -2588,6 +2703,32 @@
     OUTREG(R128_CRTC_PITCH,           restore->crtc_pitch);
 }
 
+/* Write CRTC2 registers. */
+static void R128RestoreCrtc2Registers(ScrnInfoPtr pScrn,
+				       R128SavePtr restore)
+{
+    R128InfoPtr info        = R128PTR(pScrn);
+    unsigned char *R128MMIO = info->MMIO;
+
+/*    OUTREG(RADEON_CRTC2_GEN_CNTL,  restore->crtc2_gen_cntl);*/
+    OUTREGP(R128_CRTC2_GEN_CNTL, restore->crtc2_gen_cntl,
+	    R128_CRTC2_VSYNC_DIS |
+	    R128_CRTC2_HSYNC_DIS |
+	    R128_CRTC2_DISP_DIS);
+
+    OUTREG(R128_DAC_CNTL2, restore->dac2_cntl);
+    OUTREG(R128_DISP_OUTPUT_CNTL, restore->disp_output_cntl);
+
+    OUTREG(R128_CRTC2_H_TOTAL_DISP,    restore->crtc2_h_total_disp);
+    OUTREG(R128_CRTC2_H_SYNC_STRT_WID, restore->crtc2_h_sync_strt_wid);
+    OUTREG(R128_CRTC2_V_TOTAL_DISP,    restore->crtc2_v_total_disp);
+    OUTREG(R128_CRTC2_V_SYNC_STRT_WID, restore->crtc2_v_sync_strt_wid);
+    OUTREG(R128_CRTC2_OFFSET,          restore->crtc2_offset);
+    OUTREG(R128_CRTC2_OFFSET_CNTL,     restore->crtc2_offset_cntl);
+    OUTREG(R128_CRTC2_PITCH,           restore->crtc2_pitch);
+
+}
+
 /* Write flat panel registers */
 static void R128RestoreFPRegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
 {
@@ -2685,6 +2826,80 @@
 	       (restore->ppll_div_3 & R128_PPLL_POST3_DIV_MASK) >> 16));
 }
 
+/* Write PLL2 registers. */
+static void R128RestorePLL2Registers(ScrnInfoPtr pScrn, R128SavePtr restore)
+{
+    R128InfoPtr info        = R128PTR(pScrn);
+    unsigned char *R128MMIO = info->MMIO;
+
+    OUTPLLP(pScrn, 0x2d, 0x00, ~(0x03));
+
+
+    OUTREGP(R128_CLOCK_CNTL_INDEX, 0, R128_PLL2_DIV_SEL_MASK);
+
+
+    OUTPLLP(pScrn,
+	    R128_P2PLL_CNTL,
+	    R128_P2PLL_RESET
+	    | R128_P2PLL_ATOMIC_UPDATE_EN
+	    | R128_P2PLL_VGA_ATOMIC_UPDATE_EN,
+	    0xffff);
+
+    R128PLLWaitForReadUpdateComplete(pScrn);
+
+    while ( (INPLL(pScrn, R128_P2PLL_REF_DIV) & R128_P2PLL_REF_DIV_MASK) !=
+			(restore->p2pll_ref_div & R128_P2PLL_REF_DIV_MASK)) {
+	OUTPLLP(pScrn, R128_P2PLL_REF_DIV, restore->p2pll_ref_div, ~R128_P2PLL_REF_DIV_MASK);
+    }
+
+    R128PLLWriteUpdate(pScrn);
+
+    R128PLLWaitForReadUpdateComplete(pScrn);
+
+    while ( (INPLL(pScrn, R128_P2PLL_DIV_0) & R128_P2PLL_FB0_DIV_MASK) !=
+			(restore->p2pll_div_0 & 
+R128_P2PLL_FB0_DIV_MASK)) {
+	OUTPLLP(pScrn, R128_P2PLL_DIV_0,
+			restore->p2pll_div_0, ~R128_P2PLL_FB0_DIV_MASK);
+    }
+
+    R128PLLWriteUpdate(pScrn);
+
+    R128PLLWaitForReadUpdateComplete(pScrn);
+
+    while ( (INPLL(pScrn, R128_P2PLL_DIV_0) & R128_P2PLL_POST0_DIV_MASK) !=
+			(restore->p2pll_div_0 & R128_P2PLL_POST0_DIV_MASK)) {
+	OUTPLLP(pScrn, R128_P2PLL_DIV_0,
+			restore->p2pll_div_0, ~R128_P2PLL_POST0_DIV_MASK);
+    }
+
+    R128PLLWriteUpdate(pScrn);
+
+    R128PLLWaitForReadUpdateComplete(pScrn);
+
+    OUTPLL(R128_HTOTAL2_CNTL, restore->htotal_cntl2);
+
+    R128PLLWriteUpdate(pScrn);
+
+    OUTPLLP(pScrn, R128_P2PLL_CNTL, 0, 
+           ~(R128_P2PLL_RESET | R128_P2PLL_SLEEP));
+
+    R128TRACE(("Wrote: 0x%08x 0x%08x 0x%08x (0x%08x)\n",
+	       restore->p2pll_ref_div,
+	       restore->p2pll_div_0,
+	       restore->htotal_cntl2,
+	       INPLL(pScrn, RADEON_P2PLL_CNTL)));
+    R128TRACE(("Wrote: rd=%d, fd=%d, pd=%d\n",
+	       restore->p2pll_ref_div & RADEON_P2PLL_REF_DIV_MASK,
+	       restore->p2pll_div_0 & RADEON_P2PLL_FB3_DIV_MASK,
+	       (restore->p2pll_div_0 & RADEON_P2PLL_POST3_DIV_MASK) >>16));
+
+    OUTPLLP(pScrn, 0x2d, 0x03, ~(0x03));
+
+
+}
+
+
 /* Write DDA registers. */
 static void R128RestoreDDARegisters(ScrnInfoPtr pScrn, R128SavePtr restore)
 {
@@ -2695,6 +2910,8 @@
     OUTREG(R128_DDA_ON_OFF, restore->dda_on_off);
 }
 
+#if 0 
+/* this is ifdef'ed out in radeon_driver.c */
 /* Write palette data. */
 static void R128RestorePalette(ScrnInfoPtr pScrn, R128SavePtr restore)
 {
@@ -2703,30 +2920,121 @@
     int           i;
 
     if (!restore->palette_valid) return;
-
+#if 0
+    /* new code should take care of this */
     /* Select palette 0 (main CRTC) if using FP-enabled chip */
     if (info->HasPanelRegs || info->isDFP) PAL_SELECT(0);
 
     OUTPAL_START(0);
     for (i = 0; i < 256; i++) OUTPAL_NEXT_CARD32(restore->palette[i]);
+#endif
+
+/* from radeon_driver.c */
+    PAL_SELECT(1);
+    OUTPAL_START(0);
+    for (i = 0; i < 256; i++) {
+	R128WaitForFifo(pScrn, 32); /* delay */
+	OUTPAL_NEXT_CARD32(restore->palette2[i]);
+    }
+
+    PAL_SELECT(0);
+    OUTPAL_START(0);
+    for (i = 0; i < 256; i++) {
+	R128WaitForFifo(pScrn, 32); /* delay */
+	OUTPAL_NEXT_CARD32(restore->palette[i]);
+    }
+
 }
+#endif
 
 /* Write out state to define a new video mode.  */
 static void R128RestoreMode(ScrnInfoPtr pScrn, R128SavePtr restore)
 {
     R128InfoPtr info = R128PTR(pScrn);
+    DevUnion* pPriv;
+    R128EntPtr pR128Ent;
+    static R128SaveRec restore0;
 
+    /* for Non-dual head card, we don't have private field in the Entity*/
+    if(!info->HasCRTC2)
+    {
+    	R128TRACE(("R128RestoreMode(%p)\n", restore));
+    	R128RestoreCommonRegisters(pScrn, restore);
+    	R128RestoreCrtcRegisters(pScrn, restore);
+    	if (!(info->HasPanelRegs) || info->BIOSDisplay == R128_BIOS_DISPLAY_CRT){
+        	R128RestorePLLRegisters(pScrn, restore);
+    	}
+    	R128RestoreDDARegisters(pScrn, restore);
+    	if (info->HasPanelRegs || info->isDFP)
+        	R128RestoreFPRegisters(pScrn, restore);
+    }
+    
+    pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+                   gR128EntityIndex);
+    pR128Ent = pPriv->ptr;
+   
     R128TRACE(("R128RestoreMode(%p)\n", restore));
-    R128RestoreCommonRegisters(pScrn, restore);
-    R128RestoreCrtcRegisters(pScrn, restore);
-    if (!(info->HasPanelRegs) || info->BIOSDisplay == R128_BIOS_DISPLAY_CRT){
-        R128RestorePLLRegisters(pScrn, restore);
+
+    /*****
+      When changing mode with Dual-head card (VE/M6), care must
+      be taken for the special order in setting registers. CRTC2 has
+      to be set before changing CRTC_EXT register.
+      In the dual-head setup, X server calls this routine twice with
+      primary and secondary pScrn pointers respectively. The calls
+      can come with different order. Regardless the order of X server issuing 
+      the calls, we have to ensure we set registers in the right order!!! 
+      Otherwise we may get a blank screen.
+    *****/
+    if(info->IsSecondary)
+    {
+        R128RestoreCrtc2Registers(pScrn, restore);        
+        R128RestorePLL2Registers(pScrn, restore);
+        
+        if(!info->SwitchingMode)
+        pR128Ent->IsSecondaryRestored = TRUE;
+
+        if(pR128Ent->RestorePrimary)
+        {
+            R128InfoPtr info0 = R128PTR(pR128Ent->pPrimaryScrn); 
+            pR128Ent->RestorePrimary = FALSE;
+
+            R128RestoreCrtcRegisters(pScrn, &restore0);
+            if((info0->isDFP) || (info0->HasPanelRegs))
+            {
+                R128RestoreFPRegisters(pScrn, &restore0);
+            }
+            
+            R128RestorePLLRegisters(pScrn, &restore0);   
+            pR128Ent->IsSecondaryRestored = FALSE;
+
+        }
+    }
+    else
+    {
+        R128RestoreCommonRegisters(pScrn, restore);
+        R128RestoreDDARegisters(pScrn, restore);
+        if(!pR128Ent->HasSecondary || pR128Ent->IsSecondaryRestored
+            || info->SwitchingMode)
+        {
+	    pR128Ent->IsSecondaryRestored = FALSE;
+            R128RestoreCrtcRegisters(pScrn, restore);
+            if((info->isDFP) || (info->HasPanelRegs))
+            {
+               R128RestoreFPRegisters(pScrn, restore);
+            }
+            R128RestorePLLRegisters(pScrn, restore);   
+        }
+        else
+        {
+            memcpy(&restore0, restore, sizeof(restore0));
+            pR128Ent->RestorePrimary = TRUE;
+        }
     }
-    R128RestoreDDARegisters(pScrn, restore);
-    if (info->HasPanelRegs || info->isDFP)
-        R128RestoreFPRegisters(pScrn, restore);
 
+#if 0
+    /* is this needed on R128 for dualhead? it's not used in the radeon driver */
     R128RestorePalette(pScrn, restore);
+#endif
 }
 
 /* Read common registers. */
@@ -2789,6 +3097,25 @@
     save->tmds_transmitter_cntl = INREG(R128_TMDS_TRANSMITTER_CNTL);
 }
 
+/* Read CRTC2 registers. */
+static void R128SaveCrtc2Registers(ScrnInfoPtr pScrn, R128SavePtr save)
+{
+    R128InfoPtr info        = R128PTR(pScrn);
+    unsigned char *R128MMIO = info->MMIO;
+
+    save->dac2_cntl             = INREG(R128_DAC_CNTL2);
+    save->disp_output_cntl      = INREG(R128_DISP_OUTPUT_CNTL);
+
+    save->crtc2_gen_cntl        = INREG(R128_CRTC2_GEN_CNTL);
+    save->crtc2_h_total_disp    = INREG(R128_CRTC2_H_TOTAL_DISP);
+    save->crtc2_h_sync_strt_wid = INREG(R128_CRTC2_H_SYNC_STRT_WID);
+    save->crtc2_v_total_disp    = INREG(R128_CRTC2_V_TOTAL_DISP);
+    save->crtc2_v_sync_strt_wid = INREG(R128_CRTC2_V_SYNC_STRT_WID);
+    save->crtc2_offset          = INREG(R128_CRTC2_OFFSET);
+    save->crtc2_offset_cntl     = INREG(R128_CRTC2_OFFSET_CNTL);
+    save->crtc2_pitch           = INREG(R128_CRTC2_PITCH);
+}
+
 /* Read PLL registers. */
 static void R128SavePLLRegisters(ScrnInfoPtr pScrn, R128SavePtr save)
 {
@@ -2806,6 +3133,23 @@
 	       (save->ppll_div_3 & R128_PPLL_POST3_DIV_MASK) >> 16));
 }
 
+/* Read PLL2 registers. */
+static void R128SavePLL2Registers(ScrnInfoPtr pScrn, R128SavePtr save)
+{
+    save->p2pll_ref_div        = INPLL(pScrn, R128_P2PLL_REF_DIV);
+    save->p2pll_div_0          = INPLL(pScrn, R128_P2PLL_DIV_0);
+    save->htotal_cntl2         = INPLL(pScrn, R128_HTOTAL2_CNTL);
+
+    R128TRACE(("Read: 0x%08x 0x%08x 0x%08x\n",
+	       save->p2pll_ref_div,
+	       save->p2pll_div_0,
+	       save->htotal_cntl2));
+    R128TRACE(("Read: rd=%d, fd=%d, pd=%d\n",
+	       save->p2pll_ref_div & R128_P2PLL_REF_DIV_MASK,
+	       save->p2pll_div_0 & R128_P2PLL_FB0_DIV_MASK,
+	       (save->p2pll_div_0 & R128_P2PLL_POST0_DIV_MASK) >> 16));
+}
+
 /* Read DDA registers. */
 static void R128SaveDDARegisters(ScrnInfoPtr pScrn, R128SavePtr save)
 {
@@ -2836,13 +3180,24 @@
 {
     R128TRACE(("R128SaveMode(%p)\n", save));
 
-    R128SaveCommonRegisters(pScrn, save);
-    R128SaveCrtcRegisters(pScrn, save);
-    if (R128PTR(pScrn)->HasPanelRegs || R128PTR(pScrn)->isDFP)
-	R128SaveFPRegisters(pScrn, save);
-    R128SavePLLRegisters(pScrn, save);
-    R128SaveDDARegisters(pScrn, save);
-    R128SavePalette(pScrn, save);
+    R128InfoPtr   info      = R128PTR(pScrn);
+
+    if(info->IsSecondary)
+    {
+        R128SaveCrtc2Registers(pScrn, save);
+        R128SavePLL2Registers(pScrn, save);
+    }
+    else
+    {
+    	R128SaveCommonRegisters(pScrn, save);
+    	R128SaveCrtcRegisters(pScrn, save);
+    	if (R128PTR(pScrn)->HasPanelRegs || R128PTR(pScrn)->isDFP)
+		R128SaveFPRegisters(pScrn, save);
+    	R128SavePLLRegisters(pScrn, save);
+    	R128SaveDDARegisters(pScrn, save);
+	/* this is indef'ed on radeon, what about R128? */
+    	/* R128SavePalette(pScrn, save); */
+    }
 
     R128TRACE(("R128SaveMode returns %p\n", save));
 }
@@ -2860,17 +3215,24 @@
 	fbdevHWSave(pScrn);
 	return;
     }
+
+    if(!info->IsSecondary)
+    {
     vgaHWUnlock(hwp);
     vgaHWSave(pScrn, &hwp->SavedReg, VGA_SR_ALL); /* save mode, fonts, cmap */
     vgaHWLock(hwp);
 
-    R128SaveMode(pScrn, save);
+    /* R128SaveMode(pScrn, save); */
 
     save->dp_datatype      = INREG(R128_DP_DATATYPE);
     save->gen_reset_cntl   = INREG(R128_GEN_RESET_CNTL);
     save->clock_cntl_index = INREG(R128_CLOCK_CNTL_INDEX);
     save->amcgpio_en_reg   = INREG(R128_AMCGPIO_EN_REG);
     save->amcgpio_mask     = INREG(R128_AMCGPIO_MASK);
+    }
+
+    R128SaveMode(pScrn, save);
+
 }
 
 /* Restore the original (text) mode. */
@@ -2894,12 +3256,43 @@
     OUTREG(R128_GEN_RESET_CNTL,   restore->gen_reset_cntl);
     OUTREG(R128_DP_DATATYPE,      restore->dp_datatype);
 
+#if 0
+    /* do any R128 cards need this? */
+    /* M6 card has trouble restoring text mode for its CRT.
+       Needs this workaround.*/
+    if(xf86IsEntityShared(pScrn->entityList[0]) && info->IsM6)
+        OUTREG(R128_DAC_CNTL2, restore->dac2_cntl);
+#endif
+
     R128RestoreMode(pScrn, restore);
+
+    if(!info->IsSecondary)
+    {
     vgaHWUnlock(hwp);
     vgaHWRestore(pScrn, &hwp->SavedReg, VGA_SR_MODE | VGA_SR_FONTS );
     vgaHWLock(hwp);
+    }
+    else
+    {
+        DevUnion* pPriv;
+        R128EntPtr pR128Ent;
+        pPriv = xf86GetEntityPrivate(pScrn->entityList[0], 
+            gR128EntityIndex);
+        pR128Ent = pPriv->ptr;
+        {
+            ScrnInfoPtr pScrn0 = pR128Ent->pPrimaryScrn;
+            vgaHWPtr      hwp0         = VGAHWPTR(pScrn0);
+            vgaHWUnlock(hwp0);
+            vgaHWRestore(pScrn0, &hwp0->SavedReg, 
+                    VGA_SR_MODE | VGA_SR_FONTS );
+            vgaHWLock(hwp0);
+        }
+    }
 
+#if 0
+    /* this is ifdef'ed on Radeon, what about R128? */
     R128WaitForVerticalSync(pScrn);
+#endif
     R128Unblank(pScrn);
 }
 
@@ -3058,6 +3451,99 @@
     return TRUE;
 }
 
+/* Define CRTC2 registers for requested video mode. */
+static Bool R128InitCrtc2Registers(ScrnInfoPtr pScrn, R128SavePtr save,
+				  DisplayModePtr mode, R128InfoPtr info)
+{
+    int    format;
+    int    hsync_start;
+    int    hsync_wid;
+    int    hsync_fudge;
+    int    vsync_wid;
+    int    bytpp;
+    int    hsync_fudge_default[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
+
+    switch (info->CurrentLayout.pixel_code) {
+    case 4:  format = 1; bytpp = 0; break;
+    case 8:  format = 2; bytpp = 1; break;
+    case 15: format = 3; bytpp = 2; break;      /*  555 */
+    case 16: format = 4; bytpp = 2; break;      /*  565 */
+    case 24: format = 5; bytpp = 3; break;      /*  RGB */
+    case 32: format = 6; bytpp = 4; break;      /* xRGB */
+    default:
+	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
+		   "Unsupported pixel depth (%d)\n", info->CurrentLayout.bitsPerPixel);
+	return FALSE;
+    }
+    R128TRACE(("Format = %d (%d bytes per pixel)\n", format, bytpp));
+
+    hsync_fudge = hsync_fudge_default[format-1];
+
+    save->crtc2_gen_cntl = (R128_CRTC2_EN
+                          | R128_CRTC2_CRT2_ON
+			  | (format << 8)
+			  | ((mode->Flags & V_DBLSCAN)
+			     ? R128_CRTC2_DBL_SCAN_EN
+			     : 0)
+			  | ((mode->Flags & V_INTERLACE)
+			     ? R128_CRTC2_INTERLACE_EN
+			     : 0));
+
+    save->dac2_cntl = info->SavedReg.dac2_cntl 
+                      /*| RADEON_DAC2_DAC2_CLK_SEL*/
+                      | R128_DAC2_DAC_CLK_SEL;
+
+    save->crtc2_h_total_disp = ((((mode->CrtcHTotal / 8) - 1) & 0x3ff)
+	   | ((((mode->CrtcHDisplay / 8) - 1) & 0x1ff) << 16));
+
+    hsync_wid = (mode->CrtcHSyncEnd - mode->CrtcHSyncStart) / 8;
+    if (!hsync_wid)       hsync_wid = 1;
+    if (hsync_wid > 0x3f) hsync_wid = 0x3f;
+    hsync_start = mode->CrtcHSyncStart - 8 + hsync_fudge;
+
+    save->crtc2_h_sync_strt_wid = ((hsync_start & 0x1fff)
+				 | (hsync_wid << 16)
+				 | ((mode->Flags & V_NHSYNC)
+				    ? R128_CRTC_H_SYNC_POL
+				    : R128_CRTC_H_SYNC_POL));
+
+#if 1
+				/* This works for double scan mode. */
+    save->crtc2_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+			      | ((mode->CrtcVDisplay - 1) << 16));
+#else
+				/* This is what cce/nbmode.c example code
+				   does -- is this correct? */
+    save->crtc2_v_total_disp = (((mode->CrtcVTotal - 1) & 0xffff)
+			      | ((mode->CrtcVDisplay
+				  * ((mode->Flags & V_DBLSCAN) ? 2 : 1) - 1)
+				 << 16));
+#endif
+
+    vsync_wid = mode->CrtcVSyncEnd - mode->CrtcVSyncStart;
+    if (!vsync_wid)       vsync_wid = 1;
+    if (vsync_wid > 0x1f) vsync_wid = 0x1f;
+
+    save->crtc2_v_sync_strt_wid = (((mode->CrtcVSyncStart - 1) & 0xfff)
+				 | (vsync_wid << 16)
+				 | ((mode->Flags & V_NVSYNC)
+				    ? R128_CRTC2_V_SYNC_POL
+				    : R128_CRTC2_V_SYNC_POL));
+
+    save->crtc2_offset      = 0;
+    save->crtc2_offset_cntl = 0;
+
+    save->crtc2_pitch  = ((pScrn->displayWidth * pScrn->bitsPerPixel) +
+			 ((pScrn->bitsPerPixel * 8) -1)) /
+			 (pScrn->bitsPerPixel * 8);
+    save->crtc2_pitch |= save->crtc2_pitch << 16;
+	
+    R128TRACE(("Pitch = %d bytes (virtualX = %d, displayWidth = %d)\n",
+		 save->crtc2_pitch, pScrn->virtualX,
+		 info->CurrentLayout.displayWidth));
+    return TRUE;
+}
+
 /* Define CRTC registers for requested video mode. */
 static void R128InitFPRegisters(R128SavePtr orig, R128SavePtr save,
 				DisplayModePtr mode, R128InfoPtr info)
@@ -3129,6 +3615,10 @@
        want to use the dual CRTC capabilities of the R128 to allow both
        the flat panel and external CRT to either simultaneously display
        the same image or display two different images. */
+
+    /* does something need to be done here for dualhead mode?
+       The radeon drive just disables the CRT output for DFP.
+       Should I just disable it or leave the check in? */
     
     if(!info->isDFP){
         if (info->BIOSDisplay == R128_BIOS_DISPLAY_FP_CRT) {
@@ -3218,6 +3708,57 @@
 
 }
 
+/* Define PLL2 registers for requested video mode. */
+static void R128InitPLL2Registers(R128SavePtr save, R128PLLPtr pll,
+				   double dot_clock)
+{
+    unsigned long freq = dot_clock * 100;
+    struct {
+	int divider;
+	int bitvalue;
+    } *post_div,
+      post_divs[]   = {
+				/* From RAGE 128 VR/RAGE 128 GL Register
+				   Reference Manual (Technical Reference
+				   Manual P/N RRG-G04100-C Rev. 0.04), page
+				   3-17 (PLL_DIV_[3:0]).  */
+	{  1, 0 },              /* VCLK_SRC                 */
+	{  2, 1 },              /* VCLK_SRC/2               */
+	{  4, 2 },              /* VCLK_SRC/4               */
+	{  8, 3 },              /* VCLK_SRC/8               */
+	{  3, 4 },              /* VCLK_SRC/3               */
+	{ 16, 5 },              /* VCLK_SRC/16              */
+	{  6, 6 },              /* VCLK_SRC/6               */
+	{ 12, 7 },              /* VCLK_SRC/12              */
+	{  0, 0 }
+    };
+
+    if (freq > pll->max_pll_freq)      freq = pll->max_pll_freq;
+    if (freq * 12 < pll->min_pll_freq) freq = pll->min_pll_freq / 12;
+
+    for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+	save->pll_output_freq_2 = post_div->divider * freq;
+	if (save->pll_output_freq_2 >= pll->min_pll_freq
+	    && save->pll_output_freq_2 <= pll->max_pll_freq) break;
+    }
+
+    save->dot_clock_freq_2 = freq;
+    save->feedback_div_2   = R128Div(pll->reference_div
+				     * save->pll_output_freq_2,
+				     pll->reference_freq);
+    save->post_div_2       = post_div->divider;
+
+    R128TRACE(("dc=%d, of=%d, fd=%d, pd=%d\n",
+	       save->dot_clock_freq_2,
+	       save->pll_output_freq_2,
+	       save->feedback_div_2,
+	       save->post_div_2));
+
+    save->p2pll_ref_div   = pll->reference_div;
+    save->p2pll_div_0    = (save->feedback_div_2 | (post_div->bitvalue<<16));
+    save->htotal_cntl2    = 0;
+}
+
 /* Define DDA registers for requested video mode. */
 static Bool R128InitDDARegisters(ScrnInfoPtr pScrn, R128SavePtr save,
 				 R128PLLPtr pll, R128InfoPtr info, 
@@ -3354,10 +3895,21 @@
 
     info->Flags = mode->Flags;
 
+    if(info->IsSecondary)
+    {
+        if (!R128InitCrtc2Registers(pScrn, save, 
+             pScrn->currentMode,info)) 
+            return FALSE;
+        R128InitPLL2Registers(save, &info->pll, dot_clock);
+    }
+    else
+    {
     R128InitCommonRegisters(save, info);
     if (!R128InitCrtcRegisters(pScrn, save, mode, info)) return FALSE;
+#if 0
     if (info->HasPanelRegs || info->isDFP)
 	    R128InitFPRegisters(&info->SavedReg, save, mode, info);
+#endif
     if(dot_clock > 0){
         R128InitPLLRegisters(pScrn, save, &info->pll, dot_clock);
         if (!R128InitDDARegisters(pScrn, save, &info->pll, info, mode))
@@ -3370,6 +3922,10 @@
         save->dda_config           = info->SavedReg.dda_config;
         save->dda_on_off           = info->SavedReg.dda_on_off;
     }
+    }
+
+    if (info->HasPanelRegs || info->isDFP)
+            R128InitFPRegisters(&info->SavedReg, save, mode, info);
 
     R128TRACE(("R128Init returns %p\n", save));
     return TRUE;
@@ -3412,7 +3968,17 @@
 
 Bool R128SwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
 {
+    ScrnInfoPtr   pScrn       = xf86Screens[scrnIndex];
+    R128InfoPtr info        = R128PTR(pScrn);
+    /* when switch mode in dual-head setup, this function will be called
+       separately for each screen (depending on which screen the cursor is
+       in when user press ctrl-alt-+). Since this function is always
+       called when screen already in extensive mode, the sequence of
+       setting CRTC2 and CRT_EXT regesters doesn't matter any more, 
+       So we set the flag for RADEONRestoreMode here. */
+    info->SwitchingMode = TRUE;
     return R128ModeInit(xf86Screens[scrnIndex], mode);
+    info->SwitchingMode = FALSE;
 }
 
 /* Used to disallow modes that are not supported by the hardware. */
@@ -3503,6 +4069,12 @@
     if (info->CurrentLayout.pixel_code == 24)
 	Base += 8 * (Base % 3); /* Must be multiple of 8 and 3 */
 
+    if(info->IsSecondary)    
+    {
+        Base += pScrn->fbOffset; 
+        OUTREG(R128_CRTC2_OFFSET, Base);
+    }
+    else
     OUTREG(R128_CRTC_OFFSET, Base);
 }
 
@@ -3630,25 +4202,44 @@
     int           mask      = (R128_CRTC_DISPLAY_DIS
 			       | R128_CRTC_HSYNC_DIS
 			       | R128_CRTC_VSYNC_DIS);
+    int             mask2     = (R128_CRTC2_DISP_DIS |
+    			         R128_CRTC2_VSYNC_DIS |
+		    	         R128_CRTC2_HSYNC_DIS);
 
     switch (PowerManagementMode) {
     case DPMSModeOn:
 	/* Screen: On; HSync: On, VSync: On */
-	OUTREGP(R128_CRTC_EXT_CNTL, 0, ~mask);
+	if (info->IsSecondary)
+		OUTREGP(R128_CRTC2_GEN_CNTL, 0, ~mask2);
+	else
+		OUTREGP(R128_CRTC_EXT_CNTL, 0, ~mask);
 	break;
     case DPMSModeStandby:
 	/* Screen: Off; HSync: Off, VSync: On */
-	OUTREGP(R128_CRTC_EXT_CNTL,
-		R128_CRTC_DISPLAY_DIS | R128_CRTC_HSYNC_DIS, ~mask);
+	if (info->IsSecondary)
+		OUTREGP(R128_CRTC2_GEN_CNTL,
+			R128_CRTC2_DISP_DIS | R128_CRTC2_HSYNC_DIS,
+			~mask2);
+	    else
+		OUTREGP(R128_CRTC_EXT_CNTL,
+			R128_CRTC_DISPLAY_DIS | R128_CRTC_HSYNC_DIS, ~mask);
 	break;
     case DPMSModeSuspend:
 	/* Screen: Off; HSync: On, VSync: Off */
-	OUTREGP(R128_CRTC_EXT_CNTL,
-		R128_CRTC_DISPLAY_DIS | R128_CRTC_VSYNC_DIS, ~mask);
+	if (info->IsSecondary)
+		OUTREGP(R128_CRTC2_GEN_CNTL,
+			R128_CRTC2_DISP_DIS | R128_CRTC2_VSYNC_DIS,
+			~mask2);
+	else 
+		OUTREGP(R128_CRTC_EXT_CNTL,
+			R128_CRTC_DISPLAY_DIS | R128_CRTC_VSYNC_DIS, ~mask);
 	break;
     case DPMSModeOff:
 	/* Screen: Off; HSync: Off, VSync: Off */
-	OUTREGP(R128_CRTC_EXT_CNTL, mask, ~mask);
+	if (info->IsSecondary)
+		OUTREGP(R128_CRTC2_GEN_CNTL, mask2, ~mask2);
+	else
+		OUTREGP(R128_CRTC_EXT_CNTL, mask, ~mask);
 	break;
     }
 }
--- ../ati.orig/r128.h	2002-12-16 11:19:10.000000000 -0500
+++ r128.h	2003-04-17 20:09:18.000000000 -0400
@@ -136,6 +136,16 @@
 				/* CRTC2 registers */
     CARD32     crtc2_gen_cntl;
 
+    CARD32     dac2_cntl;
+    CARD32     disp_output_cntl;
+    CARD32     crtc2_h_total_disp;
+    CARD32     crtc2_h_sync_strt_wid;
+    CARD32     crtc2_v_total_disp;
+    CARD32     crtc2_v_sync_strt_wid;
+    CARD32     crtc2_offset;
+    CARD32     crtc2_offset_cntl;
+    CARD32     crtc2_pitch;
+
 				/* Flat panel registers */
     CARD32     fp_crtc_h_total_disp;
     CARD32     fp_crtc_v_total_disp;
@@ -160,6 +170,17 @@
     CARD32     ppll_div_3;
     CARD32     htotal_cntl;
 
+				/* Computed values for PLL2 */
+    CARD32     dot_clock_freq_2;
+    CARD32     pll_output_freq_2;
+    int        feedback_div_2;
+    int        post_div_2;
+
+				/* PLL2 registers */
+    CARD32     p2pll_ref_div;
+    CARD32     p2pll_div_0;
+    CARD32     htotal_cntl2;
+
 				/* DDA register */
     CARD32     dda_config;
     CARD32     dda_on_off;
@@ -167,6 +188,7 @@
 				/* Pallet */
     Bool       palette_valid;
     CARD32     palette[256];
+    CARD32     palette2[256];
 } R128SaveRec, *R128SavePtr;
 
 typedef struct {
@@ -369,6 +391,14 @@
     int               textureSize;
     int               log2TexGran;
 
+/*
+    CARD32            frontPitchOffset;
+    CARD32            backPitchOffset;
+    CARD32            depthPitchOffset;
+
+    CARD32            dst_pitch_offset;
+*/
+
 				/* Saved scissor values */
     CARD32            sc_left;
     CARD32            sc_right;
@@ -397,6 +427,12 @@
     I2CBusPtr         pI2CBus;
     CARD32            DDCReg;
 
+    /****** Added for dualhead support *******************/
+    BOOL              HasCRTC2;     /* VE/M6/M7 */
+    BOOL              IsSecondary;  /* second Screen */
+    BOOL              UseCRT;       /* force use CRT port as primary */
+    BOOL              SwitchingMode;
+
 } R128InfoRec, *R128InfoPtr;
 
 #define R128WaitForFifo(pScrn, entries)                                      \
--- ../ati.orig/r128_probe.c	2003-02-09 10:33:17.000000000 -0500
+++ r128_probe.c	2003-04-17 19:34:09.000000000 -0400
@@ -180,6 +180,8 @@
     { -1,                 -1,                 RES_UNDEFINED }
 };
 
+int gR128EntityIndex = -1;
+
 /* Return the options for supported chipset 'n'; NULL otherwise */
 const OptionInfoRec *
 R128AvailableOptions(int chipid, int busid)
@@ -216,7 +218,6 @@
     int           numDevSections, nATIGDev, nR128GDev;
     int           *usedChips;
     GDevPtr       *devSections, *ATIGDevs, *R128GDevs;
-    EntityInfoPtr pEnt;
     Bool          foundScreen = FALSE;
     int           i;
 
@@ -262,11 +263,13 @@
     if (flags & PROBE_DETECT)
 	foundScreen = TRUE;
     else for (i = 0; i < numUsed; i++) {
-	pEnt = xf86GetEntityInfo(usedChips[i]);
-
-	if (pEnt->active) {
-	    ScrnInfoPtr pScrn = xf86AllocateScreen(drv, 0);
+        ScrnInfoPtr pScrn;
+        EntityInfoPtr pEnt;
 
+        pScrn    = NULL;
+        if((pScrn = xf86ConfigPciEntity(pScrn, 0, usedChips[i],
+             R128PciChipsets, 0, 0, 0, 0, 0)))
+	{
 #ifdef XFree86LOADER
 
 	    if (!xf86LoadSubModule(pScrn, "r128")) {
@@ -295,11 +298,43 @@
 
 	    foundScreen          = TRUE;
 
-	    xf86ConfigActivePciEntity(pScrn, usedChips[i], R128PciChipsets,
-				      0, 0, 0, 0, 0);
+        pEnt = xf86GetEntityInfo(usedChips[i]);
+
+        /* mobility cards support Dual-Head, mark the entity as sharable*/
+        if(pEnt->chipset == PCI_CHIP_RAGE128LE ||
+           pEnt->chipset == PCI_CHIP_RAGE128LF ||
+           pEnt->chipset == PCI_CHIP_RAGE128MF ||
+           pEnt->chipset == PCI_CHIP_RAGE128ML)
+        {
+            static int instance = 0;
+            DevUnion* pPriv;
+
+            xf86SetEntitySharable(usedChips[i]);
+            xf86SetEntityInstanceForScreen(pScrn,
+                pScrn->entityList[0], instance);
+
+            if(gR128EntityIndex < 0)
+            {
+                gR128EntityIndex = xf86AllocateEntityPrivateIndex();
+                pPriv = xf86GetEntityPrivate(pScrn->entityList[0],
+                        gR128EntityIndex);
+
+                if (!pPriv->ptr)
+                {
+                    R128EntPtr pR128Ent;
+                    pPriv->ptr = xnfcalloc(sizeof(R128EntRec), 1);
+                    pR128Ent = pPriv->ptr;
+                    pR128Ent->IsDRIEnabled = FALSE;
+                    pR128Ent->BypassSecondary = FALSE;
+                    pR128Ent->HasSecondary = FALSE;
+                    pR128Ent->IsSecondaryRestored = FALSE;                   
+                } 
+            }
+            instance++;
 	}
 	xfree(pEnt);
     }
+    }
 
     xfree(usedChips);
     xfree(devSections);
--- ../ati.orig/r128_probe.h	2002-04-06 14:06:06.000000000 -0500
+++ r128_probe.h	2003-04-17 19:31:11.000000000 -0400
@@ -41,6 +41,22 @@
 
 #include "xf86str.h"
 
+typedef struct
+{
+    Bool IsDRIEnabled;
+
+    Bool HasSecondary;
+    Bool BypassSecondary;
+    /*These two registers are used to make sure the CRTC2 is
+      retored before CRTC_EXT, otherwise it could lead to blank screen.*/
+    Bool IsSecondaryRestored;
+    Bool RestorePrimary;
+
+    ScrnInfoPtr pSecondaryScrn;    
+    ScrnInfoPtr pPrimaryScrn;
+}R128EntRec, *R128EntPtr;
+
+
 /* r128_probe.c */
 extern const OptionInfoRec * R128AvailableOptions
 			     FunctionPrototype((int, int));
--- ../ati.orig/r128_reg.h	2002-12-16 11:19:11.000000000 -0500
+++ r128_reg.h	2003-04-17 19:31:11.000000000 -0400
@@ -284,6 +284,7 @@
 #define R128_CLOCK_CNTL_INDEX             0x0008
 #       define R128_PLL_WR_EN             (1 << 7)
 #       define R128_PLL_DIV_SEL           (3 << 8)
+#       define R128_PLL2_DIV_SEL_MASK     ~(3 << 8)
 #define R128_CLR_CMP_CLR_3D               0x1a24
 #define R128_CLR_CMP_CLR_DST              0x15c8
 #define R128_CLR_CMP_CLR_SRC              0x15c4
@@ -374,15 +375,51 @@
 #define R128_CRTC2_CRNT_FRAME             0x0314
 #define R128_CRTC2_DEBUG                  0x031c
 #define R128_CRTC2_GEN_CNTL               0x03f8
+/* copied straight out of radeon_reg.h 
+   someone with docs might want to double check these */
+#       define R128_CRTC2_DBL_SCAN_EN      (1 <<  0)
+#       define R128_CRTC2_INTERLACE_EN     (1 <<  1)
+#       define R128_CRTC2_SYNC_TRISTAT     (1 <<  4)
+#       define R128_CRTC2_HSYNC_TRISTAT    (1 <<  5)
+#       define R128_CRTC2_VSYNC_TRISTAT    (1 <<  6)
+#       define R128_CRTC2_CRT2_ON          (1 <<  7)
+#       define R128_CRTC2_ICON_EN          (1 << 15)
+#       define R128_CRTC2_CUR_EN           (1 << 16)
+#       define R128_CRTC2_CUR_MODE_MASK    (7 << 20)
+#       define R128_CRTC2_DISP_DIS         (1 << 23)
+#       define R128_CRTC2_EN               (1 << 25)
+#       define R128_CRTC2_DISP_REQ_EN_B    (1 << 26)
+#       define R128_CRTC2_HSYNC_DIS        (1 << 28)
+#       define R128_CRTC2_VSYNC_DIS        (1 << 29)
 #define R128_CRTC2_GUI_TRIG_VLINE         0x0318
 #define R128_CRTC2_H_SYNC_STRT_WID        0x0304
+#       define R128_CRTC2_H_SYNC_STRT_PIX        (0x07  <<  0)
+#       define R128_CRTC2_H_SYNC_STRT_CHAR       (0x3ff <<  3)
+#       define R128_CRTC2_H_SYNC_STRT_CHAR_SHIFT 3
+#       define R128_CRTC2_H_SYNC_WID             (0x3f  << 16)
+#       define R128_CRTC2_H_SYNC_WID_SHIFT       16
+#       define R128_CRTC2_H_SYNC_POL             (1     << 23)
 #define R128_CRTC2_H_TOTAL_DISP           0x0300
+#       define R128_CRTC2_H_TOTAL          (0x03ff << 0)
+#       define R128_CRTC2_H_TOTAL_SHIFT    0
+#       define R128_CRTC2_H_DISP           (0x01ff << 16)
+#       define R128_CRTC2_H_DISP_SHIFT     16
 #define R128_CRTC2_OFFSET                 0x0324
 #define R128_CRTC2_OFFSET_CNTL            0x0328
+#	define R128_CRTC2_TILE_EN         (1 << 15)
 #define R128_CRTC2_PITCH                  0x032c
 #define R128_CRTC2_STATUS                 0x03fc
 #define R128_CRTC2_V_SYNC_STRT_WID        0x030c
+#       define R128_CRTC2_V_SYNC_STRT       (0x7ff <<  0)
+#       define R128_CRTC2_V_SYNC_STRT_SHIFT 0
+#       define R128_CRTC2_V_SYNC_WID        (0x1f  << 16)
+#       define R128_CRTC2_V_SYNC_WID_SHIFT  16
+#       define R128_CRTC2_V_SYNC_POL        (1     << 23)
 #define R128_CRTC2_V_TOTAL_DISP           0x0308
+#       define R128_CRTC2_V_TOTAL          (0x07ff << 0)
+#       define R128_CRTC2_V_TOTAL_SHIFT    0
+#       define R128_CRTC2_V_DISP           (0x07ff << 16)
+#       define R128_CRTC2_V_DISP_SHIFT     16
 #define R128_CRTC2_VLINE_CRNT_VLINE       0x0310
 #define R128_CRTC8_DATA                   0x03d5 /* VGA, 0x3b5 */
 #define R128_CRTC8_IDX                    0x03d4 /* VGA, 0x3b4 */
@@ -392,6 +429,12 @@
 #define R128_CUR_HORZ_VERT_POSN           0x0264
 #define R128_CUR_OFFSET                   0x0260
 #       define R128_CUR_LOCK              (1 << 31)
+#define R128_CUR2_CLR0                    0x036c
+#define R128_CUR2_CLR1                    0x0370
+#define R128_CUR2_HORZ_VERT_OFF           0x0368
+#define R128_CUR2_HORZ_VERT_POSN          0x0364
+#define R128_CUR2_OFFSET                  0x0360
+#       define R128_CUR2_LOCK             (1 << 31)
 
 #define R128_DAC_CNTL                     0x0058
 #       define R128_DAC_RANGE_CNTL        (3 <<  0)
@@ -401,6 +444,13 @@
 #       define R128_DAC_8BIT_EN           (1 <<  8)
 #       define R128_DAC_VGA_ADR_EN        (1 << 13)
 #       define R128_DAC_MASK_ALL          (0xff << 24)
+#define R128_DAC_CNTL2                     0x007c
+#       define R128_DAC2_DAC_CLK_SEL      (1 <<  0)
+#       define R128_DAC2_DAC2_CLK_SEL     (1 <<  1)
+#       define R128_DAC2_PALETTE_ACC_CTL  (1 <<  5)
+#define R128_DISP_OUTPUT_CNTL             0x0d64
+#       define R128_DISP_DAC_SOURCE_MASK  0x03
+#       define R128_DISP_DAC_SOURCE_CRTC2 0x01
 #define R128_DAC_CRC_SIG                  0x02cc
 #define R128_DAC_DATA                     0x03c9 /* VGA */
 #define R128_DAC_MASK                     0x03c6 /* VGA */
@@ -670,6 +720,7 @@
 #define R128_HOST_DATA_LAST               0x17e0
 #define R128_HOST_PATH_CNTL               0x0130
 #define R128_HTOTAL_CNTL                  0x0009 /* PLL */
+#define R128_HTOTAL2_CNTL                 0x002e /* PLL */
 #define R128_HW_DEBUG                     0x0128
 #define R128_HW_DEBUG2                    0x011c
 
@@ -875,6 +926,19 @@
 #       define R128_PPLL_REF_DIV_MASK     0x03ff
 #       define R128_PPLL_ATOMIC_UPDATE_R  (1 << 15) /* same as _W */
 #       define R128_PPLL_ATOMIC_UPDATE_W  (1 << 15) /* same as _R */
+#define R128_P2PLL_CNTL                    0x002a /* P2PLL */
+#       define R128_P2PLL_RESET               (1 <<  0)
+#       define R128_P2PLL_SLEEP               (1 <<  1)
+#       define R128_P2PLL_ATOMIC_UPDATE_EN    (1 << 16)
+#       define R128_P2PLL_VGA_ATOMIC_UPDATE_EN (1 << 17)
+#       define R128_P2PLL_ATOMIC_UPDATE_VSYNC  (1 << 18)
+#define R128_P2PLL_DIV_0                   0x002c
+#       define R128_P2PLL_FB0_DIV_MASK     0x07ff
+#       define R128_P2PLL_POST0_DIV_MASK   0x00070000
+#define R128_P2PLL_REF_DIV                 0x002B /* PLL */
+#       define R128_P2PLL_REF_DIV_MASK     0x03ff
+#       define R128_P2PLL_ATOMIC_UPDATE_R  (1 << 15) /* same as _W */
+#       define R128_P2PLL_ATOMIC_UPDATE_W  (1 << 15) /* same as _R */
 #define R128_PWR_MNGMT_CNTL_STATUS        0x0f60 /* PCI */
 #define R128_REG_BASE                     0x0f18 /* PCI */
 #define R128_REGPROG_INF                  0x0f09 /* PCI */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.