mirror of https://github.com/acidanthera/audk.git
532 lines
16 KiB
C
532 lines
16 KiB
C
/** @file
|
|
Read EDID information and parse EDID information.
|
|
|
|
Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
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.
|
|
|
|
**/
|
|
|
|
#include "CirrusLogic5430.h"
|
|
#include "CirrusLogic5430I2c.h"
|
|
|
|
//
|
|
// EDID block
|
|
//
|
|
typedef struct {
|
|
UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00"
|
|
UINT16 ManufactureName; //EISA 3-character ID
|
|
UINT16 ProductCode; //Vendor assigned code
|
|
UINT32 SerialNumber; //32-bit serial number
|
|
UINT8 WeekOfManufacture; //Week number
|
|
UINT8 YearOfManufacture; //Year
|
|
UINT8 EdidVersion; //EDID Structure Version
|
|
UINT8 EdidRevision; //EDID Structure Revision
|
|
UINT8 VideoInputDefinition;
|
|
UINT8 MaxHorizontalImageSize; //cm
|
|
UINT8 MaxVerticalImageSize; //cm
|
|
UINT8 DisplayTransferCharacteristic;
|
|
UINT8 FeatureSupport;
|
|
UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0
|
|
UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0
|
|
UINT8 RedX; //Red-x Bits 9 - 2
|
|
UINT8 RedY; //Red-y Bits 9 - 2
|
|
UINT8 GreenX; //Green-x Bits 9 - 2
|
|
UINT8 GreenY; //Green-y Bits 9 - 2
|
|
UINT8 BlueX; //Blue-x Bits 9 - 2
|
|
UINT8 BlueY; //Blue-y Bits 9 - 2
|
|
UINT8 WhiteX; //White-x Bits 9 - 2
|
|
UINT8 WhiteY; //White-x Bits 9 - 2
|
|
UINT8 EstablishedTimings[3];
|
|
UINT8 StandardTimingIdentification[16];
|
|
UINT8 DetailedTimingDescriptions[72];
|
|
UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow
|
|
UINT8 Checksum;
|
|
} EDID_BLOCK;
|
|
|
|
#define EDID_BLOCK_SIZE 128
|
|
#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17
|
|
|
|
typedef struct {
|
|
UINT16 HorizontalResolution;
|
|
UINT16 VerticalResolution;
|
|
UINT16 RefreshRate;
|
|
} EDID_TIMING;
|
|
|
|
typedef struct {
|
|
UINT32 ValidNumber;
|
|
UINT32 Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER];
|
|
} VALID_EDID_TIMING;
|
|
|
|
//
|
|
// Standard timing defined by VESA EDID
|
|
//
|
|
EDID_TIMING mVbeEstablishedEdidTiming[] = {
|
|
//
|
|
// Established Timing I
|
|
//
|
|
{800, 600, 60},
|
|
{800, 600, 56},
|
|
{640, 480, 75},
|
|
{640, 480, 72},
|
|
{640, 480, 67},
|
|
{640, 480, 60},
|
|
{720, 400, 88},
|
|
{720, 400, 70},
|
|
//
|
|
// Established Timing II
|
|
//
|
|
{1280, 1024, 75},
|
|
{1024, 768, 75},
|
|
{1024, 768, 70},
|
|
{1024, 768, 60},
|
|
{1024, 768, 87},
|
|
{832, 624, 75},
|
|
{800, 600, 75},
|
|
{800, 600, 72},
|
|
//
|
|
// Established Timing III
|
|
//
|
|
{1152, 870, 75}
|
|
};
|
|
|
|
/**
|
|
Read EDID information from I2C Bus on CirrusLogic.
|
|
|
|
@param Private Pointer to CIRRUS_LOGIC_5430_PRIVATE_DATA.
|
|
@param EdidDataBlock Pointer to EDID data block.
|
|
@param EdidSize Returned EDID block size.
|
|
|
|
@retval EFI_UNSUPPORTED
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ReadEdidData (
|
|
CIRRUS_LOGIC_5430_PRIVATE_DATA *Private,
|
|
UINT8 **EdidDataBlock,
|
|
UINTN *EdidSize
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT8 EdidData[EDID_BLOCK_SIZE * 2];
|
|
UINT8 *ValidEdid;
|
|
UINT64 Signature;
|
|
|
|
for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) {
|
|
I2cReadByte (Private->PciIo, 0xa0, (UINT8)Index, &EdidData[Index]);
|
|
}
|
|
|
|
//
|
|
// Search for the EDID signature
|
|
//
|
|
ValidEdid = &EdidData[0];
|
|
Signature = 0x00ffffffffffff00ull;
|
|
for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) {
|
|
if (CompareMem (ValidEdid, &Signature, 8) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == 256) {
|
|
//
|
|
// No EDID signature found
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*EdidDataBlock = AllocateCopyPool (
|
|
EDID_BLOCK_SIZE,
|
|
ValidEdid
|
|
);
|
|
if (*EdidDataBlock == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Currently only support EDID 1.x
|
|
//
|
|
*EdidSize = EDID_BLOCK_SIZE;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Generate a search key for a specified timing data.
|
|
|
|
@param EdidTiming Pointer to EDID timing
|
|
|
|
@return The 32 bit unique key for search.
|
|
|
|
**/
|
|
UINT32
|
|
CalculateEdidKey (
|
|
EDID_TIMING *EdidTiming
|
|
)
|
|
{
|
|
UINT32 Key;
|
|
|
|
//
|
|
// Be sure no conflicts for all standard timing defined by VESA.
|
|
//
|
|
Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution;
|
|
return Key;
|
|
}
|
|
|
|
/**
|
|
Search a specified Timing in all the valid EDID timings.
|
|
|
|
@param ValidEdidTiming All valid EDID timing information.
|
|
@param EdidTiming The Timing to search for.
|
|
|
|
@retval TRUE Found.
|
|
@retval FALSE Not found.
|
|
|
|
**/
|
|
BOOLEAN
|
|
SearchEdidTiming (
|
|
VALID_EDID_TIMING *ValidEdidTiming,
|
|
EDID_TIMING *EdidTiming
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
UINT32 Key;
|
|
|
|
Key = CalculateEdidKey (EdidTiming);
|
|
|
|
for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) {
|
|
if (Key == ValidEdidTiming->Key[Index]) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Parse the Established Timing and Standard Timing in EDID data block.
|
|
|
|
@param EdidBuffer Pointer to EDID data block
|
|
@param ValidEdidTiming Valid EDID timing information
|
|
|
|
@retval TRUE The EDID data is valid.
|
|
@retval FALSE The EDID data is invalid.
|
|
|
|
**/
|
|
BOOLEAN
|
|
ParseEdidData (
|
|
UINT8 *EdidBuffer,
|
|
VALID_EDID_TIMING *ValidEdidTiming
|
|
)
|
|
{
|
|
UINT8 CheckSum;
|
|
UINT32 Index;
|
|
UINT32 ValidNumber;
|
|
UINT32 TimingBits;
|
|
UINT8 *BufferIndex;
|
|
UINT16 HorizontalResolution;
|
|
UINT16 VerticalResolution;
|
|
UINT8 AspectRatio;
|
|
UINT8 RefreshRate;
|
|
EDID_TIMING TempTiming;
|
|
EDID_BLOCK *EdidDataBlock;
|
|
|
|
EdidDataBlock = (EDID_BLOCK *) EdidBuffer;
|
|
|
|
//
|
|
// Check the checksum of EDID data
|
|
//
|
|
CheckSum = 0;
|
|
for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) {
|
|
CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]);
|
|
}
|
|
if (CheckSum != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
ValidNumber = 0;
|
|
SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0);
|
|
|
|
if ((EdidDataBlock->EstablishedTimings[0] != 0) ||
|
|
(EdidDataBlock->EstablishedTimings[1] != 0) ||
|
|
(EdidDataBlock->EstablishedTimings[2] != 0)
|
|
) {
|
|
//
|
|
// Established timing data
|
|
//
|
|
TimingBits = EdidDataBlock->EstablishedTimings[0] |
|
|
(EdidDataBlock->EstablishedTimings[1] << 8) |
|
|
((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ;
|
|
for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) {
|
|
if (TimingBits & 0x1) {
|
|
ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]);
|
|
ValidNumber ++;
|
|
}
|
|
TimingBits = TimingBits >> 1;
|
|
}
|
|
} else {
|
|
//
|
|
// If no Established timing data, read the standard timing data
|
|
//
|
|
BufferIndex = &EdidDataBlock->StandardTimingIdentification[0];
|
|
for (Index = 0; Index < 8; Index ++) {
|
|
if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){
|
|
//
|
|
// A valid Standard Timing
|
|
//
|
|
HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248);
|
|
AspectRatio = (UINT8) (BufferIndex[1] >> 6);
|
|
switch (AspectRatio) {
|
|
case 0:
|
|
VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10);
|
|
break;
|
|
case 1:
|
|
VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
|
|
break;
|
|
case 2:
|
|
VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4);
|
|
break;
|
|
case 3:
|
|
VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9);
|
|
break;
|
|
default:
|
|
VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);
|
|
break;
|
|
}
|
|
RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60);
|
|
TempTiming.HorizontalResolution = HorizontalResolution;
|
|
TempTiming.VerticalResolution = VerticalResolution;
|
|
TempTiming.RefreshRate = RefreshRate;
|
|
ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming);
|
|
ValidNumber ++;
|
|
}
|
|
BufferIndex += 2;
|
|
}
|
|
}
|
|
|
|
ValidEdidTiming->ValidNumber = ValidNumber;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Construct the valid video modes for CirrusLogic5430.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CirrusLogic5430VideoModeSetup (
|
|
CIRRUS_LOGIC_5430_PRIVATE_DATA *Private
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
BOOLEAN EdidFound;
|
|
EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride;
|
|
UINT32 EdidAttributes;
|
|
BOOLEAN EdidOverrideFound;
|
|
UINTN EdidOverrideDataSize;
|
|
UINT8 *EdidOverrideDataBlock;
|
|
UINTN EdidDiscoveredDataSize;
|
|
UINT8 *EdidDiscoveredDataBlock;
|
|
UINTN EdidActiveDataSize;
|
|
UINT8 *EdidActiveDataBlock;
|
|
VALID_EDID_TIMING ValidEdidTiming;
|
|
UINT32 ValidModeCount;
|
|
CIRRUS_LOGIC_5430_MODE_DATA *ModeData;
|
|
BOOLEAN TimingMatch;
|
|
CIRRUS_LOGIC_5430_VIDEO_MODES *VideoMode;
|
|
EDID_TIMING TempTiming;
|
|
|
|
//
|
|
// setup EDID information
|
|
//
|
|
Private->EdidDiscovered.Edid = NULL;
|
|
Private->EdidDiscovered.SizeOfEdid = 0;
|
|
Private->EdidActive.Edid = NULL;
|
|
Private->EdidActive.SizeOfEdid = 0;
|
|
|
|
EdidFound = FALSE;
|
|
EdidOverrideFound = FALSE;
|
|
EdidAttributes = 0xff;
|
|
EdidOverrideDataSize = 0;
|
|
EdidOverrideDataBlock = NULL;
|
|
EdidActiveDataSize = 0;
|
|
EdidActiveDataBlock = NULL;
|
|
EdidDiscoveredDataBlock = NULL;
|
|
|
|
//
|
|
// Find EDID Override protocol firstly, this protocol is installed by platform if needed.
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiEdidOverrideProtocolGuid,
|
|
NULL,
|
|
(VOID **) &EdidOverride
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow
|
|
//
|
|
EdidOverrideDataBlock = AllocatePool (EDID_BLOCK_SIZE * 2);
|
|
if (NULL == EdidOverrideDataBlock) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
Status = EdidOverride->GetEdid (
|
|
EdidOverride,
|
|
Private->Handle,
|
|
&EdidAttributes,
|
|
&EdidOverrideDataSize,
|
|
(UINT8 **) &EdidOverrideDataBlock
|
|
);
|
|
if (!EFI_ERROR (Status) &&
|
|
EdidAttributes == 0 &&
|
|
EdidOverrideDataSize != 0) {
|
|
//
|
|
// Succeeded to get EDID Override Data
|
|
//
|
|
EdidOverrideFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) {
|
|
//
|
|
// If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned,
|
|
// read EDID information through I2C Bus
|
|
//
|
|
if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) {
|
|
Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize;
|
|
Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool (
|
|
EdidDiscoveredDataSize,
|
|
EdidDiscoveredDataBlock
|
|
);
|
|
|
|
if (NULL == Private->EdidDiscovered.Edid) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
|
|
EdidActiveDataSize = Private->EdidDiscovered.SizeOfEdid;
|
|
EdidActiveDataBlock = Private->EdidDiscovered.Edid;
|
|
|
|
EdidFound = TRUE;
|
|
}
|
|
}
|
|
|
|
if (EdidFound != TRUE && EdidOverrideFound == TRUE) {
|
|
EdidActiveDataSize = EdidOverrideDataSize;
|
|
EdidActiveDataBlock = EdidOverrideDataBlock;
|
|
EdidFound = TRUE;
|
|
}
|
|
|
|
if (EdidFound == TRUE) {
|
|
//
|
|
// Parse EDID data structure to retrieve modes supported by monitor
|
|
//
|
|
if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) {
|
|
//
|
|
// Copy EDID Override Data to EDID Active Data
|
|
//
|
|
Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize;
|
|
Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool (
|
|
EdidActiveDataSize,
|
|
EdidActiveDataBlock
|
|
);
|
|
if (NULL == Private->EdidActive.Edid) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto Done;
|
|
}
|
|
}
|
|
} else {
|
|
Private->EdidActive.SizeOfEdid = 0;
|
|
Private->EdidActive.Edid = NULL;
|
|
EdidFound = FALSE;
|
|
}
|
|
|
|
if (EdidFound) {
|
|
//
|
|
// Initialize the private mode data with the supported modes.
|
|
//
|
|
ValidModeCount = 0;
|
|
ModeData = &Private->ModeData[0];
|
|
VideoMode = &CirrusLogic5430VideoModes[0];
|
|
for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index++) {
|
|
|
|
TimingMatch = TRUE;
|
|
|
|
//
|
|
// Check whether match with CirrusLogic5430 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->ColorDepth = VideoMode->ColorDepth;
|
|
ModeData->RefreshRate = VideoMode->RefreshRate;
|
|
|
|
ModeData ++;
|
|
ValidModeCount ++;
|
|
}
|
|
|
|
VideoMode ++;
|
|
}
|
|
|
|
Private->MaxMode = ValidModeCount;
|
|
|
|
} else {
|
|
//
|
|
// If EDID information wasn't found
|
|
//
|
|
ModeData = &Private->ModeData[0];
|
|
VideoMode = &CirrusLogic5430VideoModes[0];
|
|
for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index ++) {
|
|
ModeData->ModeNumber = Index;
|
|
ModeData->HorizontalResolution = VideoMode->Width;
|
|
ModeData->VerticalResolution = VideoMode->Height;
|
|
ModeData->ColorDepth = VideoMode->ColorDepth;
|
|
ModeData->RefreshRate = VideoMode->RefreshRate;
|
|
|
|
ModeData ++ ;
|
|
VideoMode ++;
|
|
}
|
|
Private->MaxMode = CIRRUS_LOGIC_5430_MODE_COUNT;
|
|
}
|
|
|
|
if (EdidOverrideDataBlock != NULL) {
|
|
FreePool (EdidOverrideDataBlock);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
Done:
|
|
if (EdidOverrideDataBlock != NULL) {
|
|
FreePool (EdidOverrideDataBlock);
|
|
}
|
|
if (Private->EdidDiscovered.Edid != NULL) {
|
|
FreePool (Private->EdidDiscovered.Edid);
|
|
}
|
|
if (Private->EdidDiscovered.Edid != NULL) {
|
|
FreePool (Private->EdidActive.Edid);
|
|
}
|
|
|
|
return EFI_DEVICE_ERROR;
|
|
}
|