|
|
/******************************Module*Header**********************************\
* * ******************* * * GDI SAMPLE CODE * * ******************* * * Module Name: dma.c * * Content: Handling of DMA buffers. * * Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-2003 Microsoft Corporation. All rights reserved. \*****************************************************************************/
#include "precomp.h"
#include "glint.h"
//
// Normally, we should not use global variables but the DMA buffers provided
// by the miniport are global across all PDEVs and need be initialized only
// once.
//
typedef struct _DMA_INFORMATION { ULONG NumDMABuffers; QUERY_DMA_BUFFERS DMABuffer[1]; } DMAInformation, *LPDMAInformation;
LPDMAInformation gpDMABufferInfo = (LPDMAInformation)0;
/******************************Public*Routine******************************\
* VOID bGlintInitializeDMA * * Interrogate the miniport to see if DMA is supported. If it is, map in the * DMA buffers ready for use by the 3D extension. * \**************************************************************************/
VOID vGlintInitializeDMA(PPDEV ppdev) { DMA_NUM_BUFFERS queryDMA; ULONG enableFlags; LONG Length; LONG ExtraLength; ULONG i;
GLINT_DECL;
glintInfo->pxrxDMA = &glintInfo->pxrxDMAnonInterrupt;
return; //azntst for multimon
// check the miniport has initialised DMA
//
glintInfo->MaxDMASubBuffers = 0; if (!(ppdev->flCaps & CAPS_DMA_AVAILABLE)) { return; }
// in the multi-board case we only want one set of DMA buffers which
// are global across all boards. But we have an interrupt per board.
// So if the DMA buffers are sorted out try setting up the interrupt.
//
if (gpDMABufferInfo != NULL) { goto TryInterrupts; }
// query the number of DMA buffers. If this fails we have no DMA
//
if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_QUERY_NUM_DMA_BUFFERS, NULL, 0, &queryDMA, sizeof(DMA_NUM_BUFFERS), &Length)) { DISPDBG((ERRLVL, "QUERY_NUM_DMA_BUFFERS failed: " "No GLINT DMA available")); return; } Length = queryDMA.NumBuffers * queryDMA.BufferInformationLength; ExtraLength = sizeof(DMAInformation) - sizeof(QUERY_DMA_BUFFERS);
DISPDBG((ERRLVL, "%d DMA buffers available. Total info size = 0x%x", queryDMA.NumBuffers, Length));
// allocate space for the DMA information
//
gpDMABufferInfo = (LPDMAInformation)ENGALLOCMEM( FL_ZERO_MEMORY, ExtraLength + Length, ALLOC_TAG_GDI(1));
if (gpDMABufferInfo == NULL) { DISPDBG((ERRLVL, "vGlintInitializeDMA: Out of memory")); return; }
gpDMABufferInfo->NumDMABuffers = queryDMA.NumBuffers;
if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_QUERY_DMA_BUFFERS, NULL, 0, (PVOID)(&gpDMABufferInfo->DMABuffer[0]), Length, &Length)) { ENGFREEMEM(gpDMABufferInfo); gpDMABufferInfo = NULL; DISPDBG((ERRLVL, "QUERY_DMA_BUFFERS failed: No GLINT DMA available")); return; }
DISPDBG((ERRLVL, "IOCTL returned length %d", Length));
// zero the flags for each record
//
for (i = 0; i < queryDMA.NumBuffers; ++i) { gpDMABufferInfo->DMABuffer[i].flags = 0; }
#if DBG
{ ULONG j; PUCHAR pAddr; for (i = 0; i < queryDMA.NumBuffers; ++i) { DISPDBG((ERRLVL,"DMA buffer %d: phys 0x%x, virt 0x%x" ", size 0x%x, flags 0x%x", i, gpDMABufferInfo->DMABuffer[i].physAddr.LowPart, gpDMABufferInfo->DMABuffer[i].virtAddr, gpDMABufferInfo->DMABuffer[i].size, gpDMABufferInfo->DMABuffer[i].flags)); pAddr = gpDMABufferInfo->DMABuffer[i].virtAddr; for (j = 0; j < gpDMABufferInfo->DMABuffer[i].size; ++j) *pAddr++ = (UCHAR)(j & 0xff); } } #endif
TryInterrupts:
if (!INTERRUPTS_ENABLED) { return; }
// map in the interrupt command control block. This is a piece of memory
// shared with the intrerrupt controller which allows us to send control
// what happens on VBLANK and DMA interrupts.
//
Length = sizeof(PVOID);
DISPDBG((WRNLVL, "calling IOCTL_VIDEO_MAP_INTERRUPT_CMD_BUF"));
if (EngDeviceIoControl(ppdev->hDriver, IOCTL_VIDEO_MAP_INTERRUPT_CMD_BUF, NULL, 0, (PVOID)&(glintInfo->pInterruptCommandBlock), Length, &Length)) { DISPDBG((ERRLVL, "IOCTL_VIDEO_MAP_INTERRUPT_CMD_BUF failed.")); return; } #if DBG
else { DISPDBG((WRNLVL, "got command buffer at 0x%x", glintInfo->pInterruptCommandBlock)); DISPDBG((WRNLVL, "front, back, end indexes = %d, %d, %d", glintInfo->pInterruptCommandBlock->frontIndex, glintInfo->pInterruptCommandBlock->backIndex, glintInfo->pInterruptCommandBlock->endIndex)); } #endif
// if we get here we have both DMA and interrupts so set for interrupt
// driven DMA. Don't turn on interrupts yet. That has to be done on a
// per context basis.
//
DISPDBG((WRNLVL, "Using interrupt driven DMA")); glintInfo->flags |= GLICAP_INTERRUPT_DMA;
glintInfo->MaxDMASubBuffers = glintInfo->pInterruptCommandBlock->maximumIndex; glintInfo->pxrxDMA = &glintInfo->pInterruptCommandBlock->pxrxDMA;
return; }
/******************************Public*Routine******************************\
* ULONG anyFreeDMABuffers * * Return number of unused DMA buffers available * \**************************************************************************/
ULONG anyFreeDMABuffers(void) { PQUERY_DMA_BUFFERS pDma; ULONG i; ULONG numAvailable = 0;
if (!gpDMABufferInfo) { return 0; }
pDma = &gpDMABufferInfo->DMABuffer[0]; for (i = 0; i < gpDMABufferInfo->NumDMABuffers; ++i) { if (!(pDma->flags & DMA_BUFFER_INUSE)) { numAvailable++; } ++pDma; }
return numAvailable; }
/******************************Public*Routine******************************\
* ULONG GetFreeDMABuffer * * Return info about a DMA buffer and mark it as in use. * -1 is returned if no buffer is available. * \**************************************************************************/
LONG GetFreeDMABuffer(PQUERY_DMA_BUFFERS dmaBuf) { PQUERY_DMA_BUFFERS pDma; ULONG i;
if (!gpDMABufferInfo) { return(-1); }
pDma = &gpDMABufferInfo->DMABuffer[0]; for (i = 0; i < gpDMABufferInfo->NumDMABuffers; ++i) { if (!(pDma->flags & DMA_BUFFER_INUSE)) { pDma->flags |= DMA_BUFFER_INUSE; *dmaBuf = *pDma; DISPDBG((WRNLVL, "Allocated DMA buffer %d", i)); return(i); } ++pDma; }
// all are in use
DISPDBG((ERRLVL, "No more DMA buffers available"));
return(-1); }
/******************************Public*Routine******************************\
* VOID FreeDMABuffer * * Mark the given DMA buffer as free. The caller passes in the physical * address of the buffer. * \**************************************************************************/
VOID FreeDMABuffer(PVOID physAddr) { PQUERY_DMA_BUFFERS pDma; ULONG i;
if (!gpDMABufferInfo) { return; }
pDma = &gpDMABufferInfo->DMABuffer[0]; for (i = 0; i < gpDMABufferInfo->NumDMABuffers; ++i) { if (pDma->physAddr.LowPart == (UINT_PTR)physAddr) { pDma->flags &= ~DMA_BUFFER_INUSE; break; } ++pDma; }
return; }
|