blob: d8dba1d4ac17deeaac820333418308ba7badf9bf [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2016 secunet Security Networks AG
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02006-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02008--
9-- This program is distributed in the hope that it will be useful,
10-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-- GNU General Public License for more details.
13--
14
Nico Huber83693c82016-10-08 22:17:55 +020015with HW.Debug;
16with GNAT.Source_Info;
17
18use type HW.Byte;
Nico Huber83693c82016-10-08 22:17:55 +020019use type HW.Word16;
20
21package body HW.GFX.EDID is
22
Nico Huber88a7f172016-11-18 21:19:46 +010023 subtype Header_Index is Raw_EDID_Index range 0 .. 7;
24 type Header_Type is new Buffer (Header_Index);
25 Header_Pattern : constant Header_Type :=
26 (16#00#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#00#);
27
Nico Huber83693c82016-10-08 22:17:55 +020028 function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
29 with
30 Pre => True
31 is
32 Sum : Byte := 16#00#;
33 begin
34 for I in Raw_EDID_Index loop
35 Sum := Sum + Raw_EDID (I);
36 end loop;
37 pragma Debug (Sum /= 16#00#, Debug.Put_Line
38 (GNAT.Source_Info.Enclosing_Entity & ": EDID checksum invalid!"));
39 return Sum = 16#00#;
40 end Checksum_Valid;
41
Nico Huber88a7f172016-11-18 21:19:46 +010042 function Header_Score (Raw_EDID : Raw_EDID_Data) return Natural
43 is
44 Score : Natural := 0;
45 begin
46 for I in Header_Index loop
47 pragma Loop_Invariant (Score <= I);
48 if Raw_EDID (I) = Header_Pattern (I) then
49 Score := Score + 1;
50 end if;
51 end loop;
52 return Score;
53 end Header_Score;
54
Nico Huber83693c82016-10-08 22:17:55 +020055 function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
56 is
Nico Huber83693c82016-10-08 22:17:55 +020057 begin
Nico Huber88a7f172016-11-18 21:19:46 +010058 return Header_Score (Raw_EDID) = 8 and then Checksum_Valid (Raw_EDID);
59 end Valid;
60
61 procedure Sanitize (Raw_EDID : in out Raw_EDID_Data; Success : out Boolean)
62 is
63 Score : constant Natural := Header_Score (Raw_EDID);
64 begin
65 pragma Debug (Score /= 8, Debug.Put_Line
Nico Huber83693c82016-10-08 22:17:55 +020066 (GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
67
Nico Huber88a7f172016-11-18 21:19:46 +010068 if Score = 6 or Score = 7 then
69 pragma Debug (Debug.Put_Line
70 (GNAT.Source_Info.Enclosing_Entity & ": Trying to fix up."));
71 Raw_EDID (Header_Index) := Buffer (Header_Pattern);
72 end if;
73
74 Success := Valid (Raw_EDID);
75 end Sanitize;
Nico Huber83693c82016-10-08 22:17:55 +020076
77 ----------------------------------------------------------------------------
78
79 REVISION : constant := 19;
80 INPUT : constant := 20;
81 INPUT_DIGITAL : constant := 1 * 2 ** 7;
82 INPUT_DIGITAL_DEPTH_SHIFT : constant := 4;
83 INPUT_DIGITAL_DEPTH_MASK : constant := 7 * 2 ** 4;
84 INPUT_DIGITAL_DEPTH_UNDEF : constant := 0 * 2 ** 4;
85 INPUT_DIGITAL_DEPTH_RESERVED : constant := 7 * 2 ** 4;
86
87 ----------------------------------------------------------------------------
88
Nico Huber393aa8a2016-10-21 14:18:53 +020089 function Compatible_Display
90 (Raw_EDID : Raw_EDID_Data;
91 Display : Display_Type)
92 return Boolean
93 is
94 begin
95 return (Display = VGA) = ((Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00#);
96 end Compatible_Display;
97
Nico Huber83693c82016-10-08 22:17:55 +020098 function Read_LE16
99 (Raw_EDID : Raw_EDID_Data;
100 Offset : Raw_EDID_Index)
101 return Word16
102 is
103 begin
104 return Shift_Left (Word16 (Raw_EDID (Offset + 1)), 8) or
105 Word16 (Raw_EDID (Offset));
106 end Read_LE16;
107
Nico Huber83693c82016-10-08 22:17:55 +0200108 function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
109 is
110 Base : constant := DESCRIPTOR_1;
111 Mode : Mode_Type;
112
113 function Read_12
114 (Lower_8, Upper_4 : Raw_EDID_Index;
115 Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200116 return Word16 is
117 ( Word16 (Raw_EDID (Lower_8)) or
118 (Shift_Left (Word16 (Raw_EDID (Upper_4)), Shift) and 16#0f00#));
Nico Huber83693c82016-10-08 22:17:55 +0200119
120 function Read_10
121 (Lower_8, Upper_2 : Raw_EDID_Index;
122 Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200123 return Word16 is
124 ( Word16 (Raw_EDID (Lower_8)) or
125 (Shift_Left (Word16 (Raw_EDID (Upper_2)), Shift) and 16#0300#));
Nico Huber83693c82016-10-08 22:17:55 +0200126
127 function Read_6
128 (Lower_4 : Raw_EDID_Index;
129 Lower_Shift : Natural;
130 Upper_2 : Raw_EDID_Index;
131 Upper_Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200132 return Word8 is
133 ((Shift_Right (Word8 (Raw_EDID (Lower_4)), Lower_Shift) and 16#0f#) or
134 (Shift_Left (Word8 (Raw_EDID (Upper_2)), Upper_Shift) and 16#30#));
Nico Huber83693c82016-10-08 22:17:55 +0200135 begin
136 Mode := Mode_Type'
137 (Dotclock => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
Nico Huberc5c767a2018-06-03 01:09:04 +0200138 H_Visible => Width_Type (Read_12 (Base + 2, Base + 4, 4)),
139 H_Sync_Begin => Width_Type (Read_10 (Base + 8, Base + 11, 2)),
140 H_Sync_End => Width_Type (Read_10 (Base + 9, Base + 11, 4)),
141 H_Total => Width_Type (Read_12 (Base + 3, Base + 4, 8)),
142 V_Visible => Height_Type (Read_12 (Base + 5, Base + 7, 4)),
143 V_Sync_Begin => Height_Type (Read_6 (Base + 10, 4, Base + 11, 2)),
144 V_Sync_End => Height_Type (Read_6 (Base + 10, 0, Base + 11, 4)),
145 V_Total => Height_Type (Read_12 (Base + 6, Base + 7, 8)),
Nico Huber83693c82016-10-08 22:17:55 +0200146 H_Sync_Active_High => (Raw_EDID (Base + 17) and 16#02#) /= 0,
147 V_Sync_Active_High => (Raw_EDID (Base + 17) and 16#04#) /= 0,
148 BPC =>
149 (if Raw_EDID (REVISION) < 4 or
150 (Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00# or
151 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_UNDEF or
152 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_RESERVED
153 then
Nico Huber16f3dec2016-10-09 19:33:34 +0200154 Auto_BPC
Nico Huber83693c82016-10-08 22:17:55 +0200155 else
156 4 + 2 * Pos64 (Shift_Right
157 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK,
158 INPUT_DIGITAL_DEPTH_SHIFT))));
159
160 -- Calculate absolute values from EDID relative values.
161 Mode.H_Sync_Begin := Mode.H_Visible + Mode.H_Sync_Begin;
162 Mode.H_Sync_End := Mode.H_Sync_Begin + Mode.H_Sync_End;
163 Mode.H_Total := Mode.H_Visible + Mode.H_Total;
164 Mode.V_Sync_Begin := Mode.V_Visible + Mode.V_Sync_Begin;
165 Mode.V_Sync_End := Mode.V_Sync_Begin + Mode.V_Sync_End;
166 Mode.V_Total := Mode.V_Visible + Mode.V_Total;
167
168 return Mode;
169 end Preferred_Mode;
170
171end HW.GFX.EDID;