blob: e5bc393cef576995664108a30a3ffc197acf5b4f [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 Huber1d0abe42017-03-05 14:14:09 +010010with HW.GFX.GMA;
Nico Huber3b654a02017-07-15 22:27:14 +020011with HW.GFX.GMA.Config;
Nico Huber1d0abe42017-03-05 14:14:09 +010012with HW.GFX.GMA.Display_Probing;
13
Nico Huberfda2d6e2017-07-09 16:47:52 +020014package body HW.GFX.GMA.GFX_Test
15is
16 pragma Disable_Atomic_Synchronization;
Nico Huber1d0abe42017-03-05 14:14:09 +010017
Nico Hubera455f0e2018-01-07 11:40:40 +010018 Primary_Delay_MS : constant := 8_000;
19 Secondary_Delay_MS : constant := 4_000;
Nico Hubera563ec22019-09-29 19:07:27 +020020 HP_Delay_MS : constant := 500;
Nico Hubera455f0e2018-01-07 11:40:40 +010021 Seed : constant := 12345;
22
Nico Hubera63e8332018-02-01 16:41:30 +010023 package Rand_P is new Ada.Numerics.Discrete_Random (Natural);
Nico Huberd8282b62018-06-18 00:44:55 +020024 function Rand (Gen : Rand_P.Generator)
25 return Int32 is (Int32 (Rand_P.Random (Gen)));
Nico Hubera455f0e2018-01-07 11:40:40 +010026
Nico Huber5ef4d602017-12-13 13:56:47 +010027 Start_X : constant := 0;
28 Start_Y : constant := 0;
29
Nico Huberfda2d6e2017-07-09 16:47:52 +020030 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010031
Nico Huberc76749d2018-06-09 22:04:55 +020032 type GTT_Entry is record
33 Addr : GTT_Address_Type;
34 Valid : Boolean;
35 end record;
36 GTT_Backup : array (GTT_Range) of GTT_Entry;
Nico Huber3b654a02017-07-15 22:27:14 +020037
38 procedure Backup_GTT
39 is
40 begin
41 for Idx in GTT_Range loop
Nico Huberc76749d2018-06-09 22:04:55 +020042 Read_GTT (GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid, Idx);
Nico Huber3b654a02017-07-15 22:27:14 +020043 end loop;
44 end Backup_GTT;
45
46 procedure Restore_GTT
47 is
48 begin
49 for Idx in GTT_Range loop
Nico Huberc76749d2018-06-09 22:04:55 +020050 Write_GTT (Idx, GTT_Backup (Idx).Addr, GTT_Backup (Idx).Valid);
Nico Huber3b654a02017-07-15 22:27:14 +020051 end loop;
52 end Restore_GTT;
53
Nico Huber1d0abe42017-03-05 14:14:09 +010054 type Pixel_Type is record
55 Red : Byte;
56 Green : Byte;
57 Blue : Byte;
58 Alpha : Byte;
59 end record;
60
61 for Pixel_Type use record
62 Blue at 0 range 0 .. 7;
63 Green at 1 range 0 .. 7;
64 Red at 2 range 0 .. 7;
65 Alpha at 3 range 0 .. 7;
66 end record;
67
Nico Huber244ea7e2017-08-28 11:38:23 +020068 White : constant Pixel_Type := (255, 255, 255, 255);
69 Black : constant Pixel_Type := ( 0, 0, 0, 255);
70 Red : constant Pixel_Type := (255, 0, 0, 255);
71 Green : constant Pixel_Type := ( 0, 255, 0, 255);
72 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
73
Nico Huberfda2d6e2017-07-09 16:47:52 +020074 function Pixel_To_Word (P : Pixel_Type) return Word32
75 with
76 SPARK_Mode => Off
77 is
78 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
79 begin
80 return To_Word (P);
81 end Pixel_To_Word;
82
Nico Huber7bb10c62018-01-12 14:07:44 +010083 Max_W : constant := 4096;
84 Max_H : constant := 2160;
85 FB_Align : constant := 16#0004_0000#;
86 Cursor_Align : constant := 16#0001_0000#;
87 Max_Cursor_Wid : constant := 256;
88 subtype Screen_Index is Natural range 0 .. 3 *
89 (Max_W * Max_H + FB_Align / 4 +
90 3 * Max_Cursor_Wid * Max_Cursor_Wid + Cursor_Align / 4)
91 - 1;
Nico Huberfda2d6e2017-07-09 16:47:52 +020092 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +010093
Nico Huber34be6542017-12-13 09:26:24 +010094 function Screen_Offset (FB : Framebuffer_Type) return Natural is
95 (Natural (Phys_Offset (FB) / 4));
96
Nico Huberfda2d6e2017-07-09 16:47:52 +020097 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +010098
Nico Huber3b654a02017-07-15 22:27:14 +020099 Screen_Backup : Screen_Type;
100
101 procedure Backup_Screen (FB : Framebuffer_Type)
102 is
Nico Huber34be6542017-12-13 09:26:24 +0100103 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200104 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
105 begin
106 for Idx in Screen_Index range First .. Last loop
107 Screen.Read (Screen_Backup (Idx), Idx);
108 end loop;
109 end Backup_Screen;
110
111 procedure Restore_Screen (FB : Framebuffer_Type)
112 is
Nico Huber34be6542017-12-13 09:26:24 +0100113 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200114 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
115 begin
116 for Idx in Screen_Index range First .. Last loop
117 Screen.Write (Idx, Screen_Backup (Idx));
118 end loop;
119 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100120
Nico Huber5ef4d602017-12-13 13:56:47 +0100121 function Drawing_Width (FB : Framebuffer_Type) return Natural is
122 (Natural (FB.Width + 2 * Start_X));
123
124 function Drawing_Height (FB : Framebuffer_Type) return Natural is
125 (Natural (FB.Height + 2 * Start_Y));
126
Nico Huber244ea7e2017-08-28 11:38:23 +0200127 function Corner_Fill
128 (X, Y : Natural;
129 FB : Framebuffer_Type;
130 Pipe : Pipe_Index)
131 return Pixel_Type
132 is
133 Xrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100134 (if X < 32 then X else X - (Drawing_Width (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200135 Yrel : constant Integer :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100136 (if Y < 32 then Y else Y - (Drawing_Height (FB) - 32));
Nico Huber244ea7e2017-08-28 11:38:23 +0200137
138 function Color (Idx : Natural) return Pixel_Type is
139 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
140 when 0 => Blue, when 1 => Black,
141 when 3 => Green, when others => Red);
142 begin
143 return
144 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
145 White
146 elsif Yrel < 16 then
147 (if Xrel < 16 then Color (0) else Color (1))
148 else
149 (if Xrel < 16 then Color (3) else Color (2)));
150 end Corner_Fill;
151
Nico Huber1d0abe42017-03-05 14:14:09 +0100152 function Fill
153 (X, Y : Natural;
154 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200155 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100156 return Pixel_Type
157 is
158 use type HW.Byte;
159
Nico Huber5ef4d602017-12-13 13:56:47 +0100160 Xp : constant Natural := X * 256 / Drawing_Width (Framebuffer);
161 Yp : constant Natural := Y * 256 / Drawing_Height (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100162 Xn : constant Natural := 255 - Xp;
163 Yn : constant Natural := 255 - Yp;
164
165 function Map (X, Y : Natural) return Byte is
166 begin
167 return Byte (X * Y / 255);
168 end Map;
169 begin
170 return
171 (case Pipe is
172 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
173 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
174 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
175 end Fill;
176
177 procedure Test_Screen
178 (Framebuffer : Framebuffer_Type;
179 Pipe : GMA.Pipe_Index)
180 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100181 P : Pixel_Type;
182 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100183 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100184 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200185
186 function Top_Test (X, Y : Natural) return Boolean
187 is
Nico Huber5ef4d602017-12-13 13:56:47 +0100188 C : constant Natural := Drawing_Width (Framebuffer) / 2;
189 S_Y : constant Natural := 3 * (Y - Start_Y) / 2;
Nico Huber9ca69f12017-08-28 14:31:46 +0200190 Left : constant Integer := X - C + S_Y;
191 Right : constant Integer := X - C - S_Y;
192 begin
193 return
Nico Huber5ef4d602017-12-13 13:56:47 +0100194 (Y - Start_Y) < 12 and
Nico Huber9ca69f12017-08-28 14:31:46 +0200195 ((-1 <= Left and Left <= 0) or
196 (0 <= Right and Right <= 1));
197 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100198 begin
Nico Huber5ef4d602017-12-13 13:56:47 +0100199 for Y in 0 .. Drawing_Height (Framebuffer) - 1 loop
Nico Huber1d0abe42017-03-05 14:14:09 +0100200 Offset := Offset_Y;
Nico Huber5ef4d602017-12-13 13:56:47 +0100201 for X in 0 .. Drawing_Width (Framebuffer) - 1 loop
202 if (X < 32 or X >= Drawing_Width (Framebuffer) - 32) and
203 (Y < 32 or Y >= Drawing_Height (Framebuffer) - 32)
Nico Huber244ea7e2017-08-28 11:38:23 +0200204 then
205 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200206 elsif Framebuffer.Rotation /= No_Rotation and then
207 Top_Test (X, Y)
208 then
209 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200210 elsif Y mod 16 = 0 or X mod 16 = 0 then
211 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100212 else
213 P := Fill (X, Y, Framebuffer, Pipe);
214 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200215 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100216 Offset := Offset + 1;
217 end loop;
218 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
219 end loop;
220 end Test_Screen;
221
Nico Huber7bb10c62018-01-12 14:07:44 +0100222 function Donut (X, Y, Max : Cursor_Pos) return Byte
223 is
224 ZZ : constant Int32 := Max * Max * 2;
225 Dist_Center : constant Int32 := ((X * X + Y * Y) * 255) / ZZ;
226 Dist_Circle : constant Int32 := Dist_Center - 20;
227 begin
228 return Byte (255 - Int32'Min (255, 6 * abs Dist_Circle + 64));
229 end Donut;
230
231 procedure Draw_Cursor (Pipe : Pipe_Index; Cursor : Cursor_Type)
232 is
233 use type HW.Byte;
234 Width : constant Width_Type := Cursor_Width (Cursor.Size);
235 Screen_Offset : Natural :=
236 Natural (Shift_Left (Word32 (Cursor.GTT_Offset), 12) / 4);
237 begin
238 if Cursor.Mode /= ARGB_Cursor then
239 return;
240 end if;
241 for Y in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
242 for X in Cursor_Pos range -Width / 2 .. Width / 2 - 1 loop
243 declare
244 D : constant Byte := Donut (X, Y, Width / 2);
245 begin
246 -- Hardware seems to expect pre-multiplied alpha (i.e.
247 -- color components already contain the alpha).
248 Screen.Write
249 (Index => Screen_Offset,
250 Value => Pixel_To_Word (
251 (Red => (if Pipe = Secondary then D / 2 else 0),
252 Green => (if Pipe = Tertiary then D / 2 else 0),
253 Blue => (if Pipe = Primary then D / 2 else 0),
254 Alpha => D)));
255 Screen_Offset := Screen_Offset + 1;
256 end;
257 end loop;
258 end loop;
259 end Draw_Cursor;
260
Nico Huber1d0abe42017-03-05 14:14:09 +0100261 procedure Calc_Framebuffer
262 (FB : out Framebuffer_Type;
263 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200264 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100265 Offset : in out Word32)
266 is
Nico Huberc5c767a2018-06-03 01:09:04 +0200267 Width : constant Width_Type := Mode.H_Visible;
268 Height : constant Height_Type := Mode.V_Visible;
Nico Huber1d0abe42017-03-05 14:14:09 +0100269 begin
270 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200271 if Rotation = Rotated_90 or Rotation = Rotated_270 then
272 FB :=
Nico Huberc5c767a2018-06-03 01:09:04 +0200273 (Width => Height,
274 Height => Width,
Nico Huber5ef4d602017-12-13 13:56:47 +0100275 Start_X => Start_X,
276 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200277 BPC => 8,
Nico Huberc5c767a2018-06-03 01:09:04 +0200278 Stride => Div_Round_Up (Height + 2 * Start_X, 32) * 32,
279 V_Stride => Div_Round_Up (Width + 2 * Start_Y, 32) * 32,
Nico Huber88f3c982017-08-28 13:31:38 +0200280 Tiling => Y_Tiled,
281 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100282 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200283 else
284 FB :=
Nico Huber5ef4d602017-12-13 13:56:47 +0100285 (Width => Width,
286 Height => Height,
287 Start_X => Start_X,
288 Start_Y => Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200289 BPC => 8,
Nico Huber5ef4d602017-12-13 13:56:47 +0100290 Stride => Div_Round_Up (Width + 2 * Start_X, 16) * 16,
291 V_Stride => Height + 2 * Start_Y,
Nico Huber88f3c982017-08-28 13:31:38 +0200292 Tiling => Linear,
293 Rotation => Rotation,
294 Offset => Offset);
295 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100296 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100297 end Calc_Framebuffer;
298
Nico Huber7bb10c62018-01-12 14:07:44 +0100299 type Cursor_Array is array (Cursor_Size) of Cursor_Type;
300 Cursors : array (Pipe_Index) of Cursor_Array;
301
302 procedure Prepare_Cursors
303 (Cursors : out Cursor_Array;
304 Offset : in out Word32)
305 is
306 GMA_Phys_Base : constant PCI.Index := 16#5c#;
307 GMA_Phys_Base_Mask : constant := 16#fff0_0000#;
308
309 Phys_Base : Word32;
310 Success : Boolean;
311 begin
312 Dev.Read32 (Phys_Base, GMA_Phys_Base);
313 Phys_Base := Phys_Base and GMA_Phys_Base_Mask;
314 Success := Phys_Base /= GMA_Phys_Base_Mask and Phys_Base /= 0;
315 if not Success then
316 Debug.Put_Line ("Failed to read stolen memory base.");
317 return;
318 end if;
319
320 for Size in Cursor_Size loop
321 Offset := (Offset + Cursor_Align - 1) and not (Cursor_Align - 1);
322 declare
323 Width : constant Width_Type := Cursor_Width (Size);
324 GTT_End : constant Word32 := Offset + Word32 (Width * Width) * 4;
325 begin
326 Cursors (Size) :=
327 (Mode => ARGB_Cursor,
328 Size => Size,
329 Center_X => Width,
330 Center_Y => Width,
331 GTT_Offset => GTT_Range (Shift_Right (Offset, 12)));
332 while Offset < GTT_End loop
333 GMA.Write_GTT
334 (GTT_Page => GTT_Range (Offset / GTT_Page_Size),
335 Device_Address => GTT_Address_Type (Phys_Base + Offset),
336 Valid => True);
337 Offset := Offset + GTT_Page_Size;
338 end loop;
339 end;
340 end loop;
341 end Prepare_Cursors;
342
Nico Huber3b654a02017-07-15 22:27:14 +0200343 Pipes : GMA.Pipe_Configs;
344
Nico Huberd8282b62018-06-18 00:44:55 +0200345 procedure Prepare_Configs (Rotation : Rotation_Type; Gen : Rand_P.Generator)
Nico Huber1d0abe42017-03-05 14:14:09 +0100346 is
347 use type HW.GFX.GMA.Port_Type;
348
Nico Huberfda2d6e2017-07-09 16:47:52 +0200349 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200350 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100351 begin
352 GMA.Display_Probing.Scan_Ports (Pipes);
353
354 for Pipe in GMA.Pipe_Index loop
355 if Pipes (Pipe).Port /= GMA.Disabled then
356 Calc_Framebuffer
357 (FB => Pipes (Pipe).Framebuffer,
358 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200359 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100360 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200361 GMA.Setup_Default_FB
362 (FB => Pipes (Pipe).Framebuffer,
363 Clear => False,
364 Success => Success);
365 if not Success then
366 Pipes (Pipe).Port := GMA.Disabled;
367 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100368 end if;
Nico Huber7bb10c62018-01-12 14:07:44 +0100369 Prepare_Cursors (Cursors (Pipe), Offset);
Nico Huberd8282b62018-06-18 00:44:55 +0200370 Pipes (Pipe).Cursor := Cursors (Pipe) (Cursor_Size'Val (Rand (Gen) mod 3));
Nico Huber1d0abe42017-03-05 14:14:09 +0100371 end loop;
372
373 GMA.Dump_Configs (Pipes);
374 end Prepare_Configs;
375
Nico Hubera63e8332018-02-01 16:41:30 +0100376 procedure Script_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200377 (Pipes : in out GMA.Pipe_Configs;
378 Hotplug_List : out Display_Probing.Port_List;
379 Total_Deadline : in Time.T;
380 Time_MS : in Natural)
Nico Hubera63e8332018-02-01 16:41:30 +0100381 is
382 type Corner is (UL, UR, LR, LL);
383 type Cursor_Script_Entry is record
384 Rel : Corner;
385 X, Y : Int32;
386 end record;
387 Cursor_Script : constant array (Natural range 0 .. 19) of Cursor_Script_Entry :=
388 ((UL, 16, 16), (UL, 16, 16), (UL, 16, 16), (UL, -32, 0), (UL, 16, 16),
389 (UR, -16, 16), (UR, -16, 16), (UR, -16, 16), (UR, 0, -32), (UR, -16, 16),
390 (LR, -16, -16), (LR, -16, -16), (LR, -16, -16), (LR, 32, 0), (LR, -16, -16),
391 (LL, 16, -16), (LL, 16, -16), (LL, 16, -16), (LL, 0, 32), (LL, 16, -16));
392
393 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200394 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100395 Timed_Out : Boolean := False;
396 Cnt : Word32 := 0;
397 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200398 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100399 loop
400 for Pipe in Pipe_Index loop
401 exit when Pipes (Pipe).Port = GMA.Disabled;
402 declare
403 C : Cursor_Type renames Pipes (Pipe).Cursor;
404 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huberc5c767a2018-06-03 01:09:04 +0200405 Width : constant Width_Type := Rotated_Width (FB);
406 Height : constant Height_Type := Rotated_Height (FB);
Nico Hubera63e8332018-02-01 16:41:30 +0100407 CS : Cursor_Script_Entry renames Cursor_Script
408 (Natural (Cnt) mod (Cursor_Script'Last + 1));
409 begin
410 C.Center_X := CS.X;
411 C.Center_Y := CS.Y;
412 case CS.Rel is
413 when UL => null;
414 when UR => C.Center_X := CS.X + Width;
415 when LR => C.Center_X := CS.X + Width;
416 C.Center_Y := CS.Y + Height;
417 when LL => C.Center_Y := CS.Y + Height;
418 end case;
419 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
420 end;
421 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200422
423 Timed_Out := Time.Timed_Out (HP_Deadline);
424 if Timed_Out then
425 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
426 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
427 if Hotplug_List (Hotplug_List'First) /= Disabled then
428 return;
429 end if;
430 end if;
431
432 Timed_Out := Time.Timed_Out (Total_Deadline);
433 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100434 Timed_Out := Time.Timed_Out (Deadline);
435 exit when Timed_Out;
436 Time.M_Delay (160);
437 Cnt := Cnt + 1;
438 end loop;
439 end Script_Cursors;
440
441 type Cursor_Info is record
442 X_Velo, Y_Velo : Int32;
443 X_Acc, Y_Acc : Int32;
444 Color : Pipe_Index;
445 Size : Cursor_Size;
446 end record;
Nico Huberd8282b62018-06-18 00:44:55 +0200447 function Cursor_Rand (Gen : Rand_P.Generator)
448 return Int32 is (Rand (Gen) mod 51 - 25);
449 Cursor_Infos : array (Pipe_Index) of Cursor_Info;
Nico Hubera63e8332018-02-01 16:41:30 +0100450
451 procedure Move_Cursors
Nico Hubera563ec22019-09-29 19:07:27 +0200452 (Pipes : in out GMA.Pipe_Configs;
453 Hotplug_List : out Display_Probing.Port_List;
454 Total_Deadline : in Time.T;
455 Time_MS : in Natural;
456 Gen : in Rand_P.Generator)
Nico Hubera63e8332018-02-01 16:41:30 +0100457 is
458 procedure Select_New_Cursor
459 (P : in Pipe_Index;
460 C : in out Cursor_Type;
461 CI : in out Cursor_Info)
462 is
463 Old_C : constant Cursor_Type := C;
464 begin
465 -- change either size or color
Nico Huberd8282b62018-06-18 00:44:55 +0200466 if Rand (Gen) mod 2 = 0 then
Nico Hubera63e8332018-02-01 16:41:30 +0100467 CI.Color := Pipe_Index'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200468 ((Pipe_Index'Pos (CI.Color) + 1 + Rand (Gen) mod 2) mod 3);
Nico Hubera63e8332018-02-01 16:41:30 +0100469 else
470 CI.Size := Cursor_Size'Val
Nico Huberd8282b62018-06-18 00:44:55 +0200471 ((Cursor_Size'Pos (CI.Size) + 1 + Rand (Gen) mod 2) mod 3);
Nico Hubera63e8332018-02-01 16:41:30 +0100472 end if;
473 C := Cursors (CI.Color) (CI.Size);
474 C.Center_X := Old_C.Center_X;
475 C.Center_Y := Old_C.Center_Y;
476 GMA.Update_Cursor (P, C);
477 end Select_New_Cursor;
478
479 Deadline : constant Time.T := Time.MS_From_Now (Time_MS);
Nico Hubera563ec22019-09-29 19:07:27 +0200480 HP_Deadline : Time.T := Time.MS_From_Now (HP_Delay_MS);
Nico Hubera63e8332018-02-01 16:41:30 +0100481 Timed_Out : Boolean := False;
482 Cnt : Word32 := 0;
483 begin
Nico Hubera563ec22019-09-29 19:07:27 +0200484 Hotplug_List := (others => Disabled);
Nico Hubera63e8332018-02-01 16:41:30 +0100485 for Pipe in Pipe_Index loop
486 exit when Pipes (Pipe).Port = GMA.Disabled;
487 Select_New_Cursor (Pipe, Pipes (Pipe).Cursor, Cursor_Infos (Pipe));
488 end loop;
489 loop
490 for Pipe in Pipe_Index loop
491 exit when Pipes (Pipe).Port = GMA.Disabled;
492 declare
493 C : Cursor_Type renames Pipes (Pipe).Cursor;
494 CI : Cursor_Info renames Cursor_Infos (Pipe);
495 FB : Framebuffer_Type renames Pipes (Pipe).Framebuffer;
Nico Huberc5c767a2018-06-03 01:09:04 +0200496 Width : constant Width_Type := Rotated_Width (FB);
497 Height : constant Height_Type := Rotated_Height (FB);
Nico Hubera63e8332018-02-01 16:41:30 +0100498
499 Update : Boolean := False;
500 begin
501 if Cnt mod 16 = 0 then
Nico Huberd8282b62018-06-18 00:44:55 +0200502 CI.X_Acc := Cursor_Rand (Gen);
503 CI.Y_Acc := Cursor_Rand (Gen);
Nico Hubera63e8332018-02-01 16:41:30 +0100504 end if;
505 CI.X_Velo := CI.X_Velo + CI.X_Acc;
506 CI.Y_Velo := CI.Y_Velo + CI.Y_Acc;
507 C.Center_X := C.Center_X + CI.X_Velo / 100;
508 C.Center_Y := C.Center_Y + CI.Y_Velo / 100;
509 if C.Center_X not in 0 .. Width - 1 then
510 C.Center_X := Int32'Max (0, Int32'Min (Width, C.Center_X));
511 CI.X_Velo := -CI.X_Velo;
512 Update := True;
513 end if;
514 if C.Center_Y not in 0 .. Height - 1 then
515 C.Center_Y := Int32'Max (0, Int32'Min (Height, C.Center_Y));
516 CI.Y_Velo := -CI.Y_Velo;
517 Update := True;
518 end if;
519 if Update then
520 Select_New_Cursor (Pipe, C, CI);
521 else
522 GMA.Place_Cursor (Pipe, C.Center_X, C.Center_Y);
523 end if;
524 end;
525 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200526
527 Timed_Out := Time.Timed_Out (HP_Deadline);
528 if Timed_Out then
529 HP_Deadline := Time.MS_From_Now (HP_Delay_MS);
530 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
531 if Hotplug_List (Hotplug_List'First) /= Disabled then
532 return;
533 end if;
534 end if;
535
536 Timed_Out := Time.Timed_Out (Total_Deadline);
537 exit when Timed_Out;
Nico Hubera63e8332018-02-01 16:41:30 +0100538 Timed_Out := Time.Timed_Out (Deadline);
539 exit when Timed_Out;
540 Time.M_Delay (16); -- ~60 fps
541 Cnt := Cnt + 1;
542 end loop;
543 end Move_Cursors;
544
Nico Hubera563ec22019-09-29 19:07:27 +0200545 procedure Run_The_Show (Deadline : Time.T; Gen : Rand_P.Generator)
546 is
547 Timed_Out : Boolean;
548 Hotplug_List : GMA.Display_Probing.Port_List;
549
550 New_Pipes : GMA.Pipe_Configs := Pipes;
551
552 function Rand_Div (Num : Position_Type) return Position_Type is
553 (case Rand (Gen) mod 4 is
554 when 3 => Rand (Gen) mod Num / 3,
555 when 2 => Rand (Gen) mod Num / 2,
556 when 1 => Rand (Gen) mod Num,
557 when others => 0);
558 begin
559 for Pipe in GMA.Pipe_Index loop
560 if Pipes (Pipe).Port /= GMA.Disabled then
561 Test_Screen
562 (Framebuffer => Pipes (Pipe).Framebuffer,
563 Pipe => Pipe);
564 end if;
565 for Size in Cursor_Size loop
566 Draw_Cursor (Pipe, Cursors (Pipe) (Size));
567 end loop;
568 end loop;
569
570 Cursor_Infos :=
571 (others =>
572 (Color => Pipe_Index'Val (Rand (Gen) mod 3),
573 Size => Cursor_Size'Val (Rand (Gen) mod 3),
574 X_Velo => 3 * Cursor_Rand (Gen),
575 Y_Velo => 3 * Cursor_Rand (Gen),
576 others => Cursor_Rand (Gen)));
577
578 Script_Cursors (Pipes, Hotplug_List, Deadline, Primary_Delay_MS);
579 if Hotplug_List (Hotplug_List'First) /= Disabled then
580 return;
581 end if;
582 Timed_Out := Time.Timed_Out (Deadline);
583 if Timed_Out then
584 return;
585 end if;
586
587 Rand_P.Reset (Gen, Seed);
588 loop
589 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
590 if Hotplug_List (Hotplug_List'First) /= Disabled then
591 return;
592 end if;
593 New_Pipes := Pipes;
594 for Pipe in GMA.Pipe_Index loop
595 exit when Pipes (Pipe).Port = Disabled;
596 declare
597 New_FB : Framebuffer_Type renames
598 New_Pipes (Pipe).Framebuffer;
599 Cursor : Cursor_Type renames New_Pipes (Pipe).Cursor;
600 Width : constant Width_Type :=
601 Pipes (Pipe).Framebuffer.Width;
602 Height : constant Height_Type :=
603 Pipes (Pipe).Framebuffer.Height;
604 begin
605 New_FB.Start_X := Position_Type'Min
606 (Width - 320, Rand_Div (Width));
607 New_FB.Start_Y := Position_Type'Min
608 (Height - 320, Rand_Div (Height));
609 New_FB.Width := Width_Type'Max
610 (320, Width - New_FB.Start_X - Rand_Div (Width));
611 New_FB.Height := Height_Type'Max
612 (320, Height - New_FB.Start_Y - Rand_Div (Height));
613
614 Cursor.Center_X := Rotated_Width (New_FB) / 2;
615 Cursor.Center_Y := Rotated_Height (New_FB) / 2;
616 GMA.Update_Cursor (Pipe, Cursor);
617 end;
618 end loop;
619 GMA.Dump_Configs (New_Pipes);
620 GMA.Update_Outputs (New_Pipes);
621 Move_Cursors
622 (New_Pipes, Hotplug_List, Deadline, Secondary_Delay_MS, Gen);
623 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
624
625 Timed_Out := Time.Timed_Out (Deadline);
626 exit when Timed_Out;
627 end loop;
628 end Run_The_Show;
629
Nico Huber3b654a02017-07-15 22:27:14 +0200630 procedure Print_Usage
631 is
632 begin
633 Debug.Put_Line
Nico Huber88f3c982017-08-28 13:31:38 +0200634 ("Usage: " & Ada.Command_Line.Command_Name &
635 " <delay seconds>" &
636 " [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200637 Debug.New_Line;
638 end Print_Usage;
639
Nico Huber1d0abe42017-03-05 14:14:09 +0100640 procedure Main
641 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100642 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200643 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100644 use type Interfaces.C.int;
645
Nico Huberfda2d6e2017-07-09 16:47:52 +0200646 Res_Addr : Word64;
647
Nico Hubera455f0e2018-01-07 11:40:40 +0100648 Delay_MS : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200649 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200650
Nico Huberfda2d6e2017-07-09 16:47:52 +0200651 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100652 Initialized : Boolean;
653
Nico Huberd8282b62018-06-18 00:44:55 +0200654 Gen : Rand_P.Generator;
655
Nico Hubera563ec22019-09-29 19:07:27 +0200656 Deadline : Time.T;
657 Timed_Out : Boolean;
658 Hotplug_List : GMA.Display_Probing.Port_List;
659
Nico Huber1d0abe42017-03-05 14:14:09 +0100660 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
661 pragma Import (C, iopl, "iopl");
662 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200663 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200664 Print_Usage;
665 return;
666 end if;
667
Nico Hubera455f0e2018-01-07 11:40:40 +0100668 Delay_MS := Natural'Value (Ada.Command_Line.Argument (1)) * 1_000;
Nico Huber3b654a02017-07-15 22:27:14 +0200669
Nico Huber88f3c982017-08-28 13:31:38 +0200670 if Ada.Command_Line.Argument_Count >= 2 then
671 declare
672 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
673 begin
674 if Rotation_Degree = "0" then Rotation := No_Rotation;
675 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
676 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
677 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
678 else Print_Usage; return; end if;
679 end;
680 end if;
681
Nico Huber1d0abe42017-03-05 14:14:09 +0100682 if iopl (3) /= 0 then
683 Debug.Put_Line ("Failed to change i/o privilege level.");
684 return;
685 end if;
686
Nico Huberfda2d6e2017-07-09 16:47:52 +0200687 Dev.Initialize (Dev_Init);
688 if not Dev_Init then
689 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100690 return;
691 end if;
692
Nico Huberfda2d6e2017-07-09 16:47:52 +0200693 Dev.Map (Res_Addr, PCI.Res2, WC => True);
694 if Res_Addr = 0 then
695 Debug.Put_Line ("Failed to map PCI resource2.");
696 return;
697 end if;
698 Screen.Set_Base_Address (Res_Addr);
699
Nico Huber1d0abe42017-03-05 14:14:09 +0100700 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200701 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100702 Success => Initialized);
703
704 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200705 Backup_GTT;
706
Nico Hubera563ec22019-09-29 19:07:27 +0200707 Deadline := Time.MS_From_Now (Delay_MS);
708 loop
709 Prepare_Configs (Rotation, Gen);
Nico Huber1d0abe42017-03-05 14:14:09 +0100710
Nico Hubera563ec22019-09-29 19:07:27 +0200711 GMA.Update_Outputs (Pipes);
Nico Huber1d0abe42017-03-05 14:14:09 +0100712
Nico Hubera563ec22019-09-29 19:07:27 +0200713 if not (for all P in Pipe_Index => Pipes (P).Port = Disabled) then
714 for Pipe in GMA.Pipe_Index loop
715 if Pipes (Pipe).Port /= GMA.Disabled then
716 Backup_Screen (Pipes (Pipe).Framebuffer);
717 end if;
Nico Hubera455f0e2018-01-07 11:40:40 +0100718 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200719
Nico Hubera563ec22019-09-29 19:07:27 +0200720 Run_The_Show (Deadline, Gen);
721
722 for Pipe in GMA.Pipe_Index loop
723 if Pipes (Pipe).Port /= GMA.Disabled then
724 Restore_Screen (Pipes (Pipe).Framebuffer);
725 end if;
726 end loop;
727 else
728 loop
729 Time.M_Delay (HP_Delay_MS);
730 GMA.Display_Probing.Hotplug_Events (Hotplug_List);
731 exit when Hotplug_List (Hotplug_List'First) /= Disabled;
732
733 Timed_Out := Time.Timed_Out (Deadline);
734 exit when Timed_Out;
735 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200736 end if;
Nico Hubera563ec22019-09-29 19:07:27 +0200737
738 Timed_Out := Time.Timed_Out (Deadline);
739 exit when Timed_Out;
Nico Huber3b654a02017-07-15 22:27:14 +0200740 end loop;
Nico Hubera563ec22019-09-29 19:07:27 +0200741
Nico Huber3b654a02017-07-15 22:27:14 +0200742 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100743 end if;
744 end Main;
745
Nico Huberfda2d6e2017-07-09 16:47:52 +0200746end HW.GFX.GMA.GFX_Test;