| /** @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; | |
| } | |
| } |