/*************************************************************************** * * ****************************************** * * Copyright (c) 1996, Cirrus Logic, Inc. * * * All Rights Reserved * * ****************************************** * * PROJECT: Laguna I (CL-GD546x) - * * FILE: ddflip.c * * AUTHOR: Benny Ng * * DESCRIPTION: * This module implements the DirectDraw FLIP components * for the Laguna NT driver. * * MODULES: * vGetDisplayDuration() * vUpdateFlipStatus() * DdFlip() * DdWaitForVerticalBlank() * DdGetFlipStatus() * * REVISION HISTORY: * 7/12/96 Benny Ng Initial version * * $Log: X:/log/laguna/nt35/displays/cl546x/ddflip.c $ * * Rev 1.10 16 Sep 1997 15:04:06 bennyn * * Modified for NT DD overlay * * Rev 1.9 29 Aug 1997 17:42:20 RUSSL * Added 65 overlay support * * Rev 1.8 11 Aug 1997 14:07:58 bennyn * * Enabled GetScanLine() (for PDR 10254) * **************************************************************************** ****************************************************************************/ /*----------------------------- INCLUDES ----------------------------------*/ #include "precomp.h" // // This file isn't used in NT 3.51 // #ifndef WINNT_VER35 /*----------------------------- DEFINES -----------------------------------*/ //#define DBGBRK #define DBGLVL 1 #define CSL 0x00C4 #define CSL_5464 0x0140 /*--------------------- STATIC FUNCTION PROTOTYPES ------------------------*/ /*--------------------------- ENUMERATIONS --------------------------------*/ /*----------------------------- TYPEDEFS ----------------------------------*/ /*-------------------------- STATIC VARIABLES -----------------------------*/ /*-------------------------- GLOBAL FUNCTIONS -----------------------------*/ #if DRIVER_5465 && defined(OVERLAY) // CurrentVLine is in ddinline.h for overlay #else /*************************************************************************** * * FUNCTION: CurrentVLine * * DESCRIPTION: * ****************************************************************************/ static __inline int CurrentVLine (PDEV* ppdev) { WORD cline; PBYTE pMMReg = (PBYTE) ppdev->pLgREGS_real; PWORD pCSL; BYTE tmpb; // on 5462 there is no CurrentScanLine register // on RevAA of 5465 it's busted if ((CL_GD5462 == ppdev->dwLgDevID) || ((CL_GD5465 == ppdev->dwLgDevID) && (0 == ppdev->dwLgDevRev))) return 0; if (IN_VBLANK) return 0; // read current scanline if (ppdev->dwLgDevID == CL_GD5464) pCSL = (PWORD) (pMMReg + CSL_5464); else pCSL = (PWORD) (pMMReg + CSL); cline = *pCSL & 0x0FFF; // if scanline doubling is enabled, divide current scanline by 2 tmpb = (BYTE) LLDR_SZ (grCR9); if (0x80 & tmpb) cline /= 2; // if current scanline is past end of visible screen return 0 if (cline >= ppdev->cyScreen) return 0; else return cline; } #endif /**************************************************************************** * FUNCTION NAME: vGetDisplayDuration * * DESCRIPTION: Get the length, in EngQueryPerformanceCounter() ticks, * of a refresh cycle. * (Based on S3 DirectDraw code) ****************************************************************************/ #define NUM_VBLANKS_TO_MEASURE 1 #define NUM_MEASUREMENTS_TO_TAKE 8 VOID vGetDisplayDuration(PFLIPRECORD pflipRecord) { LONG i, j; LONGLONG li, liMin; LONGLONG aliMeasurement[NUM_MEASUREMENTS_TO_TAKE + 1]; DISPDBG((DBGLVL, "DDraw - vGetDisplayDuration\n")); #ifdef DBGBRK DBGBREAKPOINT(); #endif memset(pflipRecord, 0, sizeof(FLIPRECORD)); // Warm up EngQUeryPerformanceCounter to make sure it's in the working set EngQueryPerformanceCounter(&li); // Unfortunately, since NT is a proper multitasking system, we can't // just disable interrupts to take an accurate reading. We also can't // do anything so goofy as dynamically change our thread's priority to // real-time. // // So we just do a bunch of short measurements and take the minimum. // // It would be 'okay' if we got a result that's longer than the actual // VBlank cycle time -- nothing bad would happen except that the app // would run a little slower. We don't want to get a result that's // shorter than the actual VBlank cycle time -- that could cause us // to start drawing over a frame before the Flip has occured. while(IN_VBLANK); while(IN_DISPLAY); for (i = 0; i < NUM_MEASUREMENTS_TO_TAKE; i++) { // We're at the start of the VBlank active cycle! EngQueryPerformanceCounter(&aliMeasurement[i]); // Okay, so life in a multi-tasking environment isn't all that // simple. What if we had taken a context switch just before // the above EngQueryPerformanceCounter call, and now were half // way through the VBlank inactive cycle? Then we would measure // only half a VBlank cycle, which is obviously bad. The worst // thing we can do is get a time shorter than the actual VBlank // cycle time. // // So we solve this by making sure we're in the VBlank active // time before and after we query the time. If it's not, we'll // sync up to the next VBlank (it's okay to measure this period -- // it will be guaranteed to be longer than the VBlank cycle and // will likely be thrown out when we select the minimum sample). // There's a chance that we'll take a context switch and return // just before the end of the active VBlank time -- meaning that // the actual measured time would be less than the true amount -- // but since the VBlank is active less than 1% of the time, this // means that we would have a maximum of 1% error approximately // 1% of the times we take a context switch. An acceptable risk. // // This next line will cause us wait if we're no longer in the // VBlank active cycle as we should be at this point: while(IN_DISPLAY); for (j = 0; j < NUM_VBLANKS_TO_MEASURE; j++) { while(IN_VBLANK); while(IN_DISPLAY); }; }; EngQueryPerformanceCounter(&aliMeasurement[NUM_MEASUREMENTS_TO_TAKE]); // Use the minimum: liMin = aliMeasurement[1] - aliMeasurement[0]; for (i = 2; i <= NUM_MEASUREMENTS_TO_TAKE; i++) { li = aliMeasurement[i] - aliMeasurement[i - 1]; if (li < liMin) liMin = li; }; // Round the result: pflipRecord->liFlipDuration = (DWORD) (liMin + (NUM_VBLANKS_TO_MEASURE / 2)) / NUM_VBLANKS_TO_MEASURE; pflipRecord->liFlipTime = aliMeasurement[NUM_MEASUREMENTS_TO_TAKE]; pflipRecord->bFlipFlag = FALSE; pflipRecord->fpFlipFrom = 0; } // getDisplayDuration /**************************************************************************** * FUNCTION NAME: vUpdateFlipStatus * * DESCRIPTION: Checks and sees if the most recent flip has occurred. * (Based on S3 DirectDraw code) ****************************************************************************/ HRESULT vUpdateFlipStatus(PFLIPRECORD pflipRecord, FLATPTR fpVidMem) { LONGLONG liTime; DISPDBG((DBGLVL, "DDraw - vUpdateFlipStatus\n")); #ifdef DBGBRK DBGBREAKPOINT(); #endif // see if a flip has happened recently if ((pflipRecord->bFlipFlag) && ((fpVidMem == 0xFFFFFFFF) || (fpVidMem == pflipRecord->fpFlipFrom))) { if ((IN_VBLANK)) { if (pflipRecord->bWasEverInDisplay) pflipRecord->bHaveEverCrossedVBlank = TRUE; } else if (!(IN_DISPLAYENABLE)) { if (pflipRecord->bHaveEverCrossedVBlank) { pflipRecord->bFlipFlag = FALSE; return(DD_OK); }; pflipRecord->bWasEverInDisplay = TRUE; }; EngQueryPerformanceCounter(&liTime); if (liTime - pflipRecord->liFlipTime <= pflipRecord->liFlipDuration) { return(DDERR_WASSTILLDRAWING); }; pflipRecord->bFlipFlag = FALSE; }; return(DD_OK); } // updateFlipStatus /**************************************************************************** * FUNCTION NAME: DdFlip * * DESCRIPTION: * (Based on S3 DirectDraw code) ****************************************************************************/ DWORD DdFlip(PDD_FLIPDATA lpFlip) { DRIVERDATA* pDriverData; PDEV* ppdev; HRESULT ddrval; ULONG ulMemoryOffset; ULONG ulLowOffset; ULONG ulMiddleOffset; ULONG ulHighOffset; BYTE tmpb; DISPDBG((DBGLVL, "DDraw - DdFlip\n")); #ifdef DBGBRK DBGBREAKPOINT(); #endif ppdev = (PDEV*) lpFlip->lpDD->dhpdev; pDriverData = (DRIVERDATA*) &ppdev->DriverData; SYNC_W_3D(ppdev); #if DRIVER_5465 && defined(OVERLAY) if (DDSCAPS_OVERLAY & lpFlip->lpSurfCurr->ddsCaps.dwCaps) return pDriverData->OverlayTable.pfnFlip(ppdev,lpFlip); #endif // Is the current flip still in progress? // Don't want a flip to work until after the last flip is done, // so we ask for the general flip status. ddrval = vUpdateFlipStatus(&ppdev->flipRecord, 0xFFFFFFFF); if ((ddrval != DD_OK) || (DrawEngineBusy(pDriverData))) { lpFlip->ddRVal = DDERR_WASSTILLDRAWING; return(DDHAL_DRIVER_HANDLED); }; // everything is OK, do the flip here { DWORD dwOffset; // Determine the offset to the new area. dwOffset = lpFlip->lpSurfTarg->lpGbl->fpVidMem >> 2; // Make sure that the border/blanking period isn't active; wait if // it is. We could return DDERR_WASSTILLDRAWING in this case, but // that will increase the odds that we can't flip the next time: while (IN_DISPLAYENABLE) ; // Flip the primary surface by changing CRD, CRC, CR1B and CR1D // Do CRD last because the start address is double buffered and // will take effect after CRD is updated. // need bits 19 & 20 of address in bits 3 & 4 of CR1D tmpb = (BYTE) LLDR_SZ (grCR1D); tmpb = (tmpb & ~0x18) | (BYTE3FROMDWORD(dwOffset) & 0x18); LL8(grCR1D, tmpb); // need bits 16, 17 & 18 of address in bits 0, 2 & 3 of CR1B tmpb = (BYTE) LLDR_SZ (grCR1B); tmpb = (tmpb & ~0x0D) | ((((BYTE3FROMDWORD(dwOffset) & 0x06) << 1) | (BYTE3FROMDWORD(dwOffset) & 0x01))); LL8(grCR1B, tmpb); // bits 8-15 of address go in CRC LL8(grCRC, BYTE2FROMDWORD(dwOffset)); // bits 0-7 of address go in CRD LL8(grCRD, BYTE1FROMDWORD(dwOffset)); }; // remember where/when we were when we did the flip EngQueryPerformanceCounter(&ppdev->flipRecord.liFlipTime); ppdev->flipRecord.bFlipFlag = TRUE; ppdev->flipRecord.bHaveEverCrossedVBlank = FALSE; ppdev->flipRecord.bWasEverInDisplay = FALSE; ppdev->flipRecord.fpFlipFrom = lpFlip->lpSurfCurr->lpGbl->fpVidMem; lpFlip->ddRVal = DD_OK; return(DDHAL_DRIVER_HANDLED); } // Flip /**************************************************************************** * FUNCTION NAME: DdWaitForVerticalBlank * * DESCRIPTION: ****************************************************************************/ DWORD DdWaitForVerticalBlank(PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank) { PDEV* ppdev; DISPDBG((DBGLVL, "DDraw - DdWaitForVerticalBlank\n")); #ifdef DBGBRK DBGBREAKPOINT(); #endif ppdev = (PDEV*) lpWaitForVerticalBlank->lpDD->dhpdev; lpWaitForVerticalBlank->ddRVal = DD_OK; switch (lpWaitForVerticalBlank->dwFlags) { case DDWAITVB_I_TESTVB: // If TESTVB, it's just a request for the current vertical blank // status: lpWaitForVerticalBlank->bIsInVB = IN_VBLANK; return(DDHAL_DRIVER_HANDLED); case DDWAITVB_BLOCKBEGIN: // If BLOCKBEGIN is requested, we wait until the vertical blank // is over, and then wait for the display period to end: while(IN_VBLANK); while(IN_DISPLAY); return(DDHAL_DRIVER_HANDLED); case DDWAITVB_BLOCKEND: // If BLOCKEND is requested, we wait for the vblank interval to end: while(IN_DISPLAY); while(IN_VBLANK); return(DDHAL_DRIVER_HANDLED); default: return DDHAL_DRIVER_NOTHANDLED; }; // end switch return(DDHAL_DRIVER_NOTHANDLED); } // WaitForVerticalBlank /**************************************************************************** * FUNCTION NAME: DdGetFlipStatus * * DESCRIPTION: If the display has gone through one refresh cycle since * the flip occurred, we return DD_OK. If it has not gone * through one refresh cycle we return DDERR_WASSTILLDRAWING * to indicate that this surface is still busy "drawing" the * flipped page. We also return DDERR_WASSTILLDRAWING if the * bltter is busy and the caller wanted to know if they could * flip yet. ****************************************************************************/ DWORD DdGetFlipStatus(PDD_GETFLIPSTATUSDATA lpGetFlipStatus) { DRIVERDATA* pDriverData; PDEV* ppdev; ppdev = (PDEV*) lpGetFlipStatus->lpDD->dhpdev; pDriverData = (DRIVERDATA*) &ppdev->DriverData; DISPDBG((DBGLVL, "DDraw - DdGetFlipStatus\n")); #ifdef DBGBRK DBGBREAKPOINT(); #endif SYNC_W_3D(ppdev); #if DRIVER_5465 && defined(OVERLAY) if (DDSCAPS_OVERLAY & lpGetFlipStatus->lpDDSurface->ddsCaps.dwCaps) { DWORD dwVWIndex; LP_SURFACE_DATA pSurfaceData = (LP_SURFACE_DATA) lpGetFlipStatus->lpDDSurface->dwReserved1; dwVWIndex = GetVideoWindowIndex(pSurfaceData->dwOverlayFlags); lpGetFlipStatus->ddRVal = pDriverData->OverlayTable.pfnGetFlipStatus(ppdev, lpGetFlipStatus->lpDDSurface->lpGbl->fpVidMem, dwVWIndex); } else #endif { // We don't want a flip to work until after the last flip is done, // so we ask for the general flip status. lpGetFlipStatus->ddRVal = vUpdateFlipStatus(&ppdev->flipRecord, 0xFFFFFFFF); } // Check if the bltter is busy if someone wants to know if they can flip if (lpGetFlipStatus->dwFlags == DDGFS_CANFLIP) { if ((lpGetFlipStatus->ddRVal == DD_OK) && DrawEngineBusy(pDriverData)) lpGetFlipStatus->ddRVal = DDERR_WASSTILLDRAWING; } return(DDHAL_DRIVER_HANDLED); } // GetFlipStatus // #ifdef DDDRV_GETSCANLINE /************/ /**************************************************************************** * FUNCTION NAME: GetScanLine * * DESCRIPTION: * (Based on Laguna Win95 DirectDraw code) ****************************************************************************/ DWORD GetScanLine(PDD_GETSCANLINEDATA lpGetScanLine) { PDEV* ppdev; ppdev = (PDEV*) lpGetScanLine->lpDD->dhpdev; // If a vertical blank is in progress the scan line is in // indeterminant. If the scan line is indeterminant we return // the error code DDERR_VERTICALBLANKINPROGRESS. // Otherwise we return the scan line and a success code SYNC_W_3D(ppdev); // if 3D context(s) active, make sure 3D engine idle before continuing... if (IN_VBLANK) { lpGetScanLine->ddRVal = DDERR_VERTICALBLANKINPROGRESS; } else { lpGetScanLine->dwScanLine = CurrentVLine(ppdev); lpGetScanLine->ddRVal = DD_OK; }; return DDHAL_DRIVER_HANDLED; } // GetScanLine // #endif // DDDRV_GETSCANLINE ************ #endif // ! ver 3.51