mirror of https://github.com/acidanthera/audk.git
397 lines
10 KiB
C
397 lines
10 KiB
C
/** @file
|
|
This file implements the Graphics Output protocol for Arm platforms
|
|
|
|
Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include <PiDxe.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
#include <Guid/GlobalVariable.h>
|
|
|
|
#include "LcdGraphicsOutputDxe.h"
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
BOOLEAN mDisplayInitialized = FALSE;
|
|
|
|
LCD_INSTANCE mLcdTemplate = {
|
|
LCD_INSTANCE_SIGNATURE,
|
|
NULL, // Handle
|
|
{ // ModeInfo
|
|
0, // Version
|
|
0, // HorizontalResolution
|
|
0, // VerticalResolution
|
|
PixelBltOnly, // PixelFormat
|
|
{ 0 }, // PixelInformation
|
|
0, // PixelsPerScanLine
|
|
},
|
|
{
|
|
0, // MaxMode;
|
|
0, // Mode;
|
|
NULL, // Info;
|
|
0, // SizeOfInfo;
|
|
0, // FrameBufferBase;
|
|
0 // FrameBufferSize;
|
|
},
|
|
{ // Gop
|
|
LcdGraphicsQueryMode, // QueryMode
|
|
LcdGraphicsSetMode, // SetMode
|
|
LcdGraphicsBlt, // Blt
|
|
NULL // *Mode
|
|
},
|
|
{ // DevicePath
|
|
{
|
|
{
|
|
HARDWARE_DEVICE_PATH, HW_VENDOR_DP,
|
|
{
|
|
(UINT8)(sizeof (VENDOR_DEVICE_PATH)),
|
|
(UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
|
|
},
|
|
},
|
|
// Hardware Device Path for Lcd
|
|
EFI_CALLER_ID_GUID // Use the driver's GUID
|
|
},
|
|
|
|
{
|
|
END_DEVICE_PATH_TYPE,
|
|
END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
{
|
|
sizeof (EFI_DEVICE_PATH_PROTOCOL),
|
|
0
|
|
}
|
|
}
|
|
},
|
|
(EFI_EVENT)NULL // ExitBootServicesEvent
|
|
};
|
|
|
|
EFI_STATUS
|
|
LcdInstanceContructor (
|
|
OUT LCD_INSTANCE **NewInstance
|
|
)
|
|
{
|
|
LCD_INSTANCE *Instance;
|
|
|
|
Instance = AllocateCopyPool (sizeof (LCD_INSTANCE), &mLcdTemplate);
|
|
if (Instance == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Instance->Gop.Mode = &Instance->Mode;
|
|
Instance->Gop.Mode->MaxMode = LcdPlatformGetMaxMode ();
|
|
Instance->Mode.Info = &Instance->ModeInfo;
|
|
|
|
*NewInstance = Instance;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Function Definitions
|
|
//
|
|
|
|
EFI_STATUS
|
|
InitializeDisplay (
|
|
IN LCD_INSTANCE *Instance
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PHYSICAL_ADDRESS VramBaseAddress;
|
|
UINTN VramSize;
|
|
|
|
Status = LcdPlatformGetVram (&VramBaseAddress, &VramSize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Setup the LCD
|
|
Status = LcdInitialize (VramBaseAddress);
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT_ERROR_LCD_SHUTDOWN;
|
|
}
|
|
|
|
Status = LcdPlatformInitializeDisplay (Instance->Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT_ERROR_LCD_SHUTDOWN;
|
|
}
|
|
|
|
// Setup all the relevant mode information
|
|
Instance->Gop.Mode->SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
|
Instance->Gop.Mode->FrameBufferBase = VramBaseAddress;
|
|
|
|
// Set the flag before changing the mode, to avoid infinite loops
|
|
mDisplayInitialized = TRUE;
|
|
|
|
// All is ok, so don't deal with any errors
|
|
goto EXIT;
|
|
|
|
EXIT_ERROR_LCD_SHUTDOWN:
|
|
DEBUG ((DEBUG_ERROR, "InitializeDisplay: ERROR - Can not initialise the display. Exit Status=%r\n", Status));
|
|
|
|
LcdShutdown ();
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LcdGraphicsOutputDxeInitialize (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LCD_INSTANCE *Instance;
|
|
|
|
Status = LcdIdentify ();
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
Status = LcdInstanceContructor (&Instance);
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT;
|
|
}
|
|
|
|
// Install the Graphics Output Protocol and the Device Path
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&Instance->Handle,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
&Instance->Gop,
|
|
&gEfiDevicePathProtocolGuid,
|
|
&Instance->DevicePath,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "LcdGraphicsOutputDxeInitialize: Can not install the protocol. Exit Status=%r\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
// Register for an ExitBootServicesEvent
|
|
// When ExitBootServices starts, this function will make sure that the
|
|
// graphics driver shuts down properly, i.e. it will free up all
|
|
// allocated memory and perform any necessary hardware re-configuration.
|
|
Status = gBS->CreateEvent (
|
|
EVT_SIGNAL_EXIT_BOOT_SERVICES,
|
|
TPL_NOTIFY,
|
|
LcdGraphicsExitBootServicesEvent,
|
|
NULL,
|
|
&Instance->ExitBootServicesEvent
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "LcdGraphicsOutputDxeInitialize: Can not install the ExitBootServicesEvent handler. Exit Status=%r\n", Status));
|
|
goto EXIT_ERROR_UNINSTALL_PROTOCOL;
|
|
}
|
|
|
|
// To get here, everything must be fine, so just exit
|
|
goto EXIT;
|
|
|
|
EXIT_ERROR_UNINSTALL_PROTOCOL:
|
|
// The following function could return an error message,
|
|
// however, to get here something must have gone wrong already,
|
|
// so preserve the original error, i.e. don't change
|
|
// the Status variable, even it fails to uninstall the protocol.
|
|
gBS->UninstallMultipleProtocolInterfaces (
|
|
Instance->Handle,
|
|
&gEfiGraphicsOutputProtocolGuid,
|
|
&Instance->Gop, // Uninstall Graphics Output protocol
|
|
&gEfiDevicePathProtocolGuid,
|
|
&Instance->DevicePath, // Uninstall device path
|
|
NULL
|
|
);
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
/** This function should be called
|
|
on Event: ExitBootServices
|
|
to free up memory, stop the driver
|
|
and uninstall the protocols
|
|
**/
|
|
VOID
|
|
LcdGraphicsExitBootServicesEvent (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
// By default, this PCD is FALSE. But if a platform starts a predefined OS
|
|
// that does not use a framebuffer then we might want to disable the display
|
|
// controller to avoid to display corrupted information on the screen.
|
|
if (FeaturePcdGet (PcdGopDisableOnExitBootServices)) {
|
|
// Turn-off the Display controller
|
|
LcdShutdown ();
|
|
}
|
|
}
|
|
|
|
/** GraphicsOutput Protocol function, mapping to
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LcdGraphicsQueryMode (
|
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
|
IN UINT32 ModeNumber,
|
|
OUT UINTN *SizeOfInfo,
|
|
OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LCD_INSTANCE *Instance;
|
|
|
|
Instance = LCD_INSTANCE_FROM_GOP_THIS (This);
|
|
|
|
// Setup the hardware if not already done
|
|
if (!mDisplayInitialized) {
|
|
Status = InitializeDisplay (Instance);
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
// Error checking
|
|
if ((This == NULL) ||
|
|
(Info == NULL) ||
|
|
(SizeOfInfo == NULL) ||
|
|
(ModeNumber >= This->Mode->MaxMode))
|
|
{
|
|
DEBUG ((DEBUG_ERROR, "LcdGraphicsQueryMode: ERROR - For mode number %d : Invalid Parameter.\n", ModeNumber));
|
|
Status = EFI_INVALID_PARAMETER;
|
|
goto EXIT;
|
|
}
|
|
|
|
*Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
|
|
if (*Info == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto EXIT;
|
|
}
|
|
|
|
*SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
|
|
|
|
Status = LcdPlatformQueryMode (ModeNumber, *Info);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (*Info);
|
|
}
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
/** GraphicsOutput Protocol function, mapping to
|
|
EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
LcdGraphicsSetMode (
|
|
IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This,
|
|
IN UINT32 ModeNumber
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColour;
|
|
LCD_INSTANCE *Instance;
|
|
LCD_BPP Bpp;
|
|
|
|
Instance = LCD_INSTANCE_FROM_GOP_THIS (This);
|
|
|
|
// Setup the hardware if not already done
|
|
if (!mDisplayInitialized) {
|
|
Status = InitializeDisplay (Instance);
|
|
if (EFI_ERROR (Status)) {
|
|
goto EXIT;
|
|
}
|
|
}
|
|
|
|
// Check if this mode is supported
|
|
if (ModeNumber >= This->Mode->MaxMode) {
|
|
DEBUG ((DEBUG_ERROR, "LcdGraphicsSetMode: ERROR - Unsupported mode number %d .\n", ModeNumber));
|
|
Status = EFI_UNSUPPORTED;
|
|
goto EXIT;
|
|
}
|
|
|
|
// Set the oscillator frequency to support the new mode
|
|
Status = LcdPlatformSetMode (ModeNumber);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
// Update the UEFI mode information
|
|
This->Mode->Mode = ModeNumber;
|
|
LcdPlatformQueryMode (ModeNumber, &Instance->ModeInfo);
|
|
Status = LcdPlatformGetBpp (ModeNumber, &Bpp);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "LcdGraphicsSetMode: ERROR - Couldn't get bytes per pixel, status: %r\n", Status));
|
|
goto EXIT;
|
|
}
|
|
|
|
This->Mode->FrameBufferSize = Instance->ModeInfo.VerticalResolution
|
|
* Instance->ModeInfo.PixelsPerScanLine
|
|
* GetBytesPerPixel (Bpp);
|
|
|
|
// Set the hardware to the new mode
|
|
Status = LcdSetMode (ModeNumber);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto EXIT;
|
|
}
|
|
|
|
// The UEFI spec requires that we now clear the visible portions of the
|
|
// output display to black.
|
|
|
|
// Set the fill colour to black
|
|
SetMem (&FillColour, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);
|
|
|
|
// Fill the entire visible area with the same colour.
|
|
Status = This->Blt (
|
|
This,
|
|
&FillColour,
|
|
EfiBltVideoFill,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
This->Mode->Info->HorizontalResolution,
|
|
This->Mode->Info->VerticalResolution,
|
|
0
|
|
);
|
|
|
|
EXIT:
|
|
return Status;
|
|
}
|
|
|
|
UINTN
|
|
GetBytesPerPixel (
|
|
IN LCD_BPP Bpp
|
|
)
|
|
{
|
|
switch (Bpp) {
|
|
case LcdBitsPerPixel_24:
|
|
return 4;
|
|
|
|
case LcdBitsPerPixel_16_565:
|
|
case LcdBitsPerPixel_16_555:
|
|
case LcdBitsPerPixel_12_444:
|
|
return 2;
|
|
|
|
case LcdBitsPerPixel_8:
|
|
case LcdBitsPerPixel_4:
|
|
case LcdBitsPerPixel_2:
|
|
case LcdBitsPerPixel_1:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|