/*++

  Copyright (c)  2006 - 2010 Intel Corporation. All rights reserved
  This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php.

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.


Module Name:

  GraphicsOutput.c

Abstract:

  This file produces the graphics abstration of Graphics Output. It is called by 
  Gop.c file which deals with the EFI 1.1 driver model. 
  This file just does graphics.

--*/

#include "Gop.h"
#include "EDID.h"

//
// Local Function Prototypes
//
EFI_STATUS
InitializeGraphicsMode (
  INTEL_PRIVATE_DATA    *Private,
  INTEL_VIDEO_MODES     *ModeData
  );

EFI_STATUS
WaitForVBlank (
  INTEL_PRIVATE_DATA         *Private,
  UINT32                     Timeout
  );

EFI_STATUS
StartupGfxController (
  INTEL_PRIVATE_DATA    *Private
  );

EFI_STATUS
ShutdownGfxController (
  INTEL_PRIVATE_DATA    *Private
  );

EFI_STATUS
ConfigureGfxController (
  INTEL_PRIVATE_DATA    *Private,
  INTEL_VIDEO_MODES     *ModeData
  );

EFI_STATUS
ClearScreen (
  INTEL_PRIVATE_DATA         *Private,
  UINT32                     Color
  );

//
// Graphics Output Protocol Member Functions
//
EFI_STATUS
EFIAPI
GraphicsOutputQueryMode (
  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL          *This,
  IN  UINT32                                ModeNumber,
  OUT UINTN                                 *SizeOfInfo,
  OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION  **Info
  )
/*++

Routine Description:

  Graphics Output protocol interface to get video mode

  Arguments:
    This                  - Protocol instance pointer.
    ModeNumber            - The mode number to return information on.
    Info                  - Caller allocated buffer that returns information about ModeNumber.
    SizeOfInfo            - A pointer to the size, in bytes, of the Info buffer.

  Returns:
    EFI_SUCCESS           - Mode information returned.
    EFI_DEVICE_ERROR      - A hardware error occurred trying to retrieve the video mode.
    EFI_NOT_STARTED       - Video display is not initialized. Call SetMode ()
    EFI_INVALID_PARAMETER - One of the input args was NULL.

--*/
{
  EFI_STATUS                           Status;
  INTEL_PRIVATE_DATA                   *Private;
  INTEL_GRAPHICS_OUTPUT_MODE_DATA      *ModeData;

  //
  // Parameter checking
  //
  if (This == NULL || Info == NULL || SizeOfInfo == NULL || ModeNumber >= This->Mode->MaxMode) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // return parameters stored in local tracking structure
  // If someone changes the mode outside the GOP driver,
  // then these values will return inaccurate values.  These
  // values should really be read directly from the registers.
  // [bugbug]
  //
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
                  (VOID**)&Info
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  *SizeOfInfo = sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);

  Private = INTEL_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);
  ModeData = &Private->ModeData[ModeNumber];

  (*Info)->Version              = 0;
  (*Info)->HorizontalResolution = ModeData->HorizontalResolution;
  (*Info)->VerticalResolution   = ModeData->VerticalResolution;
  (*Info)->PixelFormat          = ModeData->PixelFormat;
  (*Info)->PixelInformation     = ModeData->PixelInformation;
  (*Info)->PixelsPerScanLine    = ModeData->PixelsPerScanLine;

  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
GraphicsOutputSetMode (
  IN  EFI_GRAPHICS_OUTPUT_PROTOCOL * This,
  IN  UINT32                       ModeNumber
  )
/*++

Routine Description:

  Graphics Output protocol interface to set video mode

  Arguments:
    This             - Protocol instance pointer.
    ModeNumber       - The mode number to be set.

  Returns:
    EFI_SUCCESS      - Graphics mode was changed.
    EFI_DEVICE_ERROR - The device had an error and could not complete the request.
    EFI_UNSUPPORTED  - ModeNumber is not supported by this device.

--*/
{
  EFI_STATUS                           Status;
  INTEL_PRIVATE_DATA                   *Private;
  INTEL_GRAPHICS_OUTPUT_MODE_DATA      *ModeData;

  //
  // Check the validity of the This pointer
  //
  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (ModeNumber >= This->Mode->MaxMode) {
    return EFI_UNSUPPORTED;
  }

  if (ModeNumber == This->Mode->Mode) {
    return EFI_SUCCESS;
  }

  //
  // Get pointer to private data
  //
  Private = INTEL_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);

  ModeData = &Private->ModeData[ModeNumber];
  This->Mode->Mode                       = ModeNumber;
  This->Mode->Info->Version              = 0;
  This->Mode->Info->HorizontalResolution = ModeData->HorizontalResolution;
  This->Mode->Info->VerticalResolution   = ModeData->VerticalResolution;
  This->Mode->Info->PixelFormat          = ModeData->PixelFormat;
  This->Mode->Info->PixelInformation     = ModeData->PixelInformation;
  This->Mode->Info->PixelsPerScanLine    = ModeData->PixelsPerScanLine;
  This->Mode->SizeOfInfo                 = sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION);

  This->Mode->FrameBufferBase = ModeData->FrameBufferBase;
  This->Mode->FrameBufferSize = ModeData->FrameBufferSize;

  //
  // The request is for a supported mode, so program
  // up the graphics controller.
  //
  Status = InitializeGraphicsMode (Private, &mVideoModes[ModeData->ModeNumber]);
  ClearScreen (Private, COLOR_BLACK);

  return Status;
}

EFI_STATUS
EFIAPI
GraphicsOutputBitBlt (
  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
  )
/*++

Routine Description:

  Graphics Output protocol instance to block transfer

Arguments:

  This          - Pointer to Graphics Output protocol instance
  BltBuffer     - The data to transfer to screen
  BltOperation  - The operation to perform
  SourceX       - The X coordinate of the source for BltOperation
  SourceY       - The Y coordinate of the source for BltOperation
  DestinationX  - The X coordinate of the destination for BltOperation
  DestinationY  - The Y coordinate of the destination for BltOperation
  Width         - The width of a rectangle in the blt rectangle in pixels
  Height        - The height of a rectangle in the blt rectangle in pixels
  Delta         - Not used for EfiBltVideoFill and EfiBltVideoToVideo operation.
                  If a Delta of 0 is used, the entire BltBuffer will be operated on.
                  If a subrectangle of the BltBuffer is used, then Delta represents
                  the number of bytes in a row of the BltBuffer.

Returns:

  EFI_INVALID_PARAMETER - Invalid parameter passed in
  EFI_SUCCESS - Blt operation success

--*/
{
  INTEL_PRIVATE_DATA                   *Private;
  EFI_PCI_IO_PROTOCOL                  *PciIo;
  EFI_TPL                              OriginalTPL;
  UINT32                               ScreenWidth;
  UINT32                               ScreenHeight;
  UINT32                               Row;
  UINTN                                SrcBuf;
  UINTN                                DestBuf;
  EFI_STATUS                           Status;
  INTEL_GRAPHICS_OUTPUT_MODE_DATA      *ModeData;
  EFI_GRAPHICS_OUTPUT_BLT_PIXEL        *TmpBuf;
  UINTN                                Index;
  Status  = EFI_SUCCESS;
  Private = INTEL_PRIVATE_DATA_FROM_GRAPHICS_OUTPUT_THIS (This);
  PciIo   = Private->PciIo;

  //
  // Parameter checking
  //
  if (This == NULL || ((UINTN) BltOperation) >= EfiGraphicsOutputBltOperationMax) {
    return EFI_INVALID_PARAMETER;
  }

  if (Width == 0 || Height == 0) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // If Delta is zero, then the entire BltBuffer is being used, so Delta
  // is the number of bytes in each row of BltBuffer.  Since BltBuffer is Width pixels size,
  // the number of bytes in each row can be computed.
  //
  if (Delta == 0) {
    Delta = Width * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);
  }

  ModeData = &Private->ModeData[This->Mode->Mode];
  //
  // Some general calculations we will need for our operations
  // Values are in pixels
  //
  ScreenWidth   = ModeData->HorizontalResolution;
  ScreenHeight  = ModeData->VerticalResolution;

  //
  // Convert Screenwidth from Pixels to buffer offset (4bytes/pixel)
  //
  ScreenWidth *= sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL);

  //
  // We need to fill the Virtual Screen buffer with the blt data.
  // The virtual screen is upside down, as the first row is the bottom row of
  // the image.
  //
  //
  // Make sure the SourceX, SourceY, DestinationX, DestinationY, Width, and Height parameters
  // are valid for the operation and the current screen geometry.
  //
  //
  if (BltOperation == EfiBltVideoToBltBuffer) {
    //
    // Video to BltBuffer: Source is Video, destination is BltBuffer
    //
    if (SourceY + Height > ModeData->VerticalResolution) {
      return EFI_INVALID_PARAMETER;
    }

    if (SourceX + Width > ModeData->HorizontalResolution) {
      return EFI_INVALID_PARAMETER;
    }
  } else {
    //
    // BltBuffer to Video: Source is BltBuffer, destination is Video
    //
    if (DestinationY + Height > ModeData->VerticalResolution) {
      return EFI_INVALID_PARAMETER;
    }

    if (DestinationX + Width > ModeData->HorizontalResolution) {
      return EFI_INVALID_PARAMETER;
    }
  }
  // We have to raise to TPL Notify, so we make an atomic write the frame buffer.
  // We would not want a timer based event (Cursor, ...) to come in while we are
  // doing this operation.
  //
  OriginalTPL = gBS->RaiseTPL (TPL_NOTIFY);

  switch (BltOperation) {
  case EfiBltVideoToBltBuffer:
    //
    // Set the memory locations of the source and destination buffer
    //
    DestBuf = DestinationY * Delta + (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + (UINTN) BltBuffer;

    //
    // Copy the video screen a scann line at a time to the blt buffer.
    //
    for (Row = 0; Row < Height; Row++) {
      PciIo->Mem.Read (
                  PciIo,
                  EfiPciIoWidthUint32,
                  GMADR_BAR_INDEX,
                  (Row + SourceY) * ScreenWidth + (SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
                  Width,
                  (VOID *) (UINTN) (DestBuf + Row * Delta)
                  );
    }
    break;

  case EfiBltVideoToVideo:
    //
    // use the 2D BLT engine to copy data within the IGD Gfx frame buffer
    //
    TmpBuf = AllocatePool(Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
	for (Index = 0; Index < Width; Index ++) {

		TmpBuf[Index] = *BltBuffer;
	}

    for (Row = 0; Row < Height; Row ++) {

		PciIo->Mem.Read (
					PciIo,
					EfiPciIoWidthUint32,
					GMADR_BAR_INDEX,
					(((SourceY > DestinationY) ? Row :(Height - 1 - Row)) + SourceY) * ScreenWidth + (SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
					Width,
					(VOID *)TmpBuf
					);
		PciIo->Mem.Write (
					PciIo,
					EfiPciIoWidthUint32,
					GMADR_BAR_INDEX,
					(((SourceY > DestinationY) ? Row :(Height - 1 - Row)) + DestinationY) * ScreenWidth + (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
					Width,
					(VOID *) TmpBuf
					);
    }
	FreePool(TmpBuf);
    break;

  case EfiBltVideoFill:
    //
    // Get the color to use for the fill; it is the color of the first
    // pixel in the bltbit buffer
    // Assuming the bltbuffer is the same width as the screen
    //
    TmpBuf = AllocatePool(Width * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
	for (Index = 0; Index < Width; Index ++) {

		TmpBuf[Index] = *BltBuffer;
	}
    for (Row = 0; Row < Height; Row ++) {

		PciIo->Mem.Write (
					PciIo,
					EfiPciIoWidthUint32,
					GMADR_BAR_INDEX,
					(Row + DestinationY) * ScreenWidth + (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
					Width,
					(VOID *) TmpBuf
					);
    }
	FreePool(TmpBuf);

    break;

  case EfiBltBufferToVideo:
    //
    // Set the memory locations of the source and destination buffer.
    // These will be the physical addresses to where we will write
    // and read data from.
    //
    SrcBuf = SourceY * Delta + (SourceX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)) + (UINTN) BltBuffer;

    //
    // Copy the Blt buffer a row at a time.
    //
    for (Row = 0; Row < Height; Row++) {
      PciIo->Mem.Write (
                  PciIo,
                  EfiPciIoWidthUint32,
                  GMADR_BAR_INDEX,
                  (Row + DestinationY) * ScreenWidth + (DestinationX * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)),
                  Width,
                  (VOID *) (UINTN) (SrcBuf + Row * Delta)
                  );
    }

    break;

  default:
    Status = EFI_INVALID_PARAMETER;
    break;
  }
    
  gBS->RestoreTPL (OriginalTPL);

  return Status;
}
//
// Construction and Destruction functions
//
EFI_STATUS
GraphicsOutputConstructor (
  INTEL_PRIVATE_DATA  *Private
  )
/*++

Routine Description:

  This function acts like a C++ constructor that is called when the 
  the started routine is called.  This function is allocates buffers, 
  GTT memory and initializes the gfx controller for the default mode.

Arguments:

  Private - Pointer to the private data members

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  UINT32                                Index;
  EFI_PCI_IO_PROTOCOL                   *PciIo;
  EFI_STATUS                            Status;
  UINT32                                Value;
  PCI_TYPE00                            Pci;
  UINT32                                EdidSize;
  VALID_EDID_TIMING                     ValidEdidTiming;
  EDID_TIMING                           TempTiming;
  BOOLEAN                               TimingMatch;
  UINT32                                ValidModeCount;
  UINT32                                PreferMode;
  INTEL_VIDEO_MODES                     *VideoMode;
  INTEL_GRAPHICS_OUTPUT_MODE_DATA       *ModeData;


  PciIo = Private->PciIo;
  
  ZeroMem (&ValidEdidTiming, sizeof (VALID_EDID_TIMING));

  //
  // Enable the PCI Device
  //
  Status = Private->PciIo->Attributes (
                            Private->PciIo,
                            EfiPciIoAttributeOperationEnable,
                            EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO,
                            NULL
                            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Read the PCI Configuration Header from the PCI Device
  //
  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0, sizeof (Pci) / sizeof (UINT32), &Pci);
  if (EFI_ERROR (Status)) {
    return Status;
  }

//  //
//  // at this point, we have a physically contiguous frame buffer pointer &
//  // a physically contiguous GTT buffer.
//  // Initialize the GTT pointer in the GFX device and enable it.
//  //
//  Value = (UINT32) (Private->GTTBaseAddress.BaseAddress | 1);
//  //
//  // OR with 1 to enable GTT
//  //
//  Status = PciIo->Mem.Write (
//                        PciIo,
//                        EfiPciIoWidthUint32,
//                        MMADR_BAR_INDEX,
//                        PGTBL_CTL,
//                        1,
//                        &Value
//                        );

  //
  // Now initialize the memory pages of the gfx GTT table with our allocated
  // memory.  Initialize all GTT entries to good RAM locations.  This is done
  // by initializing every GTT page to the first frame buffer page.  All GTT
  // entries must point to valid RAM locations.
  //
  Value   = (UINT32)((Private->AllocatedMemory.BaseAddress & 0xFFFFF000) | 1);
  for (Index = 0; Index < (GTT_ALLOC_SIZE >> 2); Index++) {
    PciIo->Mem.Write (
                PciIo,
                EfiPciIoWidthUint32,
                GTTADR_BAR_INDEX,
                Index * 4,
                1,
                &Value
                );

  }
  //
  // Initialize GTT entries with frame buffer memory.  This will get the
  // GTT pointing to the true frame buffer and enable access through the
  // GMADR BAR.
  //
  for (Index = 0; Index < Private->AllocatedMemory.Pages; Index++) {

    Value = (UINT32) (((Private->AllocatedMemory.BaseAddress & 0xFFFFF000) + (Index << 12)) | 1);
    PciIo->Mem.Write (
                PciIo,
                EfiPciIoWidthUint32,
                GTTADR_BAR_INDEX,
                Index * 4,
                1,
                &Value
                );
  }

  //
  // setup EDID information
  //
  Private->EdidDiscovered.Edid = NULL;
  Private->EdidDiscovered.SizeOfEdid = 0;
  Private->EdidActive.Edid = NULL;
  Private->EdidActive.SizeOfEdid = 0;

  Status = ReadEDID (PciIo, (VOID**)&Private->EdidDiscovered.Edid, &EdidSize);
  if (!EFI_ERROR (Status)) {
    Status = ParseEDIDTable ((EDID_BLOCK *) Private->EdidDiscovered.Edid, EdidSize, &ValidEdidTiming);
    if (!EFI_ERROR (Status)) {
      Private->EdidDiscovered.SizeOfEdid = EdidSize;
      Private->EdidActive.SizeOfEdid = EdidSize;
      Private->EdidActive.Edid = AllocateCopyPool (EdidSize, Private->EdidDiscovered.Edid);
      if (Private->EdidActive.Edid == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }
    }
  }

  //
  // Initialize the private mode data with the supported modes.
  //
  ValidModeCount = 0;
  ModeData = &Private->ModeData[0];
  VideoMode = &mVideoModes[0];
  EdidSize = Private->EdidDiscovered.SizeOfEdid;
  for (Index = 0; Index < NUM_SUPPORTED_MODES; Index++) {

    TimingMatch = TRUE;

    if (EdidSize != 0 && (ValidEdidTiming.ValidNumber > 0)) {
      //
      // EDID found, check whether match with video mode
      //
      TempTiming.HorizontalResolution = (UINT16) VideoMode->Width;
      TempTiming.VerticalResolution   = (UINT16) VideoMode->Height;
      TempTiming.RefreshRate          = (UINT16) VideoMode->RefreshRate;
      if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) {
        TimingMatch = FALSE;
      }
    }

    //
    // Not export Mode 0x0 as GOP mode, this is not defined in spec.
    //
    if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) {
      TimingMatch = FALSE;
    }

    if (TimingMatch) {
      ModeData->ModeNumber = Index;
      ModeData->HorizontalResolution          = VideoMode->Width;
      ModeData->VerticalResolution            = VideoMode->Height;
      ModeData->RefreshRate                   = VideoMode->RefreshRate;
      ModeData->PixelFormat                   = Private->PixelFormat;
      ModeData->PixelInformation.RedMask      = 0x00ff0000;
      ModeData->PixelInformation.GreenMask    = 0x0000ff00;
      ModeData->PixelInformation.BlueMask     = 0x000000ff;
      ModeData->PixelInformation.ReservedMask = 0xff000000;
      ModeData->PixelsPerScanLine             = ModeData->HorizontalResolution;
      if (ModeData->PixelFormat != PixelBltOnly) {
        ModeData->FrameBufferBase             = (EFI_PHYSICAL_ADDRESS) (UINTN) (Pci.Device.Bar[GMADR_BAR_INDEX] & 0xF0000000);
        ModeData->FrameBufferSize             = FB_SIZE;
      } else {
        ModeData->FrameBufferBase             = 0;
        ModeData->FrameBufferSize             = 0;
      }

      ModeData ++;
      ValidModeCount ++;
    }

    VideoMode ++;
  }

  if (ValidModeCount == 0) {
    return EFI_UNSUPPORTED;
  }

  //
  // Allocate buffer for Graphics Output Protocol mode information
  //
  Status = gBS->AllocatePool (
                EfiBootServicesData,
                sizeof (EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE),
                (VOID **) &Private->GraphicsOutput.Mode
                );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  Status = gBS->AllocatePool (
                EfiBootServicesData,
                sizeof (EFI_GRAPHICS_OUTPUT_MODE_INFORMATION),
                (VOID **) &Private->GraphicsOutput.Mode->Info
                );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Private->GraphicsOutput.Mode->MaxMode = ValidModeCount;

  //
  // Initialize the hardware with a prefer mode
  //
  PreferMode = 0;
  ModeData = &Private->ModeData[0];
  for (Index = 0; Index < ValidModeCount; Index++, ModeData++) {
    if ((ModeData->HorizontalResolution == 800 && ModeData->VerticalResolution == 600)
        || (ModeData->HorizontalResolution == 1024 && ModeData->VerticalResolution == 768)
        ) {
      PreferMode = Index;
      break;
    }
  }

  Private->GraphicsOutput.Mode->Mode = GRAPHICS_OUTPUT_INVALIDE_MODE_NUMBER;
  Private->GraphicsOutput.SetMode (&Private->GraphicsOutput, PreferMode);

  return EFI_SUCCESS;
}

EFI_STATUS
GraphicsOutputDestructor (
  INTEL_PRIVATE_DATA  *Private
  )
/*++

Routine Description:

  Undo what was done in the Constructor.

Arguments:

  Private - Pointer to the private data members

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  EFI_PCI_IO_PROTOCOL *PciIo;
  EFI_STATUS          Status;

  PciIo = Private->PciIo;

  //
  // First shut down the controller
  //
  Status = ShutdownGfxController (Private);

  //
  // Shutdown the hardware
  //
  Private->PciIo->Attributes (
                    Private->PciIo,
                    EfiPciIoAttributeOperationDisable,
                    EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO,
                    NULL
                    );

  //
  // Free graphics output protocol occupied resource
  //
  if (Private->GraphicsOutput.Mode != NULL) {
    if (Private->GraphicsOutput.Mode->Info != NULL) {
        gBS->FreePool (Private->GraphicsOutput.Mode->Info);
    }
    gBS->FreePool (Private->GraphicsOutput.Mode);
  }

  //
  // Free  EDID protocol occupied resource
  //
  if (Private->EdidDiscovered.Edid != NULL) {
    gBS->FreePool (Private->EdidDiscovered.Edid);
  }
  if (Private->EdidActive.Edid != NULL) {
    gBS->FreePool (Private->EdidActive.Edid);
  }

  return EFI_SUCCESS;
}

EFI_STATUS
ClearScreen (
  INTEL_PRIVATE_DATA        *Private,
  UINT32                    Color
  )
/*++

Routine Description:

  This function paints the display area by writing Color to the 
  entire frame buffer.  To clear the screen, BLACK is written 
  to the frame buffer.


Arguments:

  Private  - Pointer to the private data members
   
  Color    - Color used to fill the screen.  For screen 
             clears, color is typically BLACK.
   
Returns:

  EFI_SUCCESS if successful, else the status from the COLOR_BLT
  function is returned.
   
--*/
{
  EFI_STATUS  Status;
  INTEL_GRAPHICS_OUTPUT_MODE_DATA      *ModeData;

  ModeData = &Private->ModeData[Private->GraphicsOutput.Mode->Mode];
  Status = Private->GraphicsOutput.Blt(
  	                                 &Private->GraphicsOutput,
  	                                 (VOID*)&Color,
  	                                 EfiBltVideoFill,
  	                                 0,
  	                                 0,
  	                                 0,
  	                                 0,
  	                                 ModeData->HorizontalResolution,
  	                                 ModeData->VerticalResolution,
  	                                 ModeData->HorizontalResolution * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
  	                                 );

  return Status;
}

EFI_STATUS
ShutdownGfxController (
  INTEL_PRIVATE_DATA  *Private
  )
/*++

Routine Description:

  Shut down GFX controller

Arguments:

  Private - Pointer to the private data members

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  UINT32              Index;
  UINT32              Value;
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;

  PciIo = Private->PciIo;

  //
  // ------------------------------------------------
  // Issue shutdown sequence
  // Done in two lines for readability and debugging
  // purposes.
  //
  for (Index = 0; Index < mNUM_SHUTDOWN_ENTRIES; Index++) {
    Value = mDISPLAY_SHUTDOWN[Index].Value;
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          mDISPLAY_SHUTDOWN[Index].RegisterAddress,
                          1,
                          &Value
                          );
    if (mDISPLAY_SHUTDOWN[Index].DoubleBuffer) {
      Status = WaitForVBlank (Private, VBLANK_TIMEOUT);
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS
ConfigureGfxController (
  INTEL_PRIVATE_DATA  *Private,
  INTEL_VIDEO_MODES   *ModeData
  )
/*++

Routine Description:

  Config GFX controller

Arguments:

  Private   - Pointer to the private data members
  ModeData  - Video mode data buffer

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  UINT32              Index;
  UINT32              Value;
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;

  Status  = EFI_SUCCESS;

  PciIo   = Private->PciIo;

  //
  // -----------------------------------------------
  // Set pipe timings, planes and DPLLs to desired mode values
  // Loop thru all the entries in the configuration table.
  //
  for (Index = 0; Index < NUM_DS_ENTRIES; Index++) {
    //
    // Done in two lines for readability and debugging
    // purposes.
    //
    Value = ModeData->VideoModeData[Index].Value;
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          ModeData->VideoModeData[Index].RegisterAddress,
                          1,
                          &Value
                          );

    //
    // check if register program must wait for a vblank interval
    // to occur for the register program to take effect..
    //
    if (ModeData->VideoModeData[Index].DoubleBuffer) {
      Status = WaitForVBlank (Private, VBLANK_TIMEOUT);
    }
  }

  return Status;
}

EFI_STATUS
StartupGfxController (
  INTEL_PRIVATE_DATA  *Private
  )
/*++

Routine Description:

  Startup GFX controller

Arguments:

  Private - Pointer to the private data members

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  UINT32              Index;
  UINT32              Value;
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;

  Status  = EFI_SUCCESS;

  PciIo   = Private->PciIo;

  //
  // ------------------------------------------------
  // Issue startup sequence
  // Loop thru all the entries in the startup table.
  //
  for (Index = 0; Index < mNUM_STARTUP_ENTRIES; Index++) {
    //
    // Done in two lines for readability and debugging
    // purposes.
    //
    Value = mDISPLAY_STARTUP[Index].Value;
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          mDISPLAY_STARTUP[Index].RegisterAddress,
                          1,
                          &Value
                          );

    //
    // check if register program must wait for a vblank interval
    // to occur for the register program to take effect..
    //
    if (mDISPLAY_STARTUP[Index].DoubleBuffer) {
      Status = WaitForVBlank (Private, VBLANK_TIMEOUT);
    }
  }

  return Status;
}

EFI_STATUS
WaitForVBlank (
  INTEL_PRIVATE_DATA        *Private,
  UINT32                    Timeout
  )
/*++

Routine Description:

  Wait for VBlank

Arguments:

  Private - Pointer to the private data members
  Timeout - Time out value

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  EFI_STATUS          Status;
  UINT32              Value;
  UINT32              Index;
  EFI_PCI_IO_PROTOCOL *PciIo;

  Status  = EFI_SUCCESS;
  Index   = 0;

  PciIo   = Private->PciIo;

  //
  // Read the bit and clear the status bit by writing a 1 to the VBLANK bit
  //
  Status = PciIo->Mem.Read (
                        PciIo,
                        EfiPciIoWidthUint32,
                        MMADR_BAR_INDEX,
                        PIPEASTAT,
                        1,
                        &Value
                        );
  Value |= VBLANK_MASK;
  //
  // set the VBlank status but causing it to clear
  //
  Status = PciIo->Mem.Write (
                        PciIo,
                        EfiPciIoWidthUint32,
                        MMADR_BAR_INDEX,
                        PIPEASTAT,
                        1,
                        &Value
                        );
  Status = PciIo->Mem.Write (
                        PciIo,
                        EfiPciIoWidthUint32,
                        MMADR_BAR_INDEX,
                        PIPEASTAT,
                        1,
                        &Value
                        );

  //
  // Loop Timeout # of times, waiting for VBLANK_STATUS bit to become set
  //
  while (Index < Timeout) {
    Status = PciIo->Mem.Read (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          PIPEASTAT,
                          1,
                          &Value
                          );
    Index++;
    //
    // increment our loop counter
    //
    // If the VBLANK_MASK is set, a VBLANK has occurred and
    // we are done here.
    //
    if (Value & VBLANK_MASK) {
      break;
    }
  }
  //
  // Check the loop counter against the requested Timeout value.
  // if the time out was reached, return an error.
  //
  if (Index >= Timeout) {
    Status = EFI_TIMEOUT;
  }

  return Status;
}

EFI_STATUS
ShutdownLVDS (
  INTEL_PRIVATE_DATA  *Private
  )
/*++

Routine Description:

  Shut down GFX controller

Arguments:

  Private - Pointer to the BearLake private data members

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  UINT32              Index;
  UINT32              Value;
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;

  PciIo = Private->PciIo;

  //
  // ------------------------------------------------
  // Issue shutdown sequence
  // Done in two lines for readability and debugging
  // purposes.
  //
  for (Index = 0; TRUE; Index++) {
    if (LVDS_SHUTDOWN[Index].RegisterAddress == 0) break;
    Value = LVDS_SHUTDOWN[Index].Value;
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          LVDS_SHUTDOWN[Index].RegisterAddress,
                          1,
                          &Value
                          );
    if (LVDS_SHUTDOWN[Index].DoubleBuffer) {
      Status = WaitForVBlank (Private, VBLANK_TIMEOUT);
    }
  }

  return EFI_SUCCESS;
}

EFI_STATUS StartLVDS (
  INTEL_PRIVATE_DATA  *Private,
  INTEL_VIDEO_MODES   *ModeData
  )
{
  UINT32              Index;
  UINT32              Value;
  EFI_STATUS          Status;
  EFI_PCI_IO_PROTOCOL *PciIo;

  Status  = EFI_SUCCESS;

  PciIo   = Private->PciIo;

  //
  // -----------------------------------------------
  // Set pipe timings, planes and DPLLs to desired mode values
  // Loop thru all the entries in the configuration table.
  //
  for (Index = 0; TRUE; Index++) {
    //
    // Done in two lines for readability and debugging
    // purposes.
    //
    if (ModeData->LVDSModeData[Index].RegisterAddress == 0) break;
    Value = ModeData->LVDSModeData[Index].Value;
    Status = PciIo->Mem.Write (
                          PciIo,
                          EfiPciIoWidthUint32,
                          MMADR_BAR_INDEX,
                          ModeData->LVDSModeData[Index].RegisterAddress,
                          1,
                          &Value
                          );

    //
    // check if register program must wait for a vblank interval
    // to occur for the register program to take effect..
    //
    if (ModeData->LVDSModeData[Index].DoubleBuffer) {
      Status = WaitForVBlank (Private, VBLANK_TIMEOUT);
    }
  }

  return Status;
	
}

EFI_STATUS
InitializeGraphicsMode (
  INTEL_PRIVATE_DATA  *Private,
  INTEL_VIDEO_MODES   *ModeData
  )
/*++

Routine Description:

  Initialize graphics mode

Arguments:

  Private   - Pointer to the private data members

  ModeData  - Graphics mode data buffer

Returns:

  EFI_SUCCESS - Successfully return

--*/
{
  EFI_STATUS  Status;

  //
  // Program the controller registers.
  //
  Status = ConfigureGfxController (Private, ModeData);

  //
  // If the driver is already started, then the GTT is setup and
  // we should execute the h/w start-up sequence; otherwise, the
  // constructor will call StartupGfxController itself.  The
  // controller will hang if the startup sequence is executed
  // befor the GTT is setup (ie, we need this following check!)
  //
  Status = StartupGfxController (Private);

  ShutdownLVDS(Private);
  StartLVDS(Private, ModeData);
  return Status;
}

VOID
EFIAPI
ClearGfxController (
  IN EFI_EVENT  Event,
  IN VOID       *Context
  )
{
  INTEL_PRIVATE_DATA         *Private;
  EFI_STATUS                 Status;

  Private = (INTEL_PRIVATE_DATA *) Context;

  Status = ShutdownGfxController (Private);

  //
  // Shutdown the hardware
  //
  Private->PciIo->Attributes (
                    Private->PciIo,
                    EfiPciIoAttributeOperationDisable,
                    EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | EFI_PCI_IO_ATTRIBUTE_VGA_IO,
                    NULL
                    );

  return ;
}
