blob: 491a88f38d60f4d1befb4037c378e2e9cceffa34 [file] [log] [blame]
Nico Huber8c45bcf2016-11-20 17:30:57 +01001--
2-- Copyright (C) 2015-2016 secunet Security Networks AG
Nico Huber4fc6dc22019-05-10 13:01:29 +02003-- Copyright (C) 2019 Nico Huber <nico.h@gmx.de>
Nico Huber8c45bcf2016-11-20 17:30:57 +01004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
7-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
9--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
16with HW.GFX.I2C;
Nico Huber8c45bcf2016-11-20 17:30:57 +010017with 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;
Nico Huber92de9c42019-09-29 19:03:58 +020022with HW.GFX.GMA.Port_Detect;
Nico Huber8c45bcf2016-11-20 17:30:57 +010023with HW.GFX.GMA.Power_And_Clocks;
24
25with HW.Debug;
26with GNAT.Source_Info;
27
28package body HW.GFX.GMA.Display_Probing
29is
30
Nico Huber5a3191f2018-06-04 14:42:13 +020031 function Port_Configured (Configs : Pipe_Configs; Port : Port_Type)
32 return Boolean is
33 (Configs (Primary).Port = Port or
34 Configs (Secondary).Port = Port or
35 Configs (Tertiary).Port = Port);
Nico Huber8c45bcf2016-11-20 17:30:57 +010036
37 -- DP and HDMI share physical pins.
Nico Huber5a3191f2018-06-04 14:42:13 +020038 function Sibling_Port (Port : Port_Type) return Port_Type is
39 (case Port is
40 when HDMI1 => DP1,
41 when HDMI2 => DP2,
42 when HDMI3 => DP3,
43 when DP1 => HDMI1,
44 when DP2 => HDMI2,
45 when DP3 => HDMI3,
Nico Huber8b381fc2024-03-26 18:04:19 +010046 when HDMI_TC1 => DP_TC1,
47 when HDMI_TC2 => DP_TC2,
48 when HDMI_TC3 => DP_TC3,
49 when HDMI_TC4 => DP_TC4,
50 when DP_TC1 => HDMI_TC1,
51 when DP_TC2 => HDMI_TC2,
52 when DP_TC3 => HDMI_TC3,
53 when DP_TC4 => HDMI_TC4,
Nico Huber5a3191f2018-06-04 14:42:13 +020054 when others => Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010055
Nico Huber5a3191f2018-06-04 14:42:13 +020056 function Has_Sibling_Port (Port : Port_Type) return Boolean is
57 (Sibling_Port (Port) /= Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010058
Nico Huber5a3191f2018-06-04 14:42:13 +020059 function Is_DVI_I (Port : Active_Port_Type) return Boolean is
60 (Config.Have_DVI_I and
61 (Port = Analog or
62 Config_Helpers.To_PCH_Port (Port) = Config.Analog_I2C_Port));
Nico Huber1bc496f2017-06-09 22:23:28 +020063
Nico Huber8c45bcf2016-11-20 17:30:57 +010064 procedure Read_EDID
65 (Raw_EDID : out EDID.Raw_EDID_Data;
66 Port : in Active_Port_Type;
67 Success : out Boolean)
Nico Huber8c45bcf2016-11-20 17:30:57 +010068 is
69 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
70 begin
71 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
72
73 for I in 1 .. 2 loop
74 if Config_Helpers.To_Display_Type (Port) = DP then
Nico Huberb0bbdbc2019-09-27 22:32:21 +020075 -- May need power and CDClk to read EDID
Nico Huber8c45bcf2016-11-20 17:30:57 +010076 declare
77 Temp_Configs : Pipe_Configs := Cur_Configs;
78 begin
79 Temp_Configs (Primary).Port := Port;
80 Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
Nico Huberb0bbdbc2019-09-27 22:32:21 +020081 Power_And_Clocks.Enable_CDClk;
Nico Huber8c45bcf2016-11-20 17:30:57 +010082 end;
83
84 declare
85 DP_Port : constant GMA.DP_Port :=
Nico Huber8b381fc2024-03-26 18:04:19 +010086 (if Config.Has_Type_C_Ports then
87 (case Config_Helpers.To_GPU_Port (Pipe_Index'First, Port) is
88 when DIGI_A => DP_A,
89 when DIGI_B => DP_B,
90 when DIGI_C => DP_C,
91 when DDI_TC1 => DP_D,
92 when DDI_TC2 => DP_E,
93 when DDI_TC3 => DP_F,
94 when DDI_TC4 => DP_G,
95 when DDI_TC5 => DP_H,
96 when DDI_TC6 => DP_I,
97 when others => GMA.DP_Port'First)
98 else
99 (case Port is
100 when eDP => DP_A,
101 when DP1 => DP_B,
102 when DP2 => DP_C,
103 when DP3 => DP_D,
104 when others => GMA.DP_Port'First));
Nico Huber8c45bcf2016-11-20 17:30:57 +0100105 begin
106 DP_Aux_Ch.I2C_Read
107 (Port => DP_Port,
108 Address => 16#50#,
109 Length => Raw_EDID_Length,
110 Data => Raw_EDID,
111 Success => Success);
112 end;
113 else
114 I2C.I2C_Read
115 (Port => (if Port = Analog
116 then Config.Analog_I2C_Port
117 else Config_Helpers.To_PCH_Port (Port)),
118 Address => 16#50#,
119 Length => Raw_EDID_Length,
120 Data => Raw_EDID,
121 Success => Success);
122 end if;
123 exit when not Success; -- don't retry if reading itself failed
124
125 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
126 EDID.Sanitize (Raw_EDID, Success);
127 exit when Success;
128 end loop;
129 end Read_EDID;
130
131 procedure Probe_Port
132 (Pipe_Cfg : in out Pipe_Config;
133 Port : in Active_Port_Type;
134 Success : out Boolean)
135 with Pre => True
136 is
137 Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
138 begin
139 Success := Config.Valid_Port (Port);
140
141 if Success then
Nico Huber2bbd6e72020-01-07 18:22:59 +0100142 Panel.Wait_On (Config_Helpers.To_Panel (Port));
Nico Huber8c45bcf2016-11-20 17:30:57 +0100143 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.");
Nico Huber3c1ac182022-09-04 13:12:57 +0200156 pragma Warnings (GNATprove, Off, """Raw_EDID"" is set by * but*",
157 Reason => "We just want to check if it's readable.");
Nico Huber8c45bcf2016-11-20 17:30:57 +0100158 if Has_Sibling_Port (Port) then
159 -- Probe sibling port too and bail out if something is detected.
160 -- This is a precaution for adapters that expose the pins of a
161 -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
162 -- user might have attached both by accident and there are ru-
163 -- mors of displays that got fried by applying the wrong signal.
164 declare
165 Have_Sibling_EDID : Boolean;
166 begin
167 Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
168 if Have_Sibling_EDID then
169 Pipe_Cfg.Port := Disabled;
170 Success := False;
171 end if;
172 end;
173 end if;
Nico Huber3c1ac182022-09-04 13:12:57 +0200174 pragma Warnings (GNATprove, On, """Raw_EDID"" is set by * but*");
Nico Huber8c45bcf2016-11-20 17:30:57 +0100175 pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
176 else
177 Success := False;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100178 end if;
179 end Probe_Port;
180
181 procedure Scan_Ports
Nico Huber4c7356d2016-12-16 14:22:32 +0100182 (Configs : out Pipe_Configs;
183 Ports : in Port_List := All_Ports;
184 Max_Pipe : in Pipe_Index := Pipe_Index'Last;
185 Keep_Power : in Boolean := False)
Nico Huber8c45bcf2016-11-20 17:30:57 +0100186 is
Nico Huber2bbd6e72020-01-07 18:22:59 +0100187 Probed_Panels : array (Valid_Panels) of Boolean := (others => False);
Nico Huber6f9a50d2016-11-21 23:21:14 +0100188
Nico Huber8c45bcf2016-11-20 17:30:57 +0100189 Port_Idx : Port_List_Range := Port_List_Range'First;
190 Success : Boolean;
191 begin
192 Configs := (Pipe_Index =>
193 (Port => Disabled,
194 Mode => Invalid_Mode,
Nico Hubera02b2c62018-01-09 15:58:34 +0100195 Cursor => Default_Cursor,
Nico Huber8c45bcf2016-11-20 17:30:57 +0100196 Framebuffer => Default_FB));
197
Nico Huber6f9a50d2016-11-21 23:21:14 +0100198 -- Turn panel on early to probe other ports during the power on delay.
199 for Idx in Port_List_Range loop
200 exit when Ports (Idx) = Disabled;
Nico Huber2bbd6e72020-01-07 18:22:59 +0100201 declare
202 P : constant Panel_Control := Config_Helpers.To_Panel (Ports (Idx));
203 begin
204 if P /= No_Panel then
205 Panel.On (P, Wait => False);
206 Probed_Panels (P) := True;
207 end if;
208 end;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100209 end loop;
210
Nico Huber8c45bcf2016-11-20 17:30:57 +0100211 for Pipe in Pipe_Index range
212 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
213 loop
Nico Huber4fc6dc22019-05-10 13:01:29 +0200214 Success := False;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100215 while Ports (Port_Idx) /= Disabled loop
216 if not Port_Configured (Configs, Ports (Port_Idx)) and
217 (not Has_Sibling_Port (Ports (Port_Idx)) or
218 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
219 then
220 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
221 else
222 Success := False;
223 end if;
224
225 exit when Port_Idx = Port_List_Range'Last;
226 Port_Idx := Port_List_Range'Succ (Port_Idx);
227
228 exit when Success;
229 end loop;
Nico Huber4fc6dc22019-05-10 13:01:29 +0200230 exit when not Success;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100231 end loop;
232
Arthur Heymans960e2392026-03-03 19:45:24 +0100233 -- On pre-i965 hardware, LVDS must use Pipe B (Secondary).
234 -- If the scan assigned LVDS to Primary, swap with Secondary.
235 if Config.LVDS_Needs_Pipe_B and
236 Configs (Primary).Port = LVDS
237 then
238 declare
239 Tmp : constant Pipe_Config := Configs (Secondary);
240 begin
241 Configs (Secondary) := Configs (Primary);
242 Configs (Primary) := Tmp;
243 end;
244 end if;
245
Nico Huber8c45bcf2016-11-20 17:30:57 +0100246 -- Restore power settings
Nico Huber4c7356d2016-12-16 14:22:32 +0100247 if not Keep_Power then
248 Power_And_Clocks.Power_Set_To (Cur_Configs);
249 end if;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100250
251 -- Turn panel power off if probing failed.
Nico Huber2bbd6e72020-01-07 18:22:59 +0100252 for P in Valid_Panels loop
253 if Probed_Panels (P) and not
254 Port_Configured (Configs, Config.Panel_Ports (P))
255 then
256 Panel.Off (P);
257 end if;
258 end loop;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100259 end Scan_Ports;
260
Nico Huber92de9c42019-09-29 19:03:58 +0200261 procedure Hotplug_Events (Ports : out Port_List)
262 is
263 I : Port_List_Range := Port_List_Range'First;
264 Detected : Boolean;
265 begin
266 Ports := (others => Disabled);
267 for P in Active_Port_Type loop
268 Port_Detect.Hotplug_Detect (P, Detected);
269 if Detected then
270 Ports (I) := P;
271 exit when I = Port_List_Range'Last;
272 I := Port_List_Range'Succ (I);
273 end if;
274 end loop;
275 end Hotplug_Events;
276
Nico Huber8c45bcf2016-11-20 17:30:57 +0100277end HW.GFX.GMA.Display_Probing;