blob: 343a0cd42f1cf3a1ac2220466214be78c784cdb2 [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,
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;
64 type HPD_Delay_Type is array (Port_Type) of Time.T;
65
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 Huber99f10f32016-11-20 00:34:05 +010083 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +020084 is
85 Did_Power_Up : Boolean := False;
86
87 HPD, HPD_Delay_Over, Success : Boolean;
Nico Huber99f10f32016-11-20 00:34:05 +010088 Old_Config, New_Config : Pipe_Config;
89 Old_Configs : Pipe_Configs;
Nico Huber83693c82016-10-08 22:17:55 +020090 Port_Cfg : Port_Config;
91
92 procedure Check_HPD
93 (Port_Cfg : in Port_Config;
94 Port : in Port_Type;
95 Detected : out Boolean)
96 is
97 begin
98 HPD_Delay_Over := Time.Timed_Out (HPD_Delay (Port));
99 if HPD_Delay_Over then
100 Port_Detect.Hotplug_Detect (Port_Cfg, Detected);
101 HPD_Delay (Port) := Time.MS_From_Now (333);
102 else
103 Detected := False;
104 end if;
105 end Check_HPD;
106 begin
107 Old_Configs := Cur_Configs;
108
Nico Huber99f10f32016-11-20 00:34:05 +0100109 for I in Pipe_Index loop
Nico Huber83693c82016-10-08 22:17:55 +0200110 HPD := False;
111
112 Old_Config := Cur_Configs (I);
113 New_Config := Configs (I);
114
Nico Huber8c45bcf2016-11-20 17:30:57 +0100115 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100116 (Port_Cfg, I, Old_Configs (I).Port, Old_Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200117 if Success then
118 Check_HPD (Port_Cfg, Old_Config.Port, HPD);
119 end if;
120
121 -- Connector changed?
122 if (Success and then HPD) or
123 Old_Config.Port /= New_Config.Port or
124 Old_Config.Mode /= New_Config.Mode
125 then
126 if Old_Config.Port /= Disabled then
127 if Success then
128 pragma Debug (Debug.New_Line);
129 pragma Debug (Debug.Put_Line
130 ("Disabling port " & Port_Names (Old_Config.Port)));
131
132 Connectors.Pre_Off (Port_Cfg);
133
Nico Huber7ad2d652016-12-07 15:19:32 +0100134 Display_Controller.Off (I);
Nico Huber83693c82016-10-08 22:17:55 +0200135
136 Connectors.Post_Off (Port_Cfg);
137 end if;
138
139 -- Free PLL
140 PLLs.Free (Allocated_PLLs (I));
141
142 Cur_Configs (I).Port := Disabled;
143 end if;
144
145 if New_Config.Port /= Disabled then
Nico Huber8c45bcf2016-11-20 17:30:57 +0100146 Config_Helpers.Fill_Port_Config
Nico Huber3c544ee2016-11-20 04:56:58 +0100147 (Port_Cfg, I, Configs (I).Port, Configs (I).Mode, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200148
Nico Huber8c45bcf2016-11-20 17:30:57 +0100149 if Success then
150 Success := Config_Helpers.Validate_Config
151 (New_Config.Framebuffer, Port_Cfg, I);
152 end if;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100153
Nico Huber83693c82016-10-08 22:17:55 +0200154 if Success and then Wait_For_HPD (New_Config.Port) then
155 Check_HPD (Port_Cfg, New_Config.Port, Success);
156 Wait_For_HPD (New_Config.Port) := not Success;
157 end if;
158
159 if Success then
160 pragma Debug (Debug.New_Line);
161 pragma Debug (Debug.Put_Line
162 ("Trying to enable port " & Port_Names (New_Config.Port)));
163
164 if not Did_Power_Up then
165 Power_And_Clocks.Power_Up (Old_Configs, Configs);
166 Did_Power_Up := True;
167 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200168 end if;
169
170 if Success then
171 Connector_Info.Preferred_Link_Setting
172 (Port_Cfg => Port_Cfg,
173 Success => Success);
174 end if;
175
176 while Success loop
Nico Huber47ff0692016-11-04 14:29:39 +0100177 pragma Loop_Invariant
178 (New_Config.Port in Active_Port_Type and
179 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
Nico Huber83693c82016-10-08 22:17:55 +0200180
181 PLLs.Alloc
182 (Port_Cfg => Port_Cfg,
183 PLL => Allocated_PLLs (I),
184 Success => Success);
185
186 if Success then
187 for Try in 1 .. 2 loop
188 pragma Loop_Invariant
189 (New_Config.Port in Active_Port_Type);
190
191 Connectors.Pre_On
Nico Huber6e327c92016-12-21 14:45:45 +0100192 (Pipe => I,
193 Port_Cfg => Port_Cfg,
194 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (I)),
195 Success => Success);
Nico Huber83693c82016-10-08 22:17:55 +0200196
197 if Success then
198 Display_Controller.On
Nico Huberf3e23662016-12-05 21:33:03 +0100199 (Pipe => I,
Nico Huber83693c82016-10-08 22:17:55 +0200200 Port_Cfg => Port_Cfg,
201 Framebuffer => New_Config.Framebuffer);
202
203 Connectors.Post_On
204 (Port_Cfg => Port_Cfg,
205 PLL_Hint => PLLs.Register_Value
206 (Allocated_PLLs (I)),
207 Success => Success);
208
209 if not Success then
Nico Huber7ad2d652016-12-07 15:19:32 +0100210 Display_Controller.Off (I);
Nico Huber83693c82016-10-08 22:17:55 +0200211 Connectors.Post_Off (Port_Cfg);
212 end if;
213 end if;
214
215 exit when Success;
216 end loop;
217 exit when Success; -- connection established => stop loop
218
219 -- connection failed
220 PLLs.Free (Allocated_PLLs (I));
221 end if;
222
223 Connector_Info.Next_Link_Setting
224 (Port_Cfg => Port_Cfg,
225 Success => Success);
226 end loop;
227
228 if Success then
229 pragma Debug (Debug.Put_Line
230 ("Enabled port " & Port_Names (New_Config.Port)));
231 Cur_Configs (I) := New_Config;
Nico Huber83693c82016-10-08 22:17:55 +0200232 else
233 Wait_For_HPD (New_Config.Port) := True;
234 if New_Config.Port = Internal then
235 Panel.Off;
236 end if;
237 end if;
238 else
239 Cur_Configs (I) := New_Config;
240 end if;
241 elsif Old_Config.Framebuffer /= New_Config.Framebuffer and
242 Old_Config.Port /= Disabled
243 then
Nico Huberf3e23662016-12-05 21:33:03 +0100244 Display_Controller.Update_Offset (I, New_Config.Framebuffer);
Nico Huber83693c82016-10-08 22:17:55 +0200245 Cur_Configs (I) := New_Config;
246 end if;
247 end loop;
248
249 if Did_Power_Up then
250 Power_And_Clocks.Power_Down (Old_Configs, Configs, Cur_Configs);
251 end if;
252
253 end Update_Outputs;
254
255 ----------------------------------------------------------------------------
256
257 procedure Initialize
258 (MMIO_Base : in Word64 := 0;
259 Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100260 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200261 Success : out Boolean)
262 with
263 Refined_Global =>
264 (In_Out =>
265 (Config.Valid_Port_GPU,
266 Registers.Register_State, Port_IO.State),
267 Input =>
268 (Time.State),
269 Output =>
270 (Registers.Address_State,
271 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +0100272 Cur_Configs, Allocated_PLLs,
Nico Huber83693c82016-10-08 22:17:55 +0200273 HPD_Delay, Wait_For_HPD, Initialized))
274 is
275 use type HW.Word64;
276
277 Now : constant Time.T := Time.Now;
278
279 procedure Check_Platform (Success : out Boolean)
280 is
281 Audio_VID_DID : Word32;
282 begin
283 case Config.CPU is
284 when Haswell .. Skylake =>
285 Registers.Read (Registers.AUD_VID_DID, Audio_VID_DID);
286 when Ironlake .. Ivybridge =>
287 Registers.Read (Registers.PCH_AUD_VID_DID, Audio_VID_DID);
288 end case;
289 Success :=
290 (case Config.CPU is
291 when Skylake => Audio_VID_DID = 16#8086_2809#,
292 when Broadwell => Audio_VID_DID = 16#8086_2808#,
293 when Haswell => Audio_VID_DID = 16#8086_2807#,
294 when Ivybridge |
295 Sandybridge => Audio_VID_DID = 16#8086_2806# or
296 Audio_VID_DID = 16#8086_2805#,
Nico Hubereeb5a392016-10-09 19:28:30 +0200297 when Ironlake => Audio_VID_DID = 16#0000_0000#);
Nico Huber83693c82016-10-08 22:17:55 +0200298 end Check_Platform;
299 begin
300 pragma Warnings (GNATprove, Off, "unused variable ""Write_Delay""",
301 Reason => "Write_Delay is used for debugging only");
302
303 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
304
305 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
306
307 Wait_For_HPD := HPD_Type'(others => False);
308 HPD_Delay := HPD_Delay_Type'(others => Now);
Nico Huber83693c82016-10-08 22:17:55 +0200309 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100310 Cur_Configs := Pipe_Configs'
311 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200312 (Port => Disabled,
313 Framebuffer => HW.GFX.Default_FB,
314 Mode => HW.GFX.Invalid_Mode));
315 Registers.Set_Register_Base
316 (if MMIO_Base /= 0 then
317 MMIO_Base
318 else
319 Config.Default_MMIO_Base);
320 PLLs.Initialize;
321
322 Check_Platform (Success);
323 if not Success then
324 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
325
326 Panel.Static_Init; -- for flow analysis
327
328 Initialized := False;
329 return;
330 end if;
331
332 Panel.Setup_PP_Sequencer;
333 Port_Detect.Initialize;
334
Nico Huber793a8d42016-11-21 18:57:03 +0100335 if Clean_State then
336 Power_And_Clocks.Pre_All_Off;
337 Connectors.Pre_All_Off;
338 Display_Controller.All_Off;
339 Connectors.Post_All_Off;
340 PLLs.All_Off;
341 Power_And_Clocks.Post_All_Off;
Nico Huber33912aa2016-12-06 20:36:23 +0100342 else
343 -- According to PRMs, VGA plane is the only thing
344 -- that's enabled by default after reset.
345 Display_Controller.Legacy_VGA_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100346 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200347
348 -------------------- Now restart from a clean state ---------------------
349 Power_And_Clocks.Initialize;
350
Nico Huberf54d0962016-10-20 14:17:18 +0200351 Registers.Unset_And_Set_Mask
352 (Register => Registers.PCH_RAWCLK_FREQ,
353 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
354 Mask_Set => PCH_RAWCLK_FREQ (Config.Default_RawClk_Freq));
355
Nico Huber83693c82016-10-08 22:17:55 +0200356 Initialized := True;
357
358 end Initialize;
359
360 function Is_Initialized return Boolean
361 with
362 Refined_Post => Is_Initialized'Result = Initialized
363 is
364 begin
365 return Initialized;
366 end Is_Initialized;
367
368 ----------------------------------------------------------------------------
369
370 procedure Write_GTT
371 (GTT_Page : GTT_Range;
372 Device_Address : GTT_Address_Type;
373 Valid : Boolean) is
374 begin
375 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
376 end Write_GTT;
377
378 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_FB : Word32)
379 is
380 FB_Size : constant Pos32 :=
381 FB.Stride * FB.Height * Pos32 (((FB.BPC * 4) / 8));
382 Phys_Addr : GTT_Address_Type := GTT_Address_Type (Phys_FB);
383 begin
384 for Idx in GTT_Range range 0 .. GTT_Range (((FB_Size + 4095) / 4096) - 1)
385 loop
386 Registers.Write_GTT
387 (GTT_Page => Idx,
388 Device_Address => Phys_Addr,
389 Valid => True);
390 Phys_Addr := Phys_Addr + 4096;
391 end loop;
392 end Setup_Default_GTT;
393
394 ----------------------------------------------------------------------------
395
Nico Huber99f10f32016-11-20 00:34:05 +0100396 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200397 is
398 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +0100399 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +0200400 Pipe_Names : constant Pipe_Name_Array :=
401 (Primary => "Primary ",
402 Secondary => "Secondary",
403 Tertiary => "Tertiary ");
404 begin
405 Debug.New_Line;
406 Debug.Put_Line ("CONFIG => ");
Nico Huber99f10f32016-11-20 00:34:05 +0100407 for Pipe in Pipe_Index loop
408 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +0200409 Debug.Put (" (");
410 else
411 Debug.Put (" ");
412 end if;
413 Debug.Put_Line (Pipe_Names (Pipe) & " =>");
414 Debug.Put_Line
415 (" (Port => " & Port_Names (Configs (Pipe).Port) & ",");
416 Debug.Put_Line (" Framebuffer =>");
417 Debug.Put (" (Width => ");
418 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
419 Debug.Put_Line (",");
420 Debug.Put (" Height => ");
421 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
422 Debug.Put_Line (",");
423 Debug.Put (" Stride => ");
424 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
425 Debug.Put_Line (",");
426 Debug.Put (" Offset => ");
427 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
428 Debug.Put_Line (",");
429 Debug.Put (" BPC => ");
430 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
431 Debug.Put_Line ("),");
432 Debug.Put_Line (" Mode =>");
433 Debug.Put (" (Dotclock => ");
434 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
435 Debug.Put_Line (",");
436 Debug.Put (" H_Visible => ");
437 Debug.Put_Int16 (Configs (Pipe).Mode.H_Visible);
438 Debug.Put_Line (",");
439 Debug.Put (" H_Sync_Begin => ");
440 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_Begin);
441 Debug.Put_Line (",");
442 Debug.Put (" H_Sync_End => ");
443 Debug.Put_Int16 (Configs (Pipe).Mode.H_Sync_End);
444 Debug.Put_Line (",");
445 Debug.Put (" H_Total => ");
446 Debug.Put_Int16 (Configs (Pipe).Mode.H_Total);
447 Debug.Put_Line (",");
448 Debug.Put (" V_Visible => ");
449 Debug.Put_Int16 (Configs (Pipe).Mode.V_Visible);
450 Debug.Put_Line (",");
451 Debug.Put (" V_Sync_Begin => ");
452 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_Begin);
453 Debug.Put_Line (",");
454 Debug.Put (" V_Sync_End => ");
455 Debug.Put_Int16 (Configs (Pipe).Mode.V_Sync_End);
456 Debug.Put_Line (",");
457 Debug.Put (" V_Total => ");
458 Debug.Put_Int16 (Configs (Pipe).Mode.V_Total);
459 Debug.Put_Line (",");
460 Debug.Put_Line (" H_Sync_Active_High => " &
461 (if Configs (Pipe).Mode.H_Sync_Active_High
462 then "True,"
463 else "False,"));
464 Debug.Put_Line (" V_Sync_Active_High => " &
465 (if Configs (Pipe).Mode.V_Sync_Active_High
466 then "True,"
467 else "False,"));
468 Debug.Put (" BPC => ");
469 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +0100470 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +0200471 Debug.Put_Line (")),");
472 else
473 Debug.Put_Line (")));");
474 end if;
475 end loop;
476 end Dump_Configs;
477
478end HW.GFX.GMA;