Radeon DRI resume - submission to XFree86 CVS

"Charl P. Botha" <[email protected]>
Newsgroups gmane.comp.xfree86.expert,gmane.comp.video.dri.devel
Message-ID <[email protected]>
As discussed in the mail below, herewith a submission of the Radeon DRI
suspend/resume patch for your review.  I've updated it to and tested it with
current XFree86 CVS.

FYI, this patch enables complete suspend to/resume from disk with Radeon
chipsets with active DRI (and also running 3D-accelerated and Xv clients).
More information is available at http://cpbotha.net/dri_resume.html

Thanks,
Charl

----- Forwarded message from David Dawes <[email protected]> -----

From: David Dawes <[email protected]>
To: [email protected]
Subject: Re: [Dri-devel] Radeon DRI Resume - quo vadis?
X-BeenThere: [email protected]
X-Mailman-Version: 2.0.9-sf.net
List-Help: <mailto:[email protected]?subject=help>
List-Post: <mailto:[email protected]>
List-Subscribe: <https://lists.sourceforge.net/lists/listinfo/dri-devel>,
	<mailto:[email protected]?subject=subscribe>
List-Id: <dri-devel.lists.sourceforge.net>
List-Unsubscribe: <https://lists.sourceforge.net/lists/listinfo/dri-devel>,
	<mailto:[email protected]?subject=unsubscribe>
List-Archive: <http://sourceforge.net/mailarchive/forum.php?forum=dri-devel>
X-Original-Date: Tue, 10 Dec 2002 21:09:27 -0500
X-Spam-Status: No, hits=-4.4 required=5.0 tests=IN_REP_TO,SUBJ_ENDS_IN_Q_MARK version=2.20
X-Spam-Level: 
X-Keywords:                 
X-UID: 363

On Tue, Dec 10, 2002 at 01:45:09PM +0100, Charl P. Botha wrote:
>On Tue, 2002-12-10 at 13:36, Alan Hourihane wrote:
>> One thing though. It doesn't look like it's hooked to any APM events.
>> 
>> It's just run generically everytime on ModeInit. What happens when you
>> VT switch - does it handle them cases too ?
>
>At the moment it's called from RADEONEnterVT() in radeon_driver.c - so
>the code is called after every VT switch.  During normal operation this
>doesn't cause any problems as it's idempotent.  I would prefer hooking
>it more specifically to a power event... however, last time I checked
>the infrastructure for non-APM power events didn't seem to be ready. 
>Many people are using this on ACPI-only laptops with swsusp for software
>suspension.

If you're restoring HW state required for the correct operation of the
driver, and especially if it's state that something else driving the
video card might change while the X server doesn't have control over
it, then it should be done from EnterVT().  As a general rule, any HW
state that's set in ScreenInit() should also be set in EnterVT().

By default, XFree86 handles APM events via EnterVT/LeaveVT.  It's possible
for a driver to provide a separate function to handle PM events, but in
most cases it shouldn't be needed.

I just had another look at your patch, and I didn't see any obvious
problem with the way it's structured.  Send it to [email protected],
and Kevin Martin can review it.

David
--
David Dawes
Release Engineer/Architect                      The XFree86 Project
www.XFree86.org/~dawes

----- End forwarded message -----

-- 
charl p. botha http://cpbotha.net/ http://visualisation.tudelft.nl/
xfree86-dri-resume-v7.patch (text/plain, 15 KB)
Index: drivers/ati/radeon.h
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/drivers/ati/radeon.h,v
retrieving revision 1.32
diff -u -r1.32 radeon.h
--- drivers/ati/radeon.h	2002/10/31 18:06:59	1.32
+++ drivers/ati/radeon.h	2002/12/12 18:45:10
@@ -566,12 +566,15 @@
 extern int         RADEONMinBits(int val);
 
 extern void        RADEONInitVideo(ScreenPtr pScreen);
-
+/* added by [email protected] so that we can call this function from
+ * radeon_driver.c to get xvideo working after a resume from disc/ram */
+extern void        RADEONResetVideo(ScrnInfoPtr pScrn);
 extern void        R300CGWorkaround(ScrnInfoPtr pScrn);
 
 #ifdef XF86DRI
 extern Bool        RADEONDRIScreenInit(ScreenPtr pScreen);
 extern void        RADEONDRICloseScreen(ScreenPtr pScreen);
+extern void        RADEONDRIResume(ScreenPtr pScreen);
 extern Bool        RADEONDRIFinishScreenInit(ScreenPtr pScreen);
 
 extern drmBufPtr   RADEONCPGetBuffer(ScrnInfoPtr pScrn);
Index: drivers/ati/radeon_common.h
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_common.h,v
retrieving revision 1.1
diff -u -r1.1 radeon_common.h
--- drivers/ati/radeon_common.h	2002/10/30 12:52:13	1.1
+++ drivers/ati/radeon_common.h	2002/12/12 18:45:10
@@ -70,6 +70,7 @@
 #define DRM_RADEON_INIT_HEAP              0x15
 #define DRM_RADEON_IRQ_EMIT               0x16
 #define DRM_RADEON_IRQ_WAIT               0x17
+#define DRM_RADEON_CP_RESUME              0x18
 #define DRM_RADEON_MAX_DRM_COMMAND_INDEX  0x39
 
 
Index: drivers/ati/radeon_dri.c
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_dri.c,v
retrieving revision 1.22
diff -u -r1.22 radeon_dri.c
--- drivers/ati/radeon_dri.c	2002/11/25 14:04:57	1.22
+++ drivers/ati/radeon_dri.c	2002/12/12 18:45:10
@@ -1559,6 +1559,86 @@
     return TRUE;
 }
 
+/**
+ * This function will attempt to get the Radeon hardware back into shape
+ * after a resume from disc.  Basically, it's an extract of all hardware-
+ * affecting code from RADEONDRIAgpInit() (which is normally called by
+ * RADEONDRIScreenInit()) and RADEONDRIFinishScreenInit()
+ * including a new ioctl in the radeon DRM that in its turn is an extraction
+ * of the hardware-affecting bits from radeon_do_init_cp() (see radeon_cp.c)
+ *
+ * Charl P. Botha <http://cpbotha.net>
+ */
+void RADEONDRIResume(ScreenPtr pScreen)
+{
+    unsigned long mode;
+    int _ret;
+    ScrnInfoPtr   pScrn   = xf86Screens[pScreen->myNum];
+    RADEONInfoPtr info    = RADEONPTR(pScrn);
+    unsigned char *RADEONMMIO = info->MMIO;
+
+    xf86DrvMsg(pScreen->myNum, X_INFO,
+	       "[RESUME] Attempting to re-init Radeon hardware.\n");
+    
+    
+    /* Following bits from RADEONDRIAgpInit() */
+    /* -------------------------------------- */
+
+    mode   = drmAgpGetMode(info->drmFD);	/* Default mode */
+
+    mode &= ~RADEON_AGP_MODE_MASK;
+    switch (info->agpMode) {
+    case 4:          mode |= RADEON_AGP_4X_MODE;
+    case 2:          mode |= RADEON_AGP_2X_MODE;
+    case 1: default: mode |= RADEON_AGP_1X_MODE;
+    }
+
+    xf86DrvMsg(pScreen->myNum, X_INFO,
+		"[agp] Mode 0x%08lx [Card 0x%04x/0x%04x]\n",
+		mode,
+		info->PciInfo->vendor,
+		info->PciInfo->chipType);
+
+    if (drmAgpEnable(info->drmFD, mode) < 0) {
+	xf86DrvMsg(pScreen->myNum, X_ERROR, "[agp] AGP not enabled\n");
+	drmAgpRelease(info->drmFD);
+	return;
+    }
+
+    /* Ring buffer is at AGP offset 0 - from RADEONAgpInit() */
+    OUTREG(RADEON_AGP_BASE, info->ringHandle);
+
+    /* enable bus-mastering - we do this here bacause RADEONAgpInit() does it.
+     * The bus-mastering fix does this in RADEONEnterVT() as well, leave that!
+     */
+    /* xf86EnablePciBusMaster(info->PciInfo, TRUE); */
+    /* NB: it seems the root of this problem has been solved: X now
+     * explicitly enables bus-mastering after a VT switch */
+
+    /* Following bits from RADEONDRIFinishScreenInit() */
+    /* ----------------------------------------------- */
+    
+    /* this will make the IOCTL call we've added to try and re-tickle the 
+     * radeon chip in all the right places - similar to what the 
+     * DRM_RADEON_CP_INIT ioctl does in RADEONDRIKernelInit()
+     */
+    _ret = drmCommandNone(info->drmFD, DRM_RADEON_CP_RESUME); 
+    if (_ret) {      
+	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,  
+		   "%s: CP resume %d\n", __FUNCTION__, _ret); 
+    }       
+
+
+    /* DRM_RADEON_CP_RESUME does an engine reset, which resets some engine
+       registers back to their default values, so we need to restore
+       those engine register here. - from RADEONDRIKernelInit() that's called by
+       RADEONDRIFinishScreenInit() */
+    RADEONEngineRestore(pScrn);
+
+    /* Initialize and start the CP if required - from RADEONDRIFinishScreenInit() */
+    RADEONDRICPInit(pScrn);
+}
+
 /* The screen is being closed, so clean up any state and free any
  * resources used by the DRI.
  */
Index: drivers/ati/radeon_driver.c
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_driver.c,v
retrieving revision 1.74
diff -u -r1.74 radeon_driver.c
--- drivers/ati/radeon_driver.c	2002/12/11 03:43:33	1.74
+++ drivers/ati/radeon_driver.c	2002/12/12 18:45:12
@@ -5573,6 +5573,23 @@
     } else
 	if (!RADEONModeInit(pScrn, pScrn->currentMode)) return FALSE;
 
+    /* BEGIN RESUME CODE */
+    /*********************/
+
+#ifdef XF86DRI
+    if (info->directRenderingEnabled) {
+	/* get the Radeon back into shape after resume */
+	RADEONDRIResume(pScrn->pScreen);
+    }
+#endif
+    /* this will get XVideo going again, but only if XVideo was initialised 
+       during server startup (hence the info->adaptor if). */
+    if(info->adaptor)
+	RADEONResetVideo(pScrn);
+
+    /* END RESUME CODE */
+    /*******************/
+
     if (info->accelOn)
 	RADEONEngineRestore(pScrn);
 
Index: drivers/ati/radeon_video.c
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/drivers/ati/radeon_video.c,v
retrieving revision 1.20
diff -u -r1.20 radeon_video.c
--- drivers/ati/radeon_video.c	2002/11/01 06:08:36	1.20
+++ drivers/ati/radeon_video.c	2002/12/12 18:45:13
@@ -20,6 +20,7 @@
 
 #ifndef XvExtension
 void RADEONInitVideo(ScreenPtr pScreen) {}
+void RADEONResetVideo(ScrnInfoPtr Pscrn) {}
 #else
 
 static void RADEONInitOffscreenImages(ScreenPtr);
@@ -36,9 +37,6 @@
 static int  RADEONQueryImageAttributes(ScrnInfoPtr, int, unsigned short *,
 			unsigned short *,  int *, int *);
 
-
-static void RADEONResetVideo(ScrnInfoPtr);
-
 static void RADEONVideoTimerCallback(ScrnInfoPtr pScrn, Time now);
 
 #define MAKE_ATOM(a) MakeAtom(a, sizeof(a) - 1, TRUE)
@@ -376,7 +374,7 @@
     OUTREG(RADEON_OV0_GRAPHICS_KEY_CLR_LOW, min);
 }
 
-static void
+void
 RADEONResetVideo(ScrnInfoPtr pScrn)
 {
     RADEONInfoPtr   info      = RADEONPTR(pScrn);
Index: os-support/shared/drm/kernel/radeon.h
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/os-support/shared/drm/kernel/radeon.h,v
retrieving revision 1.1
diff -u -r1.1 radeon.h
--- os-support/shared/drm/kernel/radeon.h	2002/10/30 12:52:41	1.1
+++ os-support/shared/drm/kernel/radeon.h	2002/12/12 18:45:17
@@ -85,6 +85,7 @@
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)]    = { radeon_cp_stop,     1, 1 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)]   = { radeon_cp_reset,    1, 1 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)]    = { radeon_cp_idle,     1, 0 }, \
+ [DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESUME)]  = { radeon_cp_resume,   1, 0 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)]    = { radeon_engine_reset,  1, 0 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen,  1, 0 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)]       = { radeon_cp_swap,     1, 0 }, \
Index: os-support/shared/drm/kernel/radeon_cp.c
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/os-support/shared/drm/kernel/radeon_cp.c,v
retrieving revision 1.2
diff -u -r1.2 radeon_cp.c
--- os-support/shared/drm/kernel/radeon_cp.c	2002/11/25 14:05:04	1.2
+++ os-support/shared/drm/kernel/radeon_cp.c	2002/12/12 18:45:17
@@ -1298,6 +1298,177 @@
 	return 0;
 }
 
+/* This code will reinit the Radeon CP hardware after a resume from disc.  
+ * AFAIK, it would be very difficult to pickle the state at suspend time, so 
+ * here we make sure that all Radeon hardware initialisation is re-done without
+ * affecting running applications.  This function is called radeon_do_resume_cp()
+ * as it was derived from radeon_init_cp, where most of the initialisation takes
+ * place during DRI init.
+ *
+ * This patch is NOT to be confused with my and Michel Daenzer's earlier DRI
+ * reinit work, which de- and re-initialised the complete DRI at every VT
+ * switch.
+ *
+ * Charl P. Botha <http://cpbotha.net>
+ */
+static int radeon_do_resume_cp( drm_device_t *dev)
+{
+	drm_radeon_private_t *dev_priv;
+	u32 tmp;
+	DRM_DEBUG( "\n" );
+	
+	DRM_DEBUG("Starting radeon_do_resume_cp()\n");
+
+	/* get the existing dev_private */
+	dev_priv = dev->dev_private;
+
+#if !defined(PCIGART_ENABLED)
+	/* PCI support is not 100% working, so we disable it here.
+	 */
+	if ( dev_priv->is_pci ) {
+		DRM_ERROR( "PCI GART not yet supported for Radeon!\n" );
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+#endif
+
+	if ( dev_priv->is_pci && !dev->sg ) {
+		DRM_ERROR( "PCI GART memory not allocated!\n" );
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if ( dev_priv->usec_timeout < 1 ||
+	     dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT ) {
+		DRM_DEBUG( "TIMEOUT problem!\n" );
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if ( ( dev_priv->cp_mode != RADEON_CSQ_PRIBM_INDDIS ) &&
+	     ( dev_priv->cp_mode != RADEON_CSQ_PRIBM_INDBM ) ) {
+		DRM_DEBUG( "BAD cp_mode (%x)!\n", dev_priv->cp_mode );
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->sarea) {
+		DRM_ERROR("could not find sarea!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->fb) {
+		DRM_ERROR("could not find framebuffer!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->mmio) {
+		DRM_ERROR("could not find mmio region!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->cp_ring) {
+		DRM_ERROR("could not find cp ring region!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->ring_rptr) {
+		DRM_ERROR("could not find ring read pointer!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if(!dev_priv->buffers) {
+		DRM_ERROR("could not find dma buffer region!\n");
+		radeon_do_cleanup_cp(dev);
+		return DRM_ERR(EINVAL);
+	}
+
+	if ( !dev_priv->is_pci ) {
+		if(!dev_priv->agp_textures) {
+			DRM_ERROR("could not find agp texture region!\n");
+			radeon_do_cleanup_cp(dev);
+			return DRM_ERR(EINVAL);
+		}
+	}
+
+	if ( !dev_priv->is_pci ) {
+		if(!dev_priv->cp_ring->handle ||
+		   !dev_priv->ring_rptr->handle ||
+		   !dev_priv->buffers->handle) {
+			DRM_ERROR("could not find ioremap agp regions!\n");
+			radeon_do_cleanup_cp(dev);
+			return DRM_ERR(EINVAL);
+		}
+	} else {
+		DRM_DEBUG( "dev_priv->cp_ring->handle %p\n",
+			   dev_priv->cp_ring->handle );
+		DRM_DEBUG( "dev_priv->ring_rptr->handle %p\n",
+			   dev_priv->ring_rptr->handle );
+		DRM_DEBUG( "dev_priv->buffers->handle %p\n",
+			   dev_priv->buffers->handle );
+	}
+
+
+	DRM_DEBUG( "dev_priv->agp_size %d\n",
+		   dev_priv->agp_size );
+	DRM_DEBUG( "dev_priv->agp_vm_start 0x%x\n",
+		   dev_priv->agp_vm_start );
+	DRM_DEBUG( "dev_priv->agp_buffers_offset 0x%lx\n",
+		   dev_priv->agp_buffers_offset );
+
+#if __REALLY_HAVE_SG
+	if ( dev_priv->is_pci ) {
+	        /* I'm not so sure about this ati_picgart_init after at resume-time... */
+	        if (!DRM(ati_pcigart_init)( dev, &dev_priv->phys_pci_gart,
+					    &dev_priv->bus_pci_gart)) {   
+		    DRM_ERROR( "failed to init PCI GART!\n" );        
+		    radeon_do_cleanup_cp(dev);                        
+		    return DRM_ERR(ENOMEM);                           
+		}
+
+		tmp = RADEON_READ( RADEON_AIC_CNTL )
+		      | RADEON_PCIGART_TRANSLATE_EN;
+		RADEON_WRITE( RADEON_AIC_CNTL, tmp );
+
+		/* set PCI GART page-table base address
+		 */
+		RADEON_WRITE( RADEON_AIC_PT_BASE, dev_priv->bus_pci_gart );
+
+		/* set address range for PCI address translate
+		 */
+		RADEON_WRITE( RADEON_AIC_LO_ADDR, dev_priv->agp_vm_start );
+		RADEON_WRITE( RADEON_AIC_HI_ADDR, dev_priv->agp_vm_start
+						  + dev_priv->agp_size - 1);
+
+		/* Turn off AGP aperture -- is this required for PCIGART?
+		 */
+		RADEON_WRITE( RADEON_MC_AGP_LOCATION, 0xffffffc0 ); /* ?? */
+		RADEON_WRITE( RADEON_AGP_COMMAND, 0 ); /* clear AGP_COMMAND */
+	} else {
+#endif /* __REALLY_HAVE_SG */
+		/* Turn off PCI GART
+		 */
+		tmp = RADEON_READ( RADEON_AIC_CNTL )
+		      & ~RADEON_PCIGART_TRANSLATE_EN;
+		RADEON_WRITE( RADEON_AIC_CNTL, tmp );
+#if __REALLY_HAVE_SG
+	}
+#endif /* __REALLY_HAVE_SG */
+
+	radeon_cp_load_microcode( dev_priv );
+	radeon_cp_init_ring_buffer( dev, dev_priv );
+
+	radeon_do_engine_reset( dev );
+
+	return 0;
+}
+
+
 int radeon_cp_init( DRM_IOCTL_ARGS )
 {
 	DRM_DEVICE;
@@ -1417,6 +1588,16 @@
 
 	return radeon_do_cp_idle( dev_priv );
 }
+
+/* Added by Charl P. Botha to call radeon_do_resume_cp().
+ */
+int radeon_cp_resume( DRM_IOCTL_ARGS )
+{
+	DRM_DEVICE;
+
+	return radeon_do_resume_cp(dev);
+}
+
 
 int radeon_engine_reset( DRM_IOCTL_ARGS )
 {
Index: os-support/shared/drm/kernel/radeon_drm.h
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/os-support/shared/drm/kernel/radeon_drm.h,v
retrieving revision 1.1
diff -u -r1.1 radeon_drm.h
--- os-support/shared/drm/kernel/radeon_drm.h	2002/10/30 12:52:41	1.1
+++ os-support/shared/drm/kernel/radeon_drm.h	2002/12/12 18:45:17
@@ -391,6 +391,8 @@
 #define DRM_IOCTL_RADEON_INIT_HEAP  DRM_IOW( 0x55, drm_radeon_mem_init_heap_t)
 #define DRM_IOCTL_RADEON_IRQ_EMIT   DRM_IOWR( 0x56, drm_radeon_irq_emit_t)
 #define DRM_IOCTL_RADEON_IRQ_WAIT   DRM_IOW( 0x57, drm_radeon_irq_wait_t)
+/* added by Charl P. Botha - see radeon_cp.c for details */
+#define DRM_IOCTL_RADEON_CP_RESUME  DRM_IO(0x58)
 
 typedef struct drm_radeon_init {
 	enum {
Index: os-support/shared/drm/kernel/radeon_drv.h
===================================================================
RCS file: /cvs/xc/programs/Xserver/hw/xfree86/os-support/shared/drm/kernel/radeon_drv.h,v
retrieving revision 1.1
diff -u -r1.1 radeon_drv.h
--- os-support/shared/drm/kernel/radeon_drv.h	2002/10/30 12:52:41	1.1
+++ os-support/shared/drm/kernel/radeon_drv.h	2002/12/12 18:45:17
@@ -153,6 +153,7 @@
 extern int radeon_cp_stop( DRM_IOCTL_ARGS );
 extern int radeon_cp_reset( DRM_IOCTL_ARGS );
 extern int radeon_cp_idle( DRM_IOCTL_ARGS );
+extern int radeon_cp_resume( DRM_IOCTL_ARGS );
 extern int radeon_engine_reset( DRM_IOCTL_ARGS );
 extern int radeon_fullscreen( DRM_IOCTL_ARGS );
 extern int radeon_cp_buffers( DRM_IOCTL_ARGS );
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.