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/
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 );