edid: Sanitize bad EDID header patterns
Some devices fail to transmit the EDID header pattern correctly. To
compensate, we patch EDID header patterns when at least six out of
the eight bytes match. We then verify the checksum over all bytes
including the patched header.
Linux does it similar and gets more displays up that way, sigh.
Change-Id: Ic63880a05e64f40dfda165bd54e708cd981f08fb
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/17493
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/common/hw-gfx-edid.adb b/common/hw-gfx-edid.adb
index 44d729f..f6afb56 100644
--- a/common/hw-gfx-edid.adb
+++ b/common/hw-gfx-edid.adb
@@ -23,6 +23,11 @@
package body HW.GFX.EDID is
+ subtype Header_Index is Raw_EDID_Index range 0 .. 7;
+ type Header_Type is new Buffer (Header_Index);
+ Header_Pattern : constant Header_Type :=
+ (16#00#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#00#);
+
function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
with
Pre => True
@@ -37,24 +42,40 @@
return Sum = 16#00#;
end Checksum_Valid;
+ function Header_Score (Raw_EDID : Raw_EDID_Data) return Natural
+ is
+ Score : Natural := 0;
+ begin
+ for I in Header_Index loop
+ pragma Loop_Invariant (Score <= I);
+ if Raw_EDID (I) = Header_Pattern (I) then
+ Score := Score + 1;
+ end if;
+ end loop;
+ return Score;
+ end Header_Score;
+
function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
is
- Header_Valid : Boolean;
begin
- Header_Valid :=
- Raw_EDID (0) = 16#00# and
- Raw_EDID (1) = 16#ff# and
- Raw_EDID (2) = 16#ff# and
- Raw_EDID (3) = 16#ff# and
- Raw_EDID (4) = 16#ff# and
- Raw_EDID (5) = 16#ff# and
- Raw_EDID (6) = 16#ff# and
- Raw_EDID (7) = 16#00#;
- pragma Debug (not Header_Valid, Debug.Put_Line
+ return Header_Score (Raw_EDID) = 8 and then Checksum_Valid (Raw_EDID);
+ end Valid;
+
+ procedure Sanitize (Raw_EDID : in out Raw_EDID_Data; Success : out Boolean)
+ is
+ Score : constant Natural := Header_Score (Raw_EDID);
+ begin
+ pragma Debug (Score /= 8, Debug.Put_Line
(GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
- return Header_Valid and then Checksum_Valid (Raw_EDID);
- end Valid;
+ if Score = 6 or Score = 7 then
+ pragma Debug (Debug.Put_Line
+ (GNAT.Source_Info.Enclosing_Entity & ": Trying to fix up."));
+ Raw_EDID (Header_Index) := Buffer (Header_Pattern);
+ end if;
+
+ Success := Valid (Raw_EDID);
+ end Sanitize;
----------------------------------------------------------------------------
diff --git a/common/hw-gfx-edid.ads b/common/hw-gfx-edid.ads
index cce1a40..abbb2ee 100644
--- a/common/hw-gfx-edid.ads
+++ b/common/hw-gfx-edid.ads
@@ -22,6 +22,9 @@
subtype Raw_EDID_Data is Buffer (Raw_EDID_Index);
function Valid (Raw_EDID : Raw_EDID_Data) return Boolean;
+ procedure Sanitize (Raw_EDID : in out Raw_EDID_Data; Success : out Boolean)
+ with
+ Post => (if Success then Valid (Raw_EDID));
DESCRIPTOR_1 : constant := 54;
diff --git a/common/hw-gfx-gma-connector_info.adb b/common/hw-gfx-gma-connector_info.adb
index c5dcad2..89e0425 100644
--- a/common/hw-gfx-gma-connector_info.adb
+++ b/common/hw-gfx-gma-connector_info.adb
@@ -70,7 +70,7 @@
exit when not Success; -- don't retry if reading itself failed
pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
- Success := EDID.Valid (Raw_EDID);
+ EDID.Sanitize (Raw_EDID, Success);
exit when Success;
end loop;
end Read_EDID;