blob: 0dcad800d09d28129517e254fc4aa57bc8b57eca [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber3be61d42017-01-09 13:58:18 +01002-- Copyright (C) 2014-2017 secunet Security Networks AG
Nico Huber83693c82016-10-08 22:17:55 +02003--
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 Huber83693c82016-10-08 22:17:55 +020015with HW.GFX.GMA.Config;
Nico Huber8c45bcf2016-11-20 17:30:57 +010016with HW.GFX.GMA.Config_Helpers;
Nico Huber83693c82016-10-08 22:17:55 +020017with HW.GFX.GMA.Registers;
18with HW.GFX.GMA.Power_And_Clocks;
19with HW.GFX.GMA.Panel;
20with HW.GFX.GMA.PLLs;
21with HW.GFX.GMA.Port_Detect;
22with HW.GFX.GMA.Connectors;
23with HW.GFX.GMA.Connector_Info;
24with HW.GFX.GMA.Pipe_Setup;
25
26with System;
27
28with HW.Debug;
29with GNAT.Source_Info;
30
Nico Huber83693c82016-10-08 22:17:55 +020031use type HW.Int32;
32
33package body HW.GFX.GMA
34 with Refined_State =>
35 (State =>
36 (Registers.Address_State,
37 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +010038 Cur_Configs, Allocated_PLLs,
Nico Huber83693c82016-10-08 22:17:55 +020039 HPD_Delay, Wait_For_HPD),
40 Init_State => Initialized,
41 Config_State => Config.Valid_Port_GPU,
42 Device_State =>
43 (Registers.Register_State, Registers.GTT_State))
44is
45
46 subtype Port_Name is String (1 .. 8);
47 type Port_Name_Array is array (Port_Type) of Port_Name;
48 Port_Names : constant Port_Name_Array :=
49 (Disabled => "Disabled",
50 Internal => "Internal",
51 DP1 => "DP1 ",
52 DP2 => "DP2 ",
53 DP3 => "DP3 ",
Nico Huber0d454cd2016-11-21 13:33:43 +010054 HDMI1 => "HDMI1 ",
55 HDMI2 => "HDMI2 ",
56 HDMI3 => "HDMI3 ",
Nico Huber83693c82016-10-08 22:17:55 +020057 Analog => "Analog ");
58
59 package Display_Controller renames Pipe_Setup;
60
Nico Huber99f10f32016-11-20 00:34:05 +010061 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020062
Nico Huber83693c82016-10-08 22:17:55 +020063 type HPD_Type is array (Port_Type) of Boolean;
Nico Huber3be61d42017-01-09 13:58:18 +010064 type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
Nico Huber83693c82016-10-08 22:17:55 +020065
Nico Huber83693c82016-10-08 22:17:55 +020066 Allocated_PLLs : PLLs_Type;
Nico Huber83693c82016-10-08 22:17:55 +020067 HPD_Delay : HPD_Delay_Type;
68 Wait_For_HPD : HPD_Type;
69 Initialized : Boolean := False;
70
Nico Huber83693c82016-10-08 22:17:55 +020071 ----------------------------------------------------------------------------
72
Nico Huberf54d0962016-10-20 14:17:18 +020073 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
74
75 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
76 is
77 begin
78 return Word32 (Freq / 1_000_000);
79 end PCH_RAWCLK_FREQ;
80
81 ----------------------------------------------------------------------------
82
Nico Huber43370ba2017-01-09 15:26:19 +010083 procedure Enable_Output
84 (Pipe : in Pipe_Index;
85 Pipe_Cfg : in Pipe_Config;
86 Success : out Boolean)
87 is
88 Port_Cfg : Port_Config;
89 begin
Nico Huber3be61d42017-01-09 13:58:18 +010090 pragma Debug (Debug.New_Line);
91 pragma Debug (Debug.Put_Line
92 ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
93
Nico Huber43370ba2017-01-09 15:26:19 +010094 Config_Helpers.Fill_Port_Config
95 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
96
97 if Success then
98 Success := Config_Helpers.Validate_Config
99 (Pipe_Cfg.Framebuffer, Port_Cfg, Pipe);
100 end if;
101
Nico Huber43370ba2017-01-09 15:26:19 +0100102 if Success then
Nico Huber43370ba2017-01-09 15:26:19 +0100103 Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
104 end if;
105
106 -- loop over all possible DP-lane configurations
107 -- (non-DP ports use a single fake configuration)
108 while Success loop
109 pragma Loop_Invariant
110 (Pipe_Cfg.Port in Active_Port_Type and
111 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
112
113 PLLs.Alloc
114 (Port_Cfg => Port_Cfg,
115 PLL => Allocated_PLLs (Pipe),
116 Success => Success);
117
118 if Success then
119 -- try each DP-lane configuration twice
120 for Try in 1 .. 2 loop
121 pragma Loop_Invariant
122 (Pipe_Cfg.Port in Active_Port_Type);
123
Nico Huber4798c662017-01-11 12:44:48 +0100124 -- Clear pending hot-plug events before every try
125 Port_Detect.Clear_Hotplug_Detect (Pipe_Cfg.Port);
126
Nico Huber43370ba2017-01-09 15:26:19 +0100127 Connectors.Pre_On
128 (Pipe => Pipe,
129 Port_Cfg => Port_Cfg,
130 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
131 Success => Success);
132
133 if Success then
134 Display_Controller.On
135 (Pipe => Pipe,
136 Port_Cfg => Port_Cfg,
137 Framebuffer => Pipe_Cfg.Framebuffer);
138
139 Connectors.Post_On
140 (Port_Cfg => Port_Cfg,
141 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
142 Success => Success);
143
144 if not Success then
145 Display_Controller.Off (Pipe);
146 Connectors.Post_Off (Port_Cfg);
147 end if;
148 end if;
149
150 exit when Success;
151 end loop;
152 exit when Success; -- connection established => stop loop
153
154 -- connection failed
155 PLLs.Free (Allocated_PLLs (Pipe));
156 end if;
157
158 Connector_Info.Next_Link_Setting (Port_Cfg, Success);
159 end loop;
160
161 if Success then
162 pragma Debug (Debug.Put_Line
163 ("Enabled port " & Port_Names (Pipe_Cfg.Port)));
164 else
165 Wait_For_HPD (Pipe_Cfg.Port) := True;
166 if Pipe_Cfg.Port = Internal then
167 Panel.Off;
168 end if;
169 end if;
170 end Enable_Output;
171
Nico Huber3be61d42017-01-09 13:58:18 +0100172 procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
173 is
174 Port_Cfg : Port_Config;
175 Success : Boolean;
176 begin
177 Config_Helpers.Fill_Port_Config
178 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
179 if Success then
180 pragma Debug (Debug.New_Line);
181 pragma Debug (Debug.Put_Line
182 ("Disabling port " & Port_Names (Pipe_Cfg.Port)));
183 pragma Debug (Debug.New_Line);
184
185 Connectors.Pre_Off (Port_Cfg);
186 Display_Controller.Off (Pipe);
187 Connectors.Post_Off (Port_Cfg);
188
189 PLLs.Free (Allocated_PLLs (Pipe));
190 end if;
191 end Disable_Output;
192
Nico Huber99f10f32016-11-20 00:34:05 +0100193 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200194 is
Nico Huber3be61d42017-01-09 13:58:18 +0100195 procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
196 is
197 HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
198 begin
199 if HPD_Delay_Over then
200 Port_Detect.Hotplug_Detect (Port, Detected);
201 HPD_Delay (Port) := Time.MS_From_Now (333);
202 else
203 Detected := False;
204 end if;
205 end Check_HPD;
Nico Huberb56b9c52017-01-11 15:12:23 +0100206
Nico Huber564103f2017-01-11 15:33:07 +0100207 Power_Changed : Boolean := False;
Nico Huberb56b9c52017-01-11 15:12:23 +0100208 Old_Configs : Pipe_Configs;
Nico Huber564103f2017-01-11 15:33:07 +0100209
210 -- Only called when we actually tried to change something
211 -- so we don't congest the log with unnecessary messages.
212 procedure Update_Power
213 is
214 begin
215 if not Power_Changed then
216 Power_And_Clocks.Power_Up (Old_Configs, Configs);
217 Power_Changed := True;
218 end if;
219 end Update_Power;
Nico Huber83693c82016-10-08 22:17:55 +0200220 begin
221 Old_Configs := Cur_Configs;
222
Nico Huberb56b9c52017-01-11 15:12:23 +0100223 -- disable all pipes that changed or had a hot-plug event
224 for Pipe in Pipe_Index loop
225 declare
226 Unplug_Detected : Boolean;
227 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
228 New_Config : Pipe_Config renames Configs (Pipe);
229 begin
230 if Cur_Config.Port /= Disabled then
231 Check_HPD (Cur_Config.Port, Unplug_Detected);
Nico Huber83693c82016-10-08 22:17:55 +0200232
Nico Huberb56b9c52017-01-11 15:12:23 +0100233 if Cur_Config.Port /= New_Config.Port or
234 Cur_Config.Mode /= New_Config.Mode or
235 Unplug_Detected
236 then
237 Disable_Output (Pipe, Cur_Config);
238 Cur_Config.Port := Disabled;
Nico Huber564103f2017-01-11 15:33:07 +0100239 Update_Power;
Nico Huberb56b9c52017-01-11 15:12:23 +0100240 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200241 end if;
Nico Huberb56b9c52017-01-11 15:12:23 +0100242 end;
243 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200244
Nico Huberb56b9c52017-01-11 15:12:23 +0100245 -- enable all pipes that changed and should be active
246 for Pipe in Pipe_Index loop
247 declare
248 Success : Boolean;
249 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
250 New_Config : Pipe_Config renames Configs (Pipe);
251 begin
252 if New_Config.Port /= Disabled and then
253 (Cur_Config.Port /= New_Config.Port or
254 Cur_Config.Mode /= New_Config.Mode)
255 then
Nico Huber3be61d42017-01-09 13:58:18 +0100256 if Wait_For_HPD (New_Config.Port) then
257 Check_HPD (New_Config.Port, Success);
258 Wait_For_HPD (New_Config.Port) := not Success;
259 else
260 Success := True;
Nico Huber8c45bcf2016-11-20 17:30:57 +0100261 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100262
Nico Huber3be61d42017-01-09 13:58:18 +0100263 if Success then
Nico Huber564103f2017-01-11 15:33:07 +0100264 Update_Power;
Nico Huberb56b9c52017-01-11 15:12:23 +0100265 Enable_Output (Pipe, New_Config, Success);
Nico Huber3be61d42017-01-09 13:58:18 +0100266 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200267
268 if Success then
Nico Huberb56b9c52017-01-11 15:12:23 +0100269 Cur_Config := New_Config;
Nico Huber83693c82016-10-08 22:17:55 +0200270 end if;
Nico Huber3be61d42017-01-09 13:58:18 +0100271
Nico Huberb56b9c52017-01-11 15:12:23 +0100272 -- update framebuffer offset only
273 elsif New_Config.Port /= Disabled and
274 Cur_Config.Framebuffer /= New_Config.Framebuffer
275 then
276 Display_Controller.Update_Offset (Pipe, New_Config.Framebuffer);
277 Cur_Config := New_Config;
278 end if;
279 end;
Nico Huber83693c82016-10-08 22:17:55 +0200280 end loop;
281
Nico Huber564103f2017-01-11 15:33:07 +0100282 if Power_Changed then
Nico Huber83693c82016-10-08 22:17:55 +0200283 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
284 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200285 end Update_Outputs;
286
287 ----------------------------------------------------------------------------
288
289 procedure Initialize
290 (MMIO_Base : in Word64 := 0;
291 Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100292 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200293 Success : out Boolean)
294 with
295 Refined_Global =>
296 (In_Out =>
297 (Config.Valid_Port_GPU,
298 Registers.Register_State, Port_IO.State),
299 Input =>
300 (Time.State),
301 Output =>
302 (Registers.Address_State,
303 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +0100304 Cur_Configs, Allocated_PLLs,
Nico Huber83693c82016-10-08 22:17:55 +0200305 HPD_Delay, Wait_For_HPD, Initialized))
306 is
307 use type HW.Word64;
308
309 Now : constant Time.T := Time.Now;
310
311 procedure Check_Platform (Success : out Boolean)
312 is
313 Audio_VID_DID : Word32;
314 begin
315 case Config.CPU is
316 when Haswell .. Skylake =>
317 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
318 when Ironlake .. Ivybridge =>
319 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
320 end case;
321 Success :=
322 (case Config.CPU is
323 when Skylake => Audio_VID_DID = 16#8086_2809#,
324 when Broadwell => Audio_VID_DID = 16#8086_2808#,
325 when Haswell => Audio_VID_DID = 16#8086_2807#,
326 when Ivybridge |
327 Sandybridge => Audio_VID_DID = 16#8086_2806# or
328 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200329 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200330 end Check_Platform;
331 begin
332 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
333 Reason => "Write_Delay is used for debugging only");
334
335 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
336
337 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
338
339 Wait_For_HPD := HPD_Type'(others => False);
340 HPD_Delay := HPD_Delay_Type'(others => Now);
Nico Huber83693c82016-10-08 22:17:55 +0200341 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100342 Cur_Configs := Pipe_Configs'
343 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200344 (Port => Disabled,
345 Framebuffer => HW.GFX.Default_FB,
346 Mode => HW.GFX.Invalid_Mode));
347 Registers.Set_Register_Base
348 (if MMIO_Base /= 0 then
349 MMIO_Base
350 else
351 Config.Default_MMIO_Base);
352 PLLs.Initialize;
353
354 Check_Platform (Success);
355 if not Success then
356 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
357
358 Panel.Static_Init; -- for flow analysis
359
360 Initialized := False;
361 return;
362 end if;
363
364 Panel.Setup_PP_Sequencer;
365 Port_Detect.Initialize;
366
Nico Huber793a8d42016-11-21 18:57:03 +0100367 if Clean_State then
368 Power_And_Clocks.Pre_All_Off;
369 Connectors.Pre_All_Off;
370 Display_Controller.All_Off;
371 Connectors.Post_All_Off;
372 PLLs.All_Off;
373 Power_And_Clocks.Post_All_Off;
Nico Huber33912aa2016-12-06 20:36:23 +0100374 else
375 -- According to PRMs, VGA plane is the only thing
376 -- that's enabled by default after reset.
377 Display_Controller.Legacy_VGA_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100378 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200379
380 -------------------- Now restart from a clean state ---------------------
381 Power_And_Clocks.Initialize;
382
Nico Huberf54d0962016-10-20 14:17:18 +0200383 Registers.Unset_And_Set_Mask
384 (Register => Registers.PCH_RAWCLK_FREQ,
385 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
386 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
387
Nico Huber83693c82016-10-08 22:17:55 +0200388 Initialized := True;
389
390 end Initialize;
391
392 function Is_Initialized return Boolean
393 with
394 Refined_Post => Is_Initialized'Result = Initialized
395 is
396 begin
397 return Initialized;
398 end Is_Initialized;
399
400 ----------------------------------------------------------------------------
401
402 procedure Write_GTT
403 (GTT_Page : GTT_Range;
404 Device_Address : GTT_Address_Type;
405 Valid : Boolean) is
406 begin
407 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
408 end Write_GTT;
409
410 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
411 is
412 FB_Size : constant Pos32 :=
413 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
414 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
415 begin
416 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
417 loop
418 Registers.Write_GTT
419 (GTT_Page => Idx,
420 Device_Address => Phys_Addr,
421 Valid => True);
422 Phys_Addr := Phys_Addr + 4096;
423 end loop;
424 end Setup_Default_GTT;
425
426 ----------------------------------------------------------------------------
427
Nico Huber99f10f32016-11-20 00:34:05 +0100428 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200429 is
430 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100431 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200432 Pipe_Names : constant Pipe_Name_Array :=
433 (Primary => "Primary ",
434 Secondary => "Secondary",
435 Tertiary => "Tertiary ");
436 begin
437 Debug.New_Line;
Paul Menzelb83107c2017-05-04 09:02:33 +0200438 Debug.Put_Line ("CONFIG =>");
Nico Huber99f10f32016-11-20 00:34:05 +0100439 for Pipe in Pipe_Index loop
440 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200441 Debug.Put (" (");
442 else
443 Debug.Put (" ");
444 end if;
445 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
446 Debug.Put_Line
447 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
448 Debug.Put_Line (" Framebuffer =>");
449 Debug.Put (" (Width => ");
450 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
451 Debug.Put_Line (",");
452 Debug.Put (" Height => ");
453 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
454 Debug.Put_Line (",");
455 Debug.Put (" Stride => ");
456 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
457 Debug.Put_Line (",");
458 Debug.Put (" Offset => ");
459 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
460 Debug.Put_Line (",");
461 Debug.Put (" BPC => ");
462 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
463 Debug.Put_Line ("),");
464 Debug.Put_Line (" Mode =>");
465 Debug.Put (" (Dotclock => ");
466 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
467 Debug.Put_Line (",");
468 Debug.Put (" H_Visible => ");
469 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
470 Debug.Put_Line (",");
471 Debug.Put (" H_Sync_Begin => ");
472 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
473 Debug.Put_Line (",");
474 Debug.Put (" H_Sync_End => ");
475 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
476 Debug.Put_Line (",");
477 Debug.Put (" H_Total => ");
478 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
479 Debug.Put_Line (",");
480 Debug.Put (" V_Visible => ");
481 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
482 Debug.Put_Line (",");
483 Debug.Put (" V_Sync_Begin => ");
484 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
485 Debug.Put_Line (",");
486 Debug.Put (" V_Sync_End => ");
487 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
488 Debug.Put_Line (",");
489 Debug.Put (" V_Total => ");
490 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
491 Debug.Put_Line (",");
492 Debug.Put_Line (" H_Sync_Active_High => " &
493 (if Configs (Pipe).Mode.H_Sync_Active_High
494 then "True,"
495 else "False,"));
496 Debug.Put_Line (" V_Sync_Active_High => " &
497 (if Configs (Pipe).Mode.V_Sync_Active_High
498 then "True,"
499 else "False,"));
500 Debug.Put (" BPC => ");
501 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100502 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200503 Debug.Put_Line (")),");
504 else
505 Debug.Put_Line (")));");
506 end if;
507 end loop;
508 end Dump_Configs;
509
510end HW.GFX.GMA;