blob: 379421c618e58299de11e3e1d4677ec43fe3620b [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 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
31use type HW.Word8;
32use type HW.Int32;
33
34package body HW.GFX.GMA
35 with Refined_State =>
36 (State =>
37 (Registers.Address_State,
38 PLLs.State, Panel.Panel_State,
39 Cur_Configs, Allocated_PLLs, DP_Links,
40 HPD_Delay, Wait_For_HPD),
41 Init_State => Initialized,
42 Config_State => Config.Valid_Port_GPU,
43 Device_State =>
44 (Registers.Register_State, Registers.GTT_State))
45is
46
47 subtype Port_Name is String (1 .. 8);
48 type Port_Name_Array is array (Port_Type) of Port_Name;
49 Port_Names : constant Port_Name_Array :=
50 (Disabled => "Disabled",
51 Internal => "Internal",
52 DP1 => "DP1 ",
53 DP2 => "DP2 ",
54 DP3 => "DP3 ",
Nico Huber0d454cd2016-11-21 13:33:43 +010055 HDMI1 => "HDMI1 ",
56 HDMI2 => "HDMI2 ",
57 HDMI3 => "HDMI3 ",
Nico Huber83693c82016-10-08 22:17:55 +020058 Analog => "Analog ");
59
60 package Display_Controller renames Pipe_Setup;
61
Nico Huber99f10f32016-11-20 00:34:05 +010062 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020063
Nico Huber99f10f32016-11-20 00:34:05 +010064 type Links_Type is array (Pipe_Index) of DP_Link;
Nico Huber83693c82016-10-08 22:17:55 +020065
66 type HPD_Type is array (Port_Type) of Boolean;
67 type HPD_Delay_Type is array (Port_Type) of Time.T;
68
Nico Huber83693c82016-10-08 22:17:55 +020069 Allocated_PLLs : PLLs_Type;
70 DP_Links : Links_Type;
71 HPD_Delay : HPD_Delay_Type;
72 Wait_For_HPD : HPD_Type;
73 Initialized : Boolean := False;
74
Nico Huber83693c82016-10-08 22:17:55 +020075 ----------------------------------------------------------------------------
76
Nico Huberf54d0962016-10-20 14:17:18 +020077 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
78
79 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
80 is
81 begin
82 return Word32 (Freq / 1_000_000);
83 end PCH_RAWCLK_FREQ;
84
85 ----------------------------------------------------------------------------
86
Nico Huber83693c82016-10-08 22:17:55 +020087 procedure Legacy_VGA_Off
88 is
89 Reg8 : Word8;
90 begin
91 -- disable legacy VGA plane, taking over control now
92 Port_IO.OutB (VGA_SR_INDEX, VGA_SR01);
93 Port_IO.InB (Reg8, VGA_SR_DATA);
94 Port_IO.OutB (VGA_SR_DATA, Reg8 or 1 * 2 ** 5);
95 Time.U_Delay (100); -- PRM says 100us, Linux does 300
96 Registers.Set_Mask (Registers.VGACNTRL, 1 * 2 ** 31);
97 end Legacy_VGA_Off;
98
99 ----------------------------------------------------------------------------
100
Nico Huber99f10f32016-11-20 00:34:05 +0100101 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200102 is
103 Did_Power_Up : Boolean := False;
104
105 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +0100106 Old_Config, New_Config : Pipe_Config;
107 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +0200108 Port_Cfg : Port_Config;
109
110 procedure Check_HPD
111 (Port_Cfg : in Port_Config;
112 Port : in Port_Type;
113 Detected : out Boolean)
114 is
115 begin
116 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
117 if HPD_Delay_Over then
118 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
119 HPD_Delay (Port) := Time.MS_From_Now (333);
120 else
121 Detected := False;
122 end if;
123 end Check_HPD;
124 begin
125 Old_Configs := Cur_Configs;
126
Nico Huber99f10f32016-11-20 00:34:05 +0100127 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200128 HPD := False;
129
130 Old_Config := Cur_Configs (I);
131 New_Config := Configs (I);
132
Nico Huber8c45bcf2016-11-20 17:30:57 +0100133 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100134 (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
Nico Huber8c45bcf2016-11-20 17:30:57 +0100135 Port_Cfg.DP := DP_Links (I);
Nico Huber83693c82016-10-08 22:17:55 +0200136 if Success then
137 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
138 end if;
139
140 -- Connector changed?
141 if (Success and then HPD) or
142 Old_Config.Port /= New_Config.Port or
143 Old_Config.Mode /= New_Config.Mode
144 then
145 if Old_Config.Port /= Disabled then
146 if Success then
147 pragma Debug (Debug.New_Line);
148 pragma Debug (Debug.Put_Line
149 ("Disabling port " & Port_Names (Old_Config.Port)));
150
151 Connectors.Pre_Off (Port_Cfg);
152
Nico Huberf3e23662016-12-05 21:33:03 +0100153 Display_Controller.Off (I, Port_Cfg);
Nico Huber83693c82016-10-08 22:17:55 +0200154
155 Connectors.Post_Off (Port_Cfg);
156 end if;
157
158 -- Free PLL
159 PLLs.Free (Allocated_PLLs (I));
160
161 Cur_Configs (I).Port := Disabled;
162 end if;
163
164 if New_Config.Port /= Disabled then
Nico Huber8c45bcf2016-11-20 17:30:57 +0100165 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100166 (Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200167
Nico Huber8c45bcf2016-11-20 17:30:57 +0100168 if Success then
169 Success := Config_Helpers.Validate_Config
170 (New_Config.Framebuffer, Port_Cfg, I);
171 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100172
Nico Huber83693c82016-10-08 22:17:55 +0200173 if Success and then Wait_For_HPD (New_Config.Port) then
174 Check_HPD (Port_Cfg, New_Config.Port, Success);
175 Wait_For_HPD (New_Config.Port) := not Success;
176 end if;
177
178 if Success then
179 pragma Debug (Debug.New_Line);
180 pragma Debug (Debug.Put_Line
181 ("Trying to enable port " & Port_Names (New_Config.Port)));
182
183 if not Did_Power_Up then
184 Power_And_Clocks.Power_Up (Old_Configs, Configs);
185 Did_Power_Up := True;
186 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200187 end if;
188
189 if Success then
190 Connector_Info.Preferred_Link_Setting
191 (Port_Cfg => Port_Cfg,
192 Success => Success);
193 end if;
194
195 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100196 pragma Loop_Invariant
197 (New_Config.Port in Active_Port_Type and
198 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200199
200 PLLs.Alloc
201 (Port_Cfg => Port_Cfg,
202 PLL => Allocated_PLLs (I),
203 Success => Success);
204
205 if Success then
206 for Try in 1 .. 2 loop
207 pragma Loop_Invariant
208 (New_Config.Port in Active_Port_Type);
209
210 Connectors.Pre_On
211 (Port_Cfg => Port_Cfg,
212 PLL_Hint => PLLs.Register_Value
213 (Allocated_PLLs (I)),
Nico Huberf3e23662016-12-05 21:33:03 +0100214 Pipe_Hint => Display_Controller.Get_Pipe_Hint (I),
Nico Huber83693c82016-10-08 22:17:55 +0200215 Success => Success);
216
217 if Success then
218 Display_Controller.On
Nico Huberf3e23662016-12-05 21:33:03 +0100219 (Pipe => I,
Nico Huber83693c82016-10-08 22:17:55 +0200220 Port_Cfg => Port_Cfg,
221 Framebuffer => New_Config.Framebuffer);
222
223 Connectors.Post_On
224 (Port_Cfg => Port_Cfg,
225 PLL_Hint => PLLs.Register_Value
226 (Allocated_PLLs (I)),
227 Success => Success);
228
229 if not Success then
Nico Huberf3e23662016-12-05 21:33:03 +0100230 Display_Controller.Off (I, Port_Cfg);
Nico Huber83693c82016-10-08 22:17:55 +0200231 Connectors.Post_Off (Port_Cfg);
232 end if;
233 end if;
234
235 exit when Success;
236 end loop;
237 exit when Success; -- connection established => stop loop
238
239 -- connection failed
240 PLLs.Free (Allocated_PLLs (I));
241 end if;
242
243 Connector_Info.Next_Link_Setting
244 (Port_Cfg => Port_Cfg,
245 Success => Success);
246 end loop;
247
248 if Success then
249 pragma Debug (Debug.Put_Line
250 ("Enabled port " & Port_Names (New_Config.Port)));
251 Cur_Configs (I) := New_Config;
252 DP_Links (I) := Port_Cfg.DP;
253 else
254 Wait_For_HPD (New_Config.Port) := True;
255 if New_Config.Port = Internal then
256 Panel.Off;
257 end if;
258 end if;
259 else
260 Cur_Configs (I) := New_Config;
261 end if;
262 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
263 Old_Config.Port /= Disabled
264 then
Nico Huberf3e23662016-12-05 21:33:03 +0100265 Display_Controller.Update_Offset (I, New_Config.Framebuffer);
Nico Huber83693c82016-10-08 22:17:55 +0200266 Cur_Configs (I) := New_Config;
267 end if;
268 end loop;
269
270 if Did_Power_Up then
271 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
272 end if;
273
274 end Update_Outputs;
275
276 ----------------------------------------------------------------------------
277
278 procedure Initialize
279 (MMIO_Base : in Word64 := 0;
280 Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100281 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200282 Success : out Boolean)
283 with
284 Refined_Global =>
285 (In_Out =>
286 (Config.Valid_Port_GPU,
287 Registers.Register_State, Port_IO.State),
288 Input =>
289 (Time.State),
290 Output =>
291 (Registers.Address_State,
292 PLLs.State, Panel.Panel_State,
293 Cur_Configs, Allocated_PLLs, DP_Links,
294 HPD_Delay, Wait_For_HPD, Initialized))
295 is
296 use type HW.Word64;
297
298 Now : constant Time.T := Time.Now;
299
300 procedure Check_Platform (Success : out Boolean)
301 is
302 Audio_VID_DID : Word32;
303 begin
304 case Config.CPU is
305 when Haswell .. Skylake =>
306 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
307 when Ironlake .. Ivybridge =>
308 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
309 end case;
310 Success :=
311 (case Config.CPU is
312 when Skylake => Audio_VID_DID = 16#8086_2809#,
313 when Broadwell => Audio_VID_DID = 16#8086_2808#,
314 when Haswell => Audio_VID_DID = 16#8086_2807#,
315 when Ivybridge |
316 Sandybridge => Audio_VID_DID = 16#8086_2806# or
317 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200318 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200319 end Check_Platform;
320 begin
321 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
322 Reason => "Write_Delay is used for debugging only");
323
324 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
325
326 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
327
328 Wait_For_HPD := HPD_Type'(others => False);
329 HPD_Delay := HPD_Delay_Type'(others => Now);
330 DP_Links := Links_Type'(others => HW.GFX.Default_DP);
331 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100332 Cur_Configs := Pipe_Configs'
333 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200334 (Port => Disabled,
335 Framebuffer => HW.GFX.Default_FB,
336 Mode => HW.GFX.Invalid_Mode));
337 Registers.Set_Register_Base
338 (if MMIO_Base /= 0 then
339 MMIO_Base
340 else
341 Config.Default_MMIO_Base);
342 PLLs.Initialize;
343
344 Check_Platform (Success);
345 if not Success then
346 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
347
348 Panel.Static_Init; -- for flow analysis
349
350 Initialized := False;
351 return;
352 end if;
353
354 Panel.Setup_PP_Sequencer;
355 Port_Detect.Initialize;
356
Nico Huber793a8d42016-11-21 18:57:03 +0100357 Legacy_VGA_Off; -- According to PRMs, VGA plane is the only
358 -- thing that's enabled by default after reset.
Nico Huber83693c82016-10-08 22:17:55 +0200359
Nico Huber793a8d42016-11-21 18:57:03 +0100360 if Clean_State then
361 Power_And_Clocks.Pre_All_Off;
362 Connectors.Pre_All_Off;
363 Display_Controller.All_Off;
364 Connectors.Post_All_Off;
365 PLLs.All_Off;
366 Power_And_Clocks.Post_All_Off;
367 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200368
369 -------------------- Now restart from a clean state ---------------------
370 Power_And_Clocks.Initialize;
371
Nico Huberf54d0962016-10-20 14:17:18 +0200372 Registers.Unset_And_Set_Mask
373 (Register => Registers.PCH_RAWCLK_FREQ,
374 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
375 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
376
Nico Huber83693c82016-10-08 22:17:55 +0200377 Initialized := True;
378
379 end Initialize;
380
381 function Is_Initialized return Boolean
382 with
383 Refined_Post => Is_Initialized'Result = Initialized
384 is
385 begin
386 return Initialized;
387 end Is_Initialized;
388
389 ----------------------------------------------------------------------------
390
391 procedure Write_GTT
392 (GTT_Page : GTT_Range;
393 Device_Address : GTT_Address_Type;
394 Valid : Boolean) is
395 begin
396 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
397 end Write_GTT;
398
399 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
400 is
401 FB_Size : constant Pos32 :=
402 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
403 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
404 begin
405 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
406 loop
407 Registers.Write_GTT
408 (GTT_Page => Idx,
409 Device_Address => Phys_Addr,
410 Valid => True);
411 Phys_Addr := Phys_Addr + 4096;
412 end loop;
413 end Setup_Default_GTT;
414
415 ----------------------------------------------------------------------------
416
Nico Huber99f10f32016-11-20 00:34:05 +0100417 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200418 is
419 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100420 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200421 Pipe_Names : constant Pipe_Name_Array :=
422 (Primary => "Primary ",
423 Secondary => "Secondary",
424 Tertiary => "Tertiary ");
425 begin
426 Debug.New_Line;
427 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100428 for Pipe in Pipe_Index loop
429 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200430 Debug.Put (" (");
431 else
432 Debug.Put (" ");
433 end if;
434 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
435 Debug.Put_Line
436 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
437 Debug.Put_Line (" Framebuffer =>");
438 Debug.Put (" (Width => ");
439 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
440 Debug.Put_Line (",");
441 Debug.Put (" Height => ");
442 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
443 Debug.Put_Line (",");
444 Debug.Put (" Stride => ");
445 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
446 Debug.Put_Line (",");
447 Debug.Put (" Offset => ");
448 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
449 Debug.Put_Line (",");
450 Debug.Put (" BPC => ");
451 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
452 Debug.Put_Line ("),");
453 Debug.Put_Line (" Mode =>");
454 Debug.Put (" (Dotclock => ");
455 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
456 Debug.Put_Line (",");
457 Debug.Put (" H_Visible => ");
458 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
459 Debug.Put_Line (",");
460 Debug.Put (" H_Sync_Begin => ");
461 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
462 Debug.Put_Line (",");
463 Debug.Put (" H_Sync_End => ");
464 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
465 Debug.Put_Line (",");
466 Debug.Put (" H_Total => ");
467 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
468 Debug.Put_Line (",");
469 Debug.Put (" V_Visible => ");
470 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
471 Debug.Put_Line (",");
472 Debug.Put (" V_Sync_Begin => ");
473 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
474 Debug.Put_Line (",");
475 Debug.Put (" V_Sync_End => ");
476 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
477 Debug.Put_Line (",");
478 Debug.Put (" V_Total => ");
479 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
480 Debug.Put_Line (",");
481 Debug.Put_Line (" H_Sync_Active_High => " &
482 (if Configs (Pipe).Mode.H_Sync_Active_High
483 then "True,"
484 else "False,"));
485 Debug.Put_Line (" V_Sync_Active_High => " &
486 (if Configs (Pipe).Mode.V_Sync_Active_High
487 then "True,"
488 else "False,"));
489 Debug.Put (" BPC => ");
490 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100491 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200492 Debug.Put_Line (")),");
493 else
494 Debug.Put_Line (")));");
495 end if;
496 end loop;
497 end Dump_Configs;
498
499end HW.GFX.GMA;