blob: f6afb5656cf7e855ba8aebf98acd0b42c31d255c [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
15with HW;
16
17with HW.Debug;
18with GNAT.Source_Info;
19
20use type HW.Byte;
21use type HW.Pos16;
22use type HW.Word16;
23
24package body HW.GFX.EDID is
25
Nico Huber88a7f172016-11-18 21:19:46 +010026 subtype Header_Index is Raw_EDID_Index range 0 .. 7;
27 type Header_Type is new Buffer (Header_Index);
28 Header_Pattern : constant Header_Type :=
29 (16#00#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#00#);
30
Nico Huber83693c82016-10-08 22:17:55 +020031 function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
32 with
33 Pre => True
34 is
35 Sum : Byte := 16#00#;
36 begin
37 for I in Raw_EDID_Index loop
38 Sum := Sum + Raw_EDID (I);
39 end loop;
40 pragma Debug (Sum /= 16#00#, Debug.Put_Line
41 (GNAT.Source_Info.Enclosing_Entity & ": EDID checksum invalid!"));
42 return Sum = 16#00#;
43 end Checksum_Valid;
44
Nico Huber88a7f172016-11-18 21:19:46 +010045 function Header_Score (Raw_EDID : Raw_EDID_Data) return Natural
46 is
47 Score : Natural := 0;
48 begin
49 for I in Header_Index loop
50 pragma Loop_Invariant (Score <= I);
51 if Raw_EDID (I) = Header_Pattern (I) then
52 Score := Score + 1;
53 end if;
54 end loop;
55 return Score;
56 end Header_Score;
57
Nico Huber83693c82016-10-08 22:17:55 +020058 function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
59 is
Nico Huber83693c82016-10-08 22:17:55 +020060 begin
Nico Huber88a7f172016-11-18 21:19:46 +010061 return Header_Score (Raw_EDID) = 8 and then Checksum_Valid (Raw_EDID);
62 end Valid;
63
64 procedure Sanitize (Raw_EDID : in out Raw_EDID_Data; Success : out Boolean)
65 is
66 Score : constant Natural := Header_Score (Raw_EDID);
67 begin
68 pragma Debug (Score /= 8, Debug.Put_Line
Nico Huber83693c82016-10-08 22:17:55 +020069 (GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
70
Nico Huber88a7f172016-11-18 21:19:46 +010071 if Score = 6 or Score = 7 then
72 pragma Debug (Debug.Put_Line
73 (GNAT.Source_Info.Enclosing_Entity & ": Trying to fix up."));
74 Raw_EDID (Header_Index) := Buffer (Header_Pattern);
75 end if;
76
77 Success := Valid (Raw_EDID);
78 end Sanitize;
Nico Huber83693c82016-10-08 22:17:55 +020079
80 ----------------------------------------------------------------------------
81
82 REVISION : constant := 19;
83 INPUT : constant := 20;
84 INPUT_DIGITAL : constant := 1 * 2 ** 7;
85 INPUT_DIGITAL_DEPTH_SHIFT : constant := 4;
86 INPUT_DIGITAL_DEPTH_MASK : constant := 7 * 2 ** 4;
87 INPUT_DIGITAL_DEPTH_UNDEF : constant := 0 * 2 ** 4;
88 INPUT_DIGITAL_DEPTH_RESERVED : constant := 7 * 2 ** 4;
89
90 ----------------------------------------------------------------------------
91
Nico Huber393aa8a2016-10-21 14:18:53 +020092 function Compatible_Display
93 (Raw_EDID : Raw_EDID_Data;
94 Display : Display_Type)
95 return Boolean
96 is
97 begin
98 return (Display = VGA) = ((Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00#);
99 end Compatible_Display;
100
Nico Huber83693c82016-10-08 22:17:55 +0200101 function Read_LE16
102 (Raw_EDID : Raw_EDID_Data;
103 Offset : Raw_EDID_Index)
104 return Word16
105 is
106 begin
107 return Shift_Left (Word16 (Raw_EDID (Offset + 1)), 8) or
108 Word16 (Raw_EDID (Offset));
109 end Read_LE16;
110
111 function Has_Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Boolean
112 is
113 begin
114 return
115 Int64 (Read_LE16 (Raw_EDID, DESCRIPTOR_1)) * 10_000
116 in Frequency_Type and
117 ( Raw_EDID (DESCRIPTOR_1 + 2) /= 0 or
118 (Raw_EDID (DESCRIPTOR_1 + 4) and 16#f0#) /= 0) and
119 ( Raw_EDID (DESCRIPTOR_1 + 8) /= 0 or
120 (Raw_EDID (DESCRIPTOR_1 + 11) and 16#c0#) /= 0) and
121 ( Raw_EDID (DESCRIPTOR_1 + 9) /= 0 or
122 (Raw_EDID (DESCRIPTOR_1 + 11) and 16#30#) /= 0) and
123 ( Raw_EDID (DESCRIPTOR_1 + 3) /= 0 or
124 (Raw_EDID (DESCRIPTOR_1 + 4) and 16#0f#) /= 0) and
125 ( Raw_EDID (DESCRIPTOR_1 + 5) /= 0 or
126 (Raw_EDID (DESCRIPTOR_1 + 7) and 16#f0#) /= 0) and
127 ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#f0#) /= 0 or
128 (Raw_EDID (DESCRIPTOR_1 + 11) and 16#0c#) /= 0) and
129 ((Raw_EDID (DESCRIPTOR_1 + 10) and 16#0f#) /= 0 or
130 (Raw_EDID (DESCRIPTOR_1 + 11) and 16#03#) /= 0) and
131 ( Raw_EDID (DESCRIPTOR_1 + 6) /= 0 or
132 (Raw_EDID (DESCRIPTOR_1 + 7) and 16#0f#) /= 0);
133 end Has_Preferred_Mode;
134
135 function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
136 is
137 Base : constant := DESCRIPTOR_1;
138 Mode : Mode_Type;
139
140 function Read_12
141 (Lower_8, Upper_4 : Raw_EDID_Index;
142 Shift : Natural)
143 return Word16
144 is
145 begin
146 return
147 Word16 (Raw_EDID (Lower_8)) or
148 (Shift_Left (Word16 (Raw_EDID (Upper_4)), Shift) and 16#0f00#);
149 end Read_12;
150
151 function Read_10
152 (Lower_8, Upper_2 : Raw_EDID_Index;
153 Shift : Natural)
154 return Word16
155 is
156 begin
157 return
158 Word16 (Raw_EDID (Lower_8)) or
159 (Shift_Left (Word16 (Raw_EDID (Upper_2)), Shift) and 16#0300#);
160 end Read_10;
161
162 function Read_6
163 (Lower_4 : Raw_EDID_Index;
164 Lower_Shift : Natural;
165 Upper_2 : Raw_EDID_Index;
166 Upper_Shift : Natural)
167 return Word8
168 is
169 begin
170 return
171 (Shift_Right (Word8 (Raw_EDID (Lower_4)), Lower_Shift) and 16#0f#)
172 or
173 (Shift_Left (Word8 (Raw_EDID (Upper_2)), Upper_Shift) and 16#30#);
174 end Read_6;
175 begin
176 Mode := Mode_Type'
177 (Dotclock => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
178 H_Visible => Pos16 (Read_12 (Base + 2, Base + 4, 4)),
179 H_Sync_Begin => Pos16 (Read_10 (Base + 8, Base + 11, 2)),
180 H_Sync_End => Pos16 (Read_10 (Base + 9, Base + 11, 4)),
181 H_Total => Pos16 (Read_12 (Base + 3, Base + 4, 8)),
182 V_Visible => Pos16 (Read_12 (Base + 5, Base + 7, 4)),
183 V_Sync_Begin => Pos16 (Read_6 (Base + 10, 4, Base + 11, 2)),
184 V_Sync_End => Pos16 (Read_6 (Base + 10, 0, Base + 11, 4)),
185 V_Total => Pos16 (Read_12 (Base + 6, Base + 7, 8)),
186 H_Sync_Active_High => (Raw_EDID (Base + 17) and 16#02#) /= 0,
187 V_Sync_Active_High => (Raw_EDID (Base + 17) and 16#04#) /= 0,
188 BPC =>
189 (if Raw_EDID (REVISION) < 4 or
190 (Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00# or
191 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_UNDEF or
192 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_RESERVED
193 then
Nico Huber16f3dec2016-10-09 19:33:34 +0200194 Auto_BPC
Nico Huber83693c82016-10-08 22:17:55 +0200195 else
196 4 + 2 * Pos64 (Shift_Right
197 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK,
198 INPUT_DIGITAL_DEPTH_SHIFT))));
199
200 -- Calculate absolute values from EDID relative values.
201 Mode.H_Sync_Begin := Mode.H_Visible + Mode.H_Sync_Begin;
202 Mode.H_Sync_End := Mode.H_Sync_Begin + Mode.H_Sync_End;
203 Mode.H_Total := Mode.H_Visible + Mode.H_Total;
204 Mode.V_Sync_Begin := Mode.V_Visible + Mode.V_Sync_Begin;
205 Mode.V_Sync_End := Mode.V_Sync_Begin + Mode.V_Sync_End;
206 Mode.V_Total := Mode.V_Visible + Mode.V_Total;
207
208 return Mode;
209 end Preferred_Mode;
210
211end HW.GFX.EDID;