blob: ab6c05fba61dfebc690b72af0ec4fe0cc767f212 [file] [log] [blame]
Nico Huber8c45bcf2016-11-20 17:30:57 +01001--
2-- Copyright (C) 2015-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
6-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
8--
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.GFX.I2C;
16with HW.GFX.EDID;
17with HW.GFX.GMA.Config;
18with HW.GFX.GMA.Config_Helpers;
19with HW.GFX.GMA.I2C;
20with HW.GFX.GMA.DP_Aux_Ch;
21with HW.GFX.GMA.Panel;
22with HW.GFX.GMA.Power_And_Clocks;
23
24with HW.Debug;
25with GNAT.Source_Info;
26
27package body HW.GFX.GMA.Display_Probing
28is
29
30 function Port_Configured
31 (Configs : Pipe_Configs;
32 Port : Port_Type)
33 return Boolean
34 with
35 Global => null
36 is
37 begin
38 return Configs (Primary).Port = Port or
39 Configs (Secondary).Port = Port or
40 Configs (Tertiary).Port = Port;
41 end Port_Configured;
42
43 -- DP and HDMI share physical pins.
44 function Sibling_Port (Port : Port_Type) return Port_Type
45 is
46 begin
47 return
48 (case Port is
49 when HDMI1 => DP1,
50 when HDMI2 => DP2,
51 when HDMI3 => DP3,
52 when DP1 => HDMI1,
53 when DP2 => HDMI2,
54 when DP3 => HDMI3,
55 when others => Disabled);
56 end Sibling_Port;
57
58 function Has_Sibling_Port (Port : Port_Type) return Boolean
59 is
60 begin
61 return Sibling_Port (Port) /= Disabled;
62 end Has_Sibling_Port;
63
Nico Huber1bc496f2017-06-09 22:23:28 +020064 function Is_DVI_I (Port : Active_Port_Type) return Boolean
65 with
66 Global => null
67 is
68 begin
69 return Config.Have_DVI_I and
70 (Port = Analog or
71 Config_Helpers.To_PCH_Port (Port) = Config.Analog_I2C_Port);
72 end Is_DVI_I;
73
Nico Huber8c45bcf2016-11-20 17:30:57 +010074 procedure Read_EDID
75 (Raw_EDID : out EDID.Raw_EDID_Data;
76 Port : in Active_Port_Type;
77 Success : out Boolean)
78 with
79 Post => (if Success then EDID.Valid (Raw_EDID))
80 is
81 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
82 begin
83 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
84
85 for I in 1 .. 2 loop
86 if Config_Helpers.To_Display_Type (Port) = DP then
87 -- May need power to read edid
88 declare
89 Temp_Configs : Pipe_Configs := Cur_Configs;
90 begin
91 Temp_Configs (Primary).Port := Port;
92 Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
93 end;
94
95 declare
96 DP_Port : constant GMA.DP_Port :=
97 (case Port is
98 when Internal => DP_A,
99 when DP1 => DP_B,
100 when DP2 => DP_C,
101 when DP3 => DP_D,
102 when others => GMA.DP_Port'First);
103 begin
104 DP_Aux_Ch.I2C_Read
105 (Port => DP_Port,
106 Address => 16#50#,
107 Length => Raw_EDID_Length,
108 Data => Raw_EDID,
109 Success => Success);
110 end;
111 else
112 I2C.I2C_Read
113 (Port => (if Port = Analog
114 then Config.Analog_I2C_Port
115 else Config_Helpers.To_PCH_Port (Port)),
116 Address => 16#50#,
117 Length => Raw_EDID_Length,
118 Data => Raw_EDID,
119 Success => Success);
120 end if;
121 exit when not Success; -- don't retry if reading itself failed
122
123 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
124 EDID.Sanitize (Raw_EDID, Success);
125 exit when Success;
126 end loop;
127 end Read_EDID;
128
129 procedure Probe_Port
130 (Pipe_Cfg : in out Pipe_Config;
131 Port : in Active_Port_Type;
132 Success : out Boolean)
133 with Pre => True
134 is
135 Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
136 begin
137 Success := Config.Valid_Port (Port);
138
139 if Success then
140 if Port = Internal then
Nico Huber6f9a50d2016-11-21 23:21:14 +0100141 Panel.Wait_On;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100142 end if;
143 Read_EDID (Raw_EDID, Port, Success);
144 end if;
145
146 if Success and then
Nico Huber1bc496f2017-06-09 22:23:28 +0200147 ((not Is_DVI_I (Port) or EDID.Compatible_Display
148 (Raw_EDID, Config_Helpers.To_Display_Type (Port))) and
Nico Huber8c45bcf2016-11-20 17:30:57 +0100149 EDID.Has_Preferred_Mode (Raw_EDID))
150 then
151 Pipe_Cfg.Port := Port;
152 Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
153
154 pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
155 Reason => "We just want to check if it's readable.");
156 if Has_Sibling_Port (Port) then
157 -- Probe sibling port too and bail out if something is detected.
158 -- This is a precaution for adapters that expose the pins of a
159 -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
160 -- user might have attached both by accident and there are ru-
161 -- mors of displays that got fried by applying the wrong signal.
162 declare
163 Have_Sibling_EDID : Boolean;
164 begin
165 Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
166 if Have_Sibling_EDID then
167 Pipe_Cfg.Port := Disabled;
168 Success := False;
169 end if;
170 end;
171 end if;
172 pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
173 else
174 Success := False;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100175 end if;
176 end Probe_Port;
177
178 procedure Scan_Ports
Nico Huber4c7356d2016-12-16 14:22:32 +0100179 (Configs : out Pipe_Configs;
180 Ports : in Port_List := All_Ports;
181 Max_Pipe : in Pipe_Index := Pipe_Index'Last;
182 Keep_Power : in Boolean := False)
Nico Huber8c45bcf2016-11-20 17:30:57 +0100183 is
Nico Huber6f9a50d2016-11-21 23:21:14 +0100184 Probe_Internal : Boolean := False;
185
Nico Huber8c45bcf2016-11-20 17:30:57 +0100186 Port_Idx : Port_List_Range := Port_List_Range'First;
187 Success : Boolean;
188 begin
189 Configs := (Pipe_Index =>
190 (Port => Disabled,
191 Mode => Invalid_Mode,
Nico Hubera02b2c62018-01-09 15:58:34 +0100192 Cursor => Default_Cursor,
Nico Huber8c45bcf2016-11-20 17:30:57 +0100193 Framebuffer => Default_FB));
194
Nico Huber6f9a50d2016-11-21 23:21:14 +0100195 -- Turn panel on early to probe other ports during the power on delay.
196 for Idx in Port_List_Range loop
197 exit when Ports (Idx) = Disabled;
198 if Ports (Idx) = Internal then
199 Panel.On (Wait => False);
200 Probe_Internal := True;
201 exit;
202 end if;
203 end loop;
204
Nico Huber8c45bcf2016-11-20 17:30:57 +0100205 for Pipe in Pipe_Index range
206 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
207 loop
208 while Ports (Port_Idx) /= Disabled loop
209 if not Port_Configured (Configs, Ports (Port_Idx)) and
210 (not Has_Sibling_Port (Ports (Port_Idx)) or
211 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
212 then
213 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
214 else
215 Success := False;
216 end if;
217
218 exit when Port_Idx = Port_List_Range'Last;
219 Port_Idx := Port_List_Range'Succ (Port_Idx);
220
221 exit when Success;
222 end loop;
223 end loop;
224
225 -- Restore power settings
Nico Huber4c7356d2016-12-16 14:22:32 +0100226 if not Keep_Power then
227 Power_And_Clocks.Power_Set_To (Cur_Configs);
228 end if;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100229
230 -- Turn panel power off if probing failed.
231 if Probe_Internal and not Port_Configured (Configs, Internal) then
232 Panel.Off;
233 end if;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100234 end Scan_Ports;
235
236end HW.GFX.GMA.Display_Probing;