blob: c6a301948fa4ba3fbde0dd43e050fa81a9d12623 [file] [log] [blame]
Nico Hubera455f0e2018-01-07 11:40:40 +01001with Ada.Numerics.Discrete_Random;
Nico Huberfda2d6e2017-07-09 16:47:52 +02002with Ada.Unchecked_Conversion;
Nico Huber1d0abe42017-03-05 14:14:09 +01003with Ada.Command_Line;
4with Interfaces.C;
5
Nico Huber3b654a02017-07-15 22:27:14 +02006with HW.Time;
Nico Huber1d0abe42017-03-05 14:14:09 +01007with HW.Debug;
Nico Huberfda2d6e2017-07-09 16:47:52 +02008with HW.PCI.Dev;
9with HW.MMIO_Range;
Nico Huber3b654a02017-07-15 22:27:14 +020010with HW.GFX.GMA.Config;
Nico Huber1d0abe42017-03-05 14:14:09 +010011with HW.GFX.GMA.Display_Probing;
12
Nico Huberfda2d6e2017-07-09 16:47:52 +020013package body HW.GFX.GMA.GFX_Test
14is
15 pragma Disable_Atomic_Synchronization;
Nico Huber1d0abe42017-03-05 14:14:09 +010016
Nico Hubera455f0e2018-01-07 11:40:40 +010017 Primary_Delay_MS : constant := 8_000;
18 Secondary_Delay_MS : constant := 4_000;
Nico Hubera563ec22019-09-29 19:07:27 +020019 HP_Delay_MS : constant := 500;
Nico Hubera455f0e2018-01-07 11:40:40 +010020 Seed : constant := 12345;
21
Nico Hubera63e8332018-02-01 16:41:30 +010022 package Rand_P is new Ada.Numerics.Discrete_Random (Natural);
Nico Huberd8282b62018-06-18 00:44:55 +020023 function Rand (Gen : Rand_P.Generator)
24 return Int32 is (Int32 (Rand_P.Random (Gen)));
Nico Hubera455f0e2018-01-07 11:40:40 +010025
Nico Huber5ef4d602017-12-13 13:56:47 +010026 Start_X : constant := 0;
27 Start_Y : constant := 0;
28
Nico Huberfda2d6e2017-07-09 16:47:52 +020029 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010030
Nico Huberc76749d2018-06-09 22:04:55 +020031 type GTT_Entry is record
32 Addr : GTT_Address_Type;
33 Valid : Boolean;
34 end record;
35 GTT_Backup : array (GTT_Range) of GTT_Entry;
Arthur Heymans960e2392026-03-03 19:45:24 +010036 GTT_Backup_Count : Natural;
Nico Huber3b654a02017-07-15 22:27:14 +020037
38 procedure Backup_GTT
39 is
40 begin
Arthur Heymans960e2392026-03-03 19:45:24 +010041 GMA.GTT_Entry_Count (GTT_Backup_Count);
42 if GTT_Backup_Count = 0 then
43 Debug.Put_Line ("WARNING: GTT size is 0, skipping GTT backup.");
44 return;
45 end if;
46 for Idx in GTT_Range range 0 .. GTT_Backup_Count - 1 loop
Nico Huberc76749d2018-06-09 22:04:55 +020047 Read_GTT (GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid, Idx);
Nico Huber3b654a02017-07-15 22:27:14 +020048 end loop;
49 end Backup_GTT;
50
51 procedure Restore_GTT
52 is
53 begin
Arthur Heymans960e2392026-03-03 19:45:24 +010054 if GTT_Backup_Count = 0 then
55 return;
56 end if;
57 for Idx in GTT_Range range 0 .. GTT_Backup_Count - 1 loop
Nico Huberc76749d2018-06-09 22:04:55 +020058 Write_GTT (Idx, GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid);
Nico Huber3b654a02017-07-15 22:27:14 +020059 end loop;
60 end Restore_GTT;
61
Nico Huber1d0abe42017-03-05 14:14:09 +010062 type Pixel_Type is record
63 Red : Byte;
64 Green : Byte;
65 Blue : Byte;
66 Alpha : Byte;
67 end record;
68
69 for Pixel_Type use record
70 Blue at 0 range 0 .. 7;
71 Green at 1 range 0 .. 7;
72 Red at 2 range 0 .. 7;
73 Alpha at 3 range 0 .. 7;
74 end record;
75
Nico Huber244ea7e2017-08-28 11:38:23 +020076 White : constant Pixel_Type := (255, 255, 255, 255);
77 Black : constant Pixel_Type := ( 0, 0, 0, 255);
78 Red : constant Pixel_Type := (255, 0, 0, 255);
79 Green : constant Pixel_Type := ( 0, 255, 0, 255);
80 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
81
Nico Huberfda2d6e2017-07-09 16:47:52 +020082 function Pixel_To_Word (P : Pixel_Type) return Word32
83 with
84 SPARK_Mode => Off
85 is
86 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
87 begin
88 return To_Word (P);
89 end Pixel_To_Word;
90
Nico Huber7bb10c62018-01-12 14:07:44 +010091 Max_W : constant := 4096;
92 Max_H : constant := 2160;
93 FB_Align : constant := 16#0004_0000#;
94 Cursor_Align : constant := 16#0001_0000#;
95 Max_Cursor_Wid : constant := 256;
96 subtype Screen_Index is Natural range 0 .. 3 *
97 (Max_W * Max_H + FB_Align / 4 +
98 3 * Max_Cursor_Wid * Max_Cursor_Wid + Cursor_Align / 4)
99 - 1;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200100 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +0100101
Nico Huber34be6542017-12-13 09:26:24 +0100102 function Screen_Offset (FB : Framebuffer_Type) return Natural is
103 (Natural (Phys_Offset (FB) / 4));
104
Nico Huberfda2d6e2017-07-09 16:47:52 +0200105 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +0100106
Nico Huber3b654a02017-07-15 22:27:14 +0200107 Screen_Backup : Screen_Type;
108
109 procedure Backup_Screen (FB : Framebuffer_Type)
110 is
Nico Huber34be6542017-12-13 09:26:24 +0100111 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200112 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
113 begin
114 for Idx in Screen_Index range First .. Last loop
115 Screen.Read (Screen_Backup (Idx), Idx);
116 end loop;
117 end Backup_Screen;
118
119 procedure Restore_Screen (FB : Framebuffer_Type)
120 is
Nico Huber34be6542017-12-13 09:26:24 +0100121 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200122 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
123 begin
124 for Idx in Screen_Index range First .. Last loop
125 Screen.Write (Idx, Screen_Backup (Idx));
126 end loop;
127 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100128
Nico Huber5ef4d602017-12-13 13:56:47 +0100129 function Drawing_Width (FB : Framebuffer_Type) return Natural is
130 (Natural (FB.Width + 2 * Start_X));
131
132 function Drawing_Height (FB : Framebuffer_Type) return Natural is
133 (Natural (FB.Height + 2 * Start_Y));
134
Nico Huber244ea7e2017-08-28 11:38:23 +0200135 function Corner_Fill
136 (X, Y : Natural;
137 FB : Framebuffer_Type;
138 Pipe : Pipe_Index)
139 return Pixel_Type
140 is
141 Xrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100142 (if X < 32 then X else X - (Drawing_Width (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200143 Yrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100144 (if Y < 32 then Y else Y - (Drawing_Height (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200145
146 function Color (Idx : Natural) return Pixel_Type is
147 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
148 when 0 => Blue, when 1 => Black,
149 when 3 => Green, when others => Red);
150 begin
151 return
152 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
153 White
154 elsif Yrel < 16 then
155 (if Xrel < 16 then Color (0) else Color (1))
156 else
157 (if Xrel < 16 then Color (3) else Color (2)));
158 end Corner_Fill;
159
Nico Huber1d0abe42017-03-05 14:14:09 +0100160 function Fill
161 (X, Y : Natural;
162 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200163 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100164 return Pixel_Type
165 is
166 use type HW.Byte;
167
Nico Huber5ef4d602017-12-13 13:56:47 +0100168 Xp : constant Natural := X * 256 / Drawing_Width (Framebuffer);
169 Yp : constant Natural := Y * 256 / Drawing_Height (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100170 Xn : constant Natural := 255 - Xp;
171 Yn : constant Natural := 255 - Yp;
172
173 function Map (X, Y : Natural) return Byte is
174 begin
175 return Byte (X * Y / 255);
176 end Map;
177 begin
178 return
179 (case Pipe is
180 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
181 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
182 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
183 end Fill;
184
185 procedure Test_Screen
186 (Framebuffer : Framebuffer_Type;
187 Pipe : GMA.Pipe_Index)
188 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100189 P : Pixel_Type;
190 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100191 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100192 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200193
194 function Top_Test (X, Y : Natural) return Boolean
195 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100196 C : constant Natural := Drawing_Width (Framebuffer) / 2;
197 S_Y : constant Natural := 3 * (Y - Start_Y) / 2;
Nico Huber9ca69f12017-08-28 14:31:46 +0200198 Left : constant Integer := X - C + S_Y;
199 Right : constant Integer := X - C - S_Y;
200 begin
201 return
Nico Huber5ef4d602017-12-13 13:56:47 +0100202 (Y - Start_Y) < 12 and
Nico Huber9ca69f12017-08-28 14:31:46 +0200203 ((-1 <= Left and Left <= 0) or
204 (0 <= Right and Right <= 1));
205 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100206 begin
Nico Huber5ef4d602017-12-13 13:56:47 +0100207 for Y in 0 .. Drawing_Height (Framebuffer) - 1 loop
Nico Huber1d0abe42017-03-05 14:14:09 +0100208 Offset := Offset_Y;
Nico Huber5ef4d602017-12-13 13:56:47 +0100209 for X in 0 .. Drawing_Width (Framebuffer) - 1 loop
210 if (X < 32 or X >= Drawing_Width (Framebuffer) - 32) and
211 (Y < 32 or Y >= Drawing_Height (Framebuffer) - 32)
Nico Huber244ea7e2017-08-28 11:38:23 +0200212 then
213 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200214 elsif Framebuffer.Rotation /= No_Rotation and then
215 Top_Test (X, Y)
216 then
217 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200218 elsif Y mod 16 = 0 or X mod 16 = 0 then
219 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100220 else
221 P := Fill (X, Y, Framebuffer, Pipe);
222 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200223 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100224 Offset := Offset + 1;
225 end loop;
226 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
227 end loop;
228 end Test_Screen;
229
Nico Huber7bb10c62018-01-12 14:07:44 +0100230 function Donut (X, Y, Max : Cursor_Pos) return Byte
231 is
232 ZZ : constant Int32 := Max * Max * 2;
233 Dist_Center : constant Int32 := ((X * X + Y * Y) * 255) / ZZ;
234 Dist_Circle : constant Int32 := Dist_Center - 20;
235 begin
236 return Byte (255 - Int32'Min (255, 6 * abs Dist_Circle + 64));
237 end Donut;
238
239 procedure Draw_Cursor (Pipe : Pipe_Index; Cursor : Cursor_Type)
240 is
241 use type HW.Byte;
242 Width : constant Width_Type := Cursor_Width (Cursor.Size);
243 Screen_Offset : Natural :=
244 Natural (Shift_Left (Word32 (Cursor.GTT_Offset), 12) / 4);
245 begin
246 if Cursor.Mode /= ARGB_Cursor then
247 return;
248 end if;
249 for Y in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
250 for X in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
251 declare
252 D : constant Byte := Donut (X, Y, Width / 2);
253 begin
254 -- Hardware seems to expect pre-multiplied alpha (i.e.
255 -- color components already contain the alpha).
256 Screen.Write
257 (Index => Screen_Offset,
258 Value => Pixel_To_Word (
259 (Red => (if Pipe = Secondary then D / 2 else 0),
260 Green => (if Pipe = Tertiary then D / 2 else 0),
261 Blue => (if Pipe = Primary then D / 2 else 0),
262 Alpha => D)));
263 Screen_Offset := Screen_Offset + 1;
264 end;
265 end loop;
266 end loop;
267 end Draw_Cursor;
268
Nico Huber1d0abe42017-03-05 14:14:09 +0100269 procedure Calc_Framebuffer
270 (FB : out Framebuffer_Type;
271 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200272 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100273 Offset : in out Word32)
274 is
Nico Huberc5c767a2018-06-03 01:09:04 +0200275 Width : constant Width_Type := Mode.H_Visible;
276 Height : constant Height_Type := Mode.V_Visible;
Nico Huber1d0abe42017-03-05 14:14:09 +0100277 begin
278 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200279 if Rotation = Rotated_90 or Rotation = Rotated_270 then
280 FB :=
Nico Huberc5c767a2018-06-03 01:09:04 +0200281 (Width => Height,
282 Height => Width,
Nico Huber5ef4d602017-12-13 13:56:47 +0100283 Start_X => Start_X,
284 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200285 BPC => 8,
Nico Huberc5c767a2018-06-03 01:09:04 +0200286 Stride => Div_Round_Up (Height + 2 * Start_X, 32) * 32,
287 V_Stride => Div_Round_Up (Width + 2 * Start_Y, 32) * 32,
Nico Huber88f3c982017-08-28 13:31:38 +0200288 Tiling => Y_Tiled,
289 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100290 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200291 else
292 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100293 (Width => Width,
294 Height => Height,
295 Start_X => Start_X,
296 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200297 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100298 Stride => Div_Round_Up (Width + 2 * Start_X, 16) * 16,
299 V_Stride => Height + 2 * Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200300 Tiling => Linear,
301 Rotation => Rotation,
302 Offset => Offset);
303 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100304 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100305 end Calc_Framebuffer;
306
Nico Huber7bb10c62018-01-12 14:07:44 +0100307 type Cursor_Array is array (Cursor_Size) of Cursor_Type;
308 Cursors : array (Pipe_Index) of Cursor_Array;
309
310 procedure Prepare_Cursors
311 (Cursors : out Cursor_Array;
312 Offset : in out Word32)
313 is
Nico Huber7bb10c62018-01-12 14:07:44 +0100314 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
315
316 Phys_Base : Word32;
317 Success : Boolean;
318 begin
Nico Huber87719ae2024-06-20 13:06:05 +0000319 if Config.GMA_Base_Is_64bit then
320 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index + 4);
321 if Phys_Base /= 0 then
322 pragma Debug (Debug.Put_Line ("Cannot handle 64-bit DSM yet."));
323 return;
324 end if;
325 end if;
326
327 Dev.Read32 (Phys_Base, Config.GMA_Phys_Base_Index);
Nico Huber7bb10c62018-01-12 14:07:44 +0100328 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
329 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
330 if not Success then
331 Debug.Put_Line ("Failed to read stolen memory base.");
332 return;
333 end if;
334
335 for Size in Cursor_Size loop
336 Offset := (Offset + Cursor_Align - 1) and not (Cursor_Align - 1);
337 declare
338 Width : constant Width_Type := Cursor_Width (Size);
339 GTT_End : constant Word32 := Offset + Word32 (Width * Width) * 4;
340 begin
341 Cursors (Size) :=
342 (Mode => ARGB_Cursor,
343 Size => Size,
344 Center_X => Width,
345 Center_Y => Width,
346 GTT_Offset => GTT_Range (Shift_Right (Offset, 12)));
347 while Offset < GTT_End loop
348 GMA.Write_GTT
349 (GTT_Page => GTT_Range (Offset / GTT_Page_Size),
350 Device_Address => GTT_Address_Type (Phys_Base + Offset),
351 Valid => True);
352 Offset := Offset + GTT_Page_Size;
353 end loop;
354 end;
355 end loop;
356 end Prepare_Cursors;
357
Nico Huber3b654a02017-07-15 22:27:14 +0200358 Pipes : GMA.Pipe_Configs;
359
Nico Huberd8282b62018-06-18 00:44:55 +0200360 procedure Prepare_Configs (Rotation : Rotation_Type; Gen : Rand_P.Generator)
Nico Huber1d0abe42017-03-05 14:14:09 +0100361 is
362 use type HW.GFX.GMA.Port_Type;
363
Nico Huberfda2d6e2017-07-09 16:47:52 +0200364 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200365 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100366 begin
367 GMA.Display_Probing.Scan_Ports (Pipes);
368
369 for Pipe in GMA.Pipe_Index loop
370 if Pipes (Pipe).Port /= GMA.Disabled then
371 Calc_Framebuffer
372 (FB => Pipes (Pipe).Framebuffer,
373 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200374 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100375 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200376 GMA.Setup_Default_FB
377 (FB => Pipes (Pipe).Framebuffer,
378 Clear => False,
379 Success => Success);
380 if not Success then
381 Pipes (Pipe).Port := GMA.Disabled;
382 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100383 end if;
Nico Huber7bb10c62018-01-12 14:07:44 +0100384 Prepare_Cursors (Cursors (Pipe), Offset);
Nico Huberd8282b62018-06-18 00:44:55 +0200385 Pipes (Pipe).Cursor := Cursors (Pipe) (Cursor_Size'Val (Rand (Gen) mod 3));
Nico Huber1d0abe42017-03-05 14:14:09 +0100386 end loop;
387
388 GMA.Dump_Configs (Pipes);
389 end Prepare_Configs;
390
Nico Hubera63e8332018-02-01 16:41:30 +0100391 procedure Script_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200392 (Pipes : in out GMA.Pipe_Configs;
393 Hotplug_List : out Display_Probing.Port_List;
394 Total_Deadline : in Time.T;
395 Time_MS : in Natural)
Nico Hubera63e8332018-02-01 16:41:30 +0100396 is
397 type Corner is (UL, UR, LR, LL);
398 type Cursor_Script_Entry is record
399 Rel : Corner;
400 X, Y : Int32;
401 end record;
402 Cursor_Script : constant array (Natural range 0 .. 19) of Cursor_Script_Entry :=
403 ((UL, 16, 16), (UL, 16, 16), (UL, 16, 16), (UL, -32, 0), (UL, 16, 16),
404 (UR, -16, 16), (UR, -16, 16), (UR, -16, 16), (UR, 0, -32), (UR, -16, 16),
405 (LR, -16, -16), (LR, -16, -16), (LR, -16, -16), (LR, 32, 0), (LR, -16, -16),
406 (LL, 16, -16), (LL, 16, -16), (LL, 16, -16), (LL, 0, 32), (LL, 16, -16));
407
408 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200409 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100410 Timed_Out : Boolean := False;
411 Cnt : Word32 := 0;
412 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200413 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100414 loop
415 for Pipe in Pipe_Index loop
416 exit when Pipes (Pipe).Port = GMA.Disabled;
417 declare
418 C : Cursor_Type renames Pipes (Pipe).Cursor;
419 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huberc5c767a2018-06-03 01:09:04 +0200420 Width : constant Width_Type := Rotated_Width (FB);
421 Height : constant Height_Type := Rotated_Height (FB);
Nico Hubera63e8332018-02-01 16:41:30 +0100422 CS : Cursor_Script_Entry renames Cursor_Script
423 (Natural (Cnt) mod (Cursor_Script'Last + 1));
424 begin
425 C.Center_X := CS.X;
426 C.Center_Y := CS.Y;
427 case CS.Rel is
428 when UL => null;
429 when UR => C.Center_X := CS.X + Width;
430 when LR => C.Center_X := CS.X + Width;
431 C.Center_Y := CS.Y + Height;
432 when LL => C.Center_Y := CS.Y + Height;
433 end case;
434 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
435 end;
436 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200437
438 Timed_Out := Time.Timed_Out (HP_Deadline);
439 if Timed_Out then
440 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
441 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
442 if Hotplug_List (Hotplug_List'First) /= Disabled then
443 return;
444 end if;
445 end if;
446
447 Timed_Out := Time.Timed_Out (Total_Deadline);
448 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100449 Timed_Out := Time.Timed_Out (Deadline);
450 exit when Timed_Out;
451 Time.M_Delay (160);
452 Cnt := Cnt + 1;
453 end loop;
454 end Script_Cursors;
455
456 type Cursor_Info is record
457 X_Velo, Y_Velo : Int32;
458 X_Acc, Y_Acc : Int32;
459 Color : Pipe_Index;
460 Size : Cursor_Size;
461 end record;
Nico Huberd8282b62018-06-18 00:44:55 +0200462 function Cursor_Rand (Gen : Rand_P.Generator)
463 return Int32 is (Rand (Gen) mod 51 - 25);
464 Cursor_Infos : array (Pipe_Index) of Cursor_Info;
Nico Hubera63e8332018-02-01 16:41:30 +0100465
466 procedure Move_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200467 (Pipes : in out GMA.Pipe_Configs;
468 Hotplug_List : out Display_Probing.Port_List;
469 Total_Deadline : in Time.T;
470 Time_MS : in Natural;
471 Gen : in Rand_P.Generator)
Nico Hubera63e8332018-02-01 16:41:30 +0100472 is
473 procedure Select_New_Cursor
474 (P : in Pipe_Index;
475 C : in out Cursor_Type;
476 CI : in out Cursor_Info)
477 is
478 Old_C : constant Cursor_Type := C;
479 begin
480 -- change either size or color
Nico Huberd8282b62018-06-18 00:44:55 +0200481 if Rand (Gen) mod 2 = 0 then
Nico Hubera63e8332018-02-01 16:41:30 +0100482 CI.Color := Pipe_Index'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200483 ((Pipe_Index'Pos (CI.Color) + 1 + Rand (Gen) mod 2) mod 3);
Nico Hubera63e8332018-02-01 16:41:30 +0100484 else
485 CI.Size := Cursor_Size'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200486 ((Cursor_Size'Pos (CI.Size) + 1 + Rand (Gen) mod 2) mod 3);
Nico Huber19d13a52026-04-10 16:21:56 +0000487 if Cursor_Width (CI.Size) > Pipes (P).Framebuffer.Width or
488 Cursor_Width (CI.Size) > Pipes (P).Framebuffer.Height
489 then
490 CI.Size := Cursor_64x64;
491 end if;
Nico Hubera63e8332018-02-01 16:41:30 +0100492 end if;
493 C := Cursors (CI.Color) (CI.Size);
494 C.Center_X := Old_C.Center_X;
495 C.Center_Y := Old_C.Center_Y;
496 GMA.Update_Cursor (P, C);
497 end Select_New_Cursor;
498
499 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200500 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100501 Timed_Out : Boolean := False;
502 Cnt : Word32 := 0;
503 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200504 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100505 for Pipe in Pipe_Index loop
506 exit when Pipes (Pipe).Port = GMA.Disabled;
507 Select_New_Cursor (Pipe, Pipes (Pipe).Cursor, Cursor_Infos (Pipe));
508 end loop;
509 loop
510 for Pipe in Pipe_Index loop
511 exit when Pipes (Pipe).Port = GMA.Disabled;
512 declare
513 C : Cursor_Type renames Pipes (Pipe).Cursor;
514 CI : Cursor_Info renames Cursor_Infos (Pipe);
515 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huberc5c767a2018-06-03 01:09:04 +0200516 Width : constant Width_Type := Rotated_Width (FB);
517 Height : constant Height_Type := Rotated_Height (FB);
Nico Hubera63e8332018-02-01 16:41:30 +0100518
519 Update : Boolean := False;
520 begin
521 if Cnt mod 16 = 0 then
Nico Huberd8282b62018-06-18 00:44:55 +0200522 CI.X_Acc := Cursor_Rand (Gen);
523 CI.Y_Acc := Cursor_Rand (Gen);
Nico Hubera63e8332018-02-01 16:41:30 +0100524 end if;
525 CI.X_Velo := CI.X_Velo + CI.X_Acc;
526 CI.Y_Velo := CI.Y_Velo + CI.Y_Acc;
527 C.Center_X := C.Center_X + CI.X_Velo / 100;
528 C.Center_Y := C.Center_Y + CI.Y_Velo / 100;
529 if C.Center_X not in 0 .. Width - 1 then
530 C.Center_X := Int32'Max (0, Int32'Min (Width, C.Center_X));
531 CI.X_Velo := -CI.X_Velo;
532 Update := True;
533 end if;
534 if C.Center_Y not in 0 .. Height - 1 then
535 C.Center_Y := Int32'Max (0, Int32'Min (Height, C.Center_Y));
536 CI.Y_Velo := -CI.Y_Velo;
537 Update := True;
538 end if;
539 if Update then
540 Select_New_Cursor (Pipe, C, CI);
541 else
542 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
543 end if;
544 end;
545 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200546
547 Timed_Out := Time.Timed_Out (HP_Deadline);
548 if Timed_Out then
549 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
550 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
551 if Hotplug_List (Hotplug_List'First) /= Disabled then
552 return;
553 end if;
554 end if;
555
556 Timed_Out := Time.Timed_Out (Total_Deadline);
557 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100558 Timed_Out := Time.Timed_Out (Deadline);
559 exit when Timed_Out;
560 Time.M_Delay (16); -- ~60 fps
561 Cnt := Cnt + 1;
562 end loop;
563 end Move_Cursors;
564
Nico Hubera563ec22019-09-29 19:07:27 +0200565 procedure Run_The_Show (Deadline : Time.T; Gen : Rand_P.Generator)
566 is
567 Timed_Out : Boolean;
568 Hotplug_List : GMA.Display_Probing.Port_List;
569
570 New_Pipes : GMA.Pipe_Configs := Pipes;
571
572 function Rand_Div (Num : Position_Type) return Position_Type is
573 (case Rand (Gen) mod 4 is
574 when 3 => Rand (Gen) mod Num / 3,
575 when 2 => Rand (Gen) mod Num / 2,
576 when 1 => Rand (Gen) mod Num,
577 when others => 0);
578 begin
579 for Pipe in GMA.Pipe_Index loop
580 if Pipes (Pipe).Port /= GMA.Disabled then
581 Test_Screen
582 (Framebuffer => Pipes (Pipe).Framebuffer,
583 Pipe => Pipe);
584 end if;
585 for Size in Cursor_Size loop
586 Draw_Cursor (Pipe, Cursors (Pipe) (Size));
587 end loop;
588 end loop;
589
590 Cursor_Infos :=
591 (others =>
592 (Color => Pipe_Index'Val (Rand (Gen) mod 3),
Nico Huber19d13a52026-04-10 16:21:56 +0000593 Size => Cursor_64x64,
Nico Hubera563ec22019-09-29 19:07:27 +0200594 X_Velo => 3 * Cursor_Rand (Gen),
595 Y_Velo => 3 * Cursor_Rand (Gen),
596 others => Cursor_Rand (Gen)));
597
598 Script_Cursors (Pipes, Hotplug_List, Deadline, Primary_Delay_MS);
599 if Hotplug_List (Hotplug_List'First) /= Disabled then
600 return;
601 end if;
602 Timed_Out := Time.Timed_Out (Deadline);
603 if Timed_Out then
604 return;
605 end if;
606
607 Rand_P.Reset (Gen, Seed);
608 loop
609 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
610 if Hotplug_List (Hotplug_List'First) /= Disabled then
611 return;
612 end if;
613 New_Pipes := Pipes;
614 for Pipe in GMA.Pipe_Index loop
615 exit when Pipes (Pipe).Port = Disabled;
616 declare
617 New_FB : Framebuffer_Type renames
618 New_Pipes (Pipe).Framebuffer;
619 Cursor : Cursor_Type renames New_Pipes (Pipe).Cursor;
620 Width : constant Width_Type :=
621 Pipes (Pipe).Framebuffer.Width;
622 Height : constant Height_Type :=
623 Pipes (Pipe).Framebuffer.Height;
624 begin
625 New_FB.Start_X := Position_Type'Min
Nico Huber19d13a52026-04-10 16:21:56 +0000626 (Width - 64, Rand_Div (Width));
Nico Hubera563ec22019-09-29 19:07:27 +0200627 New_FB.Start_Y := Position_Type'Min
Nico Huber19d13a52026-04-10 16:21:56 +0000628 (Height - 64, Rand_Div (Height));
Nico Hubera563ec22019-09-29 19:07:27 +0200629 New_FB.Width := Width_Type'Max
Nico Huber19d13a52026-04-10 16:21:56 +0000630 (64, Width - New_FB.Start_X - Rand_Div (Width));
Nico Hubera563ec22019-09-29 19:07:27 +0200631 New_FB.Height := Height_Type'Max
Nico Huber19d13a52026-04-10 16:21:56 +0000632 (64, Height - New_FB.Start_Y - Rand_Div (Height));
Nico Hubera563ec22019-09-29 19:07:27 +0200633
634 Cursor.Center_X := Rotated_Width (New_FB) / 2;
635 Cursor.Center_Y := Rotated_Height (New_FB) / 2;
636 GMA.Update_Cursor (Pipe, Cursor);
637 end;
638 end loop;
639 GMA.Dump_Configs (New_Pipes);
640 GMA.Update_Outputs (New_Pipes);
641 Move_Cursors
642 (New_Pipes, Hotplug_List, Deadline, Secondary_Delay_MS, Gen);
643 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
644
645 Timed_Out := Time.Timed_Out (Deadline);
646 exit when Timed_Out;
647 end loop;
648 end Run_The_Show;
649
Nico Huber3b654a02017-07-15 22:27:14 +0200650 procedure Print_Usage
651 is
652 begin
Nico Huber30d89712021-06-11 14:13:24 +0200653 Debug.Put ("Usage: ");
654 Debug.Put (Ada.Command_Line.Command_Name);
655 Debug.Put_Line (" <delay seconds> [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200656 Debug.New_Line;
657 end Print_Usage;
658
Nico Huber1d0abe42017-03-05 14:14:09 +0100659 procedure Main
660 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100661 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200662 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100663 use type Interfaces.C.int;
664
Nico Huberfda2d6e2017-07-09 16:47:52 +0200665 Res_Addr : Word64;
666
Nico Hubera455f0e2018-01-07 11:40:40 +0100667 Delay_MS : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200668 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200669
Nico Huberfda2d6e2017-07-09 16:47:52 +0200670 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100671 Initialized : Boolean;
672
Nico Huberd8282b62018-06-18 00:44:55 +0200673 Gen : Rand_P.Generator;
674
Nico Hubera563ec22019-09-29 19:07:27 +0200675 Deadline : Time.T;
676 Timed_Out : Boolean;
677 Hotplug_List : GMA.Display_Probing.Port_List;
678
Nico Huber1d0abe42017-03-05 14:14:09 +0100679 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
680 pragma Import (C, iopl, "iopl");
681 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200682 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200683 Print_Usage;
684 return;
685 end if;
686
Nico Hubera455f0e2018-01-07 11:40:40 +0100687 Delay_MS := Natural'Value (Ada.Command_Line.Argument (1)) * 1_000;
Nico Huber3b654a02017-07-15 22:27:14 +0200688
Nico Huber88f3c982017-08-28 13:31:38 +0200689 if Ada.Command_Line.Argument_Count >= 2 then
690 declare
691 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
692 begin
693 if Rotation_Degree = "0" then Rotation := No_Rotation;
694 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
695 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
696 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
697 else Print_Usage; return; end if;
698 end;
699 end if;
700
Nico Huber1d0abe42017-03-05 14:14:09 +0100701 if iopl (3) /= 0 then
702 Debug.Put_Line ("Failed to change i/o privilege level.");
703 return;
704 end if;
705
Nico Huberfda2d6e2017-07-09 16:47:52 +0200706 Dev.Initialize (Dev_Init);
707 if not Dev_Init then
708 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100709 return;
710 end if;
711
Nico Huberfda2d6e2017-07-09 16:47:52 +0200712 Dev.Map (Res_Addr, PCI.Res2, WC => True);
713 if Res_Addr = 0 then
714 Debug.Put_Line ("Failed to map PCI resource2.");
715 return;
716 end if;
717 Screen.Set_Base_Address (Res_Addr);
718
Nico Huber1d0abe42017-03-05 14:14:09 +0100719 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200720 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100721 Success => Initialized);
722
723 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200724 Backup_GTT;
725
Nico Hubera563ec22019-09-29 19:07:27 +0200726 Deadline := Time.MS_From_Now (Delay_MS);
727 loop
728 Prepare_Configs (Rotation, Gen);
Nico Huber1d0abe42017-03-05 14:14:09 +0100729
Nico Hubera563ec22019-09-29 19:07:27 +0200730 GMA.Update_Outputs (Pipes);
Nico Huber1d0abe42017-03-05 14:14:09 +0100731
Nico Hubera563ec22019-09-29 19:07:27 +0200732 if not (for all P in Pipe_Index => Pipes (P).Port = Disabled) then
733 for Pipe in GMA.Pipe_Index loop
734 if Pipes (Pipe).Port /= GMA.Disabled then
735 Backup_Screen (Pipes (Pipe).Framebuffer);
736 end if;
Nico Hubera455f0e2018-01-07 11:40:40 +0100737 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200738
Nico Hubera563ec22019-09-29 19:07:27 +0200739 Run_The_Show (Deadline, Gen);
740
741 for Pipe in GMA.Pipe_Index loop
742 if Pipes (Pipe).Port /= GMA.Disabled then
743 Restore_Screen (Pipes (Pipe).Framebuffer);
744 end if;
745 end loop;
746 else
747 loop
748 Time.M_Delay (HP_Delay_MS);
749 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
750 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
751
752 Timed_Out := Time.Timed_Out (Deadline);
753 exit when Timed_Out;
754 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200755 end if;
Nico Hubera563ec22019-09-29 19:07:27 +0200756
757 Timed_Out := Time.Timed_Out (Deadline);
758 exit when Timed_Out;
Nico Huber3b654a02017-07-15 22:27:14 +0200759 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200760
Nico Huber3b654a02017-07-15 22:27:14 +0200761 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100762 end if;
763 end Main;
764
Nico Huberfda2d6e2017-07-09 16:47:52 +0200765end HW.GFX.GMA.GFX_Test;