blob: 8d304b6a21b715f643c285f8a1bf55c0c9623deb [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
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,
38 Cur_Configs, Allocated_PLLs, DP_Links,
39 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 Huber99f10f32016-11-20 00:34:05 +010063 type Links_Type is array (Pipe_Index) of DP_Link;
Nico Huber83693c82016-10-08 22:17:55 +020064
65 type HPD_Type is array (Port_Type) of Boolean;
66 type HPD_Delay_Type is array (Port_Type) of Time.T;
67
Nico Huber83693c82016-10-08 22:17:55 +020068 Allocated_PLLs : PLLs_Type;
69 DP_Links : Links_Type;
70 HPD_Delay : HPD_Delay_Type;
71 Wait_For_HPD : HPD_Type;
72 Initialized : Boolean := False;
73
Nico Huber83693c82016-10-08 22:17:55 +020074 ----------------------------------------------------------------------------
75
Nico Huberf54d0962016-10-20 14:17:18 +020076 PCH_RAWCLK_FREQ_MASK : constant := 16#3ff# * 2 ** 0;
77
78 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
79 is
80 begin
81 return Word32 (Freq / 1_000_000);
82 end PCH_RAWCLK_FREQ;
83
84 ----------------------------------------------------------------------------
85
Nico Huber99f10f32016-11-20 00:34:05 +010086 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +020087 is
88 Did_Power_Up : Boolean := False;
89
90 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +010091 Old_Config, New_Config : Pipe_Config;
92 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +020093 Port_Cfg : Port_Config;
94
95 procedure Check_HPD
96 (Port_Cfg : in Port_Config;
97 Port : in Port_Type;
98 Detected : out Boolean)
99 is
100 begin
101 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
102 if HPD_Delay_Over then
103 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
104 HPD_Delay (Port) := Time.MS_From_Now (333);
105 else
106 Detected := False;
107 end if;
108 end Check_HPD;
109 begin
110 Old_Configs := Cur_Configs;
111
Nico Huber99f10f32016-11-20 00:34:05 +0100112 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200113 HPD := False;
114
115 Old_Config := Cur_Configs (I);
116 New_Config := Configs (I);
117
Nico Huber8c45bcf2016-11-20 17:30:57 +0100118 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100119 (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
Nico Huber8c45bcf2016-11-20 17:30:57 +0100120 Port_Cfg.DP := DP_Links (I);
Nico Huber83693c82016-10-08 22:17:55 +0200121 if Success then
122 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
123 end if;
124
125 -- Connector changed?
126 if (Success and then HPD) or
127 Old_Config.Port /= New_Config.Port or
128 Old_Config.Mode /= New_Config.Mode
129 then
130 if Old_Config.Port /= Disabled then
131 if Success then
132 pragma Debug (Debug.New_Line);
133 pragma Debug (Debug.Put_Line
134 ("Disabling port " & Port_Names (Old_Config.Port)));
135
136 Connectors.Pre_Off (Port_Cfg);
137
Nico Huber7ad2d652016-12-07 15:19:32 +0100138 Display_Controller.Off (I);
Nico Huber83693c82016-10-08 22:17:55 +0200139
140 Connectors.Post_Off (Port_Cfg);
141 end if;
142
143 -- Free PLL
144 PLLs.Free (Allocated_PLLs (I));
145
146 Cur_Configs (I).Port := Disabled;
147 end if;
148
149 if New_Config.Port /= Disabled then
Nico Huber8c45bcf2016-11-20 17:30:57 +0100150 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100151 (Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200152
Nico Huber8c45bcf2016-11-20 17:30:57 +0100153 if Success then
154 Success := Config_Helpers.Validate_Config
155 (New_Config.Framebuffer, Port_Cfg, I);
156 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100157
Nico Huber83693c82016-10-08 22:17:55 +0200158 if Success and then Wait_For_HPD (New_Config.Port) then
159 Check_HPD (Port_Cfg, New_Config.Port, Success);
160 Wait_For_HPD (New_Config.Port) := not Success;
161 end if;
162
163 if Success then
164 pragma Debug (Debug.New_Line);
165 pragma Debug (Debug.Put_Line
166 ("Trying to enable port " & Port_Names (New_Config.Port)));
167
168 if not Did_Power_Up then
169 Power_And_Clocks.Power_Up (Old_Configs, Configs);
170 Did_Power_Up := True;
171 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200172 end if;
173
174 if Success then
175 Connector_Info.Preferred_Link_Setting
176 (Port_Cfg => Port_Cfg,
177 Success => Success);
178 end if;
179
180 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100181 pragma Loop_Invariant
182 (New_Config.Port in Active_Port_Type and
183 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200184
185 PLLs.Alloc
186 (Port_Cfg => Port_Cfg,
187 PLL => Allocated_PLLs (I),
188 Success => Success);
189
190 if Success then
191 for Try in 1 .. 2 loop
192 pragma Loop_Invariant
193 (New_Config.Port in Active_Port_Type);
194
195 Connectors.Pre_On
196 (Port_Cfg => Port_Cfg,
197 PLL_Hint => PLLs.Register_Value
198 (Allocated_PLLs (I)),
Nico Huberf3e23662016-12-05 21:33:03 +0100199 Pipe_Hint => Display_Controller.Get_Pipe_Hint (I),
Nico Huber83693c82016-10-08 22:17:55 +0200200 Success => Success);
201
202 if Success then
203 Display_Controller.On
Nico Huberf3e23662016-12-05 21:33:03 +0100204 (Pipe => I,
Nico Huber83693c82016-10-08 22:17:55 +0200205 Port_Cfg => Port_Cfg,
206 Framebuffer => New_Config.Framebuffer);
207
208 Connectors.Post_On
209 (Port_Cfg => Port_Cfg,
210 PLL_Hint => PLLs.Register_Value
211 (Allocated_PLLs (I)),
212 Success => Success);
213
214 if not Success then
Nico Huber7ad2d652016-12-07 15:19:32 +0100215 Display_Controller.Off (I);
Nico Huber83693c82016-10-08 22:17:55 +0200216 Connectors.Post_Off (Port_Cfg);
217 end if;
218 end if;
219
220 exit when Success;
221 end loop;
222 exit when Success; -- connection established => stop loop
223
224 -- connection failed
225 PLLs.Free (Allocated_PLLs (I));
226 end if;
227
228 Connector_Info.Next_Link_Setting
229 (Port_Cfg => Port_Cfg,
230 Success => Success);
231 end loop;
232
233 if Success then
234 pragma Debug (Debug.Put_Line
235 ("Enabled port " & Port_Names (New_Config.Port)));
236 Cur_Configs (I) := New_Config;
237 DP_Links (I) := Port_Cfg.DP;
238 else
239 Wait_For_HPD (New_Config.Port) := True;
240 if New_Config.Port = Internal then
241 Panel.Off;
242 end if;
243 end if;
244 else
245 Cur_Configs (I) := New_Config;
246 end if;
247 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
248 Old_Config.Port /= Disabled
249 then
Nico Huberf3e23662016-12-05 21:33:03 +0100250 Display_Controller.Update_Offset (I, New_Config.Framebuffer);
Nico Huber83693c82016-10-08 22:17:55 +0200251 Cur_Configs (I) := New_Config;
252 end if;
253 end loop;
254
255 if Did_Power_Up then
256 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
257 end if;
258
259 end Update_Outputs;
260
261 ----------------------------------------------------------------------------
262
263 procedure Initialize
264 (MMIO_Base : in Word64 := 0;
265 Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100266 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200267 Success : out Boolean)
268 with
269 Refined_Global =>
270 (In_Out =>
271 (Config.Valid_Port_GPU,
272 Registers.Register_State, Port_IO.State),
273 Input =>
274 (Time.State),
275 Output =>
276 (Registers.Address_State,
277 PLLs.State, Panel.Panel_State,
278 Cur_Configs, Allocated_PLLs, DP_Links,
279 HPD_Delay, Wait_For_HPD, Initialized))
280 is
281 use type HW.Word64;
282
283 Now : constant Time.T := Time.Now;
284
285 procedure Check_Platform (Success : out Boolean)
286 is
287 Audio_VID_DID : Word32;
288 begin
289 case Config.CPU is
290 when Haswell .. Skylake =>
291 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
292 when Ironlake .. Ivybridge =>
293 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
294 end case;
295 Success :=
296 (case Config.CPU is
297 when Skylake => Audio_VID_DID = 16#8086_2809#,
298 when Broadwell => Audio_VID_DID = 16#8086_2808#,
299 when Haswell => Audio_VID_DID = 16#8086_2807#,
300 when Ivybridge |
301 Sandybridge => Audio_VID_DID = 16#8086_2806# or
302 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200303 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200304 end Check_Platform;
305 begin
306 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
307 Reason => "Write_Delay is used for debugging only");
308
309 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
310
311 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
312
313 Wait_For_HPD := HPD_Type'(others => False);
314 HPD_Delay := HPD_Delay_Type'(others => Now);
315 DP_Links := Links_Type'(others => HW.GFX.Default_DP);
316 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100317 Cur_Configs := Pipe_Configs'
318 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200319 (Port => Disabled,
320 Framebuffer => HW.GFX.Default_FB,
321 Mode => HW.GFX.Invalid_Mode));
322 Registers.Set_Register_Base
323 (if MMIO_Base /= 0 then
324 MMIO_Base
325 else
326 Config.Default_MMIO_Base);
327 PLLs.Initialize;
328
329 Check_Platform (Success);
330 if not Success then
331 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
332
333 Panel.Static_Init; -- for flow analysis
334
335 Initialized := False;
336 return;
337 end if;
338
339 Panel.Setup_PP_Sequencer;
340 Port_Detect.Initialize;
341
Nico Huber793a8d42016-11-21 18:57:03 +0100342 if Clean_State then
343 Power_And_Clocks.Pre_All_Off;
344 Connectors.Pre_All_Off;
345 Display_Controller.All_Off;
346 Connectors.Post_All_Off;
347 PLLs.All_Off;
348 Power_And_Clocks.Post_All_Off;
Nico Huber33912aa2016-12-06 20:36:23 +0100349 else
350 -- According to PRMs, VGA plane is the only thing
351 -- that's enabled by default after reset.
352 Display_Controller.Legacy_VGA_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100353 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200354
355 -------------------- Now restart from a clean state ---------------------
356 Power_And_Clocks.Initialize;
357
Nico Huberf54d0962016-10-20 14:17:18 +0200358 Registers.Unset_And_Set_Mask
359 (Register => Registers.PCH_RAWCLK_FREQ,
360 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
361 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
362
Nico Huber83693c82016-10-08 22:17:55 +0200363 Initialized := True;
364
365 end Initialize;
366
367 function Is_Initialized return Boolean
368 with
369 Refined_Post => Is_Initialized'Result = Initialized
370 is
371 begin
372 return Initialized;
373 end Is_Initialized;
374
375 ----------------------------------------------------------------------------
376
377 procedure Write_GTT
378 (GTT_Page : GTT_Range;
379 Device_Address : GTT_Address_Type;
380 Valid : Boolean) is
381 begin
382 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
383 end Write_GTT;
384
385 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
386 is
387 FB_Size : constant Pos32 :=
388 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
389 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
390 begin
391 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
392 loop
393 Registers.Write_GTT
394 (GTT_Page => Idx,
395 Device_Address => Phys_Addr,
396 Valid => True);
397 Phys_Addr := Phys_Addr + 4096;
398 end loop;
399 end Setup_Default_GTT;
400
401 ----------------------------------------------------------------------------
402
Nico Huber99f10f32016-11-20 00:34:05 +0100403 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200404 is
405 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100406 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200407 Pipe_Names : constant Pipe_Name_Array :=
408 (Primary => "Primary ",
409 Secondary => "Secondary",
410 Tertiary => "Tertiary ");
411 begin
412 Debug.New_Line;
413 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100414 for Pipe in Pipe_Index loop
415 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200416 Debug.Put (" (");
417 else
418 Debug.Put (" ");
419 end if;
420 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
421 Debug.Put_Line
422 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
423 Debug.Put_Line (" Framebuffer =>");
424 Debug.Put (" (Width => ");
425 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
426 Debug.Put_Line (",");
427 Debug.Put (" Height => ");
428 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
429 Debug.Put_Line (",");
430 Debug.Put (" Stride => ");
431 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
432 Debug.Put_Line (",");
433 Debug.Put (" Offset => ");
434 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
435 Debug.Put_Line (",");
436 Debug.Put (" BPC => ");
437 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
438 Debug.Put_Line ("),");
439 Debug.Put_Line (" Mode =>");
440 Debug.Put (" (Dotclock => ");
441 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
442 Debug.Put_Line (",");
443 Debug.Put (" H_Visible => ");
444 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
445 Debug.Put_Line (",");
446 Debug.Put (" H_Sync_Begin => ");
447 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
448 Debug.Put_Line (",");
449 Debug.Put (" H_Sync_End => ");
450 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
451 Debug.Put_Line (",");
452 Debug.Put (" H_Total => ");
453 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
454 Debug.Put_Line (",");
455 Debug.Put (" V_Visible => ");
456 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
457 Debug.Put_Line (",");
458 Debug.Put (" V_Sync_Begin => ");
459 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
460 Debug.Put_Line (",");
461 Debug.Put (" V_Sync_End => ");
462 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
463 Debug.Put_Line (",");
464 Debug.Put (" V_Total => ");
465 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
466 Debug.Put_Line (",");
467 Debug.Put_Line (" H_Sync_Active_High => " &
468 (if Configs (Pipe).Mode.H_Sync_Active_High
469 then "True,"
470 else "False,"));
471 Debug.Put_Line (" V_Sync_Active_High => " &
472 (if Configs (Pipe).Mode.V_Sync_Active_High
473 then "True,"
474 else "False,"));
475 Debug.Put (" BPC => ");
476 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100477 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200478 Debug.Put_Line (")),");
479 else
480 Debug.Put_Line (")));");
481 end if;
482 end loop;
483 end Dump_Configs;
484
485end HW.GFX.GMA;