blob: 673f0adcce6dddbd6c8d8766215d2d19c282fc5f [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 Hubera8254482024-07-03 12:23:00 +020022with HW.GFX.GMA.Connectors;
Nico Huber92de9c42019-09-29 19:03:58 +020023with HW.GFX.GMA.Port_Detect;
Nico Huber8c45bcf2016-11-20 17:30:57 +010024with HW.GFX.GMA.Power_And_Clocks;
25
26with HW.Debug;
27with GNAT.Source_Info;
28
29package body HW.GFX.GMA.Display_Probing
30is
31
Nico Huber5a3191f2018-06-04 14:42:13 +020032 function Port_Configured (Configs : Pipe_Configs; Port : Port_Type)
33 return Boolean is
34 (Configs (Primary).Port = Port or
35 Configs (Secondary).Port = Port or
36 Configs (Tertiary).Port = Port);
Nico Huber8c45bcf2016-11-20 17:30:57 +010037
38 -- DP and HDMI share physical pins.
Nico Huber5a3191f2018-06-04 14:42:13 +020039 function Sibling_Port (Port : Port_Type) return Port_Type is
40 (case Port is
41 when HDMI1 => DP1,
42 when HDMI2 => DP2,
43 when HDMI3 => DP3,
44 when DP1 => HDMI1,
45 when DP2 => HDMI2,
46 when DP3 => HDMI3,
Nico Huber8b381fc2024-03-26 18:04:19 +010047 when HDMI_TC1 => DP_TC1,
48 when HDMI_TC2 => DP_TC2,
49 when HDMI_TC3 => DP_TC3,
50 when HDMI_TC4 => DP_TC4,
51 when DP_TC1 => HDMI_TC1,
52 when DP_TC2 => HDMI_TC2,
53 when DP_TC3 => HDMI_TC3,
54 when DP_TC4 => HDMI_TC4,
Nico Huber5a3191f2018-06-04 14:42:13 +020055 when others => Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010056
Nico Huber5a3191f2018-06-04 14:42:13 +020057 function Has_Sibling_Port (Port : Port_Type) return Boolean is
58 (Sibling_Port (Port) /= Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010059
Nico Huber5a3191f2018-06-04 14:42:13 +020060 function Is_DVI_I (Port : Active_Port_Type) return Boolean is
61 (Config.Have_DVI_I and
62 (Port = Analog or
63 Config_Helpers.To_PCH_Port (Port) = Config.Analog_I2C_Port));
Nico Huber1bc496f2017-06-09 22:23:28 +020064
Nico Huber8c45bcf2016-11-20 17:30:57 +010065 procedure Read_EDID
66 (Raw_EDID : out EDID.Raw_EDID_Data;
67 Port : in Active_Port_Type;
68 Success : out Boolean)
Nico Huber8c45bcf2016-11-20 17:30:57 +010069 is
70 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
71 begin
72 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
73
74 for I in 1 .. 2 loop
75 if Config_Helpers.To_Display_Type (Port) = DP then
Nico Huberb0bbdbc2019-09-27 22:32:21 +020076 -- May need power and CDClk to read EDID
Nico Huber41e86742024-07-17 17:10:28 +020077 Power_And_Clocks.Power_Up (Port, Success);
78 exit when not Success;
79 Power_And_Clocks.Enable_CDClk;
Nico Huber8c45bcf2016-11-20 17:30:57 +010080
81 declare
82 DP_Port : constant GMA.DP_Port :=
Nico Huber8b381fc2024-03-26 18:04:19 +010083 (if Config.Has_Type_C_Ports then
84 (case Config_Helpers.To_GPU_Port (Pipe_Index'First, Port) is
85 when DIGI_A => DP_A,
86 when DIGI_B => DP_B,
87 when DIGI_C => DP_C,
88 when DDI_TC1 => DP_D,
89 when DDI_TC2 => DP_E,
90 when DDI_TC3 => DP_F,
91 when DDI_TC4 => DP_G,
92 when DDI_TC5 => DP_H,
93 when DDI_TC6 => DP_I,
94 when others => GMA.DP_Port'First)
95 else
96 (case Port is
97 when eDP => DP_A,
98 when DP1 => DP_B,
99 when DP2 => DP_C,
100 when DP3 => DP_D,
101 when others => GMA.DP_Port'First));
Nico Huber8c45bcf2016-11-20 17:30:57 +0100102 begin
103 DP_Aux_Ch.I2C_Read
104 (Port => DP_Port,
105 Address => 16#50#,
106 Length => Raw_EDID_Length,
107 Data => Raw_EDID,
108 Success => Success);
109 end;
110 else
111 I2C.I2C_Read
112 (Port => (if Port = Analog
113 then Config.Analog_I2C_Port
114 else Config_Helpers.To_PCH_Port (Port)),
115 Address => 16#50#,
116 Length => Raw_EDID_Length,
117 Data => Raw_EDID,
118 Success => Success);
119 end if;
120 exit when not Success; -- don't retry if reading itself failed
121
122 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
123 EDID.Sanitize (Raw_EDID, Success);
124 exit when Success;
125 end loop;
126 end Read_EDID;
127
128 procedure Probe_Port
129 (Pipe_Cfg : in out Pipe_Config;
130 Port : in Active_Port_Type;
131 Success : out Boolean)
132 with Pre => True
133 is
Nico Huber41e86742024-07-17 17:10:28 +0200134 Raw_EDID : EDID.Raw_EDID_Data with Relaxed_Initialization;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100135 begin
136 Success := Config.Valid_Port (Port);
137
138 if Success then
Nico Huber2bbd6e72020-01-07 18:22:59 +0100139 Panel.Wait_On (Config_Helpers.To_Panel (Port));
Nico Huber8c45bcf2016-11-20 17:30:57 +0100140 Read_EDID (Raw_EDID, Port, Success);
141 end if;
142
143 if Success and then
Nico Huber1bc496f2017-06-09 22:23:28 +0200144 ((not Is_DVI_I (Port) or EDID.Compatible_Display
145 (Raw_EDID, Config_Helpers.To_Display_Type (Port))) and
Nico Huber8c45bcf2016-11-20 17:30:57 +0100146 EDID.Has_Preferred_Mode (Raw_EDID))
147 then
148 Pipe_Cfg.Port := Port;
149 Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
150
151 pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
152 Reason => "We just want to check if it's readable.");
Nico Huber3c1ac182022-09-04 13:12:57 +0200153 pragma Warnings (GNATprove, Off, """Raw_EDID"" is set by * but*",
154 Reason => "We just want to check if it's readable.");
Nico Huber8c45bcf2016-11-20 17:30:57 +0100155 if Has_Sibling_Port (Port) then
156 -- Probe sibling port too and bail out if something is detected.
157 -- This is a precaution for adapters that expose the pins of a
158 -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
159 -- user might have attached both by accident and there are ru-
160 -- mors of displays that got fried by applying the wrong signal.
161 declare
162 Have_Sibling_EDID : Boolean;
163 begin
164 Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
165 if Have_Sibling_EDID then
166 Pipe_Cfg.Port := Disabled;
167 Success := False;
168 end if;
169 end;
170 end if;
Nico Huber3c1ac182022-09-04 13:12:57 +0200171 pragma Warnings (GNATprove, On, """Raw_EDID"" is set by * but*");
Nico Huber8c45bcf2016-11-20 17:30:57 +0100172 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 Huber2bbd6e72020-01-07 18:22:59 +0100184 Probed_Panels : array (Valid_Panels) of Boolean := (others => False);
Nico Huber6f9a50d2016-11-21 23:21:14 +0100185
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;
Nico Huber2bbd6e72020-01-07 18:22:59 +0100198 declare
199 P : constant Panel_Control := Config_Helpers.To_Panel (Ports (Idx));
200 begin
201 if P /= No_Panel then
202 Panel.On (P, Wait => False);
203 Probed_Panels (P) := True;
204 end if;
205 end;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100206 end loop;
207
Nico Huber8c45bcf2016-11-20 17:30:57 +0100208 for Pipe in Pipe_Index range
209 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
210 loop
Nico Huber4fc6dc22019-05-10 13:01:29 +0200211 Success := False;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100212 while Ports (Port_Idx) /= Disabled loop
213 if not Port_Configured (Configs, Ports (Port_Idx)) and
214 (not Has_Sibling_Port (Ports (Port_Idx)) or
215 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
216 then
217 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
218 else
219 Success := False;
220 end if;
221
222 exit when Port_Idx = Port_List_Range'Last;
223 Port_Idx := Port_List_Range'Succ (Port_Idx);
224
225 exit when Success;
226 end loop;
Nico Huber4fc6dc22019-05-10 13:01:29 +0200227 exit when not Success;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100228 end loop;
229
Arthur Heymans960e2392026-03-03 19:45:24 +0100230 -- On pre-i965 hardware, LVDS must use Pipe B (Secondary).
231 -- If the scan assigned LVDS to Primary, swap with Secondary.
232 if Config.LVDS_Needs_Pipe_B and
233 Configs (Primary).Port = LVDS
234 then
235 declare
236 Tmp : constant Pipe_Config := Configs (Secondary);
237 begin
238 Configs (Secondary) := Configs (Primary);
239 Configs (Primary) := Tmp;
240 end;
241 end if;
242
Nico Huber8c45bcf2016-11-20 17:30:57 +0100243 -- Restore power settings
Nico Huber4c7356d2016-12-16 14:22:32 +0100244 if not Keep_Power then
245 Power_And_Clocks.Power_Set_To (Cur_Configs);
246 end if;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100247
248 -- Turn panel power off if probing failed.
Nico Huber2bbd6e72020-01-07 18:22:59 +0100249 for P in Valid_Panels loop
250 if Probed_Panels (P) and not
251 Port_Configured (Configs, Config.Panel_Ports (P))
252 then
253 Panel.Off (P);
254 end if;
255 end loop;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100256 end Scan_Ports;
257
Nico Huber92de9c42019-09-29 19:03:58 +0200258 procedure Hotplug_Events (Ports : out Port_List)
259 is
260 I : Port_List_Range := Port_List_Range'First;
261 Detected : Boolean;
262 begin
263 Ports := (others => Disabled);
264 for P in Active_Port_Type loop
265 Port_Detect.Hotplug_Detect (P, Detected);
266 if Detected then
267 Ports (I) := P;
268 exit when I = Port_List_Range'Last;
269 I := Port_List_Range'Succ (I);
270 end if;
271 end loop;
272 end Hotplug_Events;
273
Nico Huber8c45bcf2016-11-20 17:30:57 +0100274end HW.GFX.GMA.Display_Probing;