blob: f1fae583d1b9a4a951ffeea92dcbcd962c98a9f8 [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;
Nico Huber83693c82016-10-08 22:17:55 +020021use type HW.Word16;
22
23package body HW.GFX.EDID is
24
Nico Huber88a7f172016-11-18 21:19:46 +010025 subtype Header_Index is Raw_EDID_Index range 0 .. 7;
26 type Header_Type is new Buffer (Header_Index);
27 Header_Pattern : constant Header_Type :=
28 (16#00#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#ff#, 16#00#);
29
Nico Huber83693c82016-10-08 22:17:55 +020030 function Checksum_Valid (Raw_EDID : Raw_EDID_Data) return Boolean
31 with
32 Pre => True
33 is
34 Sum : Byte := 16#00#;
35 begin
36 for I in Raw_EDID_Index loop
37 Sum := Sum + Raw_EDID (I);
38 end loop;
39 pragma Debug (Sum /= 16#00#, Debug.Put_Line
40 (GNAT.Source_Info.Enclosing_Entity & ": EDID checksum invalid!"));
41 return Sum = 16#00#;
42 end Checksum_Valid;
43
Nico Huber88a7f172016-11-18 21:19:46 +010044 function Header_Score (Raw_EDID : Raw_EDID_Data) return Natural
45 is
46 Score : Natural := 0;
47 begin
48 for I in Header_Index loop
49 pragma Loop_Invariant (Score <= I);
50 if Raw_EDID (I) = Header_Pattern (I) then
51 Score := Score + 1;
52 end if;
53 end loop;
54 return Score;
55 end Header_Score;
56
Nico Huber83693c82016-10-08 22:17:55 +020057 function Valid (Raw_EDID : Raw_EDID_Data) return Boolean
58 is
Nico Huber83693c82016-10-08 22:17:55 +020059 begin
Nico Huber88a7f172016-11-18 21:19:46 +010060 return Header_Score (Raw_EDID) = 8 and then Checksum_Valid (Raw_EDID);
61 end Valid;
62
63 procedure Sanitize (Raw_EDID : in out Raw_EDID_Data; Success : out Boolean)
64 is
65 Score : constant Natural := Header_Score (Raw_EDID);
66 begin
67 pragma Debug (Score /= 8, Debug.Put_Line
Nico Huber83693c82016-10-08 22:17:55 +020068 (GNAT.Source_Info.Enclosing_Entity & ": EDID header pattern mismatch!"));
69
Nico Huber88a7f172016-11-18 21:19:46 +010070 if Score = 6 or Score = 7 then
71 pragma Debug (Debug.Put_Line
72 (GNAT.Source_Info.Enclosing_Entity & ": Trying to fix up."));
73 Raw_EDID (Header_Index) := Buffer (Header_Pattern);
74 end if;
75
76 Success := Valid (Raw_EDID);
77 end Sanitize;
Nico Huber83693c82016-10-08 22:17:55 +020078
79 ----------------------------------------------------------------------------
80
81 REVISION : constant := 19;
82 INPUT : constant := 20;
83 INPUT_DIGITAL : constant := 1 * 2 ** 7;
84 INPUT_DIGITAL_DEPTH_SHIFT : constant := 4;
85 INPUT_DIGITAL_DEPTH_MASK : constant := 7 * 2 ** 4;
86 INPUT_DIGITAL_DEPTH_UNDEF : constant := 0 * 2 ** 4;
87 INPUT_DIGITAL_DEPTH_RESERVED : constant := 7 * 2 ** 4;
88
89 ----------------------------------------------------------------------------
90
Nico Huber393aa8a2016-10-21 14:18:53 +020091 function Compatible_Display
92 (Raw_EDID : Raw_EDID_Data;
93 Display : Display_Type)
94 return Boolean
95 is
96 begin
97 return (Display = VGA) = ((Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00#);
98 end Compatible_Display;
99
Nico Huber83693c82016-10-08 22:17:55 +0200100 function Read_LE16
101 (Raw_EDID : Raw_EDID_Data;
102 Offset : Raw_EDID_Index)
103 return Word16
104 is
105 begin
106 return Shift_Left (Word16 (Raw_EDID (Offset + 1)), 8) or
107 Word16 (Raw_EDID (Offset));
108 end Read_LE16;
109
Nico Huber83693c82016-10-08 22:17:55 +0200110 function Preferred_Mode (Raw_EDID : Raw_EDID_Data) return Mode_Type
111 is
112 Base : constant := DESCRIPTOR_1;
113 Mode : Mode_Type;
114
115 function Read_12
116 (Lower_8, Upper_4 : Raw_EDID_Index;
117 Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200118 return Word16 is
119 ( Word16 (Raw_EDID (Lower_8)) or
120 (Shift_Left (Word16 (Raw_EDID (Upper_4)), Shift) and 16#0f00#));
Nico Huber83693c82016-10-08 22:17:55 +0200121
122 function Read_10
123 (Lower_8, Upper_2 : Raw_EDID_Index;
124 Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200125 return Word16 is
126 ( Word16 (Raw_EDID (Lower_8)) or
127 (Shift_Left (Word16 (Raw_EDID (Upper_2)), Shift) and 16#0300#));
Nico Huber83693c82016-10-08 22:17:55 +0200128
129 function Read_6
130 (Lower_4 : Raw_EDID_Index;
131 Lower_Shift : Natural;
132 Upper_2 : Raw_EDID_Index;
133 Upper_Shift : Natural)
Nico Huber7eb13502018-06-04 14:42:13 +0200134 return Word8 is
135 ((Shift_Right (Word8 (Raw_EDID (Lower_4)), Lower_Shift) and 16#0f#) or
136 (Shift_Left (Word8 (Raw_EDID (Upper_2)), Upper_Shift) and 16#30#));
Nico Huber83693c82016-10-08 22:17:55 +0200137 begin
138 Mode := Mode_Type'
139 (Dotclock => Pos64 (Read_LE16 (Raw_EDID, Base)) * 10_000,
Nico Huberc5c767a2018-06-03 01:09:04 +0200140 H_Visible => Width_Type (Read_12 (Base + 2, Base + 4, 4)),
141 H_Sync_Begin => Width_Type (Read_10 (Base + 8, Base + 11, 2)),
142 H_Sync_End => Width_Type (Read_10 (Base + 9, Base + 11, 4)),
143 H_Total => Width_Type (Read_12 (Base + 3, Base + 4, 8)),
144 V_Visible => Height_Type (Read_12 (Base + 5, Base + 7, 4)),
145 V_Sync_Begin => Height_Type (Read_6 (Base + 10, 4, Base + 11, 2)),
146 V_Sync_End => Height_Type (Read_6 (Base + 10, 0, Base + 11, 4)),
147 V_Total => Height_Type (Read_12 (Base + 6, Base + 7, 8)),
Nico Huber83693c82016-10-08 22:17:55 +0200148 H_Sync_Active_High => (Raw_EDID (Base + 17) and 16#02#) /= 0,
149 V_Sync_Active_High => (Raw_EDID (Base + 17) and 16#04#) /= 0,
150 BPC =>
151 (if Raw_EDID (REVISION) < 4 or
152 (Raw_EDID (INPUT) and INPUT_DIGITAL) = 16#00# or
153 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_UNDEF or
154 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK) = INPUT_DIGITAL_DEPTH_RESERVED
155 then
Nico Huber16f3dec2016-10-09 19:33:34 +0200156 Auto_BPC
Nico Huber83693c82016-10-08 22:17:55 +0200157 else
158 4 + 2 * Pos64 (Shift_Right
159 (Raw_EDID (INPUT) and INPUT_DIGITAL_DEPTH_MASK,
160 INPUT_DIGITAL_DEPTH_SHIFT))));
161
162 -- Calculate absolute values from EDID relative values.
163 Mode.H_Sync_Begin := Mode.H_Visible + Mode.H_Sync_Begin;
164 Mode.H_Sync_End := Mode.H_Sync_Begin + Mode.H_Sync_End;
165 Mode.H_Total := Mode.H_Visible + Mode.H_Total;
166 Mode.V_Sync_Begin := Mode.V_Visible + Mode.V_Sync_Begin;
167 Mode.V_Sync_End := Mode.V_Sync_Begin + Mode.V_Sync_End;
168 Mode.V_Total := Mode.V_Visible + Mode.V_Total;
169
170 return Mode;
171 end Preferred_Mode;
172
173end HW.GFX.EDID;