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