/**
|
* @file Gop.c
|
* @brief UEFI GOP protocol API implementation for USB DisplayLink driver.
|
*
|
* Copyright (c) 2018-2019, DisplayLink (UK) Ltd. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include "UsbDisplayLink.h"
|
#include "Edid.h"
|
|
|
/**
|
*
|
* @param This Pointer to the instance of the GOP protocol
|
* @param BltOperation
|
* @param SourceX
|
* @param SourceY
|
* @param Width
|
* @param Height
|
* @param DestinationX
|
* @param DestinationY
|
* @return
|
*/
|
STATIC EFI_STATUS
|
CheckBounds (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL* This,
|
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
IN UINTN SourceX,
|
IN UINTN SourceY,
|
IN UINTN Width,
|
IN UINTN Height,
|
IN UINTN DestinationX,
|
IN UINTN DestinationY
|
)
|
{
|
USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
|
EFI_GRAPHICS_OUTPUT_PROTOCOL* Gop;
|
|
UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(This);
|
Gop = &UsbDisplayLinkDev->GraphicsOutputProtocol;
|
|
CONST EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* ScreenMode = Gop->Mode->Info;
|
|
if (BltOperation == EfiBltVideoToBltBuffer || BltOperation == EfiBltVideoToVideo) {
|
if (SourceY + Height > ScreenMode->VerticalResolution) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (SourceX + Width > ScreenMode->HorizontalResolution) {
|
return EFI_INVALID_PARAMETER;
|
}
|
}
|
|
if (BltOperation == EfiBltBufferToVideo
|
|| BltOperation == EfiBltVideoToVideo
|
|| BltOperation == EfiBltVideoFill) {
|
if (DestinationY + Height > ScreenMode->VerticalResolution) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (DestinationX + Width > ScreenMode->HorizontalResolution) {
|
return EFI_INVALID_PARAMETER;
|
}
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
* Update the local copy of the Frame Buffer. This local copy is periodically transmitted to the
|
* DisplayLink device (via DlGopSendScreenUpdate)
|
* @param UsbDisplayLinkDev
|
* @param BltBuffer
|
* @param BltOperation
|
* @param SourceX
|
* @param SourceY
|
* @param DestinationX
|
* @param DestinationY
|
* @param Width
|
* @param Height
|
* @param BltBufferStride
|
* @param PixelsPerScanLine
|
*/
|
STATIC VOID
|
BuildBackBuffer (
|
IN USB_DISPLAYLINK_DEV *UsbDisplayLinkDev,
|
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
|
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
IN UINTN SourceX,
|
IN UINTN SourceY,
|
IN UINTN DestinationX,
|
IN UINTN DestinationY,
|
IN UINTN Width,
|
IN UINTN Height,
|
IN UINTN BltBufferStride,
|
IN UINTN PixelsPerScanLine
|
)
|
{
|
UINTN H;
|
UINTN W;
|
switch (BltOperation) {
|
case EfiBltVideoToBltBuffer:
|
{
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Blt;
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcB;
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)((UINT8 *)BltBuffer + (DestinationY * BltBufferStride) + DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
SrcB = UsbDisplayLinkDev->Screen + SourceY * PixelsPerScanLine + SourceX;
|
|
for (H = 0; H < Height; H++) {
|
for (W = 0; W < Width; W++) {
|
Blt[W] = *SrcB++;
|
}
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(((UINT8*)Blt) + BltBufferStride);
|
SrcB += PixelsPerScanLine - Width;
|
}
|
}
|
break;
|
|
case EfiBltBufferToVideo:
|
{
|
// Update the store of the area of the screen that is "dirty" - that we need to send in the next screen update.
|
if (DestinationY < UsbDisplayLinkDev->LastY1) {
|
UsbDisplayLinkDev->LastY1 = DestinationY;
|
}
|
if ((DestinationY + Height) > UsbDisplayLinkDev->LastY2) {
|
UsbDisplayLinkDev->LastY2 = DestinationY + Height;
|
}
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* Blt;
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)(((UINT8 *)BltBuffer) + (SourceY * BltBufferStride) + SourceX * sizeof *Blt);
|
DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
|
|
for (H = 0; H < Height; H++) {
|
for (W = 0; W < Width; W++) {
|
*DstB++ = Blt[W];
|
}
|
Blt = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)(((UINT8*)Blt) + BltBufferStride);
|
DstB += PixelsPerScanLine - Width;
|
}
|
}
|
break;
|
|
case EfiBltVideoToVideo:
|
{
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcB;
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
|
SrcB = UsbDisplayLinkDev->Screen + SourceY * PixelsPerScanLine + SourceX;
|
DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
|
|
for (H = 0; H < Height; H++) {
|
for (W = 0; W < Width; W++) {
|
*DstB++ = *SrcB++;
|
}
|
SrcB += PixelsPerScanLine - Width;
|
DstB += PixelsPerScanLine - Width;
|
}
|
}
|
break;
|
|
case EfiBltVideoFill:
|
{
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* DstB;
|
DstB = UsbDisplayLinkDev->Screen + DestinationY * PixelsPerScanLine + DestinationX;
|
for (H = 0; H < Height; H++) {
|
for (W = 0; W < Width; W++) {
|
*DstB++ = *BltBuffer;
|
}
|
DstB += PixelsPerScanLine - Width;
|
}
|
}
|
break;
|
default: break;
|
}
|
}
|
|
/**
|
* Display a colour bar pattern on the DisplayLink device.
|
* @param UsbDisplayLinkDev
|
* @param PatternNumber
|
* @return
|
*/
|
EFI_STATUS
|
DlGopSendTestPattern (
|
IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev,
|
IN UINTN PatternNumber)
|
{
|
EFI_STATUS Status;
|
UINTN DataLen;
|
UINT8 *DstBuf;
|
UINT32 USBStatus;
|
|
Status = EFI_SUCCESS;
|
DataLen = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 3; // Send 1 line @ 24 bits per pixel
|
DstBuf = AllocateZeroPool (DataLen);
|
|
if (DstBuf == NULL) {
|
DEBUG ((DEBUG_ERROR, "SendTestPattern Failed to allocate memory\n"));
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
//DEBUG ((DEBUG_ERROR, "Called DlGopSendTestPattern %d\n", PatternNumber));
|
|
CONST UINT8 RedPixel[3] = { 0xFF, 0x00, 0x00 };
|
CONST UINT8 GreenPixel[3] = { 0x00, 0xFF, 0x00 };
|
CONST UINT8 BluePixel[3] = { 0x00, 0x00, 0xFF };
|
CONST UINT8 YellowPixel[3] = { 0xFF, 0xFF, 0x00 };
|
CONST UINT8 MagentaPixel[3] = { 0xFF, 0x00, 0xFF };
|
CONST UINT8 CyanPixel[3] = { 0x00, 0xFF, 0xFF };
|
|
UINTN Row;
|
UINTN Column;
|
for (Row = 0; Row < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution; Row++) {
|
for (Column = 0; Column < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution; Column++) {
|
|
if (0 == PatternNumber) {
|
if (Row < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution / 3) {
|
CopyMem (&DstBuf[Column * 3], RedPixel, sizeof (RedPixel));
|
}
|
else if (Row < (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution * 2) / 3)
|
{
|
CopyMem (&DstBuf[Column * 3], GreenPixel, sizeof (GreenPixel));
|
}
|
else {
|
CopyMem (&DstBuf[Column * 3], BluePixel, sizeof (BluePixel));
|
}
|
}
|
else {
|
if (Column < UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution / 3) {
|
CopyMem (&DstBuf[Column * 3], YellowPixel, sizeof (RedPixel));
|
}
|
else if (Column < (UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 2) / 3)
|
{
|
CopyMem (&DstBuf[Column * 3], MagentaPixel, sizeof (GreenPixel));
|
}
|
else {
|
CopyMem (&DstBuf[Column * 3], CyanPixel, sizeof (BluePixel));
|
}
|
}
|
}
|
DlUsbBulkWrite (UsbDisplayLinkDev, DstBuf, DataLen, &USBStatus);
|
}
|
// Payload with length of 1 to terminate the frame
|
DlUsbBulkWrite (UsbDisplayLinkDev, DstBuf, 1, &USBStatus);
|
FreePool (DstBuf);
|
|
return Status;
|
}
|
|
|
/**
|
* Transfer the latest copy of the Blt buffer over USB to the DisplayLink device
|
* @param UsbDisplayLinkDev
|
* @return
|
*/
|
EFI_STATUS
|
DlGopSendScreenUpdate (
|
IN USB_DISPLAYLINK_DEV* UsbDisplayLinkDev
|
)
|
{
|
EFI_STATUS Status;
|
UINT32 USBStatus;
|
Status = EFI_SUCCESS;
|
|
// If it has been a while since we sent an update, send a full screen.
|
// This allows us to update a hot-plugged monitor quickly.
|
if (UsbDisplayLinkDev->TimeSinceLastScreenUpdate > DISPLAYLINK_FULL_SCREEN_UPDATE_PERIOD) {
|
UsbDisplayLinkDev->LastY1 = 0;
|
UsbDisplayLinkDev->LastY2 = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution - 1;
|
}
|
|
// If there has been no BLT since the last update/poll, drop out quietly.
|
if (UsbDisplayLinkDev->LastY2 < UsbDisplayLinkDev->LastY1) {
|
UsbDisplayLinkDev->TimeSinceLastScreenUpdate += (DISPLAYLINK_SCREEN_UPDATE_TIMER_PERIOD / 1000); // Convert us to ms
|
return EFI_SUCCESS;
|
}
|
|
UsbDisplayLinkDev->TimeSinceLastScreenUpdate = 0;
|
|
EFI_TPL OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
|
|
UINTN DataLen;
|
UINTN Width;
|
UINTN Height;
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL* SrcPtr;
|
UINT8* DstPtr;
|
UINT8 DstBuffer[1920 * 3]; // Get rid of the magic numbers at some point
|
// Could also use a buffer allocated at runtime to store the line, stored in the USB_DISPLAYLINK_DEV structure.
|
UINTN H;
|
|
DataLen = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution * 3; // Send 1 line @ 24 bits per pixel
|
Width = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->HorizontalResolution;
|
Height = UsbDisplayLinkDev->GraphicsOutputProtocol.Mode->Info->VerticalResolution;
|
SrcPtr = UsbDisplayLinkDev->Screen;
|
DstPtr = DstBuffer;
|
|
for (H = 0; H < Height; H++) {
|
DstPtr = DstBuffer;
|
|
UINTN W;
|
for (W = 0; W < Width; W++) {
|
// Need to swap round the RGB values
|
DstPtr[0] = ((UINT8 *)SrcPtr)[2];
|
DstPtr[1] = ((UINT8 *)SrcPtr)[1];
|
DstPtr[2] = ((UINT8 *)SrcPtr)[0];
|
SrcPtr++;
|
DstPtr += 3;
|
}
|
|
Status = DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, DataLen, &USBStatus);
|
|
// USBStatus values defined in usbio.h, e.g. EFI_USB_ERR_TIMEOUT 0x40
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Screen update - USB bulk transfer of pixel data failed. Line %d len %d, failure code %r USB status x%x\n", H, DataLen, Status, USBStatus));
|
break;
|
}
|
// Need an extra DlUsbBulkWrite if the data length is divisible by USB MaxPacketSize. This spare data will just get written into the (invisible) stride area.
|
// Note that the API doesn't let us do a bulk write of 0.
|
if ((DataLen & (UsbDisplayLinkDev->BulkOutEndpointDescriptor.MaxPacketSize - 1)) == 0) {
|
Status = DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, 2, &USBStatus);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Screen update - USB bulk transfer of pixel data failed. Line %d len %d, failure code %r USB status x%x\n", H, DataLen, Status, USBStatus));
|
break;
|
}
|
}
|
}
|
|
if (!EFI_ERROR (Status)) {
|
// If we've successfully transmitted the frame, reset the values that store which area of the screen has been BLTted to.
|
// If we haven't succeeded, this will mean we'll try to resend it after the next poll period.
|
UsbDisplayLinkDev->LastY2 = 0;
|
UsbDisplayLinkDev->LastY1 = (UINTN)-1;
|
}
|
|
// Payload with length of 1 to terminate the frame
|
// We need to do this even if we had an error, to indicate to the DL device that it should now expect a new frame.
|
DlUsbBulkWrite (UsbDisplayLinkDev, DstBuffer, 1, &USBStatus);
|
|
gBS->RestoreTPL (OriginalTPL);
|
|
return Status;
|
}
|
|
/**
|
* Calculate the video refresh rate from the video timing parameters (pixel clock etc)
|
* @param videoMode
|
* @return
|
*/
|
#ifndef MDEPKG_NDEBUG
|
STATIC inline UINT16
|
CalculateRefreshRate (
|
IN CONST struct VideoMode *VideoMode)
|
{
|
UINT16 RefreshRate;
|
UINT16 Rmod;
|
UINT16 Rate10Hz;
|
|
RefreshRate = (VideoMode->PixelClock * 10000) / ((VideoMode->HActive + VideoMode->HBlanking) * (VideoMode->VActive + VideoMode->VBlanking));
|
Rmod = RefreshRate % 10;
|
Rate10Hz = RefreshRate - Rmod;
|
|
if (Rmod >= 5) {
|
Rate10Hz += 10;
|
}
|
return Rate10Hz;
|
}
|
#endif // MDEPKG_NDEBUG
|
/* ***************************************************************************************************** */
|
/* ***************************************************************************************************** */
|
/* ****************** START OF FUNCTIONS WHICH IMPLEMENT GOP INTERFACE ****************** */
|
/* ***************************************************************************************************** */
|
/* ***************************************************************************************************** */
|
|
|
/**
|
*
|
* @param Gop Pointer to the instance of the GOP protocol
|
* @param ModeNumber
|
* @param SizeOfInfo
|
* @param Info
|
* @return
|
*/
|
EFI_STATUS
|
EFIAPI
|
DisplayLinkQueryMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
|
IN UINT32 ModeNumber,
|
OUT UINTN *SizeOfInfo,
|
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
|
)
|
{
|
USB_DISPLAYLINK_DEV *Dev;
|
CONST struct VideoMode *VideoMode;
|
EFI_STATUS Status;
|
|
Dev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(Gop);
|
Status = EFI_INVALID_PARAMETER;
|
|
if ((SizeOfInfo == NULL) || (Info == NULL)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
// Get a video mode from the EDID
|
Status = DlEdidGetSupportedVideoModeWithFallback (ModeNumber, Dev->EdidActive.Edid, Dev->EdidActive.SizeOfEdid, &VideoMode);
|
|
if (!EFI_ERROR (Status)) {
|
|
*Info = (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION*)AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
|
if (*Info == NULL) {
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
DEBUG ((DEBUG_INFO, "BIOS querying mode number %d - returning %dx%d @ %dHz\n", ModeNumber, VideoMode->HActive, VideoMode->VActive, CalculateRefreshRate (VideoMode)));
|
|
*SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
|
(*Info)->Version = 0;
|
(*Info)->HorizontalResolution = VideoMode->HActive;
|
(*Info)->VerticalResolution = VideoMode->VActive;
|
(*Info)->PixelFormat = PixelBltOnly;
|
(*Info)->PixelsPerScanLine = (*Info)->HorizontalResolution;
|
(*Info)->PixelInformation.RedMask = 0;
|
(*Info)->PixelInformation.GreenMask = 0;
|
(*Info)->PixelInformation.BlueMask = 0;
|
(*Info)->PixelInformation.ReservedMask = 0;
|
}
|
return Status;
|
}
|
|
/**
|
*
|
* @param Gop Pointer to the instance of the GOP protocol
|
* @param ModeNumber
|
* @return
|
*/
|
EFI_STATUS
|
EFIAPI
|
DisplayLinkSetMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop,
|
IN UINT32 ModeNumber
|
)
|
{
|
USB_DISPLAYLINK_DEV *UsbDisplayLinkDev;
|
EFI_STATUS Status;
|
CONST struct VideoMode *VideoMode;
|
|
UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(Gop);
|
|
// Prevent the DisplayLinkPeriodicTimer from interrupting us (bug 28877).
|
// When the driver is manually loaded, the TPL is TPL_NOTIFY (16) which prevents the interrupt from the timer.
|
// When the GOP driver is sideloaded, the TPL of this call is TPL_APPLICATION (4) and the timer can interrupt us.
|
Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
|
|
// Get a video mode from the EDID
|
Status = DlEdidGetSupportedVideoModeWithFallback (ModeNumber, UsbDisplayLinkDev->EdidActive.Edid, UsbDisplayLinkDev->EdidActive.SizeOfEdid, &VideoMode);
|
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Gop->Mode->Info->Version = 0;
|
Gop->Mode->Info->HorizontalResolution = VideoMode->HActive;
|
Gop->Mode->Info->VerticalResolution = VideoMode->VActive;
|
Gop->Mode->Info->PixelFormat = PixelBltOnly;
|
Gop->Mode->Info->PixelsPerScanLine = Gop->Mode->Info->HorizontalResolution;
|
Gop->Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
Gop->Mode->FrameBufferBase = (EFI_PHYSICAL_ADDRESS)(UINTN)NULL;
|
Gop->Mode->FrameBufferSize = 0;
|
|
//
|
// Allocate the back buffer
|
//
|
if (UsbDisplayLinkDev->Screen != NULL) {
|
FreePool (UsbDisplayLinkDev->Screen);
|
}
|
|
UsbDisplayLinkDev->Screen = (EFI_GRAPHICS_OUTPUT_BLT_PIXEL*)AllocateZeroPool (
|
Gop->Mode->Info->HorizontalResolution *
|
Gop->Mode->Info->VerticalResolution *
|
sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
|
if (UsbDisplayLinkDev->Screen == NULL) {
|
return EFI_OUT_OF_RESOURCES;
|
}
|
|
DEBUG ((DEBUG_INFO, "Video mode %d selected by BIOS - %d x %d.\n", ModeNumber, VideoMode->HActive, VideoMode->VActive));
|
// Wait until we are sure that we can set the video mode before we tell the firmware
|
Status = DlUsbSendControlWriteMessage (UsbDisplayLinkDev, SET_VIDEO_MODE, 0, VideoMode, sizeof (struct VideoMode));
|
|
if (Status != EFI_SUCCESS) {
|
// Flag up that we haven't set the video mode correctly yet.
|
DEBUG ((DEBUG_ERROR, "Failed to send USB message to DisplayLink device to set monitor video mode. Monitor connected correctly?\n"));
|
Gop->Mode->Mode = GRAPHICS_OUTPUT_INVALID_MODE_NUMBER;
|
FreePool (UsbDisplayLinkDev->Screen);
|
UsbDisplayLinkDev->Screen = NULL;
|
} else {
|
BuildBackBuffer (
|
UsbDisplayLinkDev,
|
UsbDisplayLinkDev->Screen,
|
EfiBltBufferToVideo,
|
0, 0,
|
0, 0,
|
Gop->Mode->Info->HorizontalResolution,
|
Gop->Mode->Info->VerticalResolution,
|
Gop->Mode->Info->HorizontalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
|
Gop->Mode->Info->HorizontalResolution);
|
// unlock the DisplayLinkPeriodicTimer
|
Gop->Mode->Mode = ModeNumber;
|
}
|
|
return Status;
|
}
|
|
/**
|
* Implementation of the GOP protocol Blt API function
|
* @param This Pointer to the instance of the GOP protocol
|
* @param BltBuffer
|
* @param BltOperation
|
* @param SourceX
|
* @param SourceY
|
* @param DestinationX
|
* @param DestinationY
|
* @param Width
|
* @param Height
|
* @param Delta
|
* @return
|
*/
|
EFI_STATUS
|
EFIAPI
|
DisplayLinkBlt (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer, OPTIONAL
|
IN EFI_GRAPHICS_OUTPUT_BLT_OPERATION BltOperation,
|
IN UINTN SourceX,
|
IN UINTN SourceY,
|
IN UINTN DestinationX,
|
IN UINTN DestinationY,
|
IN UINTN Width,
|
IN UINTN Height,
|
IN UINTN Delta OPTIONAL
|
)
|
{
|
USB_DISPLAYLINK_DEV* UsbDisplayLinkDev;
|
UsbDisplayLinkDev = USB_DISPLAYLINK_DEV_FROM_GRAPHICS_OUTPUT_PROTOCOL(This);
|
|
// Drop out if we haven't set the video mode up yet
|
if (This->Mode->Mode == GRAPHICS_OUTPUT_INVALID_MODE_NUMBER) {
|
return EFI_SUCCESS;
|
}
|
|
if ((BltOperation < 0) || (BltOperation >= EfiGraphicsOutputBltOperationMax)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (Width == 0 || Height == 0) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
// Lock so we make an atomic write the frame buffer.
|
// We would not want a timer based event (Cursor, ...) to come in while we are doing this operation.
|
EFI_TPL OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);
|
|
CONST UINTN BltBufferStride = (Delta == 0) ? Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) : Delta;
|
CONST EFI_STATUS boundsCheckStatus = CheckBounds (This, BltOperation, SourceX, SourceY, Width, Height, DestinationX, DestinationY);
|
if (EFI_ERROR (boundsCheckStatus)) {
|
gBS->RestoreTPL (OriginalTPL);
|
return boundsCheckStatus;
|
}
|
|
BuildBackBuffer (UsbDisplayLinkDev, BltBuffer, BltOperation, SourceX, SourceY, DestinationX, DestinationY, Width, Height, BltBufferStride, This->Mode->Info->PixelsPerScanLine);
|
|
gBS->RestoreTPL (OriginalTPL);
|
return EFI_SUCCESS;
|
}
|
|