509 lines
16 KiB
C
509 lines
16 KiB
C
/***************************************************************************
|
||
*
|
||
* ******************************************
|
||
* * 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
|
||
|
||
|
||
|