/** @file
|
*
|
* Copyright (c) 2020, ARM Limited. All rights reserved.
|
* Copyright (c) 2017-2018, Andrei Warkentin <andrey.warkentin@gmail.com>
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
*
|
* SPDX-License-Identifier: BSD-2-Clause-Patent
|
*
|
**/
|
|
#include <Base.h>
|
#include "DisplayDxe.h"
|
|
#define MODE_800_ENABLED BIT0
|
#define MODE_640_ENABLED BIT1
|
#define MODE_1024_ENABLED BIT2
|
#define MODE_720P_ENABLED BIT3
|
#define MODE_1080P_ENABLED BIT4
|
#define MODE_NATIVE_ENABLED BIT5
|
#define JUST_NATIVE_ENABLED MODE_NATIVE_ENABLED
|
#define ALL_MODES (BIT6 - 1)
|
#define POS_TO_FB(posX, posY) ((UINT8*) \
|
((UINTN)This->Mode->FrameBufferBase + \
|
(posY) * This->Mode->Info->PixelsPerScanLine * \
|
PI3_BYTES_PER_PIXEL + \
|
(posX) * PI3_BYTES_PER_PIXEL))
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverSupported (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
);
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverStart (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
);
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverStop (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN UINTN NumberOfChildren,
|
IN EFI_HANDLE *ChildHandleBuffer
|
);
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplayQueryMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
IN UINT32 ModeNumber,
|
OUT UINTN *SizeOfInfo,
|
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
|
);
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplaySetMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
IN UINT32 ModeNumber
|
);
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplayBlt (
|
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
|
);
|
|
EFI_DRIVER_BINDING_PROTOCOL mDriverBinding = {
|
DriverSupported,
|
DriverStart,
|
DriverStop,
|
0xa,
|
NULL,
|
NULL
|
};
|
|
typedef struct {
|
VENDOR_DEVICE_PATH DisplayDevicePath;
|
EFI_DEVICE_PATH EndDevicePath;
|
} DISPLAY_DEVICE_PATH;
|
|
typedef struct {
|
UINT32 Width;
|
UINT32 Height;
|
} GOP_MODE_DATA;
|
|
STATIC UINT32 mBootWidth;
|
STATIC UINT32 mBootHeight;
|
STATIC EFI_HANDLE mDevice;
|
STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
|
STATIC EFI_CPU_ARCH_PROTOCOL *mCpu;
|
|
STATIC UINTN mLastMode;
|
STATIC GOP_MODE_DATA mGopModeTemplate[] = {
|
{ 800, 600 }, /* Legacy */
|
{ 640, 480 }, /* Legacy */
|
{ 1024, 768 }, /* Legacy */
|
{ 1280, 720 }, /* 720p */
|
{ 1920, 1080 }, /* 1080p */
|
{ 0, 0 }, /* Physical */
|
};
|
|
STATIC UINTN mLastMode;
|
STATIC GOP_MODE_DATA mGopModeData[ARRAY_SIZE (mGopModeTemplate)];
|
|
STATIC DISPLAY_DEVICE_PATH mDisplayProtoDevicePath =
|
{
|
{
|
{
|
HARDWARE_DEVICE_PATH,
|
HW_VENDOR_DP,
|
{
|
(UINT8)(sizeof (VENDOR_DEVICE_PATH)),
|
(UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8),
|
}
|
},
|
EFI_CALLER_ID_GUID,
|
},
|
{
|
END_DEVICE_PATH_TYPE,
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
{
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
0
|
}
|
}
|
};
|
|
#define PI3_BITS_PER_PIXEL (32)
|
#define PI3_BYTES_PER_PIXEL (PI3_BITS_PER_PIXEL / 8)
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL gDisplayProto = {
|
DisplayQueryMode,
|
DisplaySetMode,
|
DisplayBlt,
|
NULL
|
};
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplayQueryMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
IN UINT32 ModeNumber,
|
OUT UINTN *SizeOfInfo,
|
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
|
)
|
{
|
EFI_STATUS Status;
|
GOP_MODE_DATA *Mode;
|
|
if (Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
Status = gBS->AllocatePool (
|
EfiBootServicesData,
|
sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
|
(VOID**)Info
|
);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Mode = &mGopModeData[ModeNumber];
|
|
*SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
(*Info)->Version = This->Mode->Info->Version;
|
(*Info)->HorizontalResolution = Mode->Width;
|
(*Info)->VerticalResolution = Mode->Height;
|
(*Info)->PixelFormat = This->Mode->Info->PixelFormat;
|
(*Info)->PixelInformation.RedMask = This->Mode->Info->PixelInformation.RedMask;
|
(*Info)->PixelInformation.GreenMask = This->Mode->Info->PixelInformation.GreenMask;
|
(*Info)->PixelInformation.BlueMask = This->Mode->Info->PixelInformation.BlueMask;
|
(*Info)->PixelInformation.ReservedMask = This->Mode->Info->PixelInformation.ReservedMask;
|
(*Info)->PixelsPerScanLine = Mode->Width;
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
VOID
|
ClearScreen (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This
|
)
|
{
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill;
|
|
Fill.Red = 0x00;
|
Fill.Green = 0x00;
|
Fill.Blue = 0x00;
|
This->Blt (This, &Fill, EfiBltVideoFill,
|
0, 0, 0, 0, This->Mode->Info->HorizontalResolution,
|
This->Mode->Info->VerticalResolution,
|
This->Mode->Info->HorizontalResolution *
|
sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
|
}
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplaySetMode (
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
IN UINT32 ModeNumber
|
)
|
{
|
UINTN FbSize;
|
UINTN FbPitch;
|
EFI_STATUS Status;
|
EFI_PHYSICAL_ADDRESS FbBase;
|
GOP_MODE_DATA *Mode = &mGopModeData[ModeNumber];
|
|
if (ModeNumber >= This->Mode->MaxMode) {
|
return EFI_UNSUPPORTED;
|
}
|
|
DEBUG ((DEBUG_INFO, "Setting mode %u from %u: %u x %u\n",
|
ModeNumber, This->Mode->Mode, Mode->Width, Mode->Height));
|
Status = mFwProtocol->GetFB (Mode->Width, Mode->Height,
|
PI3_BITS_PER_PIXEL, &FbBase,
|
&FbSize, &FbPitch);
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Could not set mode %u\n", ModeNumber));
|
return EFI_DEVICE_ERROR;
|
}
|
|
DEBUG ((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n",
|
ModeNumber, Mode->Width, Mode->Height, FbSize, FbBase));
|
|
if (FbPitch / PI3_BYTES_PER_PIXEL != Mode->Width) {
|
DEBUG ((DEBUG_ERROR, "Error: Expected width %u, got width %u\n",
|
Mode->Width, FbPitch / PI3_BYTES_PER_PIXEL));
|
return EFI_DEVICE_ERROR;
|
}
|
|
/*
|
* WT, because certain OS loaders access the frame buffer directly
|
* and we don't want to see corruption due to missing WB cache
|
* maintenance. Performance with WT is good.
|
*/
|
Status = mCpu->SetMemoryAttributes (mCpu, FbBase,
|
ALIGN_VALUE (FbSize, EFI_PAGE_SIZE),
|
EFI_MEMORY_WT);
|
if (Status != EFI_SUCCESS) {
|
DEBUG ((DEBUG_ERROR, "Couldn't set framebuffer attributes: %r\n", Status));
|
return Status;
|
}
|
|
This->Mode->Mode = ModeNumber;
|
This->Mode->Info->Version = 0;
|
This->Mode->Info->HorizontalResolution = Mode->Width;
|
This->Mode->Info->VerticalResolution = Mode->Height;
|
/*
|
* NOTE: Windows REQUIRES BGR in 32 or 24 bit format.
|
*/
|
This->Mode->Info->PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
|
This->Mode->Info->PixelsPerScanLine = Mode->Width;
|
This->Mode->SizeOfInfo = sizeof (*This->Mode->Info);
|
This->Mode->FrameBufferBase = FbBase;
|
This->Mode->FrameBufferSize = Mode->Width * Mode->Height * PI3_BYTES_PER_PIXEL;
|
DEBUG((DEBUG_INFO, "Reported Mode->FrameBufferSize is %u\n", This->Mode->FrameBufferSize));
|
|
ClearScreen (This);
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DisplayBlt (
|
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
|
)
|
{
|
UINT8 *VidBuf, *BltBuf, *VidBuf1;
|
UINTN i;
|
|
if ((UINTN)BltOperation >= EfiGraphicsOutputBltOperationMax) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (Width == 0 || Height == 0) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
switch (BltOperation) {
|
case EfiBltVideoFill:
|
BltBuf = (UINT8*)BltBuffer;
|
|
for (i = 0; i < Height; i++) {
|
VidBuf = POS_TO_FB (DestinationX, DestinationY + i);
|
|
SetMem32 (VidBuf, Width * PI3_BYTES_PER_PIXEL, *(UINT32*)BltBuf);
|
}
|
break;
|
|
case EfiBltVideoToBltBuffer:
|
if (Delta == 0) {
|
Delta = Width * PI3_BYTES_PER_PIXEL;
|
}
|
|
for (i = 0; i < Height; i++) {
|
VidBuf = POS_TO_FB (SourceX, SourceY + i);
|
|
BltBuf = (UINT8*)((UINTN)BltBuffer + (DestinationY + i) * Delta +
|
DestinationX * PI3_BYTES_PER_PIXEL);
|
|
gBS->CopyMem ((VOID*)BltBuf, (VOID*)VidBuf, PI3_BYTES_PER_PIXEL * Width);
|
}
|
break;
|
|
case EfiBltBufferToVideo:
|
if (Delta == 0) {
|
Delta = Width * PI3_BYTES_PER_PIXEL;
|
}
|
|
for (i = 0; i < Height; i++) {
|
VidBuf = POS_TO_FB (DestinationX, DestinationY + i);
|
BltBuf = (UINT8*)((UINTN)BltBuffer + (SourceY + i) * Delta +
|
SourceX * PI3_BYTES_PER_PIXEL);
|
|
gBS->CopyMem ((VOID*)VidBuf, (VOID*)BltBuf, Width * PI3_BYTES_PER_PIXEL);
|
}
|
break;
|
|
case EfiBltVideoToVideo:
|
for (i = 0; i < Height; i++) {
|
VidBuf = POS_TO_FB (SourceX, SourceY + i);
|
VidBuf1 = POS_TO_FB (DestinationX, DestinationY + i);
|
|
gBS->CopyMem ((VOID*)VidBuf1, (VOID*)VidBuf, Width * PI3_BYTES_PER_PIXEL);
|
}
|
break;
|
|
default:
|
return EFI_INVALID_PARAMETER;
|
break;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
Initialize the state information for the Display Dxe
|
|
@param ImageHandle of the loaded driver
|
@param SystemTable Pointer to the System Table
|
|
@retval EFI_SUCCESS Protocol registered
|
@retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure
|
@retval EFI_DEVICE_ERROR Hardware problems
|
|
**/
|
EFI_STATUS
|
EFIAPI
|
DisplayDxeInitialize (
|
IN EFI_HANDLE ImageHandle,
|
IN EFI_SYSTEM_TABLE *SystemTable
|
)
|
{
|
EFI_STATUS Status;
|
|
Status = gBS->LocateProtocol (&gRaspberryPiFirmwareProtocolGuid, NULL,
|
(VOID**)&mFwProtocol);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL,
|
(VOID**)&mCpu);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
// Query the current display resolution from mailbox
|
Status = mFwProtocol->GetFBSize (&mBootWidth, &mBootHeight);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
DEBUG ((DEBUG_INFO, "Display boot mode is %u x %u\n",
|
mBootWidth, mBootHeight));
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
&mDevice, &gEfiDevicePathProtocolGuid,
|
&mDisplayProtoDevicePath, &gEfiCallerIdGuid,
|
NULL, NULL);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
Status = EfiLibInstallDriverBindingComponentName2 (
|
ImageHandle,
|
SystemTable,
|
&mDriverBinding,
|
ImageHandle,
|
&gComponentName,
|
&gComponentName2
|
);
|
ASSERT_EFI_ERROR (Status);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
return Status;
|
}
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverSupported (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
)
|
{
|
VOID *Temp;
|
|
if (Controller != mDevice) {
|
return EFI_UNSUPPORTED;
|
}
|
|
if (gBS->HandleProtocol (Controller, &gEfiGraphicsOutputProtocolGuid,
|
(VOID**)&Temp) == EFI_SUCCESS) {
|
return EFI_ALREADY_STARTED;
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverStart (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
)
|
{
|
UINTN Index;
|
UINTN TempIndex;
|
EFI_STATUS Status;
|
VOID *Dummy;
|
|
Status = gBS->OpenProtocol (
|
Controller,
|
&gEfiCallerIdGuid,
|
(VOID**)&Dummy,
|
This->DriverBindingHandle,
|
Controller,
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
gDisplayProto.Mode = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE));
|
if (gDisplayProto.Mode == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Done;
|
}
|
|
gDisplayProto.Mode->Info = AllocateZeroPool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
|
if (gDisplayProto.Mode->Info == NULL) {
|
Status = EFI_OUT_OF_RESOURCES;
|
goto Done;
|
}
|
|
Status = PcdSet8S (PcdDisplayEnableScaledVModes,
|
PcdGet8 (PcdDisplayEnableScaledVModes) & ALL_MODES);
|
ASSERT_EFI_ERROR (Status);
|
|
if (PcdGet8 (PcdDisplayEnableScaledVModes) == 0) {
|
Status = PcdSet8S (PcdDisplayEnableScaledVModes, JUST_NATIVE_ENABLED);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
mLastMode = 0;
|
for (TempIndex = 0, Index = 0;
|
TempIndex < ARRAY_SIZE (mGopModeTemplate); TempIndex++) {
|
if ((PcdGet8 (PcdDisplayEnableScaledVModes) & (1 << TempIndex)) != 0) {
|
DEBUG ((DEBUG_ERROR, "Mode %u: %u x %u present\n",
|
TempIndex, mGopModeTemplate[TempIndex].Width,
|
mGopModeTemplate[TempIndex].Height));
|
|
CopyMem (&mGopModeData[Index], &mGopModeTemplate[TempIndex],
|
sizeof (GOP_MODE_DATA));
|
mLastMode = Index;
|
Index++;
|
}
|
}
|
|
if (PcdGet8 (PcdDisplayEnableScaledVModes) == JUST_NATIVE_ENABLED) {
|
/*
|
* mBootWidth x mBootHeight may not be sensible,
|
* so clean it up, since we won't be adding
|
* any other extra vmodes.
|
*/
|
if (mBootWidth < 640 || mBootHeight < 480) {
|
mBootWidth = 640;
|
mBootHeight = 480;
|
} else if (mBootWidth == 800 && mBootHeight == 480) {
|
/* The Pi 7" screen is close to 800x600, just pretend it is. */
|
mBootHeight = 600;
|
}
|
}
|
|
if ((PcdGet8(PcdDisplayEnableScaledVModes) & MODE_NATIVE_ENABLED) != 0) {
|
/*
|
* Adjust actual native res only if native res is enabled
|
* (so last mode is native res).
|
*/
|
mGopModeData[mLastMode].Width = mBootWidth;
|
mGopModeData[mLastMode].Height = mBootHeight;
|
}
|
|
for (Index = 0; Index <= mLastMode; Index++) {
|
UINTN FbSize;
|
UINTN FbPitch;
|
EFI_PHYSICAL_ADDRESS FbBase;
|
|
GOP_MODE_DATA *Mode = &mGopModeData[Index];
|
|
Status = mFwProtocol->GetFB (Mode->Width, Mode->Height,
|
PI3_BITS_PER_PIXEL, &FbBase,
|
&FbSize, &FbPitch);
|
if (EFI_ERROR (Status)) {
|
goto Done;
|
}
|
|
//
|
// There is no way to communicate pitch back to OS. OS and even UEFI
|
// expect a fully linear frame buffer. So the width should
|
// be based on the frame buffer's pitch value. In some cases VC
|
// firmware would allocate ao frame buffer with some padding
|
// presumably to be 8 byte align.
|
//
|
Mode->Width = FbPitch / PI3_BYTES_PER_PIXEL;
|
|
DEBUG ((DEBUG_INFO, "Mode %u: %u x %u framebuffer is %u bytes at %p\n",
|
Index, Mode->Width, Mode->Height, FbSize, FbBase));
|
|
ASSERT (FbPitch != 0);
|
ASSERT (FbBase != 0);
|
ASSERT (FbSize != 0);
|
}
|
|
// Both set the mode and initialize current mode information.
|
gDisplayProto.Mode->MaxMode = mLastMode + 1;
|
DisplaySetMode (&gDisplayProto, 0);
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
&Controller, &gEfiGraphicsOutputProtocolGuid,
|
&gDisplayProto, NULL);
|
if (EFI_ERROR (Status)) {
|
goto Done;
|
}
|
|
if (PcdGet32 (PcdDisplayEnableSShot)) {
|
RegisterScreenshotHandlers ();
|
} else {
|
DEBUG ((DEBUG_INFO, "Screenshot capture disabled\n"));
|
}
|
|
Done:
|
if (EFI_ERROR (Status)) {
|
DEBUG ((DEBUG_ERROR, "Could not start DisplayDxe: %r\n", Status));
|
if (gDisplayProto.Mode->Info != NULL) {
|
FreePool (gDisplayProto.Mode->Info);
|
gDisplayProto.Mode->Info = NULL;
|
}
|
|
if (gDisplayProto.Mode != NULL) {
|
FreePool (gDisplayProto.Mode);
|
gDisplayProto.Mode = NULL;
|
}
|
|
gBS->CloseProtocol (
|
Controller,
|
&gEfiCallerIdGuid,
|
This->DriverBindingHandle,
|
Controller
|
);
|
}
|
return Status;
|
}
|
|
STATIC
|
EFI_STATUS
|
EFIAPI
|
DriverStop (
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
IN EFI_HANDLE Controller,
|
IN UINTN NumberOfChildren,
|
IN EFI_HANDLE *ChildHandleBuffer
|
)
|
{
|
EFI_STATUS Status;
|
|
ClearScreen (&gDisplayProto);
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
Controller, &gEfiGraphicsOutputProtocolGuid,
|
&gDisplayProto, NULL);
|
if (EFI_ERROR (Status)) {
|
return Status;
|
}
|
|
FreePool (gDisplayProto.Mode->Info);
|
gDisplayProto.Mode->Info = NULL;
|
FreePool (gDisplayProto.Mode);
|
gDisplayProto.Mode = NULL;
|
|
gBS->CloseProtocol (
|
Controller,
|
&gEfiCallerIdGuid,
|
This->DriverBindingHandle,
|
Controller
|
);
|
|
return Status;
|
}
|