blob: 55f74517083eabf4022174f4422151b52faf2609 [file] [log] [blame]
/** @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;
}
}