blob: 4b09d519238a045db69d13749dbb3f999a83523a [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2014-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
Nico Huber995436b2016-11-20 02:21:51 +010015with HW.GFX.I2C;
Nico Huber83693c82016-10-08 22:17:55 +020016with HW.GFX.EDID;
17with HW.GFX.GMA.Config;
Nico Huber995436b2016-11-20 02:21:51 +010018with HW.GFX.GMA.I2C;
19with HW.GFX.GMA.DP_Aux_Ch;
Nico Huber83693c82016-10-08 22:17:55 +020020with HW.GFX.GMA.DP_Info;
21with HW.GFX.GMA.Registers;
22with HW.GFX.GMA.Power_And_Clocks;
23with HW.GFX.GMA.Panel;
24with HW.GFX.GMA.PLLs;
25with HW.GFX.GMA.Port_Detect;
26with HW.GFX.GMA.Connectors;
27with HW.GFX.GMA.Connector_Info;
28with HW.GFX.GMA.Pipe_Setup;
29
30with System;
31
32with HW.Debug;
33with GNAT.Source_Info;
34
35use type HW.Word8;
36use type HW.Int32;
37
38package body HW.GFX.GMA
39 with Refined_State =>
40 (State =>
41 (Registers.Address_State,
42 PLLs.State, Panel.Panel_State,
43 Cur_Configs, Allocated_PLLs, DP_Links,
44 HPD_Delay, Wait_For_HPD),
45 Init_State => Initialized,
46 Config_State => Config.Valid_Port_GPU,
47 Device_State =>
48 (Registers.Register_State, Registers.GTT_State))
49is
50
51 subtype Port_Name is String (1 .. 8);
52 type Port_Name_Array is array (Port_Type) of Port_Name;
53 Port_Names : constant Port_Name_Array :=
54 (Disabled => "Disabled",
55 Internal => "Internal",
56 DP1 => "DP1 ",
57 DP2 => "DP2 ",
58 DP3 => "DP3 ",
Nico Huber0d454cd2016-11-21 13:33:43 +010059 HDMI1 => "HDMI1 ",
60 HDMI2 => "HDMI2 ",
61 HDMI3 => "HDMI3 ",
Nico Huber83693c82016-10-08 22:17:55 +020062 Analog => "Analog ");
63
64 package Display_Controller renames Pipe_Setup;
65
Nico Huber99f10f32016-11-20 00:34:05 +010066 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020067
Nico Huber99f10f32016-11-20 00:34:05 +010068 type Links_Type is array (Pipe_Index) of DP_Link;
Nico Huber83693c82016-10-08 22:17:55 +020069
70 type HPD_Type is array (Port_Type) of Boolean;
71 type HPD_Delay_Type is array (Port_Type) of Time.T;
72
Nico Huber99f10f32016-11-20 00:34:05 +010073 Cur_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +020074 Allocated_PLLs : PLLs_Type;
75 DP_Links : Links_Type;
76 HPD_Delay : HPD_Delay_Type;
77 Wait_For_HPD : HPD_Type;
78 Initialized : Boolean := False;
79
80 subtype Active_Port_Type is Port_Type range Port_Type'Succ (Disabled) .. Port_Type'Last;
81
82 ----------------------------------------------------------------------------
83
Nico Huberf54d0962016-10-20 14:17:18 +020084 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
85
86 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
87 is
88 begin
89 return Word32 (Freq / 1_000_000);
90 end PCH_RAWCLK_FREQ;
91
92 ----------------------------------------------------------------------------
93
Nico Huber83693c82016-10-08 22:17:55 +020094 function To_GPU_Port
Nico Huber3c544ee2016-11-20 04:56:58 +010095 (Pipe : Pipe_Index;
96 Port : Active_Port_Type)
Nico Huber83693c82016-10-08 22:17:55 +020097 return GPU_Port
98 is
99 begin
100 return
101 (case Config.CPU is
102 when Ironlake .. Ivybridge => -- everything but eDP through FDI/PCH
Nico Huber3c544ee2016-11-20 04:56:58 +0100103 (if Config.Internal_Is_EDP and then Port = Internal then
Nico Huber83693c82016-10-08 22:17:55 +0200104 DIGI_A
105 else
Nico Huber3c544ee2016-11-20 04:56:58 +0100106 (case Pipe is
Nico Huber83693c82016-10-08 22:17:55 +0200107 -- FDIs are fixed to the CPU pipe
108 when Primary => DIGI_B,
109 when Secondary => DIGI_C,
110 when Tertiary => DIGI_D)),
111 when Haswell .. Skylake => -- everything but VGA directly on CPU
Nico Huber3c544ee2016-11-20 04:56:58 +0100112 (case Port is
Nico Huber0d454cd2016-11-21 13:33:43 +0100113 when Internal => DIGI_A, -- LVDS not available
114 when HDMI1 | DP1 => DIGI_B,
115 when HDMI2 | DP2 => DIGI_C,
116 when HDMI3 | DP3 => DIGI_D,
117 when Analog => DIGI_E));
Nico Huber83693c82016-10-08 22:17:55 +0200118 end To_GPU_Port;
119
120 function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
Nico Huber995436b2016-11-20 02:21:51 +0100121 with Pre => True
Nico Huber83693c82016-10-08 22:17:55 +0200122 is
123 begin
124 return
125 (case Port is
126 when Internal => PCH_LVDS, -- will be ignored if Internal is DP
127 when Analog => PCH_DAC,
Nico Huber0d454cd2016-11-21 13:33:43 +0100128 when HDMI1 => PCH_HDMI_B,
129 when HDMI2 => PCH_HDMI_C,
130 when HDMI3 => PCH_HDMI_D,
Nico Huber83693c82016-10-08 22:17:55 +0200131 when DP1 => PCH_DP_B,
132 when DP2 => PCH_DP_C,
133 when DP3 => PCH_DP_D);
134 end To_PCH_Port;
135
136 function To_Display_Type (Port : Active_Port_Type) return Display_Type
137 with Pre => True
138 is
139 begin
Nico Huber3c544ee2016-11-20 04:56:58 +0100140 return Display_Type'
Nico Huber83693c82016-10-08 22:17:55 +0200141 (case Port is
Nico Huber3c544ee2016-11-20 04:56:58 +0100142 when Internal => Config.Internal_Display,
143 when Analog => VGA,
144 when HDMI1 .. HDMI3 => HDMI,
145 when DP1 .. DP3 => DP);
Nico Huber83693c82016-10-08 22:17:55 +0200146 end To_Display_Type;
147
Nico Huber3c544ee2016-11-20 04:56:58 +0100148 -- Prepares link rate and lane count settings for an FDI connection.
Nico Huber83693c82016-10-08 22:17:55 +0200149 procedure Configure_FDI_Link
150 (Port_Cfg : in out Port_Config;
151 Success : out Boolean)
Nico Huber3c544ee2016-11-20 04:56:58 +0100152 with Pre => True
Nico Huber83693c82016-10-08 22:17:55 +0200153 is
154 procedure Limit_Lane_Count
155 is
156 FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
157 Enabled : Boolean;
158 begin
159 -- if DIGI_D enabled: (FDI names are off by one)
160 Registers.Is_Set_Mask
161 (Register => Registers.FDI_TX_CTL_C,
162 Mask => FDI_TX_CTL_FDI_TX_ENABLE,
163 Result => Enabled);
164 if Enabled then
165 Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
166 end if;
167 end Limit_Lane_Count;
168 begin
169 Port_Cfg.FDI.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
170 Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count :=
171 Config.FDI_Lane_Count (Port_Cfg.Port);
172 Port_Cfg.FDI.Receiver_Caps.Enhanced_Framing := True;
173 if Config.Has_FDI_C and then Port_Cfg.Port = DIGI_C then
174 Limit_Lane_Count;
175 end if;
176 DP_Info.Preferred_Link_Setting (Port_Cfg.FDI, Port_Cfg.Mode, Success);
177 end Configure_FDI_Link;
178
Nico Huber3c544ee2016-11-20 04:56:58 +0100179 -- Validates that a given configuration should work with
180 -- a given framebuffer.
Nico Huberc7a4fee2016-11-03 18:18:03 +0100181 function Validate_Config
182 (Framebuffer : Framebuffer_Type;
Nico Huberdcd274b2016-11-03 20:15:39 +0100183 Port_Cfg : Port_Config;
Nico Huber99f10f32016-11-20 00:34:05 +0100184 I : Pipe_Index)
Nico Huberc7a4fee2016-11-03 18:18:03 +0100185 return Boolean
Nico Huber47ff0692016-11-04 14:29:39 +0100186 with
187 Post =>
188 (if Validate_Config'Result then
189 Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
190 Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))
Nico Huberc7a4fee2016-11-03 18:18:03 +0100191 is
192 begin
193 -- No downscaling
Nico Huberdcd274b2016-11-03 20:15:39 +0100194 -- Respect maximum scalable width
Nico Huber3675db52016-11-04 16:27:29 +0100195 -- VGA plane is only allowed on the primary pipe
196 -- Only 32bpp RGB (ignored for VGA plane)
197 -- Stride must be a multiple of 64 (ignored for VGA plane)
Nico Huberc7a4fee2016-11-03 18:18:03 +0100198 return
Nico Huberdcd274b2016-11-03 20:15:39 +0100199 ((Framebuffer.Width = Pos32 (Port_Cfg.Mode.H_Visible) and
200 Framebuffer.Height = Pos32 (Port_Cfg.Mode.V_Visible)) or
201 (Framebuffer.Width <= Config.Maximum_Scalable_Width (I) and
202 Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
203 Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))) and
Nico Huber3675db52016-11-04 16:27:29 +0100204 (Framebuffer.Offset /= VGA_PLANE_FRAMEBUFFER_OFFSET or I = Primary) and
205 (Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET or
206 (Framebuffer.BPC = 8 and
207 Framebuffer.Stride mod 64 = 0));
Nico Huberc7a4fee2016-11-03 18:18:03 +0100208 end Validate_Config;
209
Nico Huber3c544ee2016-11-20 04:56:58 +0100210 -- Derives an internal port config.
211 --
212 -- This is where the magic happens that hides the hardware details
213 -- from libgfxinit's users. We have to map the pipe (Pipe_Index),
214 -- the user visible port (Port_Type) and the modeline (Mode_Type)
215 -- that we are supposed to output to an internal representation
216 -- (Port_Config) that applies to the selected hardware generation
217 -- (in GMA.Config).
Nico Huber83693c82016-10-08 22:17:55 +0200218 procedure Fill_Port_Config
219 (Port_Cfg : out Port_Config;
Nico Huber3c544ee2016-11-20 04:56:58 +0100220 Pipe : in Pipe_Index;
221 Port : in Port_Type;
222 Mode : in Mode_Type;
Nico Huber83693c82016-10-08 22:17:55 +0200223 Success : out Boolean)
224 with Pre => True
225 is
226 begin
227 Success :=
Nico Huber3c544ee2016-11-20 04:56:58 +0100228 GMA.Config.Supported_Pipe (Pipe) and then
229 GMA.Config.Valid_Port (Port) and then
230 Port /= Disabled; -- Valid_Port should already cover this, but the
231 -- array is writeable, so it's hard to prove this.
Nico Huber83693c82016-10-08 22:17:55 +0200232
233 if Success then
234 declare
Nico Huber3c544ee2016-11-20 04:56:58 +0100235 Link : constant DP_Link := DP_Links (Pipe);
Nico Huber83693c82016-10-08 22:17:55 +0200236 begin
237 Port_Cfg := Port_Config'
Nico Huber3c544ee2016-11-20 04:56:58 +0100238 (Port => To_GPU_Port (Pipe, Port),
Nico Huber83693c82016-10-08 22:17:55 +0200239 PCH_Port => To_PCH_Port (Port),
240 Display => To_Display_Type (Port),
241 Mode => Mode,
Nico Huber3c544ee2016-11-20 04:56:58 +0100242 Is_FDI => GMA.Config.Is_FDI_Port (Port),
Nico Huber83693c82016-10-08 22:17:55 +0200243 FDI => Default_DP,
244 DP => Link);
Nico Huber3c544ee2016-11-20 04:56:58 +0100245 end;
246
247 if Port_Cfg.Is_FDI then
248 Configure_FDI_Link (Port_Cfg, Success);
249 end if;
250
251 if Success then
Nico Huber83693c82016-10-08 22:17:55 +0200252 if Port_Cfg.Mode.BPC = Auto_BPC then
253 Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
254 end if;
Nico Huber3c544ee2016-11-20 04:56:58 +0100255
Nico Huber74ec9622016-11-19 03:00:43 +0100256 if Port_Cfg.Display = HDMI then
257 declare
258 pragma Assert (Config.HDMI_Max_Clock_24bpp * 8
259 / Port_Cfg.Mode.BPC >= Frequency_Type'First);
260 Max_Dotclock : constant Frequency_Type :=
261 Config.HDMI_Max_Clock_24bpp * 8 / Port_Cfg.Mode.BPC;
262 begin
263 if Port_Cfg.Mode.Dotclock > Max_Dotclock then
264 pragma Debug (Debug.Put ("Dotclock "));
265 pragma Debug (Debug.Put_Int64 (Port_Cfg.Mode.Dotclock));
266 pragma Debug (Debug.Put (" too high, limiting to "));
267 pragma Debug (Debug.Put_Int64 (Max_Dotclock));
268 pragma Debug (Debug.Put_Line ("."));
269 Port_Cfg.Mode.Dotclock := Max_Dotclock;
270 end if;
271 end;
272 end if;
Nico Huber3c544ee2016-11-20 04:56:58 +0100273 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200274 else
275 Port_Cfg := Port_Config'
276 (Port => GPU_Port'First,
277 PCH_Port => PCH_Port'First,
278 Display => Display_Type'First,
279 Mode => Invalid_Mode,
280 Is_FDI => False,
281 FDI => Default_DP,
282 DP => Default_DP);
283 end if;
284 end Fill_Port_Config;
285
286 ----------------------------------------------------------------------------
287
288 function To_Controller
Nico Huber99f10f32016-11-20 00:34:05 +0100289 (Dsp_Config : Pipe_Index) return Display_Controller.Controller_Type
Nico Huber83693c82016-10-08 22:17:55 +0200290 is
291 Result : Display_Controller.Controller_Type;
292 begin
293 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
294
295 case Dsp_Config is
296 when Primary =>
297 Result := Display_Controller.Controllers (Display_Controller.A);
298 when Secondary =>
299 Result := Display_Controller.Controllers (Display_Controller.B);
300 when Tertiary =>
301 Result := Display_Controller.Controllers (Display_Controller.C);
302 end case;
303 return Result;
304 end To_Controller;
305
306 ----------------------------------------------------------------------------
307
308 function To_Head
Nico Huber99f10f32016-11-20 00:34:05 +0100309 (N_Config : Pipe_Index;
Nico Huber83693c82016-10-08 22:17:55 +0200310 Port : Active_Port_Type)
311 return Display_Controller.Head_Type
312 is
313 Result : Display_Controller.Head_Type;
314 begin
315 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
316
317 if Config.Has_EDP_Pipe and then Port = Internal then
318 Result := Display_Controller.Heads (Display_Controller.Head_EDP);
319 else
320 case N_Config is
321 when Primary =>
322 Result := Display_Controller.Heads (Display_Controller.Head_A);
323 when Secondary =>
324 Result := Display_Controller.Heads (Display_Controller.Head_B);
325 when Tertiary =>
326 Result := Display_Controller.Heads (Display_Controller.Head_C);
327 end case;
328 end if;
329 return Result;
330 end To_Head;
331
332 ----------------------------------------------------------------------------
333
334 procedure Legacy_VGA_Off
335 is
336 Reg8 : Word8;
337 begin
338 -- disable legacy VGA plane, taking over control now
339 Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
340 Port_IO.InB (Reg8, VGA_SR_DATA);
341 Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
342 Time.U_Delay (100); -- PRM says 100us, Linux does 300
343 Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
344 end Legacy_VGA_Off;
345
346 ----------------------------------------------------------------------------
347
348 function Port_Configured
Nico Huber99f10f32016-11-20 00:34:05 +0100349 (Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200350 Port : Port_Type)
351 return Boolean
352 with
353 Global => null
354 is
355 begin
356 return Configs (Primary).Port = Port or
357 Configs (Secondary).Port = Port or
358 Configs (Tertiary).Port = Port;
359 end Port_Configured;
360
Nico Huber845de362016-11-21 14:00:06 +0100361 -- DP and HDMI share physical pins.
362 function Sibling_Port (Port : Port_Type) return Port_Type
363 is
364 begin
365 return
366 (case Port is
367 when HDMI1 => DP1,
368 when HDMI2 => DP2,
369 when HDMI3 => DP3,
370 when DP1 => HDMI1,
371 when DP2 => HDMI2,
372 when DP3 => HDMI3,
373 when others => Disabled);
374 end Sibling_Port;
375
376 function Has_Sibling_Port (Port : Port_Type) return Boolean
377 is
378 begin
379 return Sibling_Port (Port) /= Disabled;
380 end Has_Sibling_Port;
381
Nico Huber995436b2016-11-20 02:21:51 +0100382 procedure Read_EDID
383 (Raw_EDID : out EDID.Raw_EDID_Data;
384 Port : in Active_Port_Type;
385 Success : out Boolean)
386 with
387 Post => (if Success then EDID.Valid (Raw_EDID))
388 is
389 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
390 begin
391 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
392
393 for I in 1 .. 2 loop
394 if To_Display_Type (Port) = DP then
Nico Huber1b2c9a32016-11-20 03:42:08 +0100395 -- May need power to read edid
396 declare
397 Temp_Configs : Pipe_Configs := Cur_Configs;
398 begin
399 Temp_Configs (Primary).Port := Port;
400 Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
401 end;
402
Nico Huber995436b2016-11-20 02:21:51 +0100403 declare
404 DP_Port : constant GMA.DP_Port :=
405 (case Port is
406 when Internal => DP_A,
407 when DP1 => DP_B,
408 when DP2 => DP_C,
409 when DP3 => DP_D,
410 when others => GMA.DP_Port'First);
411 begin
412 DP_Aux_Ch.I2C_Read
413 (Port => DP_Port,
414 Address => 16#50#,
415 Length => Raw_EDID_Length,
416 Data => Raw_EDID,
417 Success => Success);
418 end;
419 else
420 I2C.I2C_Read
421 (Port => (if Port = Analog
422 then Config.Analog_I2C_Port
423 else To_PCH_Port (Port)),
424 Address => 16#50#,
425 Length => Raw_EDID_Length,
426 Data => Raw_EDID,
427 Success => Success);
428 end if;
429 exit when not Success; -- don't retry if reading itself failed
430
431 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
432 EDID.Sanitize (Raw_EDID, Success);
433 exit when Success;
434 end loop;
435 end Read_EDID;
436
Nico Huber1b2c9a32016-11-20 03:42:08 +0100437 procedure Probe_Port
438 (Pipe_Cfg : in out Pipe_Config;
439 Port : in Active_Port_Type;
440 Success : out Boolean)
Nico Huber6b7a40b2016-11-21 14:14:02 +0100441 with Pre => True
Nico Huber83693c82016-10-08 22:17:55 +0200442 is
443 Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
Nico Huber1b2c9a32016-11-20 03:42:08 +0100444 begin
445 Success := Config.Valid_Port (Port);
446
447 if Success then
448 if Port = Internal then
449 Panel.On;
450 end if;
451 Read_EDID (Raw_EDID, Port, Success);
452 end if;
453
454 if Success and then
455 (EDID.Compatible_Display (Raw_EDID, To_Display_Type (Port)) and
456 EDID.Has_Preferred_Mode (Raw_EDID))
457 then
458 Pipe_Cfg.Port := Port;
459 Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
Nico Huber6b7a40b2016-11-21 14:14:02 +0100460
461 pragma Warnings (GNATprove, Off, "unused assignment to ""Raw_EDID""",
462 Reason => "We just want to check if it's readable.");
463 if Has_Sibling_Port (Port) then
464 -- Probe sibling port too and bail out if something is detected.
465 -- This is a precaution for adapters that expose the pins of a
466 -- port for both HDMI/DVI and DP (like some ThinkPad docks). A
467 -- user might have attached both by accident and there are ru-
468 -- mors of displays that got fried by applying the wrong signal.
469 declare
470 Have_Sibling_EDID : Boolean;
471 begin
472 Read_EDID (Raw_EDID, Sibling_Port (Port), Have_Sibling_EDID);
473 if Have_Sibling_EDID then
474 Pipe_Cfg.Port := Disabled;
475 Success := False;
476 end if;
477 end;
478 end if;
479 pragma Warnings (GNATprove, On, "unused assignment to ""Raw_EDID""");
Nico Huber1b2c9a32016-11-20 03:42:08 +0100480 else
481 Success := False;
482 if Port = Internal then
483 Panel.Off;
484 end if;
485 end if;
486 end Probe_Port;
487
488 procedure Scan_Ports
489 (Configs : out Pipe_Configs;
490 Ports : in Port_List;
491 Max_Pipe : in Pipe_Index := Pipe_Index'Last)
492 is
Nico Huber83693c82016-10-08 22:17:55 +0200493 Port_Idx : Port_List_Range := Port_List_Range'First;
Nico Huber1b2c9a32016-11-20 03:42:08 +0100494 Success : Boolean;
Nico Huber83693c82016-10-08 22:17:55 +0200495 begin
Nico Huber99f10f32016-11-20 00:34:05 +0100496 Configs := (Pipe_Index =>
Nico Huber83693c82016-10-08 22:17:55 +0200497 (Port => Disabled,
498 Mode => Invalid_Mode,
499 Framebuffer => Default_FB));
500
Nico Huber1b2c9a32016-11-20 03:42:08 +0100501 for Pipe in Pipe_Index range
502 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
503 loop
Nico Huber83693c82016-10-08 22:17:55 +0200504 while Ports (Port_Idx) /= Disabled loop
Nico Huber845de362016-11-21 14:00:06 +0100505 if not Port_Configured (Configs, Ports (Port_Idx)) and
506 (not Has_Sibling_Port (Ports (Port_Idx)) or
507 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
508 then
Nico Huber1b2c9a32016-11-20 03:42:08 +0100509 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
510 else
511 Success := False;
Nico Huber83693c82016-10-08 22:17:55 +0200512 end if;
513
514 exit when Port_Idx = Port_List_Range'Last;
515 Port_Idx := Port_List_Range'Succ (Port_Idx);
516
517 exit when Success;
518 end loop;
519 end loop;
520
Nico Huber1b2c9a32016-11-20 03:42:08 +0100521 -- Restore power settings
Nico Huber83693c82016-10-08 22:17:55 +0200522 Power_And_Clocks.Power_Set_To (Cur_Configs);
523 end Scan_Ports;
524
Nico Huber83693c82016-10-08 22:17:55 +0200525 ----------------------------------------------------------------------------
526
Nico Huber99f10f32016-11-20 00:34:05 +0100527 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200528 is
529 Did_Power_Up : Boolean := False;
530
531 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +0100532 Old_Config, New_Config : Pipe_Config;
533 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200534 Port_Cfg : Port_Config;
535
536 procedure Check_HPD
537 (Port_Cfg : in Port_Config;
538 Port : in Port_Type;
539 Detected : out Boolean)
540 is
541 begin
542 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
543 if HPD_Delay_Over then
544 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
545 HPD_Delay (Port) := Time.MS_From_Now (333);
546 else
547 Detected := False;
548 end if;
549 end Check_HPD;
550 begin
551 Old_Configs := Cur_Configs;
552
Nico Huber99f10f32016-11-20 00:34:05 +0100553 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200554 HPD := False;
555
556 Old_Config := Cur_Configs (I);
557 New_Config := Configs (I);
558
Nico Huber3c544ee2016-11-20 04:56:58 +0100559 Fill_Port_Config
560 (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200561 if Success then
562 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
563 end if;
564
565 -- Connector changed?
566 if (Success and then HPD) or
567 Old_Config.Port /= New_Config.Port or
568 Old_Config.Mode /= New_Config.Mode
569 then
570 if Old_Config.Port /= Disabled then
571 if Success then
572 pragma Debug (Debug.New_Line);
573 pragma Debug (Debug.Put_Line
574 ("Disabling port " & Port_Names (Old_Config.Port)));
575
576 Connectors.Pre_Off (Port_Cfg);
577
578 Display_Controller.Off
579 (To_Controller (I), To_Head (I, Old_Config.Port));
580
581 Connectors.Post_Off (Port_Cfg);
582 end if;
583
584 -- Free PLL
585 PLLs.Free (Allocated_PLLs (I));
586
587 Cur_Configs (I).Port := Disabled;
588 end if;
589
590 if New_Config.Port /= Disabled then
Nico Huber3c544ee2016-11-20 04:56:58 +0100591 Fill_Port_Config
592 (Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200593
Nico Huberc7a4fee2016-11-03 18:18:03 +0100594 Success := Success and then
Nico Huberdcd274b2016-11-03 20:15:39 +0100595 Validate_Config (New_Config.Framebuffer, Port_Cfg, I);
Nico Huberc7a4fee2016-11-03 18:18:03 +0100596
Nico Huber83693c82016-10-08 22:17:55 +0200597 if Success and then Wait_For_HPD (New_Config.Port) then
598 Check_HPD (Port_Cfg, New_Config.Port, Success);
599 Wait_For_HPD (New_Config.Port) := not Success;
600 end if;
601
602 if Success then
603 pragma Debug (Debug.New_Line);
604 pragma Debug (Debug.Put_Line
605 ("Trying to enable port " & Port_Names (New_Config.Port)));
606
607 if not Did_Power_Up then
608 Power_And_Clocks.Power_Up (Old_Configs, Configs);
609 Did_Power_Up := True;
610 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200611 end if;
612
613 if Success then
614 Connector_Info.Preferred_Link_Setting
615 (Port_Cfg => Port_Cfg,
616 Success => Success);
617 end if;
618
619 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100620 pragma Loop_Invariant
621 (New_Config.Port in Active_Port_Type and
622 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200623
624 PLLs.Alloc
625 (Port_Cfg => Port_Cfg,
626 PLL => Allocated_PLLs (I),
627 Success => Success);
628
629 if Success then
630 for Try in 1 .. 2 loop
631 pragma Loop_Invariant
632 (New_Config.Port in Active_Port_Type);
633
634 Connectors.Pre_On
635 (Port_Cfg => Port_Cfg,
636 PLL_Hint => PLLs.Register_Value
637 (Allocated_PLLs (I)),
638 Pipe_Hint => Display_Controller.Get_Pipe_Hint
639 (To_Head (I, New_Config.Port)),
640 Success => Success);
641
642 if Success then
643 Display_Controller.On
644 (Controller => To_Controller (I),
645 Head => To_Head (I, New_Config.Port),
646 Port_Cfg => Port_Cfg,
647 Framebuffer => New_Config.Framebuffer);
648
649 Connectors.Post_On
650 (Port_Cfg => Port_Cfg,
651 PLL_Hint => PLLs.Register_Value
652 (Allocated_PLLs (I)),
653 Success => Success);
654
655 if not Success then
656 Display_Controller.Off
657 (To_Controller (I),
658 To_Head (I, New_Config.Port));
659 Connectors.Post_Off (Port_Cfg);
660 end if;
661 end if;
662
663 exit when Success;
664 end loop;
665 exit when Success; -- connection established => stop loop
666
667 -- connection failed
668 PLLs.Free (Allocated_PLLs (I));
669 end if;
670
671 Connector_Info.Next_Link_Setting
672 (Port_Cfg => Port_Cfg,
673 Success => Success);
674 end loop;
675
676 if Success then
677 pragma Debug (Debug.Put_Line
678 ("Enabled port " & Port_Names (New_Config.Port)));
679 Cur_Configs (I) := New_Config;
680 DP_Links (I) := Port_Cfg.DP;
681 else
682 Wait_For_HPD (New_Config.Port) := True;
683 if New_Config.Port = Internal then
684 Panel.Off;
685 end if;
686 end if;
687 else
688 Cur_Configs (I) := New_Config;
689 end if;
690 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
691 Old_Config.Port /= Disabled
692 then
693 Display_Controller.Update_Offset
694 (Controller => To_Controller (I),
695 Framebuffer => New_Config.Framebuffer);
696 Cur_Configs (I) := New_Config;
697 end if;
698 end loop;
699
700 if Did_Power_Up then
701 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
702 end if;
703
704 end Update_Outputs;
705
706 ----------------------------------------------------------------------------
707
708 procedure Initialize
709 (MMIO_Base : in Word64 := 0;
710 Write_Delay : in Word64 := 0;
711 Success : out Boolean)
712 with
713 Refined_Global =>
714 (In_Out =>
715 (Config.Valid_Port_GPU,
716 Registers.Register_State, Port_IO.State),
717 Input =>
718 (Time.State),
719 Output =>
720 (Registers.Address_State,
721 PLLs.State, Panel.Panel_State,
722 Cur_Configs, Allocated_PLLs, DP_Links,
723 HPD_Delay, Wait_For_HPD, Initialized))
724 is
725 use type HW.Word64;
726
727 Now : constant Time.T := Time.Now;
728
729 procedure Check_Platform (Success : out Boolean)
730 is
731 Audio_VID_DID : Word32;
732 begin
733 case Config.CPU is
734 when Haswell .. Skylake =>
735 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
736 when Ironlake .. Ivybridge =>
737 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
738 end case;
739 Success :=
740 (case Config.CPU is
741 when Skylake => Audio_VID_DID = 16#8086_2809#,
742 when Broadwell => Audio_VID_DID = 16#8086_2808#,
743 when Haswell => Audio_VID_DID = 16#8086_2807#,
744 when Ivybridge |
745 Sandybridge => Audio_VID_DID = 16#8086_2806# or
746 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200747 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200748 end Check_Platform;
749 begin
750 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
751 Reason => "Write_Delay is used for debugging only");
752
753 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
754
755 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
756
757 Wait_For_HPD := HPD_Type'(others => False);
758 HPD_Delay := HPD_Delay_Type'(others => Now);
759 DP_Links := Links_Type'(others => HW.GFX.Default_DP);
760 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100761 Cur_Configs := Pipe_Configs'
762 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200763 (Port => Disabled,
764 Framebuffer => HW.GFX.Default_FB,
765 Mode => HW.GFX.Invalid_Mode));
766 Registers.Set_Register_Base
767 (if MMIO_Base /= 0 then
768 MMIO_Base
769 else
770 Config.Default_MMIO_Base);
771 PLLs.Initialize;
772
773 Check_Platform (Success);
774 if not Success then
775 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
776
777 Panel.Static_Init; -- for flow analysis
778
779 Initialized := False;
780 return;
781 end if;
782
783 Panel.Setup_PP_Sequencer;
784 Port_Detect.Initialize;
785
786 Power_And_Clocks.Pre_All_Off;
787
788 Legacy_VGA_Off;
789
790 Connectors.Pre_All_Off;
791 Display_Controller.All_Off;
792 Connectors.Post_All_Off;
793 PLLs.All_Off;
794
795 Power_And_Clocks.Post_All_Off;
796
797 -------------------- Now restart from a clean state ---------------------
798 Power_And_Clocks.Initialize;
799
Nico Huberf54d0962016-10-20 14:17:18 +0200800 Registers.Unset_And_Set_Mask
801 (Register => Registers.PCH_RAWCLK_FREQ,
802 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
803 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
804
Nico Huber83693c82016-10-08 22:17:55 +0200805 Initialized := True;
806
807 end Initialize;
808
809 function Is_Initialized return Boolean
810 with
811 Refined_Post => Is_Initialized'Result = Initialized
812 is
813 begin
814 return Initialized;
815 end Is_Initialized;
816
817 ----------------------------------------------------------------------------
818
819 procedure Write_GTT
820 (GTT_Page : GTT_Range;
821 Device_Address : GTT_Address_Type;
822 Valid : Boolean) is
823 begin
824 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
825 end Write_GTT;
826
827 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
828 is
829 FB_Size : constant Pos32 :=
830 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
831 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
832 begin
833 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
834 loop
835 Registers.Write_GTT
836 (GTT_Page => Idx,
837 Device_Address => Phys_Addr,
838 Valid => True);
839 Phys_Addr := Phys_Addr + 4096;
840 end loop;
841 end Setup_Default_GTT;
842
843 ----------------------------------------------------------------------------
844
Nico Huber99f10f32016-11-20 00:34:05 +0100845 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200846 is
847 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100848 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200849 Pipe_Names : constant Pipe_Name_Array :=
850 (Primary => "Primary ",
851 Secondary => "Secondary",
852 Tertiary => "Tertiary ");
853 begin
854 Debug.New_Line;
855 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100856 for Pipe in Pipe_Index loop
857 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200858 Debug.Put (" (");
859 else
860 Debug.Put (" ");
861 end if;
862 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
863 Debug.Put_Line
864 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
865 Debug.Put_Line (" Framebuffer =>");
866 Debug.Put (" (Width => ");
867 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
868 Debug.Put_Line (",");
869 Debug.Put (" Height => ");
870 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
871 Debug.Put_Line (",");
872 Debug.Put (" Stride => ");
873 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
874 Debug.Put_Line (",");
875 Debug.Put (" Offset => ");
876 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
877 Debug.Put_Line (",");
878 Debug.Put (" BPC => ");
879 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
880 Debug.Put_Line ("),");
881 Debug.Put_Line (" Mode =>");
882 Debug.Put (" (Dotclock => ");
883 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
884 Debug.Put_Line (",");
885 Debug.Put (" H_Visible => ");
886 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
887 Debug.Put_Line (",");
888 Debug.Put (" H_Sync_Begin => ");
889 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
890 Debug.Put_Line (",");
891 Debug.Put (" H_Sync_End => ");
892 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
893 Debug.Put_Line (",");
894 Debug.Put (" H_Total => ");
895 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
896 Debug.Put_Line (",");
897 Debug.Put (" V_Visible => ");
898 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
899 Debug.Put_Line (",");
900 Debug.Put (" V_Sync_Begin => ");
901 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
902 Debug.Put_Line (",");
903 Debug.Put (" V_Sync_End => ");
904 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
905 Debug.Put_Line (",");
906 Debug.Put (" V_Total => ");
907 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
908 Debug.Put_Line (",");
909 Debug.Put_Line (" H_Sync_Active_High => " &
910 (if Configs (Pipe).Mode.H_Sync_Active_High
911 then "True,"
912 else "False,"));
913 Debug.Put_Line (" V_Sync_Active_High => " &
914 (if Configs (Pipe).Mode.V_Sync_Active_High
915 then "True,"
916 else "False,"));
917 Debug.Put (" BPC => ");
918 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100919 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200920 Debug.Put_Line (")),");
921 else
922 Debug.Put_Line (")));");
923 end if;
924 end loop;
925 end Dump_Configs;
926
927end HW.GFX.GMA;