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