]> git.proxmox.com Git - mirror_edk2.git/blame - OptionRomPkg/CirrusLogic5430Dxe/Edid.c
Resolve a bug where the initial ReadEdidData 'for' loop would loop
[mirror_edk2.git] / OptionRomPkg / CirrusLogic5430Dxe / Edid.c
CommitLineData
31f9e631 1/** @file\r
2 Read EDID information and parse EDID information.\r
3\r
4 Copyright (c) 2008, Intel Corporation\r
5 All rights reserved. This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "CirrusLogic5430.h"\r
16\r
17//\r
18// EDID block\r
19//\r
20typedef struct {\r
21 UINT8 Header[8]; //EDID header "00 FF FF FF FF FF FF 00"\r
22 UINT16 ManufactureName; //EISA 3-character ID\r
23 UINT16 ProductCode; //Vendor assigned code\r
24 UINT32 SerialNumber; //32-bit serial number\r
25 UINT8 WeekOfManufacture; //Week number\r
26 UINT8 YearOfManufacture; //Year\r
27 UINT8 EdidVersion; //EDID Structure Version\r
28 UINT8 EdidRevision; //EDID Structure Revision\r
29 UINT8 VideoInputDefinition;\r
30 UINT8 MaxHorizontalImageSize; //cm\r
31 UINT8 MaxVerticalImageSize; //cm\r
32 UINT8 DisplayTransferCharacteristic;\r
33 UINT8 FeatureSupport;\r
34 UINT8 RedGreenLowBits; //Rx1 Rx0 Ry1 Ry0 Gx1 Gx0 Gy1Gy0\r
35 UINT8 BlueWhiteLowBits; //Bx1 Bx0 By1 By0 Wx1 Wx0 Wy1 Wy0\r
36 UINT8 RedX; //Red-x Bits 9 - 2\r
37 UINT8 RedY; //Red-y Bits 9 - 2\r
38 UINT8 GreenX; //Green-x Bits 9 - 2\r
39 UINT8 GreenY; //Green-y Bits 9 - 2\r
40 UINT8 BlueX; //Blue-x Bits 9 - 2\r
41 UINT8 BlueY; //Blue-y Bits 9 - 2\r
42 UINT8 WhiteX; //White-x Bits 9 - 2\r
43 UINT8 WhiteY; //White-x Bits 9 - 2\r
44 UINT8 EstablishedTimings[3];\r
45 UINT8 StandardTimingIdentification[16];\r
46 UINT8 DetailedTimingDescriptions[72];\r
47 UINT8 ExtensionFlag; //Number of (optional) 128-byte EDID extension blocks to follow\r
48 UINT8 Checksum;\r
49} EDID_BLOCK;\r
50\r
51#define EDID_BLOCK_SIZE 128\r
52#define VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER 17\r
53\r
54typedef struct {\r
55 UINT16 HorizontalResolution;\r
56 UINT16 VerticalResolution;\r
57 UINT16 RefreshRate;\r
58} EDID_TIMING;\r
59\r
60typedef struct {\r
61 UINT32 ValidNumber;\r
62 UINT32 Key[VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER];\r
63} VALID_EDID_TIMING;\r
64\r
65//\r
66// Standard timing defined by VESA EDID\r
67//\r
68EDID_TIMING mVbeEstablishedEdidTiming[] = {\r
69 //\r
70 // Established Timing I\r
71 //\r
72 {800, 600, 60},\r
73 {800, 600, 56},\r
74 {640, 480, 75},\r
75 {640, 480, 72},\r
76 {640, 480, 67},\r
77 {640, 480, 60},\r
78 {720, 400, 88},\r
79 {720, 400, 70},\r
80 //\r
81 // Established Timing II\r
82 //\r
83 {1280, 1024, 75},\r
84 {1024, 768, 75},\r
85 {1024, 768, 70},\r
86 {1024, 768, 60},\r
87 {1024, 768, 87},\r
88 {832, 624, 75},\r
89 {800, 600, 75},\r
90 {800, 600, 72},\r
91 //\r
92 // Established Timing III\r
93 //\r
94 {1152, 870, 75}\r
95};\r
96\r
97/**\r
98 Read EDID information from I2C Bus on CirrusLogic.\r
99\r
100 @param Private Pointer to CIRRUS_LOGIC_5430_PRIVATE_DATA.\r
101 @param EdidDataBlock Pointer to EDID data block.\r
102 @param EdidSize Returned EDID block size.\r
103\r
104 @retval EFI_UNSUPPORTED\r
105 @retval EFI_SUCCESS\r
106\r
107**/\r
31f9e631 108EFI_STATUS\r
109ReadEdidData (\r
110 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private,\r
111 UINT8 **EdidDataBlock,\r
112 UINTN *EdidSize\r
113 )\r
114{\r
6f716497 115 UINTN Index;\r
31f9e631 116 UINT8 EdidData[EDID_BLOCK_SIZE * 2];\r
117 UINT8 *ValidEdid;\r
118 UINT64 Signature;\r
119\r
120 for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++) {\r
121 I2cReadByte (Private->PciIo, 0xa0, Index, &EdidData[Index]);\r
122 }\r
123\r
124 //\r
125 // Search for the EDID signature\r
126 //\r
127 ValidEdid = &EdidData[0];\r
128 Signature = 0x00ffffffffffff00ull;\r
129 for (Index = 0; Index < EDID_BLOCK_SIZE * 2; Index ++, ValidEdid ++) {\r
130 if (CompareMem (ValidEdid, &Signature, 8) == 0) {\r
131 break;\r
132 }\r
133 }\r
134\r
135 if (Index == 256) {\r
136 //\r
137 // No EDID signature found\r
138 //\r
139 return EFI_UNSUPPORTED;\r
140 }\r
141\r
142 *EdidDataBlock = AllocateCopyPool (\r
143 sizeof (EDID_BLOCK_SIZE),\r
144 ValidEdid\r
145 );\r
146 if (*EdidDataBlock == NULL) {\r
147 return EFI_OUT_OF_RESOURCES;\r
148 }\r
149\r
150 //\r
151 // Currently only support EDID 1.x\r
152 //\r
153 *EdidSize = EDID_BLOCK_SIZE;\r
154\r
155 return EFI_SUCCESS;\r
156}\r
157\r
158/**\r
159 Generate a search key for a specified timing data.\r
160\r
161 @param EdidTiming Pointer to EDID timing\r
162\r
163 @return The 32 bit unique key for search.\r
164\r
165**/\r
31f9e631 166UINT32\r
167CalculateEdidKey (\r
168 EDID_TIMING *EdidTiming\r
169 )\r
170{\r
171 UINT32 Key;\r
172\r
173 //\r
174 // Be sure no conflicts for all standard timing defined by VESA.\r
175 //\r
176 Key = (EdidTiming->HorizontalResolution * 2) + EdidTiming->VerticalResolution;\r
177 return Key;\r
178}\r
179\r
180/**\r
181 Search a specified Timing in all the valid EDID timings.\r
182\r
183 @param ValidEdidTiming All valid EDID timing information.\r
184 @param EdidTiming The Timing to search for.\r
185\r
186 @retval TRUE Found.\r
187 @retval FALSE Not found.\r
188\r
189**/\r
31f9e631 190BOOLEAN\r
191SearchEdidTiming (\r
192 VALID_EDID_TIMING *ValidEdidTiming,\r
193 EDID_TIMING *EdidTiming\r
194 )\r
195{\r
196 UINT32 Index;\r
197 UINT32 Key;\r
198\r
199 Key = CalculateEdidKey (EdidTiming);\r
200\r
201 for (Index = 0; Index < ValidEdidTiming->ValidNumber; Index ++) {\r
202 if (Key == ValidEdidTiming->Key[Index]) {\r
203 return TRUE;\r
204 }\r
205 }\r
206\r
207 return FALSE;\r
208}\r
209\r
210/**\r
211 Parse the Established Timing and Standard Timing in EDID data block.\r
212\r
213 @param EdidBuffer Pointer to EDID data block\r
214 @param ValidEdidTiming Valid EDID timing information\r
215\r
216 @retval TRUE The EDID data is valid.\r
217 @retval FALSE The EDID data is invalid.\r
218\r
219**/\r
31f9e631 220BOOLEAN\r
221ParseEdidData (\r
222 UINT8 *EdidBuffer,\r
223 VALID_EDID_TIMING *ValidEdidTiming\r
224 )\r
225{\r
226 UINT8 CheckSum;\r
227 UINT32 Index;\r
228 UINT32 ValidNumber;\r
229 UINT32 TimingBits;\r
230 UINT8 *BufferIndex;\r
231 UINT16 HorizontalResolution;\r
232 UINT16 VerticalResolution;\r
233 UINT8 AspectRatio;\r
234 UINT8 RefreshRate;\r
235 EDID_TIMING TempTiming;\r
236 EDID_BLOCK *EdidDataBlock;\r
237\r
238 EdidDataBlock = (EDID_BLOCK *) EdidBuffer;\r
239\r
240 //\r
241 // Check the checksum of EDID data\r
242 //\r
243 CheckSum = 0;\r
244 for (Index = 0; Index < EDID_BLOCK_SIZE; Index ++) {\r
245 CheckSum = (UINT8) (CheckSum + EdidBuffer[Index]);\r
246 }\r
247 if (CheckSum != 0) {\r
248 return FALSE;\r
249 }\r
250\r
251 ValidNumber = 0;\r
252 SetMem (ValidEdidTiming, sizeof (VALID_EDID_TIMING), 0);\r
253\r
254 if ((EdidDataBlock->EstablishedTimings[0] != 0) ||\r
255 (EdidDataBlock->EstablishedTimings[1] != 0) ||\r
256 (EdidDataBlock->EstablishedTimings[2] != 0)\r
257 ) {\r
258 //\r
259 // Established timing data\r
260 //\r
261 TimingBits = EdidDataBlock->EstablishedTimings[0] |\r
262 (EdidDataBlock->EstablishedTimings[1] << 8) |\r
263 ((EdidDataBlock->EstablishedTimings[2] & 0x80) << 9) ;\r
264 for (Index = 0; Index < VBE_EDID_ESTABLISHED_TIMING_MAX_NUMBER; Index ++) {\r
265 if (TimingBits & 0x1) {\r
266 ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&mVbeEstablishedEdidTiming[Index]);\r
267 ValidNumber ++;\r
268 }\r
269 TimingBits = TimingBits >> 1;\r
270 }\r
271 } else {\r
272 //\r
273 // If no Established timing data, read the standard timing data\r
274 //\r
275 BufferIndex = &EdidDataBlock->StandardTimingIdentification[0];\r
276 for (Index = 0; Index < 8; Index ++) {\r
277 if ((BufferIndex[0] != 0x1) && (BufferIndex[1] != 0x1)){\r
278 //\r
279 // A valid Standard Timing\r
280 //\r
281 HorizontalResolution = (UINT16) (BufferIndex[0] * 8 + 248);\r
282 AspectRatio = (UINT8) (BufferIndex[1] >> 6);\r
283 switch (AspectRatio) {\r
284 case 0:\r
285 VerticalResolution = (UINT16) (HorizontalResolution / 16 * 10);\r
286 break;\r
287 case 1:\r
288 VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);\r
289 break;\r
290 case 2:\r
291 VerticalResolution = (UINT16) (HorizontalResolution / 5 * 4);\r
292 break;\r
293 case 3:\r
294 VerticalResolution = (UINT16) (HorizontalResolution / 16 * 9);\r
295 break;\r
296 default:\r
297 VerticalResolution = (UINT16) (HorizontalResolution / 4 * 3);\r
298 break;\r
299 }\r
300 RefreshRate = (UINT8) ((BufferIndex[1] & 0x1f) + 60);\r
301 TempTiming.HorizontalResolution = HorizontalResolution;\r
302 TempTiming.VerticalResolution = VerticalResolution;\r
303 TempTiming.RefreshRate = RefreshRate;\r
304 ValidEdidTiming->Key[ValidNumber] = CalculateEdidKey (&TempTiming);\r
305 ValidNumber ++;\r
306 }\r
307 BufferIndex += 2;\r
308 }\r
309 }\r
310\r
311 ValidEdidTiming->ValidNumber = ValidNumber;\r
312 return TRUE;\r
313}\r
314\r
315/**\r
316 Construct the valid video modes for CirrusLogic5430.\r
317\r
318**/\r
319EFI_STATUS\r
320CirrusLogic5430VideoModeSetup (\r
321 CIRRUS_LOGIC_5430_PRIVATE_DATA *Private\r
322 )\r
323{\r
324 EFI_STATUS Status;\r
325 UINT32 Index;\r
326 BOOLEAN EdidFound;\r
327 EFI_EDID_OVERRIDE_PROTOCOL *EdidOverride;\r
328 UINT32 EdidAttributes;\r
329 BOOLEAN EdidOverrideFound;\r
330 UINTN EdidOverrideDataSize;\r
331 UINT8 *EdidOverrideDataBlock;\r
332 UINTN EdidDiscoveredDataSize;\r
333 UINT8 *EdidDiscoveredDataBlock;\r
334 UINTN EdidActiveDataSize;\r
335 UINT8 *EdidActiveDataBlock;\r
336 VALID_EDID_TIMING ValidEdidTiming;\r
337 UINT32 ValidModeCount;\r
338 CIRRUS_LOGIC_5430_MODE_DATA *ModeData;\r
339 BOOLEAN TimingMatch;\r
340 CIRRUS_LOGIC_5430_VIDEO_MODES *VideoMode;\r
341 EDID_TIMING TempTiming;\r
342\r
343 //\r
344 // setup EDID information\r
345 //\r
346 Private->EdidDiscovered.Edid = NULL;\r
347 Private->EdidDiscovered.SizeOfEdid = 0;\r
348 Private->EdidActive.Edid = NULL;\r
349 Private->EdidActive.SizeOfEdid = 0;\r
350\r
351 EdidFound = FALSE;\r
352 EdidOverrideFound = FALSE;\r
353 EdidAttributes = 0xff;\r
354 EdidOverrideDataSize = 0;\r
355 EdidOverrideDataBlock = NULL;\r
356 EdidActiveDataSize = 0;\r
357 EdidActiveDataBlock = NULL;\r
358 EdidDiscoveredDataBlock = NULL;\r
359\r
360 //\r
361 // Find EDID Override protocol firstly, this protocol is installed by platform if needed.\r
362 //\r
363 Status = gBS->LocateProtocol (\r
364 &gEfiEdidOverrideProtocolGuid,\r
365 NULL,\r
366 (VOID **) &EdidOverride\r
367 );\r
368 if (!EFI_ERROR (Status)) {\r
369 //\r
370 // Allocate double size of VESA_BIOS_EXTENSIONS_EDID_BLOCK_SIZE to avoid overflow\r
371 //\r
372 EdidOverrideDataBlock = AllocatePool (sizeof (EDID_BLOCK_SIZE * 2));\r
373 if (NULL == EdidOverrideDataBlock) {\r
374 Status = EFI_OUT_OF_RESOURCES;\r
375 goto Done;\r
376 }\r
377\r
378 Status = EdidOverride->GetEdid (\r
379 EdidOverride,\r
380 Private->Handle,\r
381 &EdidAttributes,\r
382 &EdidOverrideDataSize,\r
383 (UINT8 **) &EdidOverrideDataBlock\r
384 );\r
385 if (!EFI_ERROR (Status) &&\r
386 EdidAttributes == 0 &&\r
387 EdidOverrideDataSize != 0) {\r
388 //\r
389 // Succeeded to get EDID Override Data\r
390 //\r
391 EdidOverrideFound = TRUE;\r
392 }\r
393 }\r
394\r
395 if (EdidOverrideFound != TRUE || EdidAttributes == EFI_EDID_OVERRIDE_DONT_OVERRIDE) {\r
396 //\r
397 // If EDID Override data doesn't exist or EFI_EDID_OVERRIDE_DONT_OVERRIDE returned,\r
398 // read EDID information through I2C Bus\r
399 //\r
6f716497 400 if (ReadEdidData (Private, &EdidDiscoveredDataBlock, &EdidDiscoveredDataSize) == EFI_SUCCESS) {\r
31f9e631 401 Private->EdidDiscovered.SizeOfEdid = (UINT32) EdidDiscoveredDataSize;\r
402 Private->EdidDiscovered.Edid = (UINT8 *) AllocateCopyPool (\r
403 EdidDiscoveredDataSize,\r
404 EdidDiscoveredDataBlock\r
405 );\r
406\r
407 if (NULL == Private->EdidDiscovered.Edid) {\r
408 Status = EFI_OUT_OF_RESOURCES;\r
409 goto Done;\r
410 }\r
411\r
412 EdidActiveDataSize = Private->EdidDiscovered.SizeOfEdid;\r
413 EdidActiveDataBlock = Private->EdidDiscovered.Edid;\r
414\r
415 EdidFound = TRUE;\r
416 }\r
417 }\r
418\r
419 if (EdidFound != TRUE && EdidOverrideFound == TRUE) {\r
420 EdidActiveDataSize = EdidOverrideDataSize;\r
421 EdidActiveDataBlock = EdidOverrideDataBlock;\r
422 EdidFound = TRUE;\r
423 }\r
424\r
425 if (EdidFound == TRUE) {\r
426 //\r
427 // Parse EDID data structure to retrieve modes supported by monitor\r
428 //\r
429 if (ParseEdidData ((UINT8 *) EdidActiveDataBlock, &ValidEdidTiming) == TRUE) {\r
430 //\r
431 // Copy EDID Override Data to EDID Active Data\r
432 //\r
433 Private->EdidActive.SizeOfEdid = (UINT32) EdidActiveDataSize;\r
434 Private->EdidActive.Edid = (UINT8 *) AllocateCopyPool (\r
435 EdidActiveDataSize,\r
436 EdidActiveDataBlock\r
437 );\r
438 if (NULL == Private->EdidActive.Edid) {\r
439 Status = EFI_OUT_OF_RESOURCES;\r
440 goto Done;\r
441 }\r
442 }\r
443 } else {\r
444 Private->EdidActive.SizeOfEdid = 0;\r
445 Private->EdidActive.Edid = NULL;\r
446 EdidFound = FALSE;\r
447 }\r
448\r
449 if (EdidFound) {\r
450 //\r
451 // Initialize the private mode data with the supported modes.\r
452 //\r
453 ValidModeCount = 0;\r
454 ModeData = &Private->ModeData[0];\r
455 VideoMode = &CirrusLogic5430VideoModes[0];\r
456 for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index++) {\r
457\r
458 TimingMatch = TRUE;\r
459\r
460 //\r
461 // Check whether match with CirrusLogic5430 video mode\r
462 //\r
463 TempTiming.HorizontalResolution = (UINT16) VideoMode->Width;\r
464 TempTiming.VerticalResolution = (UINT16) VideoMode->Height;\r
465 TempTiming.RefreshRate = (UINT16) VideoMode->RefreshRate;\r
466 if (SearchEdidTiming (&ValidEdidTiming, &TempTiming) != TRUE) {\r
467 TimingMatch = FALSE;\r
468 }\r
469\r
470 //\r
471 // Not export Mode 0x0 as GOP mode, this is not defined in spec.\r
472 //\r
473 if ((VideoMode->Width == 0) || (VideoMode->Height == 0)) {\r
474 TimingMatch = FALSE;\r
475 }\r
476\r
477 if (TimingMatch) {\r
478 ModeData->ModeNumber = Index;\r
479 ModeData->HorizontalResolution = VideoMode->Width;\r
480 ModeData->VerticalResolution = VideoMode->Height;\r
481 ModeData->ColorDepth = VideoMode->ColorDepth;\r
482 ModeData->RefreshRate = VideoMode->RefreshRate;\r
483\r
484 ModeData ++;\r
485 ValidModeCount ++;\r
486 }\r
487\r
488 VideoMode ++;\r
489 }\r
490\r
491 Private->MaxMode = ValidModeCount;\r
492\r
493 } else {\r
494 //\r
495 // If EDID information wasn't found\r
496 //\r
497 ModeData = &Private->ModeData[0];\r
498 VideoMode = &CirrusLogic5430VideoModes[0];\r
499 for (Index = 0; Index < CIRRUS_LOGIC_5430_MODE_COUNT; Index ++) {\r
500 ModeData->ModeNumber = Index;\r
501 ModeData->HorizontalResolution = VideoMode->Width;\r
502 ModeData->VerticalResolution = VideoMode->Height;\r
503 ModeData->ColorDepth = VideoMode->ColorDepth;\r
504 ModeData->RefreshRate = VideoMode->RefreshRate;\r
505\r
506 ModeData ++ ;\r
507 VideoMode ++;\r
508 }\r
509 Private->MaxMode = CIRRUS_LOGIC_5430_MODE_COUNT;\r
510 }\r
511\r
6f716497 512 if (EdidOverrideDataBlock != NULL) {\r
513 FreePool (EdidOverrideDataBlock);\r
514 }\r
515\r
31f9e631 516 return EFI_SUCCESS;\r
517\r
518Done:\r
519 if (EdidOverrideDataBlock != NULL) {\r
520 FreePool (EdidOverrideDataBlock);\r
521 }\r
522 if (Private->EdidDiscovered.Edid != NULL) {\r
523 FreePool (Private->EdidDiscovered.Edid);\r
524 }\r
525 if (Private->EdidDiscovered.Edid != NULL) {\r
526 FreePool (Private->EdidActive.Edid);\r
527 }\r
528\r
529 return EFI_DEVICE_ERROR;\r
530}\r