blob: 3487d722b3cda5f898fcc13153231dff07a5c924 [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 Huber99f10f32016-11-20 00:34:05 +010095 (Configs : Pipe_Configs;
96 Idx : Pipe_Index)
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
103 (if Config.Internal_Is_EDP and then Configs (Idx).Port = Internal
104 then
105 DIGI_A
106 else
107 (case Idx is
108 -- FDIs are fixed to the CPU pipe
109 when Primary => DIGI_B,
110 when Secondary => DIGI_C,
111 when Tertiary => DIGI_D)),
112 when Haswell .. Skylake => -- everything but VGA directly on CPU
113 (case Configs (Idx).Port is
Nico Huber0d454cd2016-11-21 13:33:43 +0100114 when Disabled => GPU_Port'First,
115 when Internal => DIGI_A, -- LVDS not available
116 when HDMI1 | DP1 => DIGI_B,
117 when HDMI2 | DP2 => DIGI_C,
118 when HDMI3 | DP3 => DIGI_D,
119 when Analog => DIGI_E));
Nico Huber83693c82016-10-08 22:17:55 +0200120 end To_GPU_Port;
121
122 function To_PCH_Port (Port : Active_Port_Type) return PCH_Port
Nico Huber995436b2016-11-20 02:21:51 +0100123 with Pre => True
Nico Huber83693c82016-10-08 22:17:55 +0200124 is
125 begin
126 return
127 (case Port is
128 when Internal => PCH_LVDS, -- will be ignored if Internal is DP
129 when Analog => PCH_DAC,
Nico Huber0d454cd2016-11-21 13:33:43 +0100130 when HDMI1 => PCH_HDMI_B,
131 when HDMI2 => PCH_HDMI_C,
132 when HDMI3 => PCH_HDMI_D,
Nico Huber83693c82016-10-08 22:17:55 +0200133 when DP1 => PCH_DP_B,
134 when DP2 => PCH_DP_C,
135 when DP3 => PCH_DP_D);
136 end To_PCH_Port;
137
138 function To_Display_Type (Port : Active_Port_Type) return Display_Type
139 with Pre => True
140 is
141 begin
142 return
143 (case Port is
144 when Internal => Config.Internal_Display,
145 when Analog => VGA,
Nico Huber0d454cd2016-11-21 13:33:43 +0100146 when HDMI1 |
147 HDMI2 |
148 HDMI3 => HDMI,
Nico Huber83693c82016-10-08 22:17:55 +0200149 when DP1 |
150 DP2 |
151 DP3 => DP);
152 end To_Display_Type;
153
154 procedure Configure_FDI_Link
155 (Port_Cfg : in out Port_Config;
156 Success : out Boolean)
Nico Huber47ff0692016-11-04 14:29:39 +0100157 with
158 Post => Port_Cfg.Mode = Port_Cfg.Mode'Old
Nico Huber83693c82016-10-08 22:17:55 +0200159 is
160 procedure Limit_Lane_Count
161 is
162 FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
163 Enabled : Boolean;
164 begin
165 -- if DIGI_D enabled: (FDI names are off by one)
166 Registers.Is_Set_Mask
167 (Register => Registers.FDI_TX_CTL_C,
168 Mask => FDI_TX_CTL_FDI_TX_ENABLE,
169 Result => Enabled);
170 if Enabled then
171 Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count := DP_Lane_Count_2;
172 end if;
173 end Limit_Lane_Count;
174 begin
175 Port_Cfg.FDI.Receiver_Caps.Max_Link_Rate := DP_Bandwidth_2_7;
176 Port_Cfg.FDI.Receiver_Caps.Max_Lane_Count :=
177 Config.FDI_Lane_Count (Port_Cfg.Port);
178 Port_Cfg.FDI.Receiver_Caps.Enhanced_Framing := True;
179 if Config.Has_FDI_C and then Port_Cfg.Port = DIGI_C then
180 Limit_Lane_Count;
181 end if;
182 DP_Info.Preferred_Link_Setting (Port_Cfg.FDI, Port_Cfg.Mode, Success);
183 end Configure_FDI_Link;
184
Nico Huberc7a4fee2016-11-03 18:18:03 +0100185 function Validate_Config
186 (Framebuffer : Framebuffer_Type;
Nico Huberdcd274b2016-11-03 20:15:39 +0100187 Port_Cfg : Port_Config;
Nico Huber99f10f32016-11-20 00:34:05 +0100188 I : Pipe_Index)
Nico Huberc7a4fee2016-11-03 18:18:03 +0100189 return Boolean
Nico Huber47ff0692016-11-04 14:29:39 +0100190 with
191 Post =>
192 (if Validate_Config'Result then
193 Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
194 Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))
Nico Huberc7a4fee2016-11-03 18:18:03 +0100195 is
196 begin
197 -- No downscaling
Nico Huberdcd274b2016-11-03 20:15:39 +0100198 -- Respect maximum scalable width
Nico Huber3675db52016-11-04 16:27:29 +0100199 -- VGA plane is only allowed on the primary pipe
200 -- Only 32bpp RGB (ignored for VGA plane)
201 -- Stride must be a multiple of 64 (ignored for VGA plane)
Nico Huberc7a4fee2016-11-03 18:18:03 +0100202 return
Nico Huberdcd274b2016-11-03 20:15:39 +0100203 ((Framebuffer.Width = Pos32 (Port_Cfg.Mode.H_Visible) and
204 Framebuffer.Height = Pos32 (Port_Cfg.Mode.V_Visible)) or
205 (Framebuffer.Width <= Config.Maximum_Scalable_Width (I) and
206 Framebuffer.Width <= Pos32 (Port_Cfg.Mode.H_Visible) and
207 Framebuffer.Height <= Pos32 (Port_Cfg.Mode.V_Visible))) and
Nico Huber3675db52016-11-04 16:27:29 +0100208 (Framebuffer.Offset /= VGA_PLANE_FRAMEBUFFER_OFFSET or I = Primary) and
209 (Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET or
210 (Framebuffer.BPC = 8 and
211 Framebuffer.Stride mod 64 = 0));
Nico Huberc7a4fee2016-11-03 18:18:03 +0100212 end Validate_Config;
213
Nico Huber83693c82016-10-08 22:17:55 +0200214 procedure Fill_Port_Config
215 (Port_Cfg : out Port_Config;
Nico Huber99f10f32016-11-20 00:34:05 +0100216 Configs : in Pipe_Configs;
217 Idx : in Pipe_Index;
Nico Huber83693c82016-10-08 22:17:55 +0200218 Success : out Boolean)
219 with Pre => True
220 is
221 begin
222 Success :=
223 Config.Supported_Pipe (Idx) and then
224 Config.Valid_Port (Configs (Idx).Port) and then
225 Configs (Idx).Port /= Disabled;
226
227 if Success then
228 declare
229 Port : constant Port_Type := Configs (Idx).Port;
230 Mode : constant Mode_Type := Configs (Idx).Mode;
231 Link : constant DP_Link := DP_Links (Idx);
232 begin
233 Port_Cfg := Port_Config'
234 (Port => To_GPU_Port (Configs, Idx),
235 PCH_Port => To_PCH_Port (Port),
236 Display => To_Display_Type (Port),
237 Mode => Mode,
238 Is_FDI => Config.FDI_Port (To_GPU_Port (Configs, Idx)),
239 FDI => Default_DP,
240 DP => Link);
241 if Port_Cfg.Mode.BPC = Auto_BPC then
242 Port_Cfg.Mode.BPC := Connector_Info.Default_BPC (Port_Cfg);
243 end if;
Nico Huber74ec9622016-11-19 03:00:43 +0100244 if Port_Cfg.Display = HDMI then
245 declare
246 pragma Assert (Config.HDMI_Max_Clock_24bpp * 8
247 / Port_Cfg.Mode.BPC >= Frequency_Type'First);
248 Max_Dotclock : constant Frequency_Type :=
249 Config.HDMI_Max_Clock_24bpp * 8 / Port_Cfg.Mode.BPC;
250 begin
251 if Port_Cfg.Mode.Dotclock > Max_Dotclock then
252 pragma Debug (Debug.Put ("Dotclock "));
253 pragma Debug (Debug.Put_Int64 (Port_Cfg.Mode.Dotclock));
254 pragma Debug (Debug.Put (" too high, limiting to "));
255 pragma Debug (Debug.Put_Int64 (Max_Dotclock));
256 pragma Debug (Debug.Put_Line ("."));
257 Port_Cfg.Mode.Dotclock := Max_Dotclock;
258 end if;
259 end;
260 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200261 end;
262 else
263 Port_Cfg := Port_Config'
264 (Port => GPU_Port'First,
265 PCH_Port => PCH_Port'First,
266 Display => Display_Type'First,
267 Mode => Invalid_Mode,
268 Is_FDI => False,
269 FDI => Default_DP,
270 DP => Default_DP);
271 end if;
272 end Fill_Port_Config;
273
274 ----------------------------------------------------------------------------
275
276 function To_Controller
Nico Huber99f10f32016-11-20 00:34:05 +0100277 (Dsp_Config : Pipe_Index) return Display_Controller.Controller_Type
Nico Huber83693c82016-10-08 22:17:55 +0200278 is
279 Result : Display_Controller.Controller_Type;
280 begin
281 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
282
283 case Dsp_Config is
284 when Primary =>
285 Result := Display_Controller.Controllers (Display_Controller.A);
286 when Secondary =>
287 Result := Display_Controller.Controllers (Display_Controller.B);
288 when Tertiary =>
289 Result := Display_Controller.Controllers (Display_Controller.C);
290 end case;
291 return Result;
292 end To_Controller;
293
294 ----------------------------------------------------------------------------
295
296 function To_Head
Nico Huber99f10f32016-11-20 00:34:05 +0100297 (N_Config : Pipe_Index;
Nico Huber83693c82016-10-08 22:17:55 +0200298 Port : Active_Port_Type)
299 return Display_Controller.Head_Type
300 is
301 Result : Display_Controller.Head_Type;
302 begin
303 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
304
305 if Config.Has_EDP_Pipe and then Port = Internal then
306 Result := Display_Controller.Heads (Display_Controller.Head_EDP);
307 else
308 case N_Config is
309 when Primary =>
310 Result := Display_Controller.Heads (Display_Controller.Head_A);
311 when Secondary =>
312 Result := Display_Controller.Heads (Display_Controller.Head_B);
313 when Tertiary =>
314 Result := Display_Controller.Heads (Display_Controller.Head_C);
315 end case;
316 end if;
317 return Result;
318 end To_Head;
319
320 ----------------------------------------------------------------------------
321
322 procedure Legacy_VGA_Off
323 is
324 Reg8 : Word8;
325 begin
326 -- disable legacy VGA plane, taking over control now
327 Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
328 Port_IO.InB (Reg8, VGA_SR_DATA);
329 Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
330 Time.U_Delay (100); -- PRM says 100us, Linux does 300
331 Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
332 end Legacy_VGA_Off;
333
334 ----------------------------------------------------------------------------
335
336 function Port_Configured
Nico Huber99f10f32016-11-20 00:34:05 +0100337 (Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200338 Port : Port_Type)
339 return Boolean
340 with
341 Global => null
342 is
343 begin
344 return Configs (Primary).Port = Port or
345 Configs (Secondary).Port = Port or
346 Configs (Tertiary).Port = Port;
347 end Port_Configured;
348
Nico Huber845de362016-11-21 14:00:06 +0100349 -- DP and HDMI share physical pins.
350 function Sibling_Port (Port : Port_Type) return Port_Type
351 is
352 begin
353 return
354 (case Port is
355 when HDMI1 => DP1,
356 when HDMI2 => DP2,
357 when HDMI3 => DP3,
358 when DP1 => HDMI1,
359 when DP2 => HDMI2,
360 when DP3 => HDMI3,
361 when others => Disabled);
362 end Sibling_Port;
363
364 function Has_Sibling_Port (Port : Port_Type) return Boolean
365 is
366 begin
367 return Sibling_Port (Port) /= Disabled;
368 end Has_Sibling_Port;
369
Nico Huber995436b2016-11-20 02:21:51 +0100370 procedure Read_EDID
371 (Raw_EDID : out EDID.Raw_EDID_Data;
372 Port : in Active_Port_Type;
373 Success : out Boolean)
374 with
375 Post => (if Success then EDID.Valid (Raw_EDID))
376 is
377 Raw_EDID_Length : GFX.I2C.Transfer_Length := Raw_EDID'Length;
378 begin
379 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
380
381 for I in 1 .. 2 loop
382 if To_Display_Type (Port) = DP then
Nico Huber1b2c9a32016-11-20 03:42:08 +0100383 -- May need power to read edid
384 declare
385 Temp_Configs : Pipe_Configs := Cur_Configs;
386 begin
387 Temp_Configs (Primary).Port := Port;
388 Power_And_Clocks.Power_Up (Cur_Configs, Temp_Configs);
389 end;
390
Nico Huber995436b2016-11-20 02:21:51 +0100391 declare
392 DP_Port : constant GMA.DP_Port :=
393 (case Port is
394 when Internal => DP_A,
395 when DP1 => DP_B,
396 when DP2 => DP_C,
397 when DP3 => DP_D,
398 when others => GMA.DP_Port'First);
399 begin
400 DP_Aux_Ch.I2C_Read
401 (Port => DP_Port,
402 Address => 16#50#,
403 Length => Raw_EDID_Length,
404 Data => Raw_EDID,
405 Success => Success);
406 end;
407 else
408 I2C.I2C_Read
409 (Port => (if Port = Analog
410 then Config.Analog_I2C_Port
411 else To_PCH_Port (Port)),
412 Address => 16#50#,
413 Length => Raw_EDID_Length,
414 Data => Raw_EDID,
415 Success => Success);
416 end if;
417 exit when not Success; -- don't retry if reading itself failed
418
419 pragma Debug (Debug.Put_Buffer ("EDID", Raw_EDID, Raw_EDID_Length));
420 EDID.Sanitize (Raw_EDID, Success);
421 exit when Success;
422 end loop;
423 end Read_EDID;
424
Nico Huber1b2c9a32016-11-20 03:42:08 +0100425 procedure Probe_Port
426 (Pipe_Cfg : in out Pipe_Config;
427 Port : in Active_Port_Type;
428 Success : out Boolean)
Nico Huber83693c82016-10-08 22:17:55 +0200429 is
430 Raw_EDID : EDID.Raw_EDID_Data := (others => 16#00#);
Nico Huber1b2c9a32016-11-20 03:42:08 +0100431 begin
432 Success := Config.Valid_Port (Port);
433
434 if Success then
435 if Port = Internal then
436 Panel.On;
437 end if;
438 Read_EDID (Raw_EDID, Port, Success);
439 end if;
440
441 if Success and then
442 (EDID.Compatible_Display (Raw_EDID, To_Display_Type (Port)) and
443 EDID.Has_Preferred_Mode (Raw_EDID))
444 then
445 Pipe_Cfg.Port := Port;
446 Pipe_Cfg.Mode := EDID.Preferred_Mode (Raw_EDID);
447 else
448 Success := False;
449 if Port = Internal then
450 Panel.Off;
451 end if;
452 end if;
453 end Probe_Port;
454
455 procedure Scan_Ports
456 (Configs : out Pipe_Configs;
457 Ports : in Port_List;
458 Max_Pipe : in Pipe_Index := Pipe_Index'Last)
459 is
Nico Huber83693c82016-10-08 22:17:55 +0200460 Port_Idx : Port_List_Range := Port_List_Range'First;
Nico Huber1b2c9a32016-11-20 03:42:08 +0100461 Success : Boolean;
Nico Huber83693c82016-10-08 22:17:55 +0200462 begin
Nico Huber99f10f32016-11-20 00:34:05 +0100463 Configs := (Pipe_Index =>
Nico Huber83693c82016-10-08 22:17:55 +0200464 (Port => Disabled,
465 Mode => Invalid_Mode,
466 Framebuffer => Default_FB));
467
Nico Huber1b2c9a32016-11-20 03:42:08 +0100468 for Pipe in Pipe_Index range
469 Pipe_Index'First .. Pipe_Index'Min (Max_Pipe, Config.Max_Pipe)
470 loop
Nico Huber83693c82016-10-08 22:17:55 +0200471 while Ports (Port_Idx) /= Disabled loop
Nico Huber845de362016-11-21 14:00:06 +0100472 if not Port_Configured (Configs, Ports (Port_Idx)) and
473 (not Has_Sibling_Port (Ports (Port_Idx)) or
474 not Port_Configured (Configs, Sibling_Port (Ports (Port_Idx))))
475 then
Nico Huber1b2c9a32016-11-20 03:42:08 +0100476 Probe_Port (Configs (Pipe), Ports (Port_Idx), Success);
477 else
478 Success := False;
Nico Huber83693c82016-10-08 22:17:55 +0200479 end if;
480
481 exit when Port_Idx = Port_List_Range'Last;
482 Port_Idx := Port_List_Range'Succ (Port_Idx);
483
484 exit when Success;
485 end loop;
486 end loop;
487
Nico Huber1b2c9a32016-11-20 03:42:08 +0100488 -- Restore power settings
Nico Huber83693c82016-10-08 22:17:55 +0200489 Power_And_Clocks.Power_Set_To (Cur_Configs);
490 end Scan_Ports;
491
Nico Huber83693c82016-10-08 22:17:55 +0200492 ----------------------------------------------------------------------------
493
Nico Huber99f10f32016-11-20 00:34:05 +0100494 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200495 is
496 Did_Power_Up : Boolean := False;
497
498 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +0100499 Old_Config, New_Config : Pipe_Config;
500 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200501 Port_Cfg : Port_Config;
502
503 procedure Check_HPD
504 (Port_Cfg : in Port_Config;
505 Port : in Port_Type;
506 Detected : out Boolean)
507 is
508 begin
509 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
510 if HPD_Delay_Over then
511 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
512 HPD_Delay (Port) := Time.MS_From_Now (333);
513 else
514 Detected := False;
515 end if;
516 end Check_HPD;
517 begin
518 Old_Configs := Cur_Configs;
519
Nico Huber99f10f32016-11-20 00:34:05 +0100520 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200521 HPD := False;
522
523 Old_Config := Cur_Configs (I);
524 New_Config := Configs (I);
525
526 Fill_Port_Config (Port_Cfg, Old_Configs, I, Success);
527 if Success then
528 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
529 end if;
530
531 -- Connector changed?
532 if (Success and then HPD) or
533 Old_Config.Port /= New_Config.Port or
534 Old_Config.Mode /= New_Config.Mode
535 then
536 if Old_Config.Port /= Disabled then
537 if Success then
538 pragma Debug (Debug.New_Line);
539 pragma Debug (Debug.Put_Line
540 ("Disabling port " & Port_Names (Old_Config.Port)));
541
542 Connectors.Pre_Off (Port_Cfg);
543
544 Display_Controller.Off
545 (To_Controller (I), To_Head (I, Old_Config.Port));
546
547 Connectors.Post_Off (Port_Cfg);
548 end if;
549
550 -- Free PLL
551 PLLs.Free (Allocated_PLLs (I));
552
553 Cur_Configs (I).Port := Disabled;
554 end if;
555
556 if New_Config.Port /= Disabled then
557 Fill_Port_Config (Port_Cfg, Configs, I, Success);
558
Nico Huberc7a4fee2016-11-03 18:18:03 +0100559 Success := Success and then
Nico Huberdcd274b2016-11-03 20:15:39 +0100560 Validate_Config (New_Config.Framebuffer, Port_Cfg, I);
Nico Huberc7a4fee2016-11-03 18:18:03 +0100561
Nico Huber83693c82016-10-08 22:17:55 +0200562 if Success and then Wait_For_HPD (New_Config.Port) then
563 Check_HPD (Port_Cfg, New_Config.Port, Success);
564 Wait_For_HPD (New_Config.Port) := not Success;
565 end if;
566
567 if Success then
568 pragma Debug (Debug.New_Line);
569 pragma Debug (Debug.Put_Line
570 ("Trying to enable port " & Port_Names (New_Config.Port)));
571
572 if not Did_Power_Up then
573 Power_And_Clocks.Power_Up (Old_Configs, Configs);
574 Did_Power_Up := True;
575 end if;
576
577 if Port_Cfg.Is_FDI then
578 Configure_FDI_Link (Port_Cfg, Success);
579 end if;
580 end if;
581
582 if Success then
583 Connector_Info.Preferred_Link_Setting
584 (Port_Cfg => Port_Cfg,
585 Success => Success);
586 end if;
587
588 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100589 pragma Loop_Invariant
590 (New_Config.Port in Active_Port_Type and
591 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200592
593 PLLs.Alloc
594 (Port_Cfg => Port_Cfg,
595 PLL => Allocated_PLLs (I),
596 Success => Success);
597
598 if Success then
599 for Try in 1 .. 2 loop
600 pragma Loop_Invariant
601 (New_Config.Port in Active_Port_Type);
602
603 Connectors.Pre_On
604 (Port_Cfg => Port_Cfg,
605 PLL_Hint => PLLs.Register_Value
606 (Allocated_PLLs (I)),
607 Pipe_Hint => Display_Controller.Get_Pipe_Hint
608 (To_Head (I, New_Config.Port)),
609 Success => Success);
610
611 if Success then
612 Display_Controller.On
613 (Controller => To_Controller (I),
614 Head => To_Head (I, New_Config.Port),
615 Port_Cfg => Port_Cfg,
616 Framebuffer => New_Config.Framebuffer);
617
618 Connectors.Post_On
619 (Port_Cfg => Port_Cfg,
620 PLL_Hint => PLLs.Register_Value
621 (Allocated_PLLs (I)),
622 Success => Success);
623
624 if not Success then
625 Display_Controller.Off
626 (To_Controller (I),
627 To_Head (I, New_Config.Port));
628 Connectors.Post_Off (Port_Cfg);
629 end if;
630 end if;
631
632 exit when Success;
633 end loop;
634 exit when Success; -- connection established => stop loop
635
636 -- connection failed
637 PLLs.Free (Allocated_PLLs (I));
638 end if;
639
640 Connector_Info.Next_Link_Setting
641 (Port_Cfg => Port_Cfg,
642 Success => Success);
643 end loop;
644
645 if Success then
646 pragma Debug (Debug.Put_Line
647 ("Enabled port " & Port_Names (New_Config.Port)));
648 Cur_Configs (I) := New_Config;
649 DP_Links (I) := Port_Cfg.DP;
650 else
651 Wait_For_HPD (New_Config.Port) := True;
652 if New_Config.Port = Internal then
653 Panel.Off;
654 end if;
655 end if;
656 else
657 Cur_Configs (I) := New_Config;
658 end if;
659 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
660 Old_Config.Port /= Disabled
661 then
662 Display_Controller.Update_Offset
663 (Controller => To_Controller (I),
664 Framebuffer => New_Config.Framebuffer);
665 Cur_Configs (I) := New_Config;
666 end if;
667 end loop;
668
669 if Did_Power_Up then
670 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
671 end if;
672
673 end Update_Outputs;
674
675 ----------------------------------------------------------------------------
676
677 procedure Initialize
678 (MMIO_Base : in Word64 := 0;
679 Write_Delay : in Word64 := 0;
680 Success : out Boolean)
681 with
682 Refined_Global =>
683 (In_Out =>
684 (Config.Valid_Port_GPU,
685 Registers.Register_State, Port_IO.State),
686 Input =>
687 (Time.State),
688 Output =>
689 (Registers.Address_State,
690 PLLs.State, Panel.Panel_State,
691 Cur_Configs, Allocated_PLLs, DP_Links,
692 HPD_Delay, Wait_For_HPD, Initialized))
693 is
694 use type HW.Word64;
695
696 Now : constant Time.T := Time.Now;
697
698 procedure Check_Platform (Success : out Boolean)
699 is
700 Audio_VID_DID : Word32;
701 begin
702 case Config.CPU is
703 when Haswell .. Skylake =>
704 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
705 when Ironlake .. Ivybridge =>
706 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
707 end case;
708 Success :=
709 (case Config.CPU is
710 when Skylake => Audio_VID_DID = 16#8086_2809#,
711 when Broadwell => Audio_VID_DID = 16#8086_2808#,
712 when Haswell => Audio_VID_DID = 16#8086_2807#,
713 when Ivybridge |
714 Sandybridge => Audio_VID_DID = 16#8086_2806# or
715 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200716 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200717 end Check_Platform;
718 begin
719 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
720 Reason => "Write_Delay is used for debugging only");
721
722 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
723
724 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
725
726 Wait_For_HPD := HPD_Type'(others => False);
727 HPD_Delay := HPD_Delay_Type'(others => Now);
728 DP_Links := Links_Type'(others => HW.GFX.Default_DP);
729 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100730 Cur_Configs := Pipe_Configs'
731 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200732 (Port => Disabled,
733 Framebuffer => HW.GFX.Default_FB,
734 Mode => HW.GFX.Invalid_Mode));
735 Registers.Set_Register_Base
736 (if MMIO_Base /= 0 then
737 MMIO_Base
738 else
739 Config.Default_MMIO_Base);
740 PLLs.Initialize;
741
742 Check_Platform (Success);
743 if not Success then
744 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
745
746 Panel.Static_Init; -- for flow analysis
747
748 Initialized := False;
749 return;
750 end if;
751
752 Panel.Setup_PP_Sequencer;
753 Port_Detect.Initialize;
754
755 Power_And_Clocks.Pre_All_Off;
756
757 Legacy_VGA_Off;
758
759 Connectors.Pre_All_Off;
760 Display_Controller.All_Off;
761 Connectors.Post_All_Off;
762 PLLs.All_Off;
763
764 Power_And_Clocks.Post_All_Off;
765
766 -------------------- Now restart from a clean state ---------------------
767 Power_And_Clocks.Initialize;
768
Nico Huberf54d0962016-10-20 14:17:18 +0200769 Registers.Unset_And_Set_Mask
770 (Register => Registers.PCH_RAWCLK_FREQ,
771 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
772 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
773
Nico Huber83693c82016-10-08 22:17:55 +0200774 Initialized := True;
775
776 end Initialize;
777
778 function Is_Initialized return Boolean
779 with
780 Refined_Post => Is_Initialized'Result = Initialized
781 is
782 begin
783 return Initialized;
784 end Is_Initialized;
785
786 ----------------------------------------------------------------------------
787
788 procedure Write_GTT
789 (GTT_Page : GTT_Range;
790 Device_Address : GTT_Address_Type;
791 Valid : Boolean) is
792 begin
793 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
794 end Write_GTT;
795
796 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
797 is
798 FB_Size : constant Pos32 :=
799 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
800 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
801 begin
802 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
803 loop
804 Registers.Write_GTT
805 (GTT_Page => Idx,
806 Device_Address => Phys_Addr,
807 Valid => True);
808 Phys_Addr := Phys_Addr + 4096;
809 end loop;
810 end Setup_Default_GTT;
811
812 ----------------------------------------------------------------------------
813
Nico Huber99f10f32016-11-20 00:34:05 +0100814 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200815 is
816 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100817 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200818 Pipe_Names : constant Pipe_Name_Array :=
819 (Primary => "Primary ",
820 Secondary => "Secondary",
821 Tertiary => "Tertiary ");
822 begin
823 Debug.New_Line;
824 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100825 for Pipe in Pipe_Index loop
826 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200827 Debug.Put (" (");
828 else
829 Debug.Put (" ");
830 end if;
831 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
832 Debug.Put_Line
833 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
834 Debug.Put_Line (" Framebuffer =>");
835 Debug.Put (" (Width => ");
836 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
837 Debug.Put_Line (",");
838 Debug.Put (" Height => ");
839 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
840 Debug.Put_Line (",");
841 Debug.Put (" Stride => ");
842 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
843 Debug.Put_Line (",");
844 Debug.Put (" Offset => ");
845 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
846 Debug.Put_Line (",");
847 Debug.Put (" BPC => ");
848 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
849 Debug.Put_Line ("),");
850 Debug.Put_Line (" Mode =>");
851 Debug.Put (" (Dotclock => ");
852 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
853 Debug.Put_Line (",");
854 Debug.Put (" H_Visible => ");
855 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
856 Debug.Put_Line (",");
857 Debug.Put (" H_Sync_Begin => ");
858 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
859 Debug.Put_Line (",");
860 Debug.Put (" H_Sync_End => ");
861 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
862 Debug.Put_Line (",");
863 Debug.Put (" H_Total => ");
864 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
865 Debug.Put_Line (",");
866 Debug.Put (" V_Visible => ");
867 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
868 Debug.Put_Line (",");
869 Debug.Put (" V_Sync_Begin => ");
870 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
871 Debug.Put_Line (",");
872 Debug.Put (" V_Sync_End => ");
873 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
874 Debug.Put_Line (",");
875 Debug.Put (" V_Total => ");
876 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
877 Debug.Put_Line (",");
878 Debug.Put_Line (" H_Sync_Active_High => " &
879 (if Configs (Pipe).Mode.H_Sync_Active_High
880 then "True,"
881 else "False,"));
882 Debug.Put_Line (" V_Sync_Active_High => " &
883 (if Configs (Pipe).Mode.V_Sync_Active_High
884 then "True,"
885 else "False,"));
886 Debug.Put (" BPC => ");
887 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100888 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200889 Debug.Put_Line (")),");
890 else
891 Debug.Put_Line (")));");
892 end if;
893 end loop;
894 end Dump_Configs;
895
896end HW.GFX.GMA;