summaryrefslogtreecommitdiff
path: root/src/atiscreen.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/atiscreen.c')
-rw-r--r--src/atiscreen.c655
1 files changed, 655 insertions, 0 deletions
diff --git a/src/atiscreen.c b/src/atiscreen.c
new file mode 100644
index 0000000..effeb66
--- /dev/null
+++ b/src/atiscreen.c
@@ -0,0 +1,655 @@
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atiscreen.c,v 1.30 2003/04/23 21:51:30 tsi Exp $ */
+/*
+ * Copyright 1999 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of Marc Aurele La France not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Marc Aurele La France makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as-is" without express or implied warranty.
+ *
+ * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * DRI support by:
+ * Gareth Hughes <gareth@valinux.com>
+ * José Fonseca <j_r_fonseca@yahoo.co.uk>
+ * Leif Delgass <ldelgass@retinalburn.net>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "ati.h"
+#include "atibus.h"
+#include "atichip.h"
+#include "atiaccel.h"
+#include "aticonsole.h"
+#include "aticursor.h"
+#include "atidac.h"
+#include "atidga.h"
+#include "atidri.h"
+#include "atimach64.h"
+#include "atimode.h"
+#include "atiscreen.h"
+#include "atistruct.h"
+#include "atixv.h"
+#include "atimach64accel.h"
+
+#ifdef XF86DRI_DEVEL
+#include "mach64_dri.h"
+#include "mach64_sarea.h"
+#endif
+
+#ifdef TV_OUT
+
+#include "atichip.h"
+
+#endif /* TV_OUT */
+
+#include "shadowfb.h"
+#include "xf86cmap.h"
+
+#include "xf1bpp.h"
+#include "xf4bpp.h"
+
+#include "fb.h"
+
+#include "mibank.h"
+#include "micmap.h"
+#include "mipointer.h"
+
+/*
+ * ATIRefreshArea --
+ *
+ * This function is called by the shadow frame buffer code to refresh the
+ * hardware frame buffer.
+ */
+static void
+ATIRefreshArea
+(
+ ScrnInfoPtr pScreenInfo,
+ int nBox,
+ BoxPtr pBox
+)
+{
+ ATIPtr pATI = ATIPTR(pScreenInfo);
+ pointer pSrc, pDst;
+ int offset, w, h;
+
+ while (nBox-- > 0)
+ {
+ w = (pBox->x2 - pBox->x1) * pATI->AdjustDepth;
+ h = pBox->y2 - pBox->y1;
+ offset = (pBox->y1 * pATI->FBPitch) + (pBox->x1 * pATI->AdjustDepth);
+ pSrc = (char *)pATI->pShadow + offset;
+ pDst = (char *)pATI->pMemory + offset;
+
+ while (h-- > 0)
+ {
+ (void)memcpy(pDst, pSrc, w);
+ pSrc = (char *)pSrc + pATI->FBPitch;
+ pDst = (char *)pDst + pATI->FBPitch;
+ }
+
+ pBox++;
+ }
+}
+
+/*
+ * ATIMinBits --
+ *
+ * Compute log base 2 of val.
+ */
+static int
+ATIMinBits
+(
+ int val
+)
+{
+ int bits;
+
+ if (!val) return 1;
+ for (bits = 0; val; val >>= 1, ++bits);
+ return bits;
+}
+
+/*
+ * ATIScreenInit --
+ *
+ * This function is called by DIX to initialise the screen.
+ */
+_X_EXPORT Bool
+ATIScreenInit
+(
+ int iScreen,
+ ScreenPtr pScreen,
+ int argc,
+ char **argv
+)
+{
+ ScrnInfoPtr pScreenInfo = xf86Screens[iScreen];
+ ATIPtr pATI = ATIPTR(pScreenInfo);
+ pointer pFB;
+ int VisualMask;
+#ifdef XF86DRI_DEVEL
+ BoxRec ScreenArea;
+#endif
+
+ /* Set video hardware state */
+ if (!ATIEnterGraphics(pScreen, pScreenInfo, pATI))
+ return FALSE;
+
+ /* Re-initialise mi's visual list */
+ miClearVisualTypes();
+
+ if ((pATI->depth > 8) && (pATI->DAC == ATI_DAC_INTERNAL))
+ VisualMask = TrueColorMask;
+ else
+ VisualMask = miGetDefaultVisualMask(pATI->depth);
+
+ if (!miSetVisualTypes(pATI->depth, VisualMask, pATI->rgbBits,
+ pScreenInfo->defaultVisual))
+ return FALSE;
+
+ if (!miSetPixmapDepths())
+ return FALSE;
+
+ pFB = pATI->pMemory;
+ pATI->FBPitch = PixmapBytePad(pATI->displayWidth, pATI->depth);
+ if (pATI->OptionShadowFB)
+ {
+ pATI->FBBytesPerPixel = pATI->bitsPerPixel >> 3;
+ pATI->FBPitch = PixmapBytePad(pATI->displayWidth, pATI->depth);
+ if ((pATI->pShadow = xalloc(pATI->FBPitch * pScreenInfo->virtualY)))
+ {
+ pFB = pATI->pShadow;
+ }
+ else
+ {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
+ "Insufficient virtual memory for shadow frame buffer.\n");
+ pATI->OptionShadowFB = FALSE;
+ }
+ }
+
+#ifdef XF86DRI_DEVEL
+
+ /* Setup DRI after visuals have been established, but before
+ * fbScreenInit is called. fbScreenInit will eventually call the
+ * driver's InitGLXVisuals call back.
+ */
+
+ /* According to atiregs.h, GTPro (3D Rage Pro) is the first chip type with
+ * 3D triangle setup (the VERTEX_* registers)
+ */
+ if (pATI->Chip < ATI_CHIP_264GTPRO) {
+ xf86DrvMsg(iScreen, X_WARNING,
+ "Direct rendering is not supported for ATI chips earlier than "
+ "the ATI 3D Rage Pro.\n");
+ pATI->directRenderingEnabled = FALSE;
+ } else {
+ /* FIXME: When we move to dynamic allocation of back and depth
+ * buffers, we will want to revisit the following check for 3
+ * times the virtual size (or 2.5 times for 24-bit depth) of the screen below.
+ */
+ int cpp = pATI->bitsPerPixel >> 3;
+ int maxY = pScreenInfo->videoRam * 1024 / (pATI->displayWidth * cpp);
+ int requiredY;
+
+ requiredY = pScreenInfo->virtualY * 2 /* front, back buffers */
+ + (pScreenInfo->virtualY * 2 / cpp); /* depth buffer (always 16-bit) */
+
+ if (!pATI->OptionAccel) {
+ xf86DrvMsg(iScreen, X_WARNING,
+ "Acceleration disabled, not initializing the DRI\n");
+ pATI->directRenderingEnabled = FALSE;
+ } else if ( maxY > requiredY ) {
+ pATI->directRenderingEnabled = ATIDRIScreenInit(pScreen);
+ } else {
+ xf86DrvMsg(iScreen, X_WARNING,
+ "DRI static buffer allocation failed -- "
+ "need at least %d kB video memory\n",
+ (pScreenInfo->displayWidth * requiredY * cpp ) / 1024);
+ pATI->directRenderingEnabled = FALSE;
+ }
+ }
+
+#endif /* XF86DRI_DEVEL */
+
+ /* Initialise framebuffer layer */
+ switch (pATI->bitsPerPixel)
+ {
+
+#ifndef AVOID_CPIO
+
+ case 1:
+ pATI->Closeable = xf1bppScreenInit(pScreen, pFB,
+ pScreenInfo->virtualX, pScreenInfo->virtualY,
+ pScreenInfo->xDpi, pScreenInfo->yDpi, pATI->displayWidth);
+ break;
+
+ case 4:
+ pATI->Closeable = xf4bppScreenInit(pScreen, pFB,
+ pScreenInfo->virtualX, pScreenInfo->virtualY,
+ pScreenInfo->xDpi, pScreenInfo->yDpi, pATI->displayWidth);
+ break;
+
+#endif /* AVOID_CPIO */
+
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ pATI->Closeable = fbScreenInit(pScreen, pFB,
+ pScreenInfo->virtualX, pScreenInfo->virtualY,
+ pScreenInfo->xDpi, pScreenInfo->yDpi, pATI->displayWidth,
+ pATI->bitsPerPixel);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (!pATI->Closeable)
+ return FALSE;
+
+ /* Fixup RGB ordering */
+ if (pATI->depth > 8)
+ {
+ VisualPtr pVisual = pScreen->visuals + pScreen->numVisuals;
+
+ while (--pVisual >= pScreen->visuals)
+ {
+ if ((pVisual->class | DynamicClass) != DirectColor)
+ continue;
+
+ pVisual->offsetRed = pScreenInfo->offset.red;
+ pVisual->offsetGreen = pScreenInfo->offset.green;
+ pVisual->offsetBlue = pScreenInfo->offset.blue;
+
+ pVisual->redMask = pScreenInfo->mask.red;
+ pVisual->greenMask = pScreenInfo->mask.green;
+ pVisual->blueMask = pScreenInfo->mask.blue;
+ }
+ }
+
+ /* If applicable, initialise RENDER extension */
+ if (pATI->bitsPerPixel > 4)
+ {
+ if (pATI->OptionShadowFB)
+ {
+ if (serverGeneration == 1)
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
+ "RENDER extension not supported with a shadowed"
+ " framebuffer.\n");
+ }
+
+#ifndef AVOID_CPIO
+
+ else if (pATI->BankInfo.BankSize)
+ {
+ if (serverGeneration == 1)
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
+ "RENDER extension not supported with a banked"
+ " framebuffer.\n");
+ }
+
+#endif /* AVOID_CPIO */
+
+ else if (!fbPictureInit(pScreen, NULL, 0) &&
+ (serverGeneration == 1))
+ {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
+ "RENDER extension initialisation failed.\n");
+ }
+ }
+
+ xf86SetBlackWhitePixels(pScreen);
+
+#ifndef AVOID_CPIO
+
+ /* Initialise banking if needed */
+ if (!miInitializeBanking(pScreen,
+ pScreenInfo->virtualX, pScreenInfo->virtualY,
+ pATI->displayWidth, &pATI->BankInfo))
+ return FALSE;
+
+#endif /* AVOID_CPIO */
+
+ /* Memory manager setup */
+
+#ifdef XF86DRI_DEVEL
+
+ if (pATI->directRenderingEnabled)
+ {
+ ATIDRIServerInfoPtr pATIDRIServer = pATI->pDRIServerInfo;
+ int cpp = pATI->bitsPerPixel >> 3;
+ int widthBytes = pScreenInfo->displayWidth * cpp;
+ int zWidthBytes = pScreenInfo->displayWidth * 2; /* always 16-bit z-buffer */
+ int fbSize = pScreenInfo->videoRam * 1024;
+ int bufferSize = pScreenInfo->virtualY * widthBytes;
+ int zBufferSize = pScreenInfo->virtualY * zWidthBytes;
+ int offscreenBytes, total, scanlines;
+
+ pATIDRIServer->fbX = 0;
+ pATIDRIServer->fbY = 0;
+ pATIDRIServer->frontOffset = 0;
+ pATIDRIServer->frontPitch = pScreenInfo->displayWidth;
+
+ /* Calculate memory remaining for pixcache and textures after
+ * front, back, and depth buffers
+ */
+ offscreenBytes = fbSize - ( 2 * bufferSize + zBufferSize );
+
+ if ( !pATIDRIServer->IsPCI && !pATI->OptionLocalTextures ) {
+ /* Don't allocate a local texture heap for AGP unless requested */
+ pATIDRIServer->textureSize = 0;
+ } else {
+ int l, maxPixcache;
+
+#ifdef XvExtension
+
+ int xvBytes;
+
+ /* Try for enough pixmap cache for DVD and a full viewport
+ */
+ xvBytes = 720*480*cpp; /* enough for single-buffered DVD */
+ maxPixcache = xvBytes > bufferSize ? xvBytes : bufferSize;
+
+#else /* XvExtension */
+
+ /* Try for one viewport */
+ maxPixcache = bufferSize;
+
+#endif /* XvExtension */
+
+ pATIDRIServer->textureSize = offscreenBytes - maxPixcache;
+
+ /* If that gives us less than half the offscreen mem available for textures, split
+ * the available mem between textures and pixmap cache
+ */
+ if (pATIDRIServer->textureSize < (offscreenBytes/2)) {
+ pATIDRIServer->textureSize = offscreenBytes/2;
+ }
+
+ if (pATIDRIServer->textureSize <= 0)
+ pATIDRIServer->textureSize = 0;
+
+ l = ATIMinBits((pATIDRIServer->textureSize-1) / MACH64_NR_TEX_REGIONS);
+ if (l < MACH64_LOG_TEX_GRANULARITY) l = MACH64_LOG_TEX_GRANULARITY;
+
+ /* Round the texture size up to the nearest whole number of
+ * texture regions. Again, be greedy about this, don't round
+ * down.
+ */
+ pATIDRIServer->logTextureGranularity = l;
+ pATIDRIServer->textureSize =
+ (pATIDRIServer->textureSize >> l) << l;
+ }
+
+ total = fbSize - pATIDRIServer->textureSize;
+ scanlines = total / widthBytes;
+ if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
+
+ /* Recalculate the texture offset and size to accomodate any
+ * rounding to a whole number of scanlines.
+ * FIXME: Is this actually needed?
+ */
+ pATIDRIServer->textureOffset = scanlines * widthBytes;
+ pATIDRIServer->textureSize = fbSize - pATIDRIServer->textureOffset;
+
+ /* Set a minimum usable local texture heap size. This will fit
+ * two 256x256 textures. We check this after any rounding of
+ * the texture area.
+ */
+ if (pATIDRIServer->textureSize < 256*256 * cpp * 2) {
+ pATIDRIServer->textureOffset = 0;
+ pATIDRIServer->textureSize = 0;
+ scanlines = fbSize / widthBytes;
+ if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
+ }
+
+ pATIDRIServer->depthOffset = scanlines * widthBytes - zBufferSize;
+ pATIDRIServer->depthPitch = pScreenInfo->displayWidth;
+ pATIDRIServer->depthY = pATIDRIServer->depthOffset/widthBytes;
+ pATIDRIServer->depthX = (pATIDRIServer->depthOffset -
+ (pATIDRIServer->depthY * widthBytes)) / cpp;
+
+ pATIDRIServer->backOffset = pATIDRIServer->depthOffset - bufferSize;
+ pATIDRIServer->backPitch = pScreenInfo->displayWidth;
+ pATIDRIServer->backY = pATIDRIServer->backOffset/widthBytes;
+ pATIDRIServer->backX = (pATIDRIServer->backOffset -
+ (pATIDRIServer->backY * widthBytes)) / cpp;
+
+ scanlines = fbSize / widthBytes;
+ if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
+
+ if ( pATIDRIServer->IsPCI && pATIDRIServer->textureSize == 0 ) {
+ xf86DrvMsg(iScreen, X_WARNING,
+ "Not enough memory for local textures, disabling DRI\n");
+ ATIDRICloseScreen(pScreen);
+ pATI->directRenderingEnabled = FALSE;
+ } else {
+
+ ScreenArea.x1 = 0;
+ ScreenArea.y1 = 0;
+ ScreenArea.x2 = pATI->displayWidth;
+ ScreenArea.y2 = scanlines;
+
+ if (!xf86InitFBManager(pScreen, &ScreenArea)) {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
+ "Memory manager initialization to (%d,%d) (%d,%d) failed\n",
+ ScreenArea.x1, ScreenArea.y1,
+ ScreenArea.x2, ScreenArea.y2);
+ return FALSE;
+ } else {
+ int width, height;
+
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
+ "Memory manager initialized to (%d,%d) (%d,%d)\n",
+ ScreenArea.x1, ScreenArea.y1, ScreenArea.x2, ScreenArea.y2);
+
+ if (xf86QueryLargestOffscreenArea(pScreen, &width, &height, 0, 0, 0)) {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
+ "Largest offscreen area available: %d x %d\n",
+ width, height);
+
+ /* lines in offscreen area needed for depth buffer and textures */
+ pATI->depthTexLines = scanlines
+ - pATIDRIServer->depthOffset / widthBytes;
+ pATI->backLines = scanlines
+ - pATIDRIServer->backOffset / widthBytes
+ - pATI->depthTexLines;
+ pATI->depthTexArea = NULL;
+ pATI->backArea = NULL;
+ } else {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
+ "Unable to determine largest offscreen area available\n");
+ return FALSE;
+ }
+
+ }
+
+ xf86DrvMsg(iScreen, X_INFO, "Will use %d kB of offscreen memory for XAA\n",
+ (offscreenBytes - pATIDRIServer->textureSize)/1024);
+
+ xf86DrvMsg(iScreen, X_INFO, "Will use back buffer at offset 0x%x\n",
+ pATIDRIServer->backOffset);
+
+ xf86DrvMsg(iScreen, X_INFO, "Will use depth buffer at offset 0x%x\n",
+ pATIDRIServer->depthOffset);
+
+ if (pATIDRIServer->textureSize > 0) {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
+ "Will use %d kB for local textures at offset 0x%x\n",
+ pATIDRIServer->textureSize/1024,
+ pATIDRIServer->textureOffset);
+ }
+ }
+ }
+
+#endif /* XF86DRI_DEVEL */
+
+ /* Setup acceleration */
+ /* If direct rendering is not enabled, the framebuffer memory
+ * manager is initialized by this function call */
+ if (!ATIInitializeAcceleration(pScreen, pScreenInfo, pATI))
+ return FALSE;
+
+#ifndef AVOID_DGA
+
+ /* Initialise DGA support */
+ (void)ATIDGAInit(pScreen, pScreenInfo, pATI);
+
+#endif /* AVOID_DGA */
+
+ /* Initialise backing store */
+ miInitializeBackingStore(pScreen);
+ xf86SetBackingStore(pScreen);
+
+ /* Initialise cursor */
+ if (!ATIInitializeCursor(pScreen, pATI))
+ return FALSE;
+
+ /* Create default colourmap */
+ if (!miCreateDefColormap(pScreen))
+ return FALSE;
+
+#ifdef AVOID_CPIO
+
+ if (!xf86HandleColormaps(pScreen, 256, pATI->rgbBits, ATILoadPalette, NULL,
+ CMAP_PALETTED_TRUECOLOR |
+ CMAP_LOAD_EVEN_IF_OFFSCREEN))
+ return FALSE;
+
+#else /* AVOID_CPIO */
+
+ if (pATI->depth > 1)
+ if (!xf86HandleColormaps(pScreen, (pATI->depth == 4) ? 16 : 256,
+ pATI->rgbBits, ATILoadPalette, NULL,
+ CMAP_PALETTED_TRUECOLOR |
+ CMAP_LOAD_EVEN_IF_OFFSCREEN))
+ return FALSE;
+
+#endif /* AVOID_CPIO */
+
+ /* Initialise shadow framebuffer */
+ if (pATI->OptionShadowFB &&
+ !ShadowFBInit(pScreen, ATIRefreshArea))
+ return FALSE;
+
+ /* Initialise DPMS support */
+ (void)xf86DPMSInit(pScreen, ATISetDPMSMode, 0);
+
+ /* Initialise XVideo support */
+ (void)ATIInitializeXVideo(pScreen, pScreenInfo, pATI);
+
+ /* Set pScreen->SaveScreen and wrap CloseScreen vector */
+ pScreen->SaveScreen = ATISaveScreen;
+ pATI->CloseScreen = pScreen->CloseScreen;
+ pScreen->CloseScreen = ATICloseScreen;
+
+ if (serverGeneration == 1)
+ xf86ShowUnusedOptions(pScreenInfo->scrnIndex, pScreenInfo->options);
+
+#ifdef TV_OUT
+ /* Fix-up TV out after ImpacTV probe */
+ if (pATI->OptionTvOut && pATI->Chip < ATI_CHIP_264GTPRO)
+ ATISwitchMode(0, pScreenInfo->currentMode, 0);
+#endif /* TV_OUT */
+
+#ifdef XF86DRI_DEVEL
+
+ /* DRI finalization */
+ if (pATI->directRenderingEnabled) {
+ /* Now that mi, fb, drm and others have done their thing,
+ * complete the DRI setup.
+ */
+ pATI->directRenderingEnabled = ATIDRIFinishScreenInit(pScreen);
+ }
+ if (pATI->directRenderingEnabled) {
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
+ "Direct rendering enabled\n");
+ } else {
+ /* FIXME: Release unused offscreen mem here? */
+ xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
+ "Direct rendering disabled\n");
+ }
+
+#endif /* XF86DRI_DEVEL */
+
+ return TRUE;
+}
+
+/*
+ * ATICloseScreen --
+ *
+ * This function is called by DIX to close the screen.
+ */
+Bool
+ATICloseScreen
+(
+ int iScreen,
+ ScreenPtr pScreen
+)
+{
+ ScrnInfoPtr pScreenInfo = xf86Screens[iScreen];
+ ATIPtr pATI = ATIPTR(pScreenInfo);
+ Bool Closed = TRUE;
+
+#ifdef XF86DRI_DEVEL
+
+ /* Disable direct rendering */
+ if (pATI->directRenderingEnabled)
+ {
+ ATIDRICloseScreen(pScreen);
+ pATI->directRenderingEnabled = FALSE;
+ }
+
+#endif /* XF86DRI_DEVEL */
+
+ ATICloseXVideo(pScreen, pScreenInfo, pATI);
+
+ if (pATI->pXAAInfo)
+ {
+ XAADestroyInfoRec(pATI->pXAAInfo);
+ pATI->pXAAInfo = NULL;
+ }
+
+ if ((pScreen->CloseScreen = pATI->CloseScreen))
+ {
+ pATI->CloseScreen = NULL;
+ Closed = (*pScreen->CloseScreen)(iScreen, pScreen);
+ }
+
+ pATI->Closeable = FALSE;
+
+ if (pATI->pCursorInfo)
+ {
+ xf86DestroyCursorInfoRec(pATI->pCursorInfo);
+ pATI->pCursorInfo = NULL;
+ }
+
+ ATILeaveGraphics(pScreenInfo, pATI);
+
+ xfree(pATI->ExpansionBitmapScanlinePtr[1]);
+ pATI->ExpansionBitmapScanlinePtr[0] =
+ pATI->ExpansionBitmapScanlinePtr[1] = NULL;
+
+ xfree(pATI->pShadow);
+ pATI->pShadow = NULL;
+ pScreenInfo->pScreen = NULL;
+
+ return Closed;
+}