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;
 
    ----------------------------------------------------------------------------