You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
509 lines
16 KiB
509 lines
16 KiB
/***************************************************************************
|
|
*
|
|
* ******************************************
|
|
* * 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
|
|
|
|
|
|
|