windows-nt/Source/XPSP1/NT/drivers/video/ms/port/dmapub.txt
2020-09-26 16:20:57 +08:00

640 lines
24 KiB
Plaintext

Video DMA Interface.
This note outlines a proposed interface between the display driver,
miniport and the video port. The objective is to provide an interface
for the display driver that optimizes DMA throughput for devices that
support scattergather while maintaining system throughput. The design
also optimizes locking, performing the DMA and unlocking into one
IOCTL if desired, a significant performance win. The idea is to provide
a handle available to the display driver that represents locked memory
and for that handle to be returned from each DMA IOCTL request. The
miniport fields some of theses IOCTLs and calls into the video port for
support.
This interface supports only PCI busmaster devices.
///////////////////////////////////////////////////////////////////
Video port to miniport interface.
///////////////////////////////////////////////////////////////////
The miniport must provide 2 things at DriverEntry time:
A) Set Master in VIDEO_PORT_CONFIG_INFO to TRUE.
B) Provide a callback HwStartDma() of type PVIDEO_HW_START_DMA
in the VIDEO_HW_INITIALIZATION_DATA.
Then the following interfaces can be used:
The NT video port support now exports to the miniport the following
function:
1) BOOLEAN
VideoPortLockPages(
IN PVOID HwDeviceExtension,
IN OUT PVIDEO_REQUEST_PACKET pVrp
IN OUT PEVENT pMappedUserEvent,
IN PEVENT pDisplayEvent,
IN DMA_FLAGS DmaFlags
);
This routine can be called by the miniport to do busmaster DMA for
DMA devices. It returns TRUE if successful and FALSE if not successful.
It can only be called in the context of an IOCTL. It cannot be called
from an ISR or DPC.
Its arguments are:
1) A pointer to a DEVICE_EXTENSION.
2) A pointer to a VIDEO_REQUEST_PACKET, whose OutputBuffer it may
modify. The InputBuffer must be the virtual address of the memory
to be locked. The InputBufferSize must be the size of that memory.
The output buffer will receive a PDMA from which may be extracted
a pointer to a scattergather list of physical pages which comprise
the locked down virtual address (via GET_VIDEO_SCATTERGATHER). From
this pointer, one can extract the physical address of any virtual
address (see GET_VIDEO_PHYSICAL_ADDRESS).
3) A pointer to a mapped user event, which may be set by the miniport.
This is either a valid event returned from EngMapEvent or NULL. This
should be received from the display driver. It will be passed into
HwStartDma every time the resulting handle from VideoPortLockPages()
is passed into the Video Port for a DMA operation. All events can only
be set in the miniport, not waited on.
4) A pointer to an event received from the display driver, intended
to be used by the display driver to wait on DMA completion. May be
NULL. Again, may only be set in miniport.
This pointer to an event will also be passed into HwStartDma every
time the resulting handle is passed into the Video Port for a DMA
operation.
4) An enum of type DMA_FLAGS defined in video.h. The values can be:
a) VideoPortUnlockAfterDma
This value should be used for a "one shot" dma action, where
the memory is locked and a dma handle passed to HwStartDma(),
then the memory is unlocked after the miniport signals via
setting pDmaCompletionEvent.
b) VideoPortKeepPagesLocked
This value should only be used for dedicated graphics units.
It does not guarantee that the virtual memory passed in will
remain locked after a dma has completed, only that the system
will try to keep it locked.
c) VideoPortDmaInitOnly
A typical initialization value. If used, the InputBuffer must
contain a pointer to virtual memory. The HwStartDma will not
be called in this case (see below). The memory will remained
locked if possible.
2) PDMA
VideoPortDoDma(
IN PVOID HwDeviceExtension,
IN PDMA pDma,
IN DMA_FLAGS DmaFlags
);
Routine Description:
This function is called by the miniport when a it has a valid DMA
handle to cause HwStartDma to be called. It can be called outside
the context of an IOCTL, but not from an ISR. It must execute at
irql <= DISPATCH_LEVEL.
Arguments:
HwDeviceExtension - Pointer to miniport HWDeviceExtension.
pDma - Non - NULL DMA handle returned by this routine or
VideoPortLockPages() in OutputBuffer.
DmaFlags - Flags specifying desired action.
Return Value:
Non NULL pDma if the corresponding memory is still locked, NULL
otherwise.
3) PVOID
VideoPortGetCommonBuffer(
IN PVOID HwDeviceExtension,
IN ULONG DesiredLength,
IN ULONG Alignment,
OUT PVOID * pVirtualAddress,
OUT PPHYSICAL_ADDRESS pLogicalAddress,
OUT PULONG pActualLength,
IN BOOLEAN CacheEnabled
);
Routine Description:
Provides physical address visible to both device and system. Memory seen as contiguous
by device. This routine can only be reliably called at driver load time. Memory allocated
must be less than 256K.
Arguments:
HwDeviceExtension - device extension available to miniport.
DesiredLength - size of desired memory (should be minimal).
Alignment - Desired liagnment of buffer, currently unused.
pVirtualAddress - unused.
pLogicalAddress - [out] parameter which will hold physical address of
of the buffer upon function return.
pActualLength - Actual length of buffer.
CacheEnabled - Specifies whether the allocated memory can be cached.
Return Value:
Virtual address of the common buffer.
4) PDMA
VideoPortGetMdl(
PVOID HwDeviceExtension,
PDMA pDma
);
Routine Description:
Returns a PMDL representing the page table of the locked buffer.
Arguments:
HwDeviceExtension - device extension available to miniport.
pDma - Dma handle received from either VideoPortLockPages()
or VideoPortDoDma().
Return Value:
A PMDL reprsenting the locked buffer.
5) BOOLEAN
VideoPortSignalDmaComplete(
IN PVOID HwDeviceExtension,
IN PVOID pDmaHandle
)
/*++
Routine Description:
Arguments:
HwDeviceExtension - a pointer to the miniport HW_DEVICE_EXTENSION.
pDmaHandle - the handle returned in the output buffer of the
VIDEO_REQUEST_PACKET after VideoPortLockPages() returns.
Return Value:
TRUE if the DPC was scheduled, FALSE otherwise.
--*/
6) PVOID
VideoPortGetDmaContext(
IN PVOID HwDeviceExtension,
IN PDMA pDma
);
/*++
Routine Description:
Arguments:
HwDeviceExtension - a pointer to the miniport HW_DEVICE_EXTENSION.
pDma - the handle returned in the output buffer of the
VIDEO_REQUEST_PACKET after VideoPortLockPages() returns.
Return Value:
The Context previously associated with this PDMA.
--*/
7) VOID
VideoPortSetDmaContext(
IN PVOID HwDeviceExtension,
OUT PDMA pDma,
IN PVOID InstanceContext
);
/*++
Routine Description:
Arguments:
HwDeviceExtension - a pointer to the miniport HW_DEVICE_EXTENSION.
pDma - the handle returned in the output buffer of the
VIDEO_REQUEST_PACKET after VideoPortLockPages() returns.
InstanceContext - any PVOID supplied by user.
Return Value:
NONE.
--*/
8) ULONG
VideoPortGetBytesUsed(
IN PVOID HwDeviceExtension,
IN PDMA pDma
);
/*++
Routine Description:
Arguments:
HwDeviceExtension - a pointer to the miniport HW_DEVICE_EXTENSION.
pDma - the handle returned in the output buffer of the
VIDEO_REQUEST_PACKET after VideoPortLockPages() returns.
Return Value:
The number of bytes used in the buffer associated with this PDMA
--*/
9) VOID
VideoPortSetBytesUsed(
IN PVOID HwDeviceExtension,
IN OUT PDMA pDma,
IN ULONG BytesUsed
);
/*++
Routine Description:
Arguments:
HwDeviceExtension - a pointer to the miniport HW_DEVICE_EXTENSION.
pDma - the handle returned in the output buffer of the
VIDEO_REQUEST_PACKET after VideoPortLockPages() returns.
BytesUsed - The number of bytes written to the buffer.
Return Value:
NONE.
--*/
///////////////////////////////////////////////////////////////////
Display driver to miniport interface (IOCTL interface).
///////////////////////////////////////////////////////////////////
1) Miniport IOCTL routine.
The design attemps to optimize DMA transfer from a fixed piece of virtual
memory. These IOCTLs are to be defined by the miniport and the following
descriptions of the IOCTLs are only suggestions.
IOCTL_VIDEO_DMA_INIT - set by DispDrvr
Causes VideoPortLockPages() to be called by the miniport where the DMA_FLAGS is
set to VideoPortDmaInitOnly. This should only be done in graphics dedicated
contexts, where disk and network IO is secondary to video IO. Driver writers
must be aware that system performance (e.g. WinBench) can be damaged by leaving
memory locked.
IOCTL_VIDEO_DMA_TRANSFER_KEEP_LOCKED - set by DispDrvr
The miniport may set up this IOCTL such that VideoPortDoDma() is called with the
VideoPortKeepPagesLocked DMA_FLAGS argument. This scenario is oriented to graphics
dedicated applications. It optimizes throughput so that the HwStartDma() routine
in the miniport is called. In this way, DMA operations are optimized such that
memory resources to other parts of the system are constrained.
The scatter gather list available to HwStartDma() is valid:
a) before the routine HwStartDma() returns.
b) if HwStartDma() returns asynchronously, the list may be valid
if the system memory manager is not stressed. If the PEVENT
which is the fourth argument to HwStartDma() is set when the DMA is
done, it will remain valid until then. If the PEVENT is not set,
the list may become invalid at random times. This PEVENT must
be set by :
VideoPortSetEvent(HwDeviceExtension, PEVENT);
c) if HwStartDma returns synchronously and the system memory
manager is not stressed, the list will remain valid. if HwStartDma()
return synchronously and the memory manager is stressed, the list
will become invalid after HwStartDma returns.
IOCTL_VIDEO_DMA_TRANSFER_ONCE - set by DispDrvr
The miniport may set up this IOCTL so that VideoPortLockPages() is called with
the VideoPortUnlockAfterDma DMA_FLAGS. This allows the buffer to be locked down,
the HwStartDma miniport routine to be called back and the memory to be unlocked
after the dma transfer has completed. This IOCTL is tuned for systems in which
disk and network IO is very important and memory may be at a premium.
The same remarks apply to the scatter gather list as for
IOCTL_VIDEO_TRANSFER_KEEP_LOCKED.
IOCTL_VIDEO_DMA_UNLOCK_PAGES - set by DispDrvr
The miniport should simply call VideoPortUnlockPages() with the appropriate
PDMA.
Locking memory
The video port must lock down the memory in order to perform dma. The
amount of memory locked down is restricted by three things:
1) Maximal number of physical page breaks supported by the driver.
This is strictly a function of the dma hardware.
2) The number of map registers the system has available at
initialization. This is usually not bounded for busmaster devices,
except by that indicated by HalGetAdapter().
3) System performance contraints. In order to provide reasonable
throughput for other parts of the system, the amount of memory the
video port allows to be locked down is currently set as follows:
a) small systems (12-16 meg) 256k
b) medium systems (16-31 meg) 512k
c) large systems (>32 meg) 1M
These default values can be overridden by setting MaxDmaSize value under the
Devicexxx key in the CurrentControlSet in the registry. Again, drivers which
leave more than these amounts of memory locked down can severely impact system
performance.
Examples:
from a miniport StartIo routine (note that the display driver formatting
is unique to the display driver):
case IOCTL_VIDEO_DMA_INIT:
{
//
// This IOCTL should only be used for buffers that may remained locked
// down for more than one dma transfer.
//
//
// Map display driver representation into video port. Display
// driver input buffer is of form
//
// typedef struct _DMA_CONTROL
// {
// void * pBitmap; // Pointer to memory
// // to be locked.
// ULONG ulSize; // size of memory to
// // be locked.
// PVOID pDma; // Dma handle [OUT].
// PVOID * pPhysAddr; // Location to put
// // Physaddr.
// PEVENT pDisplayEvent // PEVENT.
// PEVENT pMappedUserEvent// Mapped User Mode EVENT
// // handle.
// } DMA_CONTROL, *PDMA_CONTROL;
//
//
//
//
PDMA_CONTROL pDmaCtrl = (PDMA_CONTROL) RequestPacket->InputBuffer;
PUCHAR ptmp = pDmaCrtl->bitmap;
ULONG size = pDmaCrtl->size;
VideoDebugPrint(( 0,"\t InputBuffer:%x\n", ptmp));
VideoDebugPrint(( 0,"\t InputBufferLength:%x\n", size));
//
// Save the location to put physaddr.
//
busAddress = (PULONG)(pDmaCtrl->pPhysAddr);
RequestPacket->InputBuffer = ptmp;
RequestPacket->InputBufferLength = size;
if (RequestPacket->OutputBufferLength <
(RequestPacket->StatusBlock->Information =
sizeof(ULONG) )) {
VideoDebugPrint((0, "IOCTL_VIDEO_DMA_INIT error1\n"));
status = ERROR_INSUFFICIENT_BUFFER;
break;
}
if (!VideoPortLockPages(HwDeviceExtension,
RequestPacket,
pMappedUserEvent,
pDisplayEvent,
VideoPortDmaInitOnly))
{
RequestPacket->StatusBlock->Information = 0;
VideoDebugPrint((0, "IOCTL_VIDEO_DMA_INIT error2\n"));
status = ERROR_INSUFFICIENT_BUFFER;
}
else
{
//
// Have to extract Physical address from scatterlist via DMA context in
// OutputBuffer and put it back into OutputBuffer.
//
PVOID * ppDmaHandle = (PVOID *)(RequestPacket->OutputBuffer);
PVRB_SG pSG = GET_VIDEO_SCATTERGATHER((PULONG)ppDmaHandle);
ULONG physaddr;
pDmaCrtl->DmaHandle = *ppDmaHandle;
hwDeviceExtension->IoBufferSize = size;
hwDeviceExtension->IoBuffer = ptmp;
GET_VIDEO_PHYSICAL_ADDRESS(pSG, ptmp, ptmp, &size, physaddr);
*busAddress = physaddr;
status = NO_ERROR;
}
break;
}
case IOCTL_VIDEO_DMA_TRANSFER:
//
// This IOCTL is optimized so that the display driver can get a buffer locked,
// dmaed and unlocked in one IOCTL.
//
{
PDSP_DMA_ARGS pDSPDmaArgs = (PDSP_DMA_ARGS)RequestPacket->InputBuffer;
PDSP_DMA pDSPDma = pDSPDmaArgs->pDmaControl;
PUCHAR ptmp = pDSPDma ->bitmap;
ULONG size = pDSPDma ->size;
if (RequestPacket->InputBufferLength < sizeof(DSP_DMA_ARGS)) {
VideoDebugPrint(( 2,"\n Insufficient Buffer" ));
status = ERROR_INSUFFICIENT_BUFFER;
break;
}
RequestPacket->InputBuffer = ptmp;
RequestPacket->InputBufferLength = size;
ASSERT(pDSPDma);
if (RequestPacket->OutputBufferLength <
(RequestPacket->StatusBlock->Information =
sizeof(ULONG) )) {
VideoDebugPrint((0, "IOCTL_VIDEO_DMA_INIT error1\n"));
status = ERROR_INSUFFICIENT_BUFFER;
break;
}
hwDeviceExtension->pDSPDmaArgs = pDSPDmaArgs;
if (!VideoPortLockPages(HwDeviceExtension,
RequestPacket,
pDSPDma->pMappedUserEvent,
pDSPDma->pDisplayEvent,
VideoPortUnlockAfterDma))
{
RequestPacket->StatusBlock->Information = 0;
VideoDebugPrint((0, "IOCTL_VIDEO_DMA_TRANSFER error\n"));
status = ERROR_INSUFFICIENT_BUFFER;
}
else
status = NO_ERROR;
break;
}
case IOCTL_VIDEO_DMA_UNLOCK:
{
//
// Private cleanup code. The memory has already been unlocked.
// The InputBuffer contains the Dma Handle.
//
PDMA pDma = *(PDMA*) (RequestPacket->InputBuffer);
VideoPortUnlockPages(HwDeviceExtension, pDma);
break;
}
2) Display driver code.
DISPDBG((1, "Target, DmaControl.pBitmap:%x, DmaControl.ulSize:%x\n", DmaControl.pBitmap, DmaControl.ulSize));
//
// Ask the miniport to lock the pages needed for this DMA, do the dma and unlock them.
//
DSPDmaArgs.pDma = (PVOID)DmaHandle;
DSPDmaArgs.DmaBase = ptrgBase;
DSPDmaArgs.WidthTrg = widthTrg;
DSPDmaArgs.WidthSrc = widthSrc;
DSPDmaArgs.width__ = width * DSPSRCPIXELBYTES;
DSPDmaArgs.Height = height;
DSPDmaArgs.Offset = ulOffset;
DSPDmaArgs.pPhysAddr = pPCIAddress;
DSPDmaArgs.bitdepth = DSPSRCPIXELBITS;
DSPDmaArgs.HS = TRUE;
DSPDmaArgs.pDmaControl = &DmaControl;
if (EngDeviceIoControl(ppdev->hDriver,
IOCTL_VIDEO_DMA_TRANSFER,
&DSPDmaArgs,
sizeof(DSPDmaArgs),
&(DSPDmaArgs.pDma),
sizeof(DSPDmaArgs.pDma),
&returnedDataLength))
{
DISPDBG((0, "DSP.DLL!MSDMA - EngDeviceIoControl IOCTL_VIDEO_DMA_TRANSFER Error!!!\n"));
DISPDBG((0, "DSP.DLL!vBitbltHSDMA - Exit\n"));
return;
}
The requirements for these interfaces include:
1) In the PVIDEO_HW_INITIALIZATION_DATA, the following fields need to be filled:
PVIDEO_HW_START_DMA HwStartDma - a pointer to a function which can be
called when page locking is complete. This function returns
Dma_Async_Return if it returns before the transfer is complete or
Dma_Sync_Return if it returns after the transfer is complete.
These return values are typedefed in video.h. This function takes
as arguments:
a) pHwDeviceExtension - a pointer to a miniport device
extension.
b) a dma handle returned from a IOCTL_VIDEO_DMA_INIT or
IOCTL_VIDEO_DMA_TRANSFER call.
c) a PEVENT which is the user Event mapped into kernel mode.
(NULL if pMappedUserEvent is NULL).
d) pDisplayEvent - a PEVENT, intended to be created and
waited on by the DisplayDriver and set by the miniport.
e) another PEVENT which must be set if the routine returns
Dma_Async_Return when the DMA completes. Failure to set
this PEVENT may invalidate the scatter gather lists at
any time. It must be set by:
VideoPortSetEvent(HwDeviceExtension, pVPEvent);
Also, HwDeviceExtension should make a copy of the elements of the
irp it intends to use or pass on, as the irp will be completed when
HwStartDma returns, and hence it's fields will be invalidated.
2) In the PORT_CONFIG_INFO, the following fields need to be
filled:
ULONG DmaChannel - a value indicating if the device supports
DMA.
ULONG DmaPort - a value indicating if the device supports
microchannel DMA.
ULONG NumberOfPhysicalBreaks - a value indicating the
maximal number of physical breaks the device supports.
DMA_WIDTH DmaWidth - a value indicating the width of the dma
device.
DMA_SPEED DmaSpeed - a value indicating the specified transfer
speed.
BOOLEAN DemandMode - a BOOLEAN indicating that the device can
be programmed for demand mode rather than single cycle operations.
BOOLEAN bMapBuffers- a BOOLEAN indicating if an adapter
requires that the data buffers be mapped into virtual address space.
BOOLEAN NeedPhysicalAddresses - a BOOLEAN indicating that the
driver will need to translate virtual to physical addresses.
BOOLEAN ScatterGather - a BOOLEAN indicating that the
driver will support scatter gather.
BOOLEAN Master - a BOOLEAN indicating that the adapter
is a bus master. Again, currently required to be TRUE.
ULONG MaximumScatterGatherChunkSize - the largest contiguous
piece of memory the dma controller can handle. This value is zero
if and only if the size is unbounded.