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