atiscreen.c revision 0b0ce0bf
1/*
2 * Copyright 1999 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of Marc Aurele La France not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  Marc Aurele La France makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as-is" without express or implied warranty.
13 *
14 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
16 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 *
22 * DRI support by:
23 *    Gareth Hughes <gareth@valinux.com>
24 *    José Fonseca <j_r_fonseca@yahoo.co.uk>
25 *    Leif Delgass <ldelgass@retinalburn.net>
26 */
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include <string.h>
33
34#include "ati.h"
35#include "atibus.h"
36#include "atichip.h"
37#include "aticursor.h"
38#include "atidac.h"
39#include "atidga.h"
40#include "atidri.h"
41#include "atimach64.h"
42#include "atimode.h"
43#include "atistruct.h"
44#include "atiscreen.h"
45#include "atixv.h"
46#include "atimach64accel.h"
47#include "aticonsole.h"
48
49#ifdef XF86DRI_DEVEL
50#include "mach64_dri.h"
51#include "mach64_sarea.h"
52#endif
53
54#ifdef TV_OUT
55
56#include "atichip.h"
57
58#endif /* TV_OUT */
59
60#include "shadowfb.h"
61#include "xf86cmap.h"
62
63#include "fb.h"
64
65#include "micmap.h"
66#include "mipointer.h"
67
68/*
69 * ATIRefreshArea --
70 *
71 * This function is called by the shadow frame buffer code to refresh the
72 * hardware frame buffer.
73 */
74static void
75ATIRefreshArea
76(
77    ScrnInfoPtr pScreenInfo,
78    int         nBox,
79    BoxPtr      pBox
80)
81{
82    ATIPtr  pATI = ATIPTR(pScreenInfo);
83    pointer pSrc, pDst;
84    int     offset, w, h;
85
86    while (nBox-- > 0)
87    {
88        w = (pBox->x2 - pBox->x1) * pATI->AdjustDepth;
89        h = pBox->y2 - pBox->y1;
90        offset = (pBox->y1 * pATI->FBPitch) + (pBox->x1 * pATI->AdjustDepth);
91        pSrc = (char *)pATI->pShadow + offset;
92        pDst = (char *)pATI->pMemory + offset;
93
94        while (h-- > 0)
95        {
96            (void)memcpy(pDst, pSrc, w);
97            pSrc = (char *)pSrc + pATI->FBPitch;
98            pDst = (char *)pDst + pATI->FBPitch;
99        }
100
101        pBox++;
102    }
103}
104
105/*
106 * ATIMinBits --
107 *
108 * Compute log base 2 of val.
109 */
110static int
111ATIMinBits
112(
113    int val
114)
115{
116    int bits;
117
118    if (!val) return 1;
119    for (bits = 0; val; val >>= 1, ++bits);
120    return bits;
121}
122
123#ifdef USE_XAA
124static Bool
125ATIMach64SetupMemXAA_NoDRI
126(
127    ScrnInfoPtr pScreenInfo,
128    ScreenPtr pScreen
129)
130{
131    ATIPtr       pATI        = ATIPTR(pScreenInfo);
132
133    int maxScanlines = ATIMach64MaxY;
134    int maxPixelArea, PixelArea;
135
136    {
137        /*
138         * Note:  If PixelArea exceeds the engine's maximum, the excess is
139         *        never used, even though it would be useful for such things
140         *        as XVideo buffers.
141         */
142        maxPixelArea = maxScanlines * pScreenInfo->displayWidth;
143        PixelArea = pScreenInfo->videoRam * 1024 * 8 / pATI->bitsPerPixel;
144        if (PixelArea > maxPixelArea)
145            PixelArea = maxPixelArea;
146        xf86InitFBManagerArea(pScreen, PixelArea, 2);
147    }
148
149    return TRUE;
150}
151
152#ifdef XF86DRI_DEVEL
153/*
154 * Memory layour for XAA with DRI (no local_textures):
155 * | front  | pixmaps, xv | back   | depth  | textures | c |
156 *
157 * 1024x768@16bpp with 8 MB:
158 * | 1.5 MB | ~3.5 MB     | 1.5 MB | 1.5 MB | 0        | c |
159 *
160 * 1024x768@32bpp with 8 MB:
161 * | 3.0 MB | ~0.5 MB     | 3.0 MB | 1.5 MB | 0        | c |
162 *
163 * "c" is the hw cursor which occupies 1KB
164 */
165static Bool
166ATIMach64SetupMemXAA
167(
168    ScrnInfoPtr pScreenInfo,
169    ScreenPtr pScreen
170)
171{
172	ATIPtr       pATI        = ATIPTR(pScreenInfo);
173
174	ATIDRIServerInfoPtr pATIDRIServer = pATI->pDRIServerInfo;
175	int cpp = pATI->bitsPerPixel >> 3;
176	int widthBytes = pScreenInfo->displayWidth * cpp;
177	int zWidthBytes = pScreenInfo->displayWidth * 2; /* always 16-bit z-buffer */
178	int fbSize = pScreenInfo->videoRam * 1024;
179	int bufferSize = pScreenInfo->virtualY * widthBytes;
180	int zBufferSize = pScreenInfo->virtualY * zWidthBytes;
181	int offscreenBytes, total, scanlines;
182
183	pATIDRIServer->fbX = 0;
184	pATIDRIServer->fbY = 0;
185	pATIDRIServer->frontOffset = 0;
186	pATIDRIServer->frontPitch = pScreenInfo->displayWidth;
187
188	/* Calculate memory remaining for pixcache and textures after
189	 * front, back, and depth buffers
190	 */
191	offscreenBytes = fbSize - ( 2 * bufferSize + zBufferSize );
192
193	if ( !pATIDRIServer->IsPCI && !pATI->OptionLocalTextures ) {
194	    /* Don't allocate a local texture heap for AGP unless requested */
195	    pATIDRIServer->textureSize = 0;
196	} else {
197	    int l, maxPixcache;
198
199#ifdef XvExtension
200
201	    int xvBytes;
202
203	    /* Try for enough pixmap cache for DVD and a full viewport
204	     */
205	    xvBytes = 720*480*cpp; /* enough for single-buffered DVD */
206	    maxPixcache = xvBytes > bufferSize ? xvBytes : bufferSize;
207
208#else /* XvExtension */
209
210	    /* Try for one viewport */
211	    maxPixcache = bufferSize;
212
213#endif /* XvExtension */
214
215	    pATIDRIServer->textureSize = offscreenBytes - maxPixcache;
216
217	    /* If that gives us less than half the offscreen mem available for textures, split
218	     * the available mem between textures and pixmap cache
219	     */
220	    if (pATIDRIServer->textureSize < (offscreenBytes/2)) {
221		pATIDRIServer->textureSize = offscreenBytes/2;
222	    }
223
224	    if (pATIDRIServer->textureSize <= 0)
225		pATIDRIServer->textureSize = 0;
226
227	    l = ATIMinBits((pATIDRIServer->textureSize-1) / MACH64_NR_TEX_REGIONS);
228	    if (l < MACH64_LOG_TEX_GRANULARITY) l = MACH64_LOG_TEX_GRANULARITY;
229
230	    /* Round the texture size up to the nearest whole number of
231	     * texture regions.  Again, be greedy about this, don't round
232	     * down.
233	     */
234	    pATIDRIServer->logTextureGranularity = l;
235	    pATIDRIServer->textureSize =
236		(pATIDRIServer->textureSize >> l) << l;
237	}
238
239	total = fbSize - pATIDRIServer->textureSize;
240	scanlines = total / widthBytes;
241	if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
242
243	/* Recalculate the texture offset and size to accommodate any
244	 * rounding to a whole number of scanlines.
245	 * FIXME: Is this actually needed?
246	 */
247	pATIDRIServer->textureOffset = scanlines * widthBytes;
248	pATIDRIServer->textureSize = fbSize - pATIDRIServer->textureOffset;
249
250	/* Set a minimum usable local texture heap size.  This will fit
251	 * two 256x256 textures.  We check this after any rounding of
252	 * the texture area.
253	 */
254	if (pATIDRIServer->textureSize < 256*256 * cpp * 2) {
255	    pATIDRIServer->textureOffset = 0;
256	    pATIDRIServer->textureSize = 0;
257	    scanlines = fbSize / widthBytes;
258	    if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
259	}
260
261	pATIDRIServer->depthOffset = scanlines * widthBytes - zBufferSize;
262	pATIDRIServer->depthPitch = pScreenInfo->displayWidth;
263	pATIDRIServer->depthY = pATIDRIServer->depthOffset/widthBytes;
264	pATIDRIServer->depthX =  (pATIDRIServer->depthOffset -
265				  (pATIDRIServer->depthY * widthBytes)) / cpp;
266
267	pATIDRIServer->backOffset = pATIDRIServer->depthOffset - bufferSize;
268	pATIDRIServer->backPitch = pScreenInfo->displayWidth;
269	pATIDRIServer->backY = pATIDRIServer->backOffset/widthBytes;
270	pATIDRIServer->backX =  (pATIDRIServer->backOffset -
271				  (pATIDRIServer->backY * widthBytes)) / cpp;
272
273	scanlines = fbSize / widthBytes;
274	if (scanlines > ATIMach64MaxY) scanlines = ATIMach64MaxY;
275
276	if ( pATIDRIServer->IsPCI && pATIDRIServer->textureSize == 0 ) {
277	    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
278		       "Not enough memory for local textures, disabling DRI\n");
279	    ATIDRICloseScreen(pScreen);
280	    pATI->directRenderingEnabled = FALSE;
281	} else {
282	    BoxRec ScreenArea;
283
284	    ScreenArea.x1 = 0;
285	    ScreenArea.y1 = 0;
286	    ScreenArea.x2 = pATI->displayWidth;
287	    ScreenArea.y2 = scanlines;
288
289	    if (!xf86InitFBManager(pScreen, &ScreenArea)) {
290		xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
291			   "Memory manager initialization to (%d,%d) (%d,%d) failed\n",
292			   ScreenArea.x1, ScreenArea.y1,
293			   ScreenArea.x2, ScreenArea.y2);
294		return FALSE;
295	    } else {
296		int width, height;
297
298		xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
299			   "Memory manager initialized to (%d,%d) (%d,%d)\n",
300			   ScreenArea.x1, ScreenArea.y1, ScreenArea.x2, ScreenArea.y2);
301
302		if (xf86QueryLargestOffscreenArea(pScreen, &width, &height, 0, 0, 0)) {
303		    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
304			       "Largest offscreen area available: %d x %d\n",
305			       width, height);
306
307		    /* lines in offscreen area needed for depth buffer and textures */
308		    pATI->depthTexLines = scanlines
309			- pATIDRIServer->depthOffset / widthBytes;
310		    pATI->backLines     = scanlines
311			- pATIDRIServer->backOffset / widthBytes
312			- pATI->depthTexLines;
313		    pATI->depthTexArea  = NULL;
314		    pATI->backArea      = NULL;
315		} else {
316		    xf86DrvMsg(pScreenInfo->scrnIndex, X_ERROR,
317			       "Unable to determine largest offscreen area available\n");
318		    return FALSE;
319		}
320
321	    }
322
323	    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO, "Will use %d kB of offscreen memory for XAA\n",
324		       (offscreenBytes - pATIDRIServer->textureSize)/1024);
325
326	    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO, "Will use back buffer at offset 0x%x\n",
327		       pATIDRIServer->backOffset);
328
329	    xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO, "Will use depth buffer at offset 0x%x\n",
330		       pATIDRIServer->depthOffset);
331
332	    if (pATIDRIServer->textureSize > 0) {
333		xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
334			   "Will use %d kB for local textures at offset 0x%x\n",
335			   pATIDRIServer->textureSize/1024,
336			   pATIDRIServer->textureOffset);
337	    }
338	}
339
340	return TRUE;
341}
342#endif /* XF86DRI_DEVEL */
343#endif /* USE_XAA */
344
345/*
346 * ATIScreenInit --
347 *
348 * This function is called by DIX to initialise the screen.
349 */
350Bool
351ATIScreenInit(SCREEN_INIT_ARGS_DECL)
352{
353    ScrnInfoPtr  pScreenInfo = xf86ScreenToScrn(pScreen);
354    ATIPtr       pATI        = ATIPTR(pScreenInfo);
355    pointer      pFB;
356    int          VisualMask;
357
358    /* Set video hardware state */
359    if (!ATIEnterGraphics(pScreen, pScreenInfo, pATI))
360        return FALSE;
361
362    /* Re-initialise mi's visual list */
363    miClearVisualTypes();
364
365    if ((pATI->depth > 8) && (pATI->DAC == ATI_DAC_INTERNAL))
366        VisualMask = TrueColorMask;
367    else
368        VisualMask = miGetDefaultVisualMask(pATI->depth);
369
370    if (!miSetVisualTypes(pATI->depth, VisualMask, pATI->rgbBits,
371                          pScreenInfo->defaultVisual))
372        return FALSE;
373
374    if (!miSetPixmapDepths())
375        return FALSE;
376
377    pFB = pATI->pMemory;
378    pATI->FBPitch = PixmapBytePad(pATI->displayWidth, pATI->depth);
379    if (pATI->OptionShadowFB)
380    {
381        pATI->FBBytesPerPixel = pATI->bitsPerPixel >> 3;
382        pATI->FBPitch = PixmapBytePad(pATI->displayWidth, pATI->depth);
383        if ((pATI->pShadow = malloc(pATI->FBPitch * pScreenInfo->virtualY)))
384        {
385            pFB = pATI->pShadow;
386        }
387        else
388        {
389            xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
390                "Insufficient virtual memory for shadow frame buffer.\n");
391            pATI->OptionShadowFB = FALSE;
392        }
393    }
394
395#ifdef XF86DRI_DEVEL
396
397    /* Setup DRI after visuals have been established, but before
398     * fbScreenInit is called.
399     */
400
401    /* According to atiregs.h, GTPro (3D Rage Pro) is the first chip type with
402     * 3D triangle setup (the VERTEX_* registers)
403     */
404    if (pATI->Chip < ATI_CHIP_264GTPRO) {
405	xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
406		   "Direct rendering is not supported for ATI chips earlier than "
407		   "the ATI 3D Rage Pro.\n");
408	pATI->directRenderingEnabled = FALSE;
409    } else {
410	/* FIXME: When we move to dynamic allocation of back and depth
411	 * buffers, we will want to revisit the following check for 3
412	 * times the virtual size (or 2.5 times for 24-bit depth) of the screen below.
413	 */
414	int cpp = pATI->bitsPerPixel >> 3;
415	int maxY = pScreenInfo->videoRam * 1024 / (pATI->displayWidth * cpp);
416	int requiredY;
417
418	requiredY = pScreenInfo->virtualY * 2     /* front, back buffers */
419	    + (pScreenInfo->virtualY * 2 / cpp);  /* depth buffer (always 16-bit) */
420
421	if (!pATI->OptionAccel) {
422	    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
423		       "Acceleration disabled, not initializing the DRI\n");
424	    pATI->directRenderingEnabled = FALSE;
425	} else if ( maxY > requiredY ) {
426	    pATI->directRenderingEnabled = ATIDRIScreenInit(pScreen);
427	} else {
428	    xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
429		       "DRI static buffer allocation failed -- "
430		       "need at least %d kB video memory\n",
431		       (pScreenInfo->displayWidth * requiredY * cpp ) / 1024);
432	    pATI->directRenderingEnabled = FALSE;
433	}
434    }
435
436#endif /* XF86DRI_DEVEL */
437
438    /* Initialise framebuffer layer */
439    switch (pATI->bitsPerPixel)
440    {
441        case 8:
442        case 16:
443        case 24:
444        case 32:
445            pATI->Closeable = fbScreenInit(pScreen, pFB,
446                pScreenInfo->virtualX, pScreenInfo->virtualY,
447                pScreenInfo->xDpi, pScreenInfo->yDpi, pATI->displayWidth,
448                pATI->bitsPerPixel);
449            break;
450
451        default:
452            return FALSE;
453    }
454
455    if (!pATI->Closeable)
456        return FALSE;
457
458    /* Fixup RGB ordering */
459    if (pATI->depth > 8)
460    {
461        VisualPtr pVisual = pScreen->visuals + pScreen->numVisuals;
462
463        while (--pVisual >= pScreen->visuals)
464        {
465            if ((pVisual->class | DynamicClass) != DirectColor)
466                continue;
467
468            pVisual->offsetRed = pScreenInfo->offset.red;
469            pVisual->offsetGreen = pScreenInfo->offset.green;
470            pVisual->offsetBlue = pScreenInfo->offset.blue;
471
472            pVisual->redMask = pScreenInfo->mask.red;
473            pVisual->greenMask = pScreenInfo->mask.green;
474            pVisual->blueMask = pScreenInfo->mask.blue;
475        }
476    }
477
478    /* initialise RENDER extension */
479    if (!fbPictureInit(pScreen, NULL, 0) && (serverGeneration == 1))
480    {
481	xf86DrvMsg(pScreenInfo->scrnIndex, X_WARNING,
482	    "RENDER extension initialisation failed.\n");
483    }
484
485    xf86SetBlackWhitePixels(pScreen);
486
487#ifdef USE_XAA
488
489    if (!pATI->useEXA) {
490
491    /* Memory manager setup */
492
493#ifdef XF86DRI_DEVEL
494    if (pATI->directRenderingEnabled)
495    {
496        if (!ATIMach64SetupMemXAA(pScreenInfo, pScreen))
497            return FALSE;
498    }
499    else
500#endif /* XF86DRI_DEVEL */
501    {
502        if (!ATIMach64SetupMemXAA_NoDRI(pScreenInfo, pScreen))
503            return FALSE;
504    }
505
506    /* Setup acceleration */
507
508    if (pATI->OptionAccel && !ATIMach64AccelInit(pScreen))
509        return FALSE;
510
511    }
512
513#endif /* USE_XAA */
514
515#ifdef USE_EXA
516
517    if (pATI->useEXA) {
518        /* EXA setups both memory manager and acceleration here */
519
520        if (pATI->OptionAccel && !ATIMach64ExaInit(pScreen))
521            return FALSE;
522    }
523
524#endif /* USE_EXA */
525
526#ifndef AVOID_DGA
527
528    /* Initialise DGA support */
529    (void)ATIDGAInit(pScreen, pScreenInfo, pATI);
530
531#endif /* AVOID_DGA */
532
533    /* Initialise backing store */
534    xf86SetBackingStore(pScreen);
535
536    /* Initialise cursor */
537    if (!ATIMach64CursorInit(pScreen))
538        return FALSE;
539
540    /* Create default colourmap */
541    if (!miCreateDefColormap(pScreen))
542        return FALSE;
543
544    if (!xf86HandleColormaps(pScreen, 256, pATI->rgbBits, ATILoadPalette, NULL,
545                             CMAP_PALETTED_TRUECOLOR |
546                             CMAP_LOAD_EVEN_IF_OFFSCREEN))
547            return FALSE;
548
549    /* Initialise shadow framebuffer */
550    if (pATI->OptionShadowFB &&
551        !ShadowFBInit(pScreen, ATIRefreshArea))
552        return FALSE;
553
554    /* Initialise DPMS support */
555    (void)xf86DPMSInit(pScreen, ATISetDPMSMode, 0);
556
557    /* Initialise XVideo support */
558    (void)ATIInitializeXVideo(pScreen, pScreenInfo, pATI);
559
560    /* Set pScreen->SaveScreen and wrap CloseScreen vector */
561    pScreen->SaveScreen = ATISaveScreen;
562    pATI->CloseScreen = pScreen->CloseScreen;
563    pScreen->CloseScreen = ATICloseScreen;
564
565    if (serverGeneration == 1)
566        xf86ShowUnusedOptions(pScreenInfo->scrnIndex, pScreenInfo->options);
567
568#ifdef TV_OUT
569    /* Fix-up TV out after ImpacTV probe */
570    if (pATI->OptionTvOut && pATI->Chip < ATI_CHIP_264GTPRO)
571        ATISwitchMode(SWITCH_MODE_ARGS(pScreenInfo, pScreenInfo->currentMode));
572#endif /* TV_OUT */
573
574#ifdef XF86DRI_DEVEL
575
576    /* DRI finalization */
577    if (pATI->directRenderingEnabled) {
578	/* Now that mi, fb, drm and others have done their thing,
579	 * complete the DRI setup.
580	 */
581	pATI->directRenderingEnabled = ATIDRIFinishScreenInit(pScreen);
582    }
583    if (pATI->directRenderingEnabled) {
584	xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
585		   "Direct rendering enabled\n");
586    } else {
587        /* FIXME: Release unused offscreen mem here? */
588	xf86DrvMsg(pScreenInfo->scrnIndex, X_INFO,
589		   "Direct rendering disabled\n");
590    }
591
592#endif /* XF86DRI_DEVEL */
593
594    return TRUE;
595}
596
597/*
598 * ATICloseScreen --
599 *
600 * This function is called by DIX to close the screen.
601 */
602Bool
603ATICloseScreen (CLOSE_SCREEN_ARGS_DECL)
604{
605    ScrnInfoPtr pScreenInfo = xf86ScreenToScrn(pScreen);
606    ATIPtr      pATI        = ATIPTR(pScreenInfo);
607
608#ifdef XF86DRI_DEVEL
609
610    /* Disable direct rendering */
611    if (pATI->directRenderingEnabled)
612    {
613	ATIDRICloseScreen(pScreen);
614	pATI->directRenderingEnabled = FALSE;
615    }
616
617#endif /* XF86DRI_DEVEL */
618
619    ATICloseXVideo(pScreen, pScreenInfo, pATI);
620
621#ifdef USE_EXA
622    if (pATI->pExa)
623    {
624        exaDriverFini(pScreen);
625        free(pATI->pExa);
626        pATI->pExa = NULL;
627    }
628#endif
629#ifdef USE_XAA
630    if (pATI->pXAAInfo)
631    {
632        XAADestroyInfoRec(pATI->pXAAInfo);
633        pATI->pXAAInfo = NULL;
634    }
635#endif
636    if (pATI->pCursorInfo)
637    {
638        xf86DestroyCursorInfoRec(pATI->pCursorInfo);
639        pATI->pCursorInfo = NULL;
640    }
641
642    pATI->Closeable = FALSE;
643    ATILeaveGraphics(pScreenInfo, pATI);
644
645#ifdef USE_XAA
646    if (!pATI->useEXA)
647    {
648        free(pATI->ExpansionBitmapScanlinePtr[1]);
649        pATI->ExpansionBitmapScanlinePtr[0] = NULL;
650        pATI->ExpansionBitmapScanlinePtr[1] = NULL;
651    }
652#endif
653
654    free(pATI->pShadow);
655    pATI->pShadow = NULL;
656    pScreenInfo->pScreen = NULL;
657
658    pScreen->CloseScreen = pATI->CloseScreen;
659    return (*pScreen->CloseScreen)(CLOSE_SCREEN_ARGS);
660}
661