1 /** @file
2 
3  Copyright (c) 2011-2014, ARM Ltd. All rights reserved.<BR>
4  This program and the accompanying materials
5  are licensed and made available under the terms and conditions of the BSD License
6  which accompanies this distribution.  The full text of the license may be found at
7  http://opensource.org/licenses/bsd-license.php
8 
9  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12  **/
13 
14 #include <PiDxe.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/DevicePathLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiRuntimeServicesTableLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 
21 #include <Guid/GlobalVariable.h>
22 
23 #include "LcdGraphicsOutputDxe.h"
24 
25 /**********************************************************************
26  *
27  *  This file implements the Graphics Output protocol on ArmVersatileExpress
28  *  using the Lcd controller
29  *
30  **********************************************************************/
31 
32 //
33 // Global variables
34 //
35 
36 BOOLEAN mDisplayInitialized = FALSE;
37 
38 LCD_INSTANCE mLcdTemplate = {
39   LCD_INSTANCE_SIGNATURE,
40   NULL, // Handle
41   { // ModeInfo
42     0, // Version
43     0, // HorizontalResolution
44     0, // VerticalResolution
45     PixelBltOnly, // PixelFormat
46     { 0 }, // PixelInformation
47     0, // PixelsPerScanLine
48   },
49   {
50     0, // MaxMode;
51     0, // Mode;
52     NULL, // Info;
53     0, // SizeOfInfo;
54     0, // FrameBufferBase;
55     0 // FrameBufferSize;
56   },
57   { // Gop
58     LcdGraphicsQueryMode,  // QueryMode
59     LcdGraphicsSetMode,    // SetMode
60     LcdGraphicsBlt,        // Blt
61     NULL                     // *Mode
62   },
63   { // DevicePath
64     {
65       {
66         HARDWARE_DEVICE_PATH, HW_VENDOR_DP,
67         { (UINT8) (sizeof(VENDOR_DEVICE_PATH)), (UINT8) ((sizeof(VENDOR_DEVICE_PATH)) >> 8) },
68       },
69       // Hardware Device Path for Lcd
70       EFI_CALLER_ID_GUID // Use the driver's GUID
71     },
72 
73     {
74       END_DEVICE_PATH_TYPE,
75       END_ENTIRE_DEVICE_PATH_SUBTYPE,
76       { sizeof(EFI_DEVICE_PATH_PROTOCOL), 0 }
77     }
78   },
79   (EFI_EVENT) NULL // ExitBootServicesEvent
80 };
81 
82 EFI_STATUS
LcdInstanceContructor(OUT LCD_INSTANCE ** NewInstance)83 LcdInstanceContructor (
84   OUT LCD_INSTANCE** NewInstance
85   )
86 {
87   LCD_INSTANCE* Instance;
88 
89   Instance = AllocateCopyPool (sizeof(LCD_INSTANCE), &mLcdTemplate);
90   if (Instance == NULL) {
91     return EFI_OUT_OF_RESOURCES;
92   }
93 
94   Instance->Gop.Mode          = &Instance->Mode;
95   Instance->Gop.Mode->MaxMode = LcdPlatformGetMaxMode ();
96   Instance->Mode.Info         = &Instance->ModeInfo;
97 
98   *NewInstance = Instance;
99   return EFI_SUCCESS;
100 }
101 
102 //
103 // Function Definitions
104 //
105 
106 EFI_STATUS
InitializeDisplay(IN LCD_INSTANCE * Instance)107 InitializeDisplay (
108   IN LCD_INSTANCE* Instance
109   )
110 {
111   EFI_STATUS             Status = EFI_SUCCESS;
112   EFI_PHYSICAL_ADDRESS   VramBaseAddress;
113   UINTN                  VramSize;
114 
115   Status = LcdPlatformGetVram (&VramBaseAddress, &VramSize);
116   if (EFI_ERROR(Status)) {
117     return Status;
118   }
119 
120   // Setup the LCD
121   Status = LcdInitialize (VramBaseAddress);
122   if (EFI_ERROR(Status)) {
123     goto EXIT_ERROR_LCD_SHUTDOWN;
124   }
125 
126   Status = LcdPlatformInitializeDisplay (Instance->Handle);
127   if (EFI_ERROR(Status)) {
128     goto EXIT_ERROR_LCD_SHUTDOWN;
129   }
130 
131   // Setup all the relevant mode information
132   Instance->Gop.Mode->SizeOfInfo      = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
133   Instance->Gop.Mode->FrameBufferBase = VramBaseAddress;
134 
135   // Set the flag before changing the mode, to avoid infinite loops
136   mDisplayInitialized = TRUE;
137 
138   // All is ok, so don't deal with any errors
139   goto EXIT;
140 
141 EXIT_ERROR_LCD_SHUTDOWN:
142   DEBUG((DEBUG_ERROR, "InitializeDisplay: ERROR - Can not initialise the display. Exit Status=%r\n", Status));
143   LcdShutdown ();
144 
145 EXIT:
146   return Status;
147 }
148 
149 EFI_STATUS
150 EFIAPI
LcdGraphicsOutputDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)151 LcdGraphicsOutputDxeInitialize (
152   IN EFI_HANDLE         ImageHandle,
153   IN EFI_SYSTEM_TABLE   *SystemTable
154   )
155 {
156   EFI_STATUS  Status = EFI_SUCCESS;
157   LCD_INSTANCE* Instance;
158 
159   Status = LcdIdentify ();
160   if (EFI_ERROR(Status)) {
161     goto EXIT;
162   }
163 
164   Status = LcdInstanceContructor (&Instance);
165   if (EFI_ERROR(Status)) {
166     goto EXIT;
167   }
168 
169   // Install the Graphics Output Protocol and the Device Path
170   Status = gBS->InstallMultipleProtocolInterfaces(
171             &Instance->Handle,
172             &gEfiGraphicsOutputProtocolGuid, &Instance->Gop,
173             &gEfiDevicePathProtocolGuid,     &Instance->DevicePath,
174             NULL
175             );
176 
177   if (EFI_ERROR(Status)) {
178     DEBUG((DEBUG_ERROR, "GraphicsOutputDxeInitialize: Can not install the protocol. Exit Status=%r\n", Status));
179     goto EXIT;
180   }
181 
182   // Register for an ExitBootServicesEvent
183   // When ExitBootServices starts, this function here will make sure that the graphics driver will shut down properly,
184   // i.e. it will free up all allocated memory and perform any necessary hardware re-configuration.
185   Status = gBS->CreateEvent (
186             EVT_SIGNAL_EXIT_BOOT_SERVICES,
187             TPL_NOTIFY,
188             LcdGraphicsExitBootServicesEvent, NULL,
189             &Instance->ExitBootServicesEvent
190             );
191 
192   if (EFI_ERROR(Status)) {
193     DEBUG((DEBUG_ERROR, "GraphicsOutputDxeInitialize: Can not install the ExitBootServicesEvent handler. Exit Status=%r\n", Status));
194     goto EXIT_ERROR_UNINSTALL_PROTOCOL;
195   }
196 
197   // To get here, everything must be fine, so just exit
198   goto EXIT;
199 
200 EXIT_ERROR_UNINSTALL_PROTOCOL:
201   /* The following function could return an error message,
202    * however, to get here something must have gone wrong already,
203    * so preserve the original error, i.e. don't change
204    * the Status variable, even it fails to uninstall the protocol.
205    */
206   gBS->UninstallMultipleProtocolInterfaces (
207     Instance->Handle,
208     &gEfiGraphicsOutputProtocolGuid, &Instance->Gop, // Uninstall Graphics Output protocol
209     &gEfiDevicePathProtocolGuid,     &Instance->DevicePath,     // Uninstall device path
210     NULL
211     );
212 
213 EXIT:
214   return Status;
215 
216 }
217 
218 /***************************************
219  * This function should be called
220  * on Event: ExitBootServices
221  * to free up memory, stop the driver
222  * and uninstall the protocols
223  ***************************************/
224 VOID
LcdGraphicsExitBootServicesEvent(IN EFI_EVENT Event,IN VOID * Context)225 LcdGraphicsExitBootServicesEvent (
226   IN EFI_EVENT  Event,
227   IN VOID       *Context
228   )
229 {
230   // By default, this PCD is FALSE. But if a platform starts a predefined OS that
231   // does not use a framebuffer then we might want to disable the display controller
232   // to avoid to display corrupted information on the screen.
233   if (FeaturePcdGet (PcdGopDisableOnExitBootServices)) {
234     // Turn-off the Display controller
235     LcdShutdown ();
236   }
237 }
238 
239 /***************************************
240  * GraphicsOutput Protocol function, mapping to
241  * EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode
242  ***************************************/
243 EFI_STATUS
244 EFIAPI
LcdGraphicsQueryMode(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,IN UINT32 ModeNumber,OUT UINTN * SizeOfInfo,OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION ** Info)245 LcdGraphicsQueryMode (
246   IN EFI_GRAPHICS_OUTPUT_PROTOCOL            *This,
247   IN UINT32                                  ModeNumber,
248   OUT UINTN                                  *SizeOfInfo,
249   OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION   **Info
250   )
251 {
252   EFI_STATUS Status = EFI_SUCCESS;
253   LCD_INSTANCE *Instance;
254 
255   Instance = LCD_INSTANCE_FROM_GOP_THIS(This);
256 
257   // Setup the hardware if not already done
258   if( !mDisplayInitialized ) {
259     Status = InitializeDisplay(Instance);
260     if (EFI_ERROR(Status)) {
261       goto EXIT;
262     }
263   }
264 
265   // Error checking
266   if ( (This == NULL) || (Info == NULL) || (SizeOfInfo == NULL) || (ModeNumber >= This->Mode->MaxMode) ) {
267     DEBUG((DEBUG_ERROR, "LcdGraphicsQueryMode: ERROR - For mode number %d : Invalid Parameter.\n", ModeNumber ));
268     Status = EFI_INVALID_PARAMETER;
269     goto EXIT;
270   }
271 
272   *Info = AllocatePool (sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION));
273   if (*Info == NULL) {
274     Status = EFI_OUT_OF_RESOURCES;
275     goto EXIT;
276   }
277 
278   *SizeOfInfo = sizeof( EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);
279 
280   Status = LcdPlatformQueryMode (ModeNumber,*Info);
281   if (EFI_ERROR(Status)) {
282     FreePool(*Info);
283   }
284 
285 EXIT:
286   return Status;
287 }
288 
289 /***************************************
290  * GraphicsOutput Protocol function, mapping to
291  * EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode
292  ***************************************/
293 EFI_STATUS
294 EFIAPI
LcdGraphicsSetMode(IN EFI_GRAPHICS_OUTPUT_PROTOCOL * This,IN UINT32 ModeNumber)295 LcdGraphicsSetMode (
296   IN EFI_GRAPHICS_OUTPUT_PROTOCOL   *This,
297   IN UINT32                         ModeNumber
298   )
299 {
300   EFI_STATUS                      Status = EFI_SUCCESS;
301   EFI_GRAPHICS_OUTPUT_BLT_PIXEL   FillColour;
302   LCD_INSTANCE*                   Instance;
303   LCD_BPP                         Bpp;
304 
305   Instance = LCD_INSTANCE_FROM_GOP_THIS (This);
306 
307   // Setup the hardware if not already done
308   if(!mDisplayInitialized) {
309     Status = InitializeDisplay (Instance);
310     if (EFI_ERROR(Status)) {
311       goto EXIT;
312     }
313   }
314 
315   // Check if this mode is supported
316   if( ModeNumber >= This->Mode->MaxMode ) {
317     DEBUG((DEBUG_ERROR, "LcdGraphicsSetMode: ERROR - Unsupported mode number %d .\n", ModeNumber ));
318     Status = EFI_UNSUPPORTED;
319     goto EXIT;
320   }
321 
322   // Set the oscillator frequency to support the new mode
323   Status = LcdPlatformSetMode (ModeNumber);
324   if (EFI_ERROR(Status)) {
325     Status = EFI_DEVICE_ERROR;
326     goto EXIT;
327   }
328 
329   // Update the UEFI mode information
330   This->Mode->Mode = ModeNumber;
331   LcdPlatformQueryMode (ModeNumber,&Instance->ModeInfo);
332   Status = LcdPlatformGetBpp(ModeNumber, &Bpp);
333   if (EFI_ERROR(Status)) {
334     DEBUG ((DEBUG_ERROR, "LcdGraphicsSetMode: ERROR - Couldn't get bytes per pixel, status: %r\n", Status));
335     goto EXIT;
336   }
337   This->Mode->FrameBufferSize =  Instance->ModeInfo.VerticalResolution
338                                * Instance->ModeInfo.PixelsPerScanLine
339                                * GetBytesPerPixel(Bpp);
340 
341   // Set the hardware to the new mode
342   Status = LcdSetMode (ModeNumber);
343   if (EFI_ERROR(Status)) {
344     Status = EFI_DEVICE_ERROR;
345     goto EXIT;
346   }
347 
348   // The UEFI spec requires that we now clear the visible portions of the output display to black.
349 
350   // Set the fill colour to black
351   SetMem (&FillColour, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL), 0x0);
352 
353   // Fill the entire visible area with the same colour.
354   Status = This->Blt (
355       This,
356       &FillColour,
357       EfiBltVideoFill,
358       0,
359       0,
360       0,
361       0,
362       This->Mode->Info->HorizontalResolution,
363       This->Mode->Info->VerticalResolution,
364       0);
365 
366 EXIT:
367   return Status;
368 }
369 
370 UINTN
GetBytesPerPixel(IN LCD_BPP Bpp)371 GetBytesPerPixel (
372   IN  LCD_BPP       Bpp
373   )
374 {
375   switch(Bpp) {
376   case LCD_BITS_PER_PIXEL_24:
377     return 4;
378 
379   case LCD_BITS_PER_PIXEL_16_565:
380   case LCD_BITS_PER_PIXEL_16_555:
381   case LCD_BITS_PER_PIXEL_12_444:
382     return 2;
383 
384   case LCD_BITS_PER_PIXEL_8:
385   case LCD_BITS_PER_PIXEL_4:
386   case LCD_BITS_PER_PIXEL_2:
387   case LCD_BITS_PER_PIXEL_1:
388     return 1;
389 
390   default:
391     return 0;
392   }
393 }
394