blob: fc8005f7b11f542e96f60c3c079ecb33d179daa2 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
Nico Huber9a4c4c32019-09-16 22:05:11 +02002-- Copyright (C) 2014-2019 secunet Security Networks AG
Nico Huber2b6f6992017-07-09 18:11:34 +02003-- Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
Nico Huber83693c82016-10-08 22:17:55 +02004--
5-- This program is free software; you can redistribute it and/or modify
6-- it under the terms of the GNU General Public License as published by
Nico Huber125a29e2016-10-18 00:23:54 +02007-- the Free Software Foundation; either version 2 of the License, or
8-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02009--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-- GNU General Public License for more details.
14--
15
Nico Huber2b6f6992017-07-09 18:11:34 +020016with HW.MMIO_Range;
17pragma Elaborate_All (HW.MMIO_Range);
18with HW.PCI.Dev;
19pragma Elaborate_All (HW.PCI.Dev);
20
Nico Huber83693c82016-10-08 22:17:55 +020021with HW.GFX.GMA.Config;
Nico Huber8c45bcf2016-11-20 17:30:57 +010022with HW.GFX.GMA.Config_Helpers;
Nico Huber83693c82016-10-08 22:17:55 +020023with HW.GFX.GMA.Registers;
Nico Huber312433c2019-09-28 03:15:48 +020024with HW.GFX.GMA.PCode;
Nico Huber83693c82016-10-08 22:17:55 +020025with HW.GFX.GMA.Power_And_Clocks;
26with HW.GFX.GMA.Panel;
27with HW.GFX.GMA.PLLs;
28with HW.GFX.GMA.Port_Detect;
29with HW.GFX.GMA.Connectors;
30with HW.GFX.GMA.Connector_Info;
31with HW.GFX.GMA.Pipe_Setup;
32
Nico Huber83693c82016-10-08 22:17:55 +020033with HW.Debug;
34with GNAT.Source_Info;
35
Nico Huber83693c82016-10-08 22:17:55 +020036
37package body HW.GFX.GMA
38 with Refined_State =>
39 (State =>
Nico Hubere317e9c2019-09-29 03:03:18 +020040 (Config.Variable,
41 PCI_Usable,
Nico Huberc5c66ec2019-09-28 23:59:45 +020042 Dev.Address_State,
Nico Huber2b6f6992017-07-09 18:11:34 +020043 Registers.Address_State,
Nico Huber312433c2019-09-28 03:15:48 +020044 PCode.Mailbox_Ready,
Nico Huber83693c82016-10-08 22:17:55 +020045 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +010046 Cur_Configs, Allocated_PLLs,
Nico Huberc3f66f62017-07-16 21:39:54 +020047 HPD_Delay, Wait_For_HPD,
48 Linear_FB_Base),
Nico Huber83693c82016-10-08 22:17:55 +020049 Init_State => Initialized,
Nico Huber83693c82016-10-08 22:17:55 +020050 Device_State =>
Nico Huber2b6f6992017-07-09 18:11:34 +020051 (Dev.PCI_State, Registers.Register_State, Registers.GTT_State))
Nico Huber83693c82016-10-08 22:17:55 +020052is
Nico Huber2b6f6992017-07-09 18:11:34 +020053 pragma Disable_Atomic_Synchronization;
Nico Huber83693c82016-10-08 22:17:55 +020054
Nico Huberad096092024-07-02 18:45:44 +020055 subtype Port_Name is String (1 .. 8);
Nico Huber83693c82016-10-08 22:17:55 +020056 type Port_Name_Array is array (Port_Type) of Port_Name;
57 Port_Names : constant Port_Name_Array :=
Nico Huberad096092024-07-02 18:45:44 +020058 (Disabled => "Disabled",
59 LVDS => "LVDS ",
60 eDP => "eDP ",
61 DP1 => "DP1 ",
62 DP2 => "DP2 ",
63 DP3 => "DP3 ",
64 DP_TC1 => "DP_TC1 ",
65 DP_TC2 => "DP_TC2 ",
66 DP_TC3 => "DP_TC3 ",
67 DP_TC4 => "DP_TC4 ",
68 HDMI1 => "HDMI1 ",
69 HDMI2 => "HDMI2 ",
70 HDMI3 => "HDMI3 ",
71 HDMI_TC1 => "HDMI_TC1",
72 HDMI_TC2 => "HDMI_TC2",
73 HDMI_TC3 => "HDMI_TC3",
74 HDMI_TC4 => "HDMI_TC4",
75 Analog => "Analog ",
76 USBC1 => "USBC1 ",
77 USBC2 => "USBC2 ",
78 USBC3 => "USBC3 ",
79 USBC4 => "USBC4 ");
Nico Huber83693c82016-10-08 22:17:55 +020080
Nico Huber4cafa4a2020-06-10 16:14:36 +020081 package Dev is new HW.PCI.Dev
82 (Dev => Config.PCI_Dev,
83 MMConf_Base => Config.Override_MMConf_Base);
Nico Huber2b6f6992017-07-09 18:11:34 +020084
Nico Huber83693c82016-10-08 22:17:55 +020085 package Display_Controller renames Pipe_Setup;
86
Nico Huber99f10f32016-11-20 00:34:05 +010087 type PLLs_Type is array (Pipe_Index) of PLLs.T;
Nico Huber83693c82016-10-08 22:17:55 +020088
Nico Huber83693c82016-10-08 22:17:55 +020089 type HPD_Type is array (Port_Type) of Boolean;
Nico Huber3be61d42017-01-09 13:58:18 +010090 type HPD_Delay_Type is array (Active_Port_Type) of Time.T;
Nico Huber83693c82016-10-08 22:17:55 +020091
Nico Huber83693c82016-10-08 22:17:55 +020092 Allocated_PLLs : PLLs_Type;
Nico Huber83693c82016-10-08 22:17:55 +020093 HPD_Delay : HPD_Delay_Type;
94 Wait_For_HPD : HPD_Type;
95 Initialized : Boolean := False;
96
Nico Huberc3f66f62017-07-16 21:39:54 +020097 Linear_FB_Base : Word64;
98
Nico Huber83693c82016-10-08 22:17:55 +020099 ----------------------------------------------------------------------------
100
Tim Wawrzynczakfc49b602022-09-09 10:29:24 -0600101 ICP_RAWCLK_NUM : constant := 1 * 2 ** 11;
102
103 function PCH_RAWCLK_FREQ_MASK return Word32 is
104 Mask : Word32;
105 begin
106 if Config.Need_Rawclk_Numerator then
107 Mask := 16#ffff_ffff#;
108 elsif Config.Has_Fractional_RawClk then
109 Mask := 16#3fff# * 2 ** 16;
110 else
111 Mask := 16#3ff# * 2 ** 0;
112 end if;
113 return Mask;
114 end PCH_RAWCLK_FREQ_MASK;
Nico Huberf54d0962016-10-20 14:17:18 +0200115
116 function PCH_RAWCLK_FREQ (Freq : Frequency_Type) return Word32
117 is
118 begin
Nico Huberdde06302020-12-20 02:18:30 +0100119 if Config.Has_Fractional_RawClk then
120 declare
121 Fraction_K : constant Int64 := Freq / 1_000 mod 1_000;
122 Freq32 : Word32 := Shift_Left (Word32 (Freq / 1_000_000), 16);
123 begin
124 if Fraction_K /= 0 then
125 Freq32 := Freq32 or Shift_Left
126 (Word32 (Div_Round_Closest (1_000, Fraction_K) - 1), 26);
127 end if;
Tim Wawrzynczakfc49b602022-09-09 10:29:24 -0600128
129 if Config.Need_Rawclk_Numerator then
130 Freq32 := Freq32 or ICP_RAWCLK_NUM;
131 end if;
Nico Huberdde06302020-12-20 02:18:30 +0100132 return Freq32;
133 end;
134 else
135 return Word32 (Freq / 1_000_000);
136 end if;
Nico Huberf54d0962016-10-20 14:17:18 +0200137 end PCH_RAWCLK_FREQ;
138
139 ----------------------------------------------------------------------------
140
Nico Huber43370ba2017-01-09 15:26:19 +0100141 procedure Enable_Output
142 (Pipe : in Pipe_Index;
143 Pipe_Cfg : in Pipe_Config;
144 Success : out Boolean)
Nico Huber8a5a3b52018-06-04 14:42:13 +0200145 with
Nico Huber9a4c4c32019-09-16 22:05:11 +0200146 Pre =>
147 Pipe_Cfg.Port in Active_Port_Type and
148 Config_Helpers.Valid_FB (Pipe_Cfg.Framebuffer, Pipe_Cfg.Mode)
Nico Huber43370ba2017-01-09 15:26:19 +0100149 is
150 Port_Cfg : Port_Config;
151 begin
Nico Huber3be61d42017-01-09 13:58:18 +0100152 pragma Debug (Debug.New_Line);
153 pragma Debug (Debug.Put_Line
154 ("Trying to enable port " & Port_Names (Pipe_Cfg.Port)));
155
Nico Huber43370ba2017-01-09 15:26:19 +0100156 Config_Helpers.Fill_Port_Config
157 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
158
159 if Success then
Nico Huber41e86742024-07-17 17:10:28 +0200160 Power_And_Clocks.Power_Up (Pipe_Cfg.Port, Success);
161 end if;
162
163 if Success then
Nico Huber43370ba2017-01-09 15:26:19 +0100164 Connector_Info.Preferred_Link_Setting (Port_Cfg, Success);
165 end if;
166
Nico Hubera8254482024-07-03 12:23:00 +0200167 if Success then
168 Connectors.Prepare (Pipe_Cfg.Port, Port_Cfg, Success);
169 end if;
170
Nico Huber43370ba2017-01-09 15:26:19 +0100171 -- loop over all possible DP-lane configurations
172 -- (non-DP ports use a single fake configuration)
173 while Success loop
174 pragma Loop_Invariant
175 (Pipe_Cfg.Port in Active_Port_Type and
176 Port_Cfg.Mode = Port_Cfg.Mode'Loop_Entry);
177
178 PLLs.Alloc
179 (Port_Cfg => Port_Cfg,
180 PLL => Allocated_PLLs (Pipe),
181 Success => Success);
182
183 if Success then
184 -- try each DP-lane configuration twice
185 for Try in 1 .. 2 loop
186 pragma Loop_Invariant
187 (Pipe_Cfg.Port in Active_Port_Type);
188
Nico Huber4798c662017-01-11 12:44:48 +0100189 -- Clear pending hot-plug events before every try
190 Port_Detect.Clear_Hotplug_Detect (Pipe_Cfg.Port);
191
Nico Huber43370ba2017-01-09 15:26:19 +0100192 Connectors.Pre_On
193 (Pipe => Pipe,
194 Port_Cfg => Port_Cfg,
195 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
196 Success => Success);
197
198 if Success then
199 Display_Controller.On
200 (Pipe => Pipe,
Nico Huber4d592b42026-06-23 12:21:37 +0000201 Pipe_Cfg => Pipe_Cfg,
202 Port_Cfg => Port_Cfg);
Nico Huber43370ba2017-01-09 15:26:19 +0100203
204 Connectors.Post_On
Arthur Heymans60d0e5f2018-03-28 17:08:27 +0200205 (Pipe => Pipe,
206 Port_Cfg => Port_Cfg,
Nico Huber43370ba2017-01-09 15:26:19 +0100207 PLL_Hint => PLLs.Register_Value (Allocated_PLLs (Pipe)),
208 Success => Success);
209
210 if not Success then
211 Display_Controller.Off (Pipe);
Nico Huberbfea6a32024-03-07 15:22:36 +0000212 Connectors.Post_Off (Pipe, Port_Cfg);
Nico Huber43370ba2017-01-09 15:26:19 +0100213 end if;
214 end if;
215
216 exit when Success;
217 end loop;
218 exit when Success; -- connection established => stop loop
219
220 -- connection failed
221 PLLs.Free (Allocated_PLLs (Pipe));
222 end if;
223
224 Connector_Info.Next_Link_Setting (Port_Cfg, Success);
Nico Hubera8254482024-07-03 12:23:00 +0200225 exit when not Success;
226
227 Connectors.Prepare (Pipe_Cfg.Port, Port_Cfg, Success);
Nico Huber43370ba2017-01-09 15:26:19 +0100228 end loop;
229
230 if Success then
231 pragma Debug (Debug.Put_Line
232 ("Enabled port " & Port_Names (Pipe_Cfg.Port)));
233 else
234 Wait_For_HPD (Pipe_Cfg.Port) := True;
Nico Huber2bbd6e72020-01-07 18:22:59 +0100235 Panel.Off (Config_Helpers.To_Panel (Pipe_Cfg.Port));
Nico Huber43370ba2017-01-09 15:26:19 +0100236 end if;
237 end Enable_Output;
238
Nico Huber3be61d42017-01-09 13:58:18 +0100239 procedure Disable_Output (Pipe : Pipe_Index; Pipe_Cfg : Pipe_Config)
240 is
241 Port_Cfg : Port_Config;
242 Success : Boolean;
243 begin
244 Config_Helpers.Fill_Port_Config
245 (Port_Cfg, Pipe, Pipe_Cfg.Port, Pipe_Cfg.Mode, Success);
246 if Success then
247 pragma Debug (Debug.New_Line);
248 pragma Debug (Debug.Put_Line
249 ("Disabling port " & Port_Names (Pipe_Cfg.Port)));
250 pragma Debug (Debug.New_Line);
251
Jeremy Compostellafe80fbb2023-01-11 14:05:42 -0700252 if Pipe_Cfg.Framebuffer.Offset = VGA_PLANE_FRAMEBUFFER_OFFSET then
253 Display_Controller.Legacy_VGA_Off;
254 end if;
255
Nico Huberbfea6a32024-03-07 15:22:36 +0000256 Connectors.Pre_Off (Pipe, Port_Cfg);
Nico Huber3be61d42017-01-09 13:58:18 +0100257 Display_Controller.Off (Pipe);
Nico Huberbfea6a32024-03-07 15:22:36 +0000258 Connectors.Post_Off (Pipe, Port_Cfg);
Nico Huber3be61d42017-01-09 13:58:18 +0100259
260 PLLs.Free (Allocated_PLLs (Pipe));
261 end if;
262 end Disable_Output;
263
Nico Huber99f10f32016-11-20 00:34:05 +0100264 procedure Update_Outputs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +0200265 is
Nico Huber3be61d42017-01-09 13:58:18 +0100266 procedure Check_HPD (Port : in Active_Port_Type; Detected : out Boolean)
267 is
268 HPD_Delay_Over : constant Boolean := Time.Timed_Out (HPD_Delay (Port));
269 begin
270 if HPD_Delay_Over then
271 Port_Detect.Hotplug_Detect (Port, Detected);
272 HPD_Delay (Port) := Time.MS_From_Now (333);
273 else
274 Detected := False;
275 end if;
276 end Check_HPD;
Nico Huberb56b9c52017-01-11 15:12:23 +0100277
Nico Huber9a4c4c32019-09-16 22:05:11 +0200278 Scaler_Reservation : Display_Controller.Scaler_Reservation :=
279 Display_Controller.Null_Scaler_Reservation;
Nico Huber564103f2017-01-11 15:33:07 +0100280
Nico Huber9a4c4c32019-09-16 22:05:11 +0200281 Update_Power : Boolean := False;
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200282 Update_CDClk : Boolean;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200283 Old_Configs,
284 New_Configs : Pipe_Configs;
Nico Huber3d06de82018-05-29 01:35:04 +0200285
286 function Full_Update (Cur_Config, New_Config : Pipe_Config) return Boolean
287 is
288 begin
289 return
Nico Huber958c5642018-06-02 16:59:31 +0200290 Cur_Config.Port /= New_Config.Port
291 or else
292 Cur_Config.Mode /= New_Config.Mode
293 or else
Nico Huber3d06de82018-05-29 01:35:04 +0200294 (Config.Use_PDW_For_EDP_Scaling and then
Nico Huber8beafd72020-01-07 14:59:44 +0100295 (Cur_Config.Port = eDP and
Nico Huber958c5642018-06-02 16:59:31 +0200296 Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config)))
297 or else
298 (Config.Has_GMCH_PFIT_CONTROL and then
299 (Requires_Scaling (Cur_Config) /= Requires_Scaling (New_Config) or
300 Scaling_Type (Cur_Config) /= Scaling_Type (New_Config)));
Nico Huber3d06de82018-05-29 01:35:04 +0200301 end Full_Update;
Nico Huber83693c82016-10-08 22:17:55 +0200302 begin
303 Old_Configs := Cur_Configs;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200304 New_Configs := Configs;
305
306 -- validate new configs, filter invalid configs and those waiting for HPD
307 for Pipe in Pipe_Index loop
308 declare
309 Success : Boolean := True;
310 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
311 New_Config : Pipe_Config renames New_Configs (Pipe);
312 begin
313 if New_Config.Port /= Disabled then
314 if Wait_For_HPD (New_Config.Port) then
315 Check_HPD (New_Config.Port, Success);
316 Wait_For_HPD (New_Config.Port) := not Success;
317 end if;
318
319 Success := Success and then
320 Config_Helpers.Validate_Config
321 (New_Config.Framebuffer, New_Config.Mode, Pipe);
322
323 if Success and then Requires_Scaling (New_Config) then
324 Display_Controller.Reserve_Scaler
325 (Success, Scaler_Reservation, Pipe);
326 end if;
327
328 if not Success then
329 New_Config.Port := Disabled;
330 end if;
331 end if;
332 end;
333 pragma Loop_Invariant
334 (for all P in Pipe_Index'First .. Pipe =>
335 New_Configs (P).Port = Disabled or
336 Config_Helpers.Valid_FB
337 (New_Configs (P).Framebuffer, New_Configs (P).Mode));
338 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200339
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200340 -- limit dotclocks to maximum CDClk, if we are about
341 -- to switch CDClk, all pipes have to be disabled
342 Power_And_Clocks.Limit_Dotclocks (New_Configs, Update_CDClk);
343
Nico Huberb56b9c52017-01-11 15:12:23 +0100344 -- disable all pipes that changed or had a hot-plug event
345 for Pipe in Pipe_Index loop
346 declare
347 Unplug_Detected : Boolean;
348 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200349 New_Config : Pipe_Config renames New_Configs (Pipe);
Nico Huberb56b9c52017-01-11 15:12:23 +0100350 begin
351 if Cur_Config.Port /= Disabled then
352 Check_HPD (Cur_Config.Port, Unplug_Detected);
Nico Huber83693c82016-10-08 22:17:55 +0200353
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200354 if Update_CDClk or
355 Unplug_Detected or
356 Full_Update (Cur_Config, New_Config)
357 then
Nico Huberb56b9c52017-01-11 15:12:23 +0100358 Disable_Output (Pipe, Cur_Config);
359 Cur_Config.Port := Disabled;
Nico Huber9a4c4c32019-09-16 22:05:11 +0200360 Update_Power := True;
Nico Huberb56b9c52017-01-11 15:12:23 +0100361 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200362 end if;
Nico Huberb56b9c52017-01-11 15:12:23 +0100363 end;
364 end loop;
Nico Huber83693c82016-10-08 22:17:55 +0200365
Nico Huberb0bbdbc2019-09-27 22:32:21 +0200366 -- switch CDClk if necessary and possible, limit dotclocks accordingly
367 if Update_CDClk then
368 Power_And_Clocks.Update_CDClk (New_Configs);
369 end if;
370
Nico Huberb56b9c52017-01-11 15:12:23 +0100371 -- enable all pipes that changed and should be active
372 for Pipe in Pipe_Index loop
373 declare
374 Success : Boolean;
375 Cur_Config : Pipe_Config renames Cur_Configs (Pipe);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200376 New_Config : Pipe_Config renames New_Configs (Pipe);
Nico Huberb56b9c52017-01-11 15:12:23 +0100377 begin
Nico Huber9a4c4c32019-09-16 22:05:11 +0200378 -- full update
Nico Huber3d06de82018-05-29 01:35:04 +0200379 if New_Config.Port /= Disabled and
380 Full_Update (Cur_Config, New_Config)
Nico Huberb56b9c52017-01-11 15:12:23 +0100381 then
Nico Huber9a4c4c32019-09-16 22:05:11 +0200382 Power_And_Clocks.Power_Up (Old_Configs, New_Configs);
383 Update_Power := True;
Nico Huberc7a4fee2016-11-03 18:18:03 +0100384
Nico Huber9a4c4c32019-09-16 22:05:11 +0200385 Enable_Output (Pipe, New_Config, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200386 if Success then
Nico Huberb56b9c52017-01-11 15:12:23 +0100387 Cur_Config := New_Config;
Nico Huber83693c82016-10-08 22:17:55 +0200388 end if;
Nico Huber3be61d42017-01-09 13:58:18 +0100389
Nico Huberb56b9c52017-01-11 15:12:23 +0100390 -- update framebuffer offset only
391 elsif New_Config.Port /= Disabled and
Nico Huberf361ec82018-06-02 18:01:45 +0200392 Cur_Config.Framebuffer /= New_Config.Framebuffer
Nico Huberb56b9c52017-01-11 15:12:23 +0100393 then
Nico Huber9a4c4c32019-09-16 22:05:11 +0200394 Display_Controller.Setup_FB
Nico Huber4d592b42026-06-23 12:21:37 +0000395 (Pipe, New_Config, New_Config.Mode);
Nico Huber9a4c4c32019-09-16 22:05:11 +0200396 Display_Controller.Update_Cursor
397 (Pipe, New_Config.Framebuffer, New_Config.Cursor);
398 Cur_Config := New_Config;
Nico Huberb56b9c52017-01-11 15:12:23 +0100399 end if;
400 end;
Nico Huber83693c82016-10-08 22:17:55 +0200401 end loop;
402
Nico Huber9a4c4c32019-09-16 22:05:11 +0200403 if Update_Power then
404 Power_And_Clocks.Power_Down (Old_Configs, New_Configs, Cur_Configs);
Nico Huber83693c82016-10-08 22:17:55 +0200405 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200406 end Update_Outputs;
407
408 ----------------------------------------------------------------------------
409
Nico Huber15ffc4f2018-01-11 14:44:43 +0100410 procedure Update_Cursor (Pipe : Pipe_Index; Cursor : Cursor_Type)
411 is
412 begin
413 Cur_Configs (Pipe).Cursor := Cursor;
414 Display_Controller.Update_Cursor
415 (Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
416 end Update_Cursor;
417
418 procedure Place_Cursor
419 (Pipe : Pipe_Index;
420 X : Cursor_Pos;
421 Y : Cursor_Pos)
422 is
423 begin
424 Cur_Configs (Pipe).Cursor.Center_X := X;
425 Cur_Configs (Pipe).Cursor.Center_Y := Y;
426 Display_Controller.Place_Cursor
427 (Pipe, Cur_Configs (Pipe).Framebuffer, Cur_Configs (Pipe).Cursor);
428 end Place_Cursor;
429
430 procedure Move_Cursor
431 (Pipe : Pipe_Index;
432 X : Cursor_Pos;
433 Y : Cursor_Pos)
434 is
435 function Cap_Add (A, B : Cursor_Pos) return Cursor_Pos is
436 (if A + B < 0
437 then Int32'Max (Cursor_Pos'First, A + B)
438 else Int32'Min (Cursor_Pos'Last, A + B));
439 begin
440 Place_Cursor
441 (Pipe => Pipe,
442 X => Cap_Add (Cur_Configs (Pipe).Cursor.Center_X, X),
443 Y => Cap_Add (Cur_Configs (Pipe).Cursor.Center_Y, Y));
444 end Move_Cursor;
445
446 ----------------------------------------------------------------------------
447
Nico Huberbc0588e2020-07-21 12:17:19 +0200448 procedure Backlight_On (Port : Active_Port_Type)
449 with
450 Refined_Global => (In_Out => Registers.Register_State)
451 is
452 begin
453 Panel.Backlight_On (Config_Helpers.To_Panel (Port));
454 end Backlight_On;
455
456 procedure Backlight_Off (Port : Active_Port_Type)
457 with
458 Refined_Global => (In_Out => Registers.Register_State)
459 is
460 begin
461 Panel.Backlight_Off (Config_Helpers.To_Panel (Port));
462 end Backlight_Off;
463
464 procedure Set_Brightness (Port : Active_Port_Type; Level : Word32)
465 with
466 Refined_Global => (In_Out => Registers.Register_State)
467 is
468 begin
469 Panel.Set_Backlight (Config_Helpers.To_Panel (Port), Level);
470 end Set_Brightness;
471
472 procedure Get_Max_Brightness (Port : Active_Port_Type; Level : out Word32)
473 with
474 Refined_Global => (In_Out => Registers.Register_State)
475 is
476 begin
477 Panel.Get_Max_Backlight (Config_Helpers.To_Panel (Port), Level);
478 end Get_Max_Brightness;
479
480 ----------------------------------------------------------------------------
481
Nico Huber793f4f82022-09-04 14:24:00 +0000482 pragma Warnings
483 (GNATprove, Off, """Registers.GTT_State"" * is not modified*",
484 Reason => "The whole, abstract Device_State is modified in certain configurations.");
485 pragma Warnings
486 (GNATprove, Off, "no check message justified*", Reason => "see below");
Nico Huber83693c82016-10-08 22:17:55 +0200487 procedure Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200488 (Write_Delay : in Word64 := 0;
Nico Huber793a8d42016-11-21 18:57:03 +0100489 Clean_State : in Boolean := False;
Nico Huber83693c82016-10-08 22:17:55 +0200490 Success : out Boolean)
491 with
492 Refined_Global =>
Nico Huber27088aa2018-06-10 13:28:05 +0200493 (Input => (Time.State),
Nico Huber793f4f82022-09-04 14:24:00 +0000494 In_Out =>
495 (Dev.PCI_State, Port_IO.State,
496 Registers.Register_State, Registers.GTT_State),
Nico Huber83693c82016-10-08 22:17:55 +0200497 Output =>
Nico Huberc5c66ec2019-09-28 23:59:45 +0200498 (PCI_Usable,
499 Config.Variable,
Nico Huber27088aa2018-06-10 13:28:05 +0200500 Dev.Address_State,
Nico Huber2b6f6992017-07-09 18:11:34 +0200501 Registers.Address_State,
Nico Huber312433c2019-09-28 03:15:48 +0200502 PCode.Mailbox_Ready,
Nico Huber83693c82016-10-08 22:17:55 +0200503 PLLs.State, Panel.Panel_State,
Nico Huber1a712d32017-01-09 15:11:04 +0100504 Cur_Configs, Allocated_PLLs,
Nico Huberc3f66f62017-07-16 21:39:54 +0200505 HPD_Delay, Wait_For_HPD,
506 Linear_FB_Base, Initialized))
Nico Huber83693c82016-10-08 22:17:55 +0200507 is
508 use type HW.Word64;
509
Nico Huber2b6f6992017-07-09 18:11:34 +0200510 PCI_MMIO_Base, PCI_GTT_Base : Word64;
511
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100512 function Default_GTT_Base return Word64 is
513 (Config.Default_MMIO_Base + Word64 (Config.MMIO_GTT_Offset));
514
Nico Huber83693c82016-10-08 22:17:55 +0200515 Now : constant Time.T := Time.Now;
516
517 procedure Check_Platform (Success : out Boolean)
518 is
519 Audio_VID_DID : Word32;
520 begin
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100521 if Config.Gen_I945 or Config.GMCH_GM965 then
522 -- i945 and GM965 have no integrated audio DID to verify.
Arthur Heymans960e2392026-03-03 19:45:24 +0100523 Success := True;
524 return;
525 end if;
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100526 Registers.Read_AUD_VID_DID (Audio_VID_DID);
Nico Huber83693c82016-10-08 22:17:55 +0200527 Success :=
Nico Huber998ee2b2018-06-12 23:02:17 +0200528 ((Config.Gen_Broxton and Audio_VID_DID = 16#8086_280a#) or
Nico Huber88badbe2018-09-27 16:36:47 +0200529 (Config.CPU_Kabylake and Audio_VID_DID = 16#8086_280b#) or
530 (Config.CPU_Skylake and Audio_VID_DID = 16#8086_2809#) or
Nico Huber998ee2b2018-06-12 23:02:17 +0200531 (Config.CPU_Broadwell and Audio_VID_DID = 16#8086_2808#) or
532 (Config.CPU_Haswell and Audio_VID_DID = 16#8086_2807#) or
533 ((Config.CPU_Ivybridge or
534 Config.CPU_Sandybridge) and (Audio_VID_DID = 16#8086_2806# or
535 Audio_VID_DID = 16#8086_2805#)) or
536 (Config.CPU_Ironlake and Audio_VID_DID = 16#0000_0000#) or
537 (Config.Gen_G45 and (Audio_VID_DID = 16#8086_2801# or
538 Audio_VID_DID = 16#8086_2802# or
Tim Wawrzynczak605660b2022-06-08 12:48:19 -0600539 Audio_VID_DID = 16#8086_2803#)) or
540 (Config.CPU_Tigerlake and (Audio_VID_DID = 16#8086_2812#)));
Nico Huber83693c82016-10-08 22:17:55 +0200541 end Check_Platform;
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200542
543 procedure Check_Platform_PCI (Success : out Boolean)
544 is
545 use type HW.Word16;
546 Vendor, Device : Word16;
547 begin
548 Dev.Read16 (Vendor, PCI.Vendor_Id);
549 Dev.Read16 (Device, PCI.Device_Id);
550
Nico Huber6a996dc2018-06-17 16:30:33 +0200551 Config.Detect_CPU (Device);
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200552 Success := Vendor = 16#8086# and Config.Compatible_GPU (Device);
553 end Check_Platform_PCI;
Nico Huber83693c82016-10-08 22:17:55 +0200554 begin
Nico Huber83693c82016-10-08 22:17:55 +0200555 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
556
557 pragma Debug (Debug.Set_Register_Write_Delay (Write_Delay));
558
Nico Huberc5c66ec2019-09-28 23:59:45 +0200559 PCI_Usable := False;
Nico Huberc3f66f62017-07-16 21:39:54 +0200560 Linear_FB_Base := 0;
Nico Huber312433c2019-09-28 03:15:48 +0200561 PCode.Mailbox_Ready := False;
Nico Huber83693c82016-10-08 22:17:55 +0200562 Wait_For_HPD := HPD_Type'(others => False);
563 HPD_Delay := HPD_Delay_Type'(others => Now);
Nico Huber83693c82016-10-08 22:17:55 +0200564 Allocated_PLLs := (others => PLLs.Invalid);
Nico Huber99f10f32016-11-20 00:34:05 +0100565 Cur_Configs := Pipe_Configs'
566 (others => Pipe_Config'
Nico Huber83693c82016-10-08 22:17:55 +0200567 (Port => Disabled,
568 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000569 Alignment => Alignment'First,
570 Scaling => Scaling'First,
Nico Hubera02b2c62018-01-09 15:58:34 +0100571 Cursor => Default_Cursor,
Nico Huber83693c82016-10-08 22:17:55 +0200572 Mode => HW.GFX.Invalid_Mode));
Nico Huber27088aa2018-06-10 13:28:05 +0200573 Config.Variable := Config.Initial_Settings;
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100574 Registers.Set_Register_Base (Config.Default_MMIO_Base, Default_GTT_Base);
Nico Huber83693c82016-10-08 22:17:55 +0200575 PLLs.Initialize;
576
Nico Huber2b6f6992017-07-09 18:11:34 +0200577 Dev.Initialize (Success);
578
579 if Success then
Nico Huber6a996dc2018-06-17 16:30:33 +0200580 Check_Platform_PCI (Success);
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200581 if Success then
Arthur Heymans960e2392026-03-03 19:45:24 +0100582 if Config.Has_I945_GTT_BAR then
583 -- i945: MMIO is on BAR0, GTT is on separate BAR3
584 Dev.Map (PCI_MMIO_Base, PCI.Res0);
585 Dev.Map (PCI_GTT_Base, PCI.Res3);
586 else
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100587 Dev.Map
588 (PCI_MMIO_Base, PCI.Res0, Length => Config.MMIO_GTT_Offset);
589 Dev.Map
590 (PCI_GTT_Base, PCI.Res0, Offset => Config.MMIO_GTT_Offset);
Arthur Heymans960e2392026-03-03 19:45:24 +0100591 end if;
Nico Huber6a996dc2018-06-17 16:30:33 +0200592 if PCI_MMIO_Base /= 0 and PCI_GTT_Base /= 0 then
593 Registers.Set_Register_Base (PCI_MMIO_Base, PCI_GTT_Base);
Nico Huberc5c66ec2019-09-28 23:59:45 +0200594 PCI_Usable := True;
Nico Huber6a996dc2018-06-17 16:30:33 +0200595 else
596 pragma Debug (Debug.Put_Line
Arthur Heymans960e2392026-03-03 19:45:24 +0100597 ("ERROR: Couldn't map resource0."));
Nico Huber6a996dc2018-06-17 16:30:33 +0200598 Success := Config.Default_MMIO_Base_Set;
599 end if;
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200600 end if;
Nico Huber2b6f6992017-07-09 18:11:34 +0200601 else
602 pragma Debug (Debug.Put_Line
603 ("WARNING: Couldn't initialize PCI dev."));
Nico Huber2b6f6992017-07-09 18:11:34 +0200604 Success := Config.Default_MMIO_Base_Set;
Nico Huber2b6f6992017-07-09 18:11:34 +0200605
Nico Hubere7ac6eb2017-09-04 23:54:13 +0200606 if Success then
607 Check_Platform (Success);
608 end if;
Nico Huber2b6f6992017-07-09 18:11:34 +0200609 end if;
610
Nico Huber5dbaf4b2020-01-08 17:24:58 +0100611 Panel.Static_Init; -- early for flow analysis
612
Nico Huber83693c82016-10-08 22:17:55 +0200613 if not Success then
614 pragma Debug (Debug.Put_Line ("ERROR: Incompatible CPU or PCH."));
615
Nico Huber83693c82016-10-08 22:17:55 +0200616 Initialized := False;
617 return;
618 end if;
619
620 Panel.Setup_PP_Sequencer;
Nico Huber83693c82016-10-08 22:17:55 +0200621
Nico Huber793a8d42016-11-21 18:57:03 +0100622 if Clean_State then
623 Power_And_Clocks.Pre_All_Off;
624 Connectors.Pre_All_Off;
625 Display_Controller.All_Off;
626 Connectors.Post_All_Off;
627 PLLs.All_Off;
628 Power_And_Clocks.Post_All_Off;
Nico Huber17d64b62017-07-15 20:51:25 +0200629 Registers.Clear_Fences;
Nico Huber33912aa2016-12-06 20:36:23 +0100630 else
631 -- According to PRMs, VGA plane is the only thing
Nico Huber3a0e2a02017-07-19 14:41:46 +0200632 -- that's enabled by default after reset...
Nico Huber33912aa2016-12-06 20:36:23 +0100633 Display_Controller.Legacy_VGA_Off;
Nico Huber3a0e2a02017-07-19 14:41:46 +0200634 -- ... along with some DDI port bits since Skylake.
635 Connectors.Post_Reset_Off;
Nico Huber793a8d42016-11-21 18:57:03 +0100636 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200637
638 -------------------- Now restart from a clean state ---------------------
639 Power_And_Clocks.Initialize;
Nico Huber1b991852024-07-17 16:44:05 +0200640 Port_Detect.Initialize;
641 Connectors.Initialize;
Nico Huber83693c82016-10-08 22:17:55 +0200642
Nico Huber1c3b9282017-02-09 13:57:04 +0100643 if Config.Has_PCH then
644 Registers.Unset_And_Set_Mask
645 (Register => Registers.PCH_RAWCLK_FREQ,
646 Mask_Unset => PCH_RAWCLK_FREQ_MASK,
Nico Huberc9ad9de2020-12-20 02:34:37 +0100647 Mask_Set => PCH_RAWCLK_FREQ (Config.Raw_Clock));
Nico Huber1c3b9282017-02-09 13:57:04 +0100648 end if;
Nico Huberf54d0962016-10-20 14:17:18 +0200649
Nico Huber83693c82016-10-08 22:17:55 +0200650 Initialized := True;
651
652 end Initialize;
Nico Huber793f4f82022-09-04 14:24:00 +0000653 pragma Annotate
654 (GNATprove, Intentional, "unused global",
655 "The whole, abstract Device_State is modified in certain configurations.");
656 pragma Warnings (GNATprove, On, "no check message justified*");
657 pragma Warnings
658 (GNATprove, On, """Registers.GTT_State"" * is not modified*");
Nico Huber83693c82016-10-08 22:17:55 +0200659
660 function Is_Initialized return Boolean
661 with
662 Refined_Post => Is_Initialized'Result = Initialized
663 is
664 begin
665 return Initialized;
666 end Is_Initialized;
667
668 ----------------------------------------------------------------------------
669
Nico Hubercf88f3d2018-06-05 13:27:34 +0200670 pragma Warnings
671 (GNATprove, Off, """Registers.Register_State"" * is not modified*",
Nico Huberadfe11f2018-06-10 14:59:04 +0200672 Reason => "Power_Up_VGA is only effective in certain configurations.");
Nico Huber17b513e2022-09-04 13:36:02 +0200673 pragma Warnings
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700674 (GNATprove, Off, """PCode.Mailbox_Ready"" * is not modified*",
675 Reason => "Pcode is only used in certain configurations.");
676 pragma Warnings
Nico Huber17b513e2022-09-04 13:36:02 +0200677 (GNATprove, Off, "no check message justified*", Reason => "see below");
Nico Huber42fb2d02017-09-01 17:01:51 +0200678 procedure Power_Up_VGA
Nico Hubercf88f3d2018-06-05 13:27:34 +0200679 with
680 Refined_Global =>
Nico Huberadfe11f2018-06-10 14:59:04 +0200681 (Input => (Cur_Configs, Config.Variable, Time.State),
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700682 In_Out => (Registers.Register_State, PCode.Mailbox_Ready),
Nico Hubercf88f3d2018-06-05 13:27:34 +0200683 Proof_In => (Initialized))
Nico Huber42fb2d02017-09-01 17:01:51 +0200684 is
685 Fake_Config : constant Pipe_Configs :=
686 (Primary =>
687 (Port => Analog,
688 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000689 Alignment => Center,
690 Scaling => Fit,
Nico Hubera02b2c62018-01-09 15:58:34 +0100691 Cursor => Default_Cursor,
Nico Huber42fb2d02017-09-01 17:01:51 +0200692 Mode => HW.GFX.Invalid_Mode),
693 others =>
694 (Port => Disabled,
695 Framebuffer => HW.GFX.Default_FB,
Nico Huber20fd3fc2026-06-23 11:26:16 +0000696 Alignment => Alignment'First,
697 Scaling => Scaling'First,
Nico Hubera02b2c62018-01-09 15:58:34 +0100698 Cursor => Default_Cursor,
Nico Huber42fb2d02017-09-01 17:01:51 +0200699 Mode => HW.GFX.Invalid_Mode));
700 begin
701 Power_And_Clocks.Power_Up (Cur_Configs, Fake_Config);
702 end Power_Up_VGA;
Nico Hubercf88f3d2018-06-05 13:27:34 +0200703 pragma Annotate
704 (GNATprove, Intentional, "unused global",
Nico Huberadfe11f2018-06-10 14:59:04 +0200705 "Power_Up_VGA is only effective in certain configurations.");
Tim Wawrzynczak5473d292023-02-06 16:46:33 -0700706 pragma Warnings
707 (GNATprove, On, """PCode.Mailbox_Ready"" * is not modified*");
Nico Hubercf88f3d2018-06-05 13:27:34 +0200708 pragma Warnings (GNATprove, On, "no check message justified*");
709 pragma Warnings
710 (GNATprove, On, """Registers.Register_State"" * is not modified*");
Nico Huber42fb2d02017-09-01 17:01:51 +0200711
712 ----------------------------------------------------------------------------
713
Nico Huber5374c3a2017-07-15 21:48:06 +0200714 function FB_First_Page (FB : Framebuffer_Type) return Natural is
Nico Huber34be6542017-12-13 09:26:24 +0100715 (Natural (Phys_Offset (FB) / GTT_Page_Size));
Nico Huber5374c3a2017-07-15 21:48:06 +0200716 function FB_Pages (FB : Framebuffer_Type) return Natural is
717 (Natural (Div_Round_Up (FB_Size (FB), GTT_Page_Size)));
718 function FB_Last_Page (FB : Framebuffer_Type) return Natural is
719 (FB_First_Page (FB) + FB_Pages (FB) - 1);
720
Nico Huber34be6542017-12-13 09:26:24 +0100721 -- Check basics and that it fits in GTT. For 90 degree rotations,
722 -- the Offset should be above GTT_Rotation_Offset. The latter will
723 -- be subtracted for the aperture mapping.
Nico Huber5374c3a2017-07-15 21:48:06 +0200724 function Valid_FB (FB : Framebuffer_Type) return Boolean is
Nico Huber34be6542017-12-13 09:26:24 +0100725 (Valid_Stride (FB) and
726 FB_First_Page (FB) in GTT_Range and
Nico Huber2e87c0d2020-04-18 00:46:39 +0200727 FB_Last_Page (FB) + 128 in GTT_Range and
Nico Huber34be6542017-12-13 09:26:24 +0100728 (not Rotation_90 (FB) or
Nico Huber2e87c0d2020-04-18 00:46:39 +0200729 (FB_First_Page (FB) mod 64 = 0 and
730 FB_Last_Page (FB) + 128 + GTT_Rotation_Offset in GTT_Range and
Nico Huber34be6542017-12-13 09:26:24 +0100731 FB.Offset >= Word32 (GTT_Rotation_Offset) * GTT_Page_Size)));
Nico Huber5374c3a2017-07-15 21:48:06 +0200732
733 -- Also check that we don't overflow the GTT's 39-bit space
734 -- (always true with a 32-bit base)
735 function Valid_Phys_FB (FB : Framebuffer_Type; Phys_Base : Word32)
736 return Boolean is
737 (Valid_FB (FB) and
Nico Huber34be6542017-12-13 09:26:24 +0100738 Int64 (Phys_Base) + Int64 (Phys_Offset (FB)) + Int64 (FB_Size (FB)) <=
Nico Huber5374c3a2017-07-15 21:48:06 +0200739 Int64 (GTT_Address_Type'Last))
740 with
741 Ghost;
742
Nico Huber83693c82016-10-08 22:17:55 +0200743 procedure Write_GTT
744 (GTT_Page : GTT_Range;
745 Device_Address : GTT_Address_Type;
Nico Huber5374c3a2017-07-15 21:48:06 +0200746 Valid : Boolean)
747 is
Nico Huber83693c82016-10-08 22:17:55 +0200748 begin
749 Registers.Write_GTT (GTT_Page, Device_Address, Valid);
750 end Write_GTT;
751
Nico Huberceda17d2018-06-09 22:00:29 +0200752 procedure Read_GTT
753 (Device_Address : out GTT_Address_Type;
754 Valid : out Boolean;
755 GTT_Page : in GTT_Range)
756 is
757 begin
758 Registers.Read_GTT (Device_Address, Valid, GTT_Page);
759 end Read_GTT;
760
Nico Huber194e57e2017-07-15 21:15:46 +0200761 procedure Setup_Default_GTT (FB : Framebuffer_Type; Phys_Base : Word32)
Nico Huber5374c3a2017-07-15 21:48:06 +0200762 with
763 Pre => Is_Initialized and Valid_Phys_FB (FB, Phys_Base)
Nico Huber83693c82016-10-08 22:17:55 +0200764 is
Nico Huber194e57e2017-07-15 21:15:46 +0200765 Phys_Addr : GTT_Address_Type :=
Nico Huber34be6542017-12-13 09:26:24 +0100766 GTT_Address_Type (Phys_Base) + GTT_Address_Type (Phys_Offset (FB));
Nico Huber83693c82016-10-08 22:17:55 +0200767 begin
Nico Huber194e57e2017-07-15 21:15:46 +0200768 for Idx in FB_First_Page (FB) .. FB_Last_Page (FB) loop
Nico Huber83693c82016-10-08 22:17:55 +0200769 Registers.Write_GTT
770 (GTT_Page => Idx,
771 Device_Address => Phys_Addr,
772 Valid => True);
Nico Huber194e57e2017-07-15 21:15:46 +0200773 Phys_Addr := Phys_Addr + GTT_Page_Size;
Nico Huber83693c82016-10-08 22:17:55 +0200774 end loop;
Nico Huber2e87c0d2020-04-18 00:46:39 +0200775 -- Add another 128 dummy pages to work around buggy VT-d
776 for Idx in FB_Last_Page (FB) + 1 .. FB_Last_Page (FB) + 128 loop
777 Registers.Write_GTT (Idx, Phys_Addr, True);
778 end loop;
Nico Huber9b479412017-08-27 11:55:56 +0200779
780 if Rotation_90 (FB) and FB.Tiling = Y_Tiled and FB.V_Stride >= 32 then
781 declare
782 V_Pages : constant Natural := Natural (FB.V_Stride) / 32;
783 Bytes_Per_Row : constant GTT_Address_Type :=
784 GTT_Address_Type (Pixel_To_Bytes (32 * FB.Stride, FB));
785 begin
786 Phys_Addr := GTT_Address_Type (Phys_Base) +
Nico Huber34be6542017-12-13 09:26:24 +0100787 GTT_Address_Type (Phys_Offset (FB)) +
Nico Huber9b479412017-08-27 11:55:56 +0200788 GTT_Address_Type (FB_Size (FB));
789 for Page in FB_First_Page (FB) .. FB_Last_Page (FB) loop
790 Phys_Addr := Phys_Addr - Bytes_Per_Row;
791 Registers.Write_GTT
792 (GTT_Page => GTT_Rotation_Offset + Page,
793 Device_Address => Phys_Addr,
794 Valid => True);
795
796 if (Page - FB_First_Page (FB) + 1) mod V_Pages = 0 then
797 Phys_Addr := Phys_Addr + GTT_Page_Size +
798 GTT_Address_Type (V_Pages) * Bytes_Per_Row;
799 end if;
800 end loop;
801 end;
Nico Huber2e87c0d2020-04-18 00:46:39 +0200802 -- Add another 128 dummy pages to work around buggy VT-d
803 for Idx in FB_Last_Page (FB) + 1 .. FB_Last_Page (FB) + 128 loop
804 Registers.Write_GTT (GTT_Rotation_Offset + Idx, Phys_Addr, True);
805 end loop;
Nico Huber9b479412017-08-27 11:55:56 +0200806 end if;
Nico Huber83693c82016-10-08 22:17:55 +0200807 end Setup_Default_GTT;
808
809 ----------------------------------------------------------------------------
810
Nico Hubereedde882017-07-16 02:54:39 +0200811 use type HW.Word16;
812 subtype Stolen_Size_Range is Int64 range 0 .. 2 ** 33;
813
814 function GGMS_Gen4 (GGC : Word16) return Natural is
815 (Natural (Shift_Right (GGC, 8) and 16#07#));
816 function GTT_Size_Gen4 (GGC : Word16) return Natural is
817 (if GGMS_Gen4 (GGC) in 1 .. 3 then
818 (GGMS_Gen4 (GGC) + 1) * 2 ** 19 else 0);
819
820 function GMS_Gen4 (GGC : Word16) return Natural is
821 (Natural (Shift_Right (GGC, 4) and 16#0f#));
822 Valid_Stolen_Size_Gen4 : constant
823 array (Natural range 1 .. 13) of Stolen_Size_Range :=
824 (1, 4, 8, 16, 32, 48, 64, 128, 256, 96, 160, 224, 352);
825 function Stolen_Size_Gen4 (GGC : Word16) return Stolen_Size_Range is
826 (if GMS_Gen4 (GGC) in Valid_Stolen_Size_Gen4'Range then
Arthur Heymans5fd9a312017-09-12 12:45:18 +0200827 Valid_Stolen_Size_Gen4 (GMS_Gen4 (GGC)) * 2 ** 20 else 0);
Nico Hubereedde882017-07-16 02:54:39 +0200828
829 function GTT_Size_Gen6 (GGC : Word16) return Natural is
830 (Natural (Shift_Right (GGC, 8) and 16#03#) * 2 ** 20);
831
832 function Stolen_Size_Gen6 (GGC : Word16) return Stolen_Size_Range is
833 (Stolen_Size_Range (Shift_Right (GGC, 3) and 16#1f#) * 32 * 2 ** 20);
834
Nico Huberfe7985f2019-10-12 22:19:24 +0200835 function GGMS_Gen8 (GGC : Word16) return Natural is
836 (Natural (Shift_Right (GGC, 6) and 16#03#));
Nico Hubereedde882017-07-16 02:54:39 +0200837 function GTT_Size_Gen8 (GGC : Word16) return Natural is
Nico Huberfe7985f2019-10-12 22:19:24 +0200838 (if GGMS_Gen8 (GGC) /= 0 then
839 Natural (Shift_Left (Word32'(1), 20 + GGMS_Gen8 (GGC))) else 0);
Nico Hubereedde882017-07-16 02:54:39 +0200840
841 function GMS_Gen8 (GGC : Word16) return Stolen_Size_Range is
842 (Stolen_Size_Range (Shift_Right (GGC, 8) and 16#ff#));
843 function Stolen_Size_Gen8 (GGC : Word16) return Stolen_Size_Range is
844 (GMS_Gen8 (GGC) * 32 * 2 ** 20);
845
846 function Stolen_Size_Gen9 (GGC : Word16) return Stolen_Size_Range is
847 (if GMS_Gen8 (GGC) < 16#f0# then
848 Stolen_Size_Gen8 (GGC)
849 else
850 (GMS_Gen8 (GGC) - 16#f0# + 1) * 4 * 2 ** 20);
851
852 procedure Decode_Stolen
853 (GTT_Size : out Natural;
854 Stolen_Size : out Stolen_Size_Range)
855 with
856 Pre => Is_Initialized
857 is
Nico Huber63ec8362018-06-09 17:42:19 +0200858 GGC_Reg : constant PCI.Index :=
Arthur Heymans960e2392026-03-03 19:45:24 +0100859 (if Config.Gen_I945 or Config.Gen_G45 or Config.CPU_Ironlake
860 then 16#52# else 16#50#);
Nico Hubereedde882017-07-16 02:54:39 +0200861 GGC : Word16;
862 begin
863 Dev.Read16 (GGC, GGC_Reg);
Arthur Heymans960e2392026-03-03 19:45:24 +0100864 if Config.Gen_I945 then
865 -- i945 GTT is on a separate BAR3; GGC GGMS encoding differs
866 -- from Gen4+. Match the Linux driver and use the BAR size.
867 Dev.Resource_Size (GTT_Size, PCI.Res3);
868 Stolen_Size := Stolen_Size_Gen4 (GGC);
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100869 elsif Config.GMCH_GM965 then
870 -- GM965 has no GGMS field in GGC. The GTT is a fixed 512 KiB
871 -- region in the upper half of the 1 MiB GTTMMADR BAR.
872 GTT_Size := 512 * 2 ** 10;
873 Stolen_Size := Stolen_Size_Gen4 (GGC);
Arthur Heymans960e2392026-03-03 19:45:24 +0100874 elsif Config.Gen_G45 or Config.CPU_Ironlake then
Nico Huber998ee2b2018-06-12 23:02:17 +0200875 GTT_Size := GTT_Size_Gen4 (GGC);
876 Stolen_Size := Stolen_Size_Gen4 (GGC);
877 elsif Config.CPU_Sandybridge or Config.CPU_Ivybridge or Config.CPU_Haswell
878 then
879 GTT_Size := GTT_Size_Gen6 (GGC);
880 Stolen_Size := Stolen_Size_Gen6 (GGC);
881 elsif Config.CPU_Broadwell then
882 GTT_Size := GTT_Size_Gen8 (GGC);
883 Stolen_Size := Stolen_Size_Gen8 (GGC);
884 else
885 GTT_Size := GTT_Size_Gen8 (GGC);
886 Stolen_Size := Stolen_Size_Gen9 (GGC);
887 end if;
Nico Hubereedde882017-07-16 02:54:39 +0200888 end Decode_Stolen;
889
Arthur Heymans960e2392026-03-03 19:45:24 +0100890 procedure GTT_Entry_Count (Count : out Natural)
891 is
892 GTT_Size : Natural;
893
894 procedure Fake_Config_State_Access
895 with
896 Global => (Input => Config.Variable),
897 Annotate => (GNATprove, Intentional, "unused global",
898 "Used to have a common contract across platforms.");
899 procedure Fake_Config_State_Access is null;
900 begin
901 Fake_Config_State_Access;
902
903 if Config.Has_I945_GTT_BAR then
904 -- i945 GTT is on a separate BAR3; its size is the BAR size.
905 Dev.Resource_Size (GTT_Size, PCI.Res3);
Arthur Heymans3f37cce2026-03-03 18:52:12 +0100906 elsif Config.GMCH_GM965 then
907 -- GM965 has no GGMS field in GGC; its GTT is fixed at 512 KiB.
908 GTT_Size := 512 * 2 ** 10;
Arthur Heymans960e2392026-03-03 19:45:24 +0100909 else
910 -- Gen4+: GTT size is encoded in the GGC register.
911 declare
912 GGC_Reg : constant PCI.Index :=
913 (if Config.Gen_G45 or Config.CPU_Ironlake
914 then 16#52# else 16#50#);
915 GGC : Word16;
916 begin
917 Dev.Read16 (GGC, GGC_Reg);
918 if Config.Gen_G45 or Config.CPU_Ironlake then
919 GTT_Size := GTT_Size_Gen4 (GGC);
920 elsif Config.CPU_Sandybridge or
921 Config.CPU_Ivybridge or
922 Config.CPU_Haswell
923 then
924 GTT_Size := GTT_Size_Gen6 (GGC);
925 else
926 GTT_Size := GTT_Size_Gen8 (GGC);
927 end if;
928 end;
929 end if;
930 Count := Natural'Min (GTT_Size / Config.GTT_PTE_Size, GTT_Range'Last + 1);
931 end GTT_Entry_Count;
932
Nico Hubereedde882017-07-16 02:54:39 +0200933 -- Additional runtime validation that FB fits stolen memory and aperture.
934 procedure Validate_FB (FB : Framebuffer_Type; Valid : out Boolean)
935 with
936 Pre => Is_Initialized,
937 Post => (if Valid then Valid_FB (FB))
938 is
Nico Huber2e87c0d2020-04-18 00:46:39 +0200939 GTT_Off : constant Natural :=
940 (if Rotation_90 (FB) then GTT_Rotation_Offset else 0);
941
Nico Hubereedde882017-07-16 02:54:39 +0200942 GTT_Size, Aperture_Size : Natural;
943 Stolen_Size : Stolen_Size_Range;
944 begin
945 Valid := Valid_FB (FB);
946
947 if Valid then
948 Decode_Stolen (GTT_Size, Stolen_Size);
949 Dev.Resource_Size (Aperture_Size, PCI.Res2);
950 Valid :=
Nico Huber2e87c0d2020-04-18 00:46:39 +0200951 FB_Last_Page (FB) + 128 + GTT_Off < GTT_Size / Config.GTT_PTE_Size
952 and
953 FB_Last_Page (FB) < Natural (Stolen_Size / GTT_Page_Size)
954 and
Nico Hubereedde882017-07-16 02:54:39 +0200955 FB_Last_Page (FB) < Aperture_Size / GTT_Page_Size;
Nico Huber34be6542017-12-13 09:26:24 +0100956 pragma Debug (not Valid, Debug.Put_Line
Nico Hubereedde882017-07-16 02:54:39 +0200957 ("Stolen memory too small to hold framebuffer."));
958 end if;
959 end Validate_FB;
960
Nico Huber5374c3a2017-07-15 21:48:06 +0200961 procedure Setup_Default_FB
962 (FB : in Framebuffer_Type;
963 Clear : in Boolean := True;
964 Success : out Boolean)
965 is
Nico Huber5374c3a2017-07-15 21:48:06 +0200966 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
967
968 Phys_Base : Word32;
969 begin
Nico Hubereedde882017-07-16 02:54:39 +0200970 Validate_FB (FB, Success);
Nico Huber5374c3a2017-07-15 21:48:06 +0200971
972 if Success then
Tim Wawrzynczak1b65b842022-09-09 10:23:06 -0600973 if Config.GMA_Base_Is_64bit then
974 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index + 4);
975 if Phys_Base /= 0 then
976 pragma Debug (Debug.Put_Line ("Cannot handle 64-bit DSM yet."));
977 Success := False;
978 return;
979 end if;
980 end if;
981
982 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index);
Nico Huber5374c3a2017-07-15 21:48:06 +0200983 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
984 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
985 pragma Debug (not Success, Debug.Put_Line
986 ("Failed to read stolen memory base."));
Nico Huber0164b022017-08-24 15:12:51 +0200987
988 if Success then
989 if FB.Tiling in XY_Tiling then
990 Registers.Add_Fence
991 (First_Page => FB_First_Page (FB),
992 Last_Page => FB_Last_Page (FB),
993 Tiling => FB.Tiling,
994 Pitch => FB_Pitch (FB.Stride, FB),
995 Success => Success);
996 end if;
997 pragma Debug (not Success, Debug.Put_Line
998 ("Tiled framebuffer but no fence regs available."));
999 end if;
1000
Nico Huber5374c3a2017-07-15 21:48:06 +02001001 if Success then
1002 Setup_Default_GTT (FB, Phys_Base);
1003 end if;
1004 end if;
1005
1006 if Success and then Clear then
1007 declare
1008 use type HW.Word64;
1009 Linear_FB : Word64;
1010 begin
Nico Huberc3f66f62017-07-16 21:39:54 +02001011 Map_Linear_FB (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +02001012 if Linear_FB /= 0 then
Nico Huberc3f66f62017-07-16 21:39:54 +02001013 Framebuffer_Filler.Fill (Linear_FB, FB);
Nico Huber5374c3a2017-07-15 21:48:06 +02001014 end if;
Nico Huber5374c3a2017-07-15 21:48:06 +02001015 end;
1016 end if;
1017 end Setup_Default_FB;
1018
Nico Huberc3f66f62017-07-16 21:39:54 +02001019 procedure Map_Linear_FB (Linear_FB : out Word64; FB : in Framebuffer_Type)
1020 is
1021 use type HW.Word64;
1022
1023 Valid : Boolean;
1024 begin
1025 Linear_FB := 0;
1026
1027 if Linear_FB_Base = 0 then
1028 Dev.Map (Linear_FB_Base, PCI.Res2);
1029 pragma Debug
1030 (Linear_FB_Base = 0, Debug.Put_Line ("Failed to map resource2."));
1031 end if;
1032
1033 if Linear_FB_Base /= 0 then
1034 Validate_FB (FB, Valid);
1035 if Valid then
Nico Huber34be6542017-12-13 09:26:24 +01001036 Linear_FB := Linear_FB_Base + Word64 (Phys_Offset (FB));
Nico Huberc3f66f62017-07-16 21:39:54 +02001037 end if;
1038 end if;
1039 end Map_Linear_FB;
1040
Nico Huber5374c3a2017-07-15 21:48:06 +02001041 ----------------------------------------------------------------------------
1042
Nico Huber99f10f32016-11-20 00:34:05 +01001043 procedure Dump_Configs (Configs : Pipe_Configs)
Nico Huber83693c82016-10-08 22:17:55 +02001044 is
1045 subtype Pipe_Name is String (1 .. 9);
Nico Huber99f10f32016-11-20 00:34:05 +01001046 type Pipe_Name_Array is array (Pipe_Index) of Pipe_Name;
Nico Huber83693c82016-10-08 22:17:55 +02001047 Pipe_Names : constant Pipe_Name_Array :=
1048 (Primary => "Primary ",
1049 Secondary => "Secondary",
1050 Tertiary => "Tertiary ");
Nico Huber5ef4d602017-12-13 13:56:47 +01001051
1052 subtype Tiling_Name is String (1 .. 7);
1053 type Tiling_Name_Array is array (Tiling_Type) of Tiling_Name;
1054 Tilings : constant Tiling_Name_Array :=
1055 (Linear => "Linear ",
1056 X_Tiled => "X_Tiled",
1057 Y_Tiled => "Y_Tiled");
1058
1059 subtype Rotation_Name is String (1 .. 11);
1060 type Rotation_Name_Array is array (Rotation_Type) of Rotation_Name;
1061 Rotations : constant Rotation_Name_Array :=
1062 (No_Rotation => "No_Rotation",
1063 Rotated_90 => "Rotated_90 ",
1064 Rotated_180 => "Rotated_180",
1065 Rotated_270 => "Rotated_270");
Nico Huber83693c82016-10-08 22:17:55 +02001066 begin
1067 Debug.New_Line;
Paul Menzelb83107c2017-05-04 09:02:33 +02001068 Debug.Put_Line ("CONFIG =>");
Nico Huber99f10f32016-11-20 00:34:05 +01001069 for Pipe in Pipe_Index loop
1070 if Pipe = Pipe_Index'First then
Nico Huber83693c82016-10-08 22:17:55 +02001071 Debug.Put (" (");
1072 else
1073 Debug.Put (" ");
1074 end if;
Nico Huberfb80da92026-07-02 15:01:13 +02001075 Debug.Put (Pipe_Names (Pipe));
1076 Debug.Put_Line (" =>");
1077 Debug.Put (" (Port => ");
1078 Debug.Put (Port_Names (Configs (Pipe).Port));
1079 Debug.Put_Line (",");
Nico Huber83693c82016-10-08 22:17:55 +02001080 Debug.Put_Line (" Framebuffer =>");
Nico Huber5ef4d602017-12-13 13:56:47 +01001081 Debug.Put (" (Width => ");
Nico Huber83693c82016-10-08 22:17:55 +02001082 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Width);
1083 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001084 Debug.Put (" Height => ");
Nico Huber83693c82016-10-08 22:17:55 +02001085 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Height);
1086 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001087 Debug.Put (" Start_X => ");
1088 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_X);
1089 Debug.Put_Line (",");
1090 Debug.Put (" Start_Y => ");
1091 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Start_Y);
1092 Debug.Put_Line (",");
1093 Debug.Put (" Stride => ");
Nico Huber83693c82016-10-08 22:17:55 +02001094 Debug.Put_Int32 (Configs (Pipe).Framebuffer.Stride);
1095 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001096 Debug.Put (" V_Stride => ");
1097 Debug.Put_Int32 (Configs (Pipe).Framebuffer.V_Stride);
1098 Debug.Put_Line (",");
1099 Debug.Put (" Tiling => ");
Nico Huberfb80da92026-07-02 15:01:13 +02001100 Debug.Put (Tilings (Configs (Pipe).Framebuffer.Tiling));
1101 Debug.Put_Line (",");
Nico Huber5ef4d602017-12-13 13:56:47 +01001102 Debug.Put (" Rotation => ");
Nico Huberfb80da92026-07-02 15:01:13 +02001103 Debug.Put (Rotations (Configs (Pipe).Framebuffer.Rotation));
1104 Debug.Put_Line (",");
Nico Huber83693c82016-10-08 22:17:55 +02001105 Debug.Put (" Offset => ");
1106 Debug.Put_Word32 (Configs (Pipe).Framebuffer.Offset);
1107 Debug.Put_Line (",");
1108 Debug.Put (" BPC => ");
1109 Debug.Put_Int64 (Configs (Pipe).Framebuffer.BPC);
1110 Debug.Put_Line ("),");
1111 Debug.Put_Line (" Mode =>");
1112 Debug.Put (" (Dotclock => ");
1113 Debug.Put_Int64 (Configs (Pipe).Mode.Dotclock);
1114 Debug.Put_Line (",");
1115 Debug.Put (" H_Visible => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001116 Debug.Put_Int32 (Configs (Pipe).Mode.H_Visible);
Nico Huber83693c82016-10-08 22:17:55 +02001117 Debug.Put_Line (",");
1118 Debug.Put (" H_Sync_Begin => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001119 Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_Begin);
Nico Huber83693c82016-10-08 22:17:55 +02001120 Debug.Put_Line (",");
1121 Debug.Put (" H_Sync_End => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001122 Debug.Put_Int32 (Configs (Pipe).Mode.H_Sync_End);
Nico Huber83693c82016-10-08 22:17:55 +02001123 Debug.Put_Line (",");
1124 Debug.Put (" H_Total => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001125 Debug.Put_Int32 (Configs (Pipe).Mode.H_Total);
Nico Huber83693c82016-10-08 22:17:55 +02001126 Debug.Put_Line (",");
1127 Debug.Put (" V_Visible => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001128 Debug.Put_Int32 (Configs (Pipe).Mode.V_Visible);
Nico Huber83693c82016-10-08 22:17:55 +02001129 Debug.Put_Line (",");
1130 Debug.Put (" V_Sync_Begin => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001131 Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_Begin);
Nico Huber83693c82016-10-08 22:17:55 +02001132 Debug.Put_Line (",");
1133 Debug.Put (" V_Sync_End => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001134 Debug.Put_Int32 (Configs (Pipe).Mode.V_Sync_End);
Nico Huber83693c82016-10-08 22:17:55 +02001135 Debug.Put_Line (",");
1136 Debug.Put (" V_Total => ");
Nico Huberc5c767a2018-06-03 01:09:04 +02001137 Debug.Put_Int32 (Configs (Pipe).Mode.V_Total);
Nico Huber83693c82016-10-08 22:17:55 +02001138 Debug.Put_Line (",");
Nico Huberfb80da92026-07-02 15:01:13 +02001139 Debug.Put (" H_Sync_Active_High => ");
1140 if Configs (Pipe).Mode.H_Sync_Active_High then
1141 Debug.Put_Line ("True,");
1142 else
1143 Debug.Put_Line ( "False,");
1144 end if;
1145 Debug.Put (" V_Sync_Active_High => ");
1146 if Configs (Pipe).Mode.V_Sync_Active_High then
1147 Debug.Put_Line ("True,");
1148 else
1149 Debug.Put_Line ("False,");
1150 end if;
Nico Huber83693c82016-10-08 22:17:55 +02001151 Debug.Put (" BPC => ");
1152 Debug.Put_Int64 (Configs (Pipe).Mode.BPC);
Nico Huber99f10f32016-11-20 00:34:05 +01001153 if Pipe /= Pipe_Index'Last then
Nico Huber83693c82016-10-08 22:17:55 +02001154 Debug.Put_Line (")),");
1155 else
1156 Debug.Put_Line (")));");
1157 end if;
1158 end loop;
1159 end Dump_Configs;
1160
Nico Huberc5c66ec2019-09-28 23:59:45 +02001161 ----------------------------------------------------------------------------
1162
1163 procedure PCI_Read16 (Value : out Word16; Offset : HW.PCI.Index) is
1164 begin
1165 Dev.Read16 (Value, Offset);
1166 end PCI_Read16;
1167
Nico Huber83693c82016-10-08 22:17:55 +02001168end HW.GFX.GMA;