mirror of https://github.com/lianthony/NT4.0
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.
459 lines
11 KiB
459 lines
11 KiB
/*
|
|
* stream.c
|
|
*
|
|
* 32-bit Video Capture driver
|
|
* hardware-specific streaming routines for Bravado board.
|
|
*
|
|
*
|
|
*
|
|
* Geraint Davies, Feb 93
|
|
*/
|
|
|
|
#include <vckernel.h>
|
|
#include <bravado.h>
|
|
#include "hardware.h"
|
|
#include "vidcio.h"
|
|
|
|
#include "profile.h"
|
|
#if DBG
|
|
profiling prf_core, prf_line;
|
|
#endif
|
|
|
|
/*
|
|
* outline of data streaming interface:
|
|
*
|
|
*
|
|
* StreamInit will be called to initialise for streaming.
|
|
* This will be followed by a call to StreamStart to start
|
|
* streaming (immediately). Thus any time-consuming setup or cueing should
|
|
* be done in the StreamInit call.
|
|
*
|
|
* After StreamStart has enabled interrupts, our InterruptAck routine
|
|
* will be called on each interrupt. This is responsible for:
|
|
* -- clearing and re-enabling interrupts
|
|
* -- determining if it is time yet to capture a frame (based on the
|
|
* microsecs/frame argument to StreamInit).
|
|
*
|
|
* If InterruptAck returns TRUE to request capture service, then the
|
|
* CaptureService function will be called at some later point - IF there is
|
|
* a user-supplied buffer to copy into. At this point, the buffer will be
|
|
* locked down and the data can be copied in, subject to any conversion.
|
|
* If there is no buffer, the capture service routine will be called with
|
|
* a NULL buffer pointer, for it to discard any data and re-arm for capture:
|
|
* we dont currently use this in the bravado driver.
|
|
*
|
|
*
|
|
* Mutual exclusion outside of this module ensures that only one
|
|
* of the init/fini/start/stop functions is called at one time. However,
|
|
* there is nothing to prevent the InterruptAcknowledge or the
|
|
* CaptureService being called at any time. The interrupt ack routine
|
|
* needs to package up all code that needs to be synchronised (in
|
|
* our case, access to the PCV index register) in calls to
|
|
* VC_SynchronizeExecution.
|
|
*
|
|
* The capture service routine only accesses the frame buffer, and no other
|
|
* routine does that, so there is no contention. If there were any
|
|
* contention, we would have to package the code into calls to
|
|
* VC_SynchronizeDPC (for sync between the CaptureService and the
|
|
* passive-level functions) or VC_SynchronizeExecution (for sync with
|
|
* the interrupt routine as well as everything else).
|
|
*/
|
|
|
|
VOID HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
|
|
VOID HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
|
|
VOID HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr);
|
|
|
|
/*
|
|
* initialise and cue ready for streaming from the framebuffer into
|
|
* memory. The argument gives the desired frame rate.
|
|
*
|
|
* We have very little to do at this point except store the msperframe arg.
|
|
*
|
|
*/
|
|
BOOLEAN
|
|
HW_StreamInit(PDEVICE_INFO pDevInfo, ULONG microsecperframe)
|
|
{
|
|
|
|
PHWINFO pHw;
|
|
|
|
pHw = VC_GetHWInfo(pDevInfo);
|
|
|
|
/* remember the frame rate. Keep it in microsec per frame
|
|
* to reduce the rounding errors that would be introduced if we
|
|
* did everything in millisecs
|
|
*/
|
|
pHw->dwMicroPerFrame = microsecperframe;
|
|
|
|
/* check that a format has been set */
|
|
if (pHw->Format == FmtInvalid) {
|
|
dprintf1(("trying to capture without setting format"));
|
|
return(FALSE);
|
|
}
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
/* clean up after streaming. nothing to do here */
|
|
BOOLEAN
|
|
HW_StreamFini(PDEVICE_INFO pDevInfo)
|
|
{
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* start streaming data into the users buffers.
|
|
*
|
|
* Reset the counters and flag that capture is in progress.
|
|
* Then enable interrupts (per frame) and wait for the right number
|
|
* of interrupts.
|
|
*/
|
|
BOOLEAN
|
|
HW_StreamStart(PDEVICE_INFO pDevInfo)
|
|
{
|
|
/* we set the microsecs per vsync interrupt based on the
|
|
* PAl/NTSC field rate, and we use this to update the video
|
|
* clock every interrupt. When this reaches the next frame time,
|
|
* we capture the frame.
|
|
*
|
|
*/
|
|
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
|
|
|
|
pHw->dwNextFrameNr = 0;
|
|
pHw->dwMicroPerInterrupt = (pHw->VideoStd == PAL) ? PAL_MICROSPERFRAME : NTSC_MICROSPERFRAME;
|
|
|
|
pHw->dwMicroPerInterrupt /= 2;
|
|
|
|
pHw->dwNextFrameTime = 0;
|
|
pHw->dwVideoTime = 0;
|
|
pHw->dwInterruptCount = 0;
|
|
|
|
pHw->bVideoIn = TRUE;
|
|
pHw->bCapturing = FALSE;
|
|
pHw->iNotBusy = 0;
|
|
|
|
INIT_PROFILING(&prf_core);
|
|
INIT_PROFILING(&prf_line);
|
|
|
|
/* enable acquisition into the buffer */
|
|
if (!HW_Capture(pDevInfo, TRUE)) {
|
|
return(FALSE);
|
|
}
|
|
|
|
HW_EnableInts(pDevInfo, FALSE);
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* stop streaming - simply disable interrupts on the PCVideo chip.
|
|
*
|
|
*/
|
|
|
|
BOOLEAN
|
|
HW_StreamStop(PDEVICE_INFO pDevInfo)
|
|
{
|
|
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
|
|
|
|
pHw->bVideoIn = FALSE;
|
|
|
|
HW_DisableInts(pDevInfo, FALSE);
|
|
|
|
#if DBG
|
|
if (PROFILING_COUNT(&prf_core) > 1) {
|
|
dprintf1(("core loop (%d) %d usecs/frame",
|
|
PROFILING_COUNT(&prf_core), PROFILING_TIME(&prf_core)));
|
|
dprintf1(("line loop (%d) %d usecs/line",
|
|
PROFILING_COUNT(&prf_line), PROFILING_TIME(&prf_line)));
|
|
}
|
|
#endif
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* get the position - for this we return the number of millisecs
|
|
* since capture began, according to the video vsync clock
|
|
*/
|
|
ULONG
|
|
HW_StreamGetPosition(PDEVICE_INFO pDevInfo)
|
|
{
|
|
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
|
|
|
|
return( (ULONG) pHw->dwVideoTime);
|
|
}
|
|
|
|
|
|
/*
|
|
* interrupt acknowledge routine. This is called to ack the interrupt
|
|
* and re-enable it for next time. It should return TRUE if it is time
|
|
* to capture a frame.
|
|
*
|
|
* If we are not currently capturing (bVideoIn is false), then disable
|
|
* interrupts and stop. Inc the interrupt count in any case, since the
|
|
* interrupt may have been generated by HW_Init to test the hardware.
|
|
*/
|
|
BOOLEAN
|
|
HW_InterruptAcknowledge(PDEVICE_INFO pDevInfo)
|
|
{
|
|
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
|
|
|
|
pHw->dwInterruptCount++;
|
|
|
|
/* clear and re-enable interrupts */
|
|
HW_ReEnableInts(pDevInfo, TRUE);
|
|
|
|
/* are we actually streaming ? */
|
|
if (!pHw->bVideoIn) {
|
|
HW_DisableInts(pDevInfo, TRUE);
|
|
return(FALSE);
|
|
}
|
|
|
|
pHw->dwVideoTime = (pHw->dwInterruptCount * pHw->dwMicroPerInterrupt) / 1000;
|
|
|
|
|
|
// we only capture on even field interrupts
|
|
if ((pHw->dwInterruptCount & 1) ||
|
|
(pHw->dwVideoTime < pHw->dwNextFrameTime)) {
|
|
if (pHw->bCapturing) {
|
|
pHw->iNotBusy = 0;
|
|
} else {
|
|
pHw->iNotBusy++;
|
|
}
|
|
return(FALSE);
|
|
} else {
|
|
|
|
// check that we are not overrunning
|
|
if (pHw->bCapturing) {
|
|
//dprintf(("overrun"));
|
|
pHw->iNotBusy = 0;
|
|
return(FALSE);
|
|
}
|
|
|
|
/* check that we have had enough fields with
|
|
* acquire enabled for a frame to be captured
|
|
*
|
|
* - check the acquisition mode to see if we are capturing
|
|
* 1 or 2 fields.
|
|
*/
|
|
if ((pHw->iNotBusy < 1) ||
|
|
((pHw->iNotBusy < 2) && !(pHw->bRegPCVideo[REG_ACQMODE] & 0x4))) {
|
|
// we didn't re-enable soon enough to get this frame
|
|
return(FALSE);
|
|
}
|
|
|
|
// reset the count of safely captured fields
|
|
pHw->iNotBusy = 0;
|
|
|
|
/* advance next frame time */
|
|
pHw->dwNextFrameNr++;
|
|
pHw->dwNextFrameTime = (pHw->dwNextFrameNr * pHw->dwMicroPerFrame) / 1000;
|
|
|
|
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Capture a frame - copy the frame buffer into the buffer passed
|
|
* as argument. The user buffer is at this point locked down and
|
|
* mapped into system memory.
|
|
*
|
|
* returns bytes written to buffer PUCHAR (whose length is ULONG)
|
|
* called with null if no buffer - nothing to do in this case.
|
|
*
|
|
* returns the time stamp in pTimeStamp (the millisec count
|
|
* since capture began - based on the number of video syncs and the
|
|
* video standard (PAL, NTSC...).
|
|
*/
|
|
ULONG
|
|
HW_CaptureService(
|
|
PDEVICE_INFO pDevInfo,
|
|
PUCHAR pBuffer,
|
|
PULONG pTimeStamp,
|
|
ULONG BufferLength)
|
|
{
|
|
PHWINFO pHw = VC_GetHWInfo(pDevInfo);
|
|
PUCHAR pFrame = VC_GetFrameBuffer(pDevInfo);
|
|
ULONG BitmapSize;
|
|
|
|
/*
|
|
* do nothing if no buffer is ready
|
|
*/
|
|
if (pBuffer == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
|
|
*pTimeStamp = (ULONG) pHw->dwVideoTime;
|
|
|
|
|
|
/*
|
|
* disable acquisition: we can't access the frame buffer at the
|
|
* same time as acquisition
|
|
*/
|
|
pHw->bCapturing = TRUE;
|
|
HW_Capture(pDevInfo, FALSE);
|
|
|
|
START_PROFILING(&prf_core);
|
|
|
|
|
|
switch(pHw->Format) {
|
|
|
|
case FmtPal8:
|
|
/*
|
|
* convert to palettised data (using the translation table) while
|
|
* copying out of the frame buffer
|
|
*/
|
|
CopyYUVToPal8(pBuffer,
|
|
pFrame,
|
|
pHw->pXlate,
|
|
pHw->dwWidth,
|
|
pHw->dwHeight,
|
|
FRAMEBUFFERWIDTH);
|
|
|
|
BitmapSize = pHw->dwHeight * pHw->dwWidth;
|
|
break;
|
|
|
|
case FmtRGB555:
|
|
|
|
/*
|
|
* convert to 16-bit RGB while copying from the buffer (using
|
|
* the translation table)
|
|
*/
|
|
CopyYUVToRGB555(pBuffer,
|
|
pFrame,
|
|
(PWORD) pHw->pXlate,
|
|
pHw->dwWidth,
|
|
pHw->dwHeight,
|
|
FRAMEBUFFERWIDTH);
|
|
|
|
BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
|
|
break;
|
|
|
|
|
|
case FmtRGB24:
|
|
/*
|
|
* convert to 24-bit RGB while copying from the buffer*/
|
|
CopyYUVToRGB24(pBuffer,
|
|
pFrame,
|
|
pHw->dwWidth,
|
|
pHw->dwHeight,
|
|
FRAMEBUFFERWIDTH);
|
|
|
|
BitmapSize = pHw->dwHeight * pHw->dwWidth * 3;
|
|
break;
|
|
|
|
case FmtYUV:
|
|
/* straight copy of the rectangle. call the generic
|
|
* rectangle copy routine - tell it sizes in bytes,
|
|
* not pixels
|
|
*/
|
|
CopyRectFromIOMemory(pBuffer,
|
|
pFrame,
|
|
pHw->dwWidth * 2, // width in bytes, not pixels
|
|
pHw->dwHeight,
|
|
FRAMEBUFFERWIDTH);
|
|
|
|
BitmapSize = pHw->dwHeight * pHw->dwWidth * 2;
|
|
break;
|
|
|
|
}
|
|
|
|
STOP_PROFILING(&prf_core);
|
|
|
|
/*
|
|
* now that we have finished with the frame buffer, re-start
|
|
* acquisition
|
|
*/
|
|
HW_Capture(pDevInfo, TRUE);
|
|
pHw->bCapturing = FALSE;
|
|
|
|
return(BitmapSize);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* -- internal functions ----------------------------------------------*/
|
|
|
|
/*
|
|
* enable vertical-sync interrupts. We use one interrupt per field
|
|
*/
|
|
VOID
|
|
HW_EnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
|
|
{
|
|
if (bInIsr) {
|
|
SYNC_REG_ARG SyncArg;
|
|
|
|
SyncArg.pDevInfo = pDevInfo;
|
|
SyncArg.bRegister = REG_INTERRUPT;
|
|
SyncArg.bData = 0x3;
|
|
|
|
HW_SetPCVideoReg_Sync(&SyncArg);
|
|
} else {
|
|
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* clear an interrupt and re-enable
|
|
*/
|
|
VOID
|
|
HW_ReEnableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
|
|
{
|
|
if (bInIsr) {
|
|
SYNC_REG_ARG SyncArg;
|
|
|
|
SyncArg.pDevInfo = pDevInfo;
|
|
SyncArg.bRegister = REG_INTERRUPT;
|
|
SyncArg.bData = 0x0;
|
|
|
|
HW_SetPCVideoReg_Sync(&SyncArg);
|
|
|
|
SyncArg.bData = 0x3;
|
|
HW_SetPCVideoReg_Sync(&SyncArg);
|
|
} else {
|
|
|
|
|
|
// clear the interrupt
|
|
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
|
|
|
|
|
|
// re-enable frame-based interrupts
|
|
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0x3);
|
|
}
|
|
}
|
|
|
|
/* disable interrupts on the card */
|
|
VOID
|
|
HW_DisableInts(PDEVICE_INFO pDevInfo, BOOL bInIsr)
|
|
{
|
|
|
|
if (bInIsr) {
|
|
SYNC_REG_ARG SyncArg;
|
|
|
|
SyncArg.pDevInfo = pDevInfo;
|
|
SyncArg.bRegister = REG_INTERRUPT;
|
|
SyncArg.bData = 0x0;
|
|
|
|
HW_SetPCVideoReg_Sync(&SyncArg);
|
|
|
|
} else {
|
|
HW_SetPCVideoReg(pDevInfo, REG_INTERRUPT, 0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|