blob: cd2a452150f219ce3796c7b34aee80c9b334d4b6 [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;
Nico Huber8c45bcf2016-11-20 17:30:57 +010016with HW.GFX.GMA.Config;
17with HW.GFX.GMA.Config_Helpers;
18with HW.GFX.GMA.I2C;
19with HW.GFX.GMA.DP_Aux_Ch;
20with HW.GFX.GMA.Panel;
21with HW.GFX.GMA.Power_And_Clocks;
22
23with HW.Debug;
24with GNAT.Source_Info;
25
26package body HW.GFX.GMA.Display_Probing
27is
28
Nico Huber5a3191f2018-06-04 14:42:13 +020029 function Port_Configured (Configs : Pipe_Configs; Port : Port_Type)
30 return Boolean is
31 (Configs (Primary).Port = Port or
32 Configs (Secondary).Port = Port or
33 Configs (Tertiary).Port = Port);
Nico Huber8c45bcf2016-11-20 17:30:57 +010034
35 -- DP and HDMI share physical pins.
Nico Huber5a3191f2018-06-04 14:42:13 +020036 function Sibling_Port (Port : Port_Type) return Port_Type is
37 (case Port is
38 when HDMI1 => DP1,
39 when HDMI2 => DP2,
40 when HDMI3 => DP3,
41 when DP1 => HDMI1,
42 when DP2 => HDMI2,
43 when DP3 => HDMI3,
44 when others => Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010045
Nico Huber5a3191f2018-06-04 14:42:13 +020046 function Has_Sibling_Port (Port : Port_Type) return Boolean is
47 (Sibling_Port (Port) /= Disabled);
Nico Huber8c45bcf2016-11-20 17:30:57 +010048
Nico Huber5a3191f2018-06-04 14:42:13 +020049 function Is_DVI_I (Port : Active_Port_Type) return Boolean is
50 (Config.Have_DVI_I and
51 (Port = Analog or
52 Config_Helpers.To_PCH_Port (Port) = Config.Analog_I2C_Port));
Nico Huber1bc496f2017-06-09 22:23:28 +020053
Nico Huber8c45bcf2016-11-20 17:30:57 +010054 procedure Read_EDID
55 (Raw_EDID : out EDID.Raw_EDID_Data;
56 Port : in Active_Port_Type;
57 Success : out Boolean)
Nico Huber8c45bcf2016-11-20 17:30:57 +010058 is
59 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
60 begin
61 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
62
63 for I in 1 .. 2 loop
64 if Config_Helpers.To_Display_Type (Port) = DP then
65 -- May need power to read edid
66 declare
67 Temp_Configs : Pipe_Configs := Cur_Configs;
68 begin
69 Temp_Configs (Primary).Port := Port;
70 Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
71 end;
72
73 declare
74 DP_Port : constant GMA.DP_Port :=
75 (case Port is
76 when Internal => DP_A,
77 when DP1 => DP_B,
78 when DP2 => DP_C,
79 when DP3 => DP_D,
80 when others => GMA.DP_Port'First);
81 begin
82 DP_Aux_Ch.I2C_Read
83 (Port => DP_Port,
84 Address => 16#50#,
85 Length => Raw_EDID_Length,
86 Data => Raw_EDID,
87 Success => Success);
88 end;
89 else
90 I2C.I2C_Read
91 (Port => (if Port = Analog
92 then Config.Analog_I2C_Port
93 else Config_Helpers.To_PCH_Port (Port)),
94 Address => 16#50#,
95 Length => Raw_EDID_Length,
96 Data => Raw_EDID,
97 Success => Success);
98 end if;
99 exit when not Success; -- don't retry if reading itself failed
100
101 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
102 EDID.Sanitize (Raw_EDID, Success);
103 exit when Success;
104 end loop;
105 end Read_EDID;
106
107 procedure Probe_Port
108 (Pipe_Cfg : in out Pipe_Config;
109 Port : in Active_Port_Type;
110 Success : out Boolean)
111 with Pre => True
112 is
113 Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
114 begin
115 Success := Config.Valid_Port (Port);
116
117 if Success then
118 if Port = Internal then
Nico Huber6f9a50d2016-11-21 23:21:14 +0100119 Panel.Wait_On;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100120 end if;
121 Read_EDID (Raw_EDID, Port, Success);
122 end if;
123
124 if Success and then
Nico Huber1bc496f2017-06-09 22:23:28 +0200125 ((not Is_DVI_I (Port) or EDID.Compatible_Display
126 (Raw_EDID, Config_Helpers.To_Display_Type (Port))) and
Nico Huber8c45bcf2016-11-20 17:30:57 +0100127 EDID.Has_Preferred_Mode (Raw_EDID))
128 then
129 Pipe_Cfg.Port := Port;
130 Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
131
132 pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
133 Reason => "We just want to check if it's readable.");
134 if Has_Sibling_Port (Port) then
135 -- Probe sibling port too and bail out if something is detected.
136 -- This is a precaution for adapters that expose the pins of a
137 -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
138 -- user might have attached both by accident and there are ru-
139 -- mors of displays that got fried by applying the wrong signal.
140 declare
141 Have_Sibling_EDID : Boolean;
142 begin
143 Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
144 if Have_Sibling_EDID then
145 Pipe_Cfg.Port := Disabled;
146 Success := False;
147 end if;
148 end;
149 end if;
150 pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
151 else
152 Success := False;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100153 end if;
154 end Probe_Port;
155
156 procedure Scan_Ports
Nico Huber4c7356d2016-12-16 14:22:32 +0100157 (Configs : out Pipe_Configs;
158 Ports : in Port_List := All_Ports;
159 Max_Pipe : in Pipe_Index := Pipe_Index'Last;
160 Keep_Power : in Boolean := False)
Nico Huber8c45bcf2016-11-20 17:30:57 +0100161 is
Nico Huber6f9a50d2016-11-21 23:21:14 +0100162 Probe_Internal : Boolean := False;
163
Nico Huber8c45bcf2016-11-20 17:30:57 +0100164 Port_Idx : Port_List_Range := Port_List_Range'First;
165 Success : Boolean;
166 begin
167 Configs := (Pipe_Index =>
168 (Port => Disabled,
169 Mode => Invalid_Mode,
Nico Hubera02b2c62018-01-09 15:58:34 +0100170 Cursor => Default_Cursor,
Nico Huber8c45bcf2016-11-20 17:30:57 +0100171 Framebuffer => Default_FB));
172
Nico Huber6f9a50d2016-11-21 23:21:14 +0100173 -- Turn panel on early to probe other ports during the power on delay.
174 for Idx in Port_List_Range loop
175 exit when Ports (Idx) = Disabled;
176 if Ports (Idx) = Internal then
177 Panel.On (Wait => False);
178 Probe_Internal := True;
179 exit;
180 end if;
181 end loop;
182
Nico Huber8c45bcf2016-11-20 17:30:57 +0100183 for Pipe in Pipe_Index range
184 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
185 loop
186 while Ports (Port_Idx) /= Disabled loop
187 if not Port_Configured (Configs, Ports (Port_Idx)) and
188 (not Has_Sibling_Port (Ports (Port_Idx)) or
189 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
190 then
191 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
192 else
193 Success := False;
194 end if;
195
196 exit when Port_Idx = Port_List_Range'Last;
197 Port_Idx := Port_List_Range'Succ (Port_Idx);
198
199 exit when Success;
200 end loop;
201 end loop;
202
203 -- Restore power settings
Nico Huber4c7356d2016-12-16 14:22:32 +0100204 if not Keep_Power then
205 Power_And_Clocks.Power_Set_To (Cur_Configs);
206 end if;
Nico Huber6f9a50d2016-11-21 23:21:14 +0100207
208 -- Turn panel power off if probing failed.
209 if Probe_Internal and not Port_Configured (Configs, Internal) then
210 Panel.Off;
211 end if;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100212 end Scan_Ports;
213
214end HW.GFX.GMA.Display_Probing;