blob: cb09c630173c826d551fdea45e140a0838935588 [file] [log] [blame]
Nico Huberfda2d6e2017-07-09 16:47:52 +02001with Ada.Unchecked_Conversion;
Nico Huber1d0abe42017-03-05 14:14:09 +01002with Ada.Command_Line;
3with Interfaces.C;
4
Nico Huber3b654a02017-07-15 22:27:14 +02005with HW.Time;
Nico Huber1d0abe42017-03-05 14:14:09 +01006with HW.Debug;
Nico Huberfda2d6e2017-07-09 16:47:52 +02007with HW.PCI.Dev;
8with HW.MMIO_Range;
Nico Huber1d0abe42017-03-05 14:14:09 +01009with HW.GFX.GMA;
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 Huberfda2d6e2017-07-09 16:47:52 +020017 package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
Nico Huber1d0abe42017-03-05 14:14:09 +010018
Nico Huber3b654a02017-07-15 22:27:14 +020019 type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
20 type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
21 package GTT is new MMIO_Range
22 (Base_Addr => 0,
23 Element_T => GTT_PTE_Type,
24 Index_T => GTT_Range,
25 Array_T => GTT_Registers_Type);
26
27 GTT_Backup : GTT_Registers_Type;
28
29 procedure Backup_GTT
30 is
31 begin
32 for Idx in GTT_Range loop
33 GTT.Read (GTT_Backup (Idx), Idx);
34 end loop;
35 end Backup_GTT;
36
37 procedure Restore_GTT
38 is
39 begin
40 for Idx in GTT_Range loop
41 GTT.Write (Idx, GTT_Backup (Idx));
42 end loop;
43 end Restore_GTT;
44
Nico Huber1d0abe42017-03-05 14:14:09 +010045 type Pixel_Type is record
46 Red : Byte;
47 Green : Byte;
48 Blue : Byte;
49 Alpha : Byte;
50 end record;
51
52 for Pixel_Type use record
53 Blue at 0 range 0 .. 7;
54 Green at 1 range 0 .. 7;
55 Red at 2 range 0 .. 7;
56 Alpha at 3 range 0 .. 7;
57 end record;
58
Nico Huber244ea7e2017-08-28 11:38:23 +020059 White : constant Pixel_Type := (255, 255, 255, 255);
60 Black : constant Pixel_Type := ( 0, 0, 0, 255);
61 Red : constant Pixel_Type := (255, 0, 0, 255);
62 Green : constant Pixel_Type := ( 0, 255, 0, 255);
63 Blue : constant Pixel_Type := ( 0, 0, 255, 255);
64
Nico Huberfda2d6e2017-07-09 16:47:52 +020065 function Pixel_To_Word (P : Pixel_Type) return Word32
66 with
67 SPARK_Mode => Off
68 is
69 function To_Word is new Ada.Unchecked_Conversion (Pixel_Type, Word32);
70 begin
71 return To_Word (P);
72 end Pixel_To_Word;
73
Nico Huber1d0abe42017-03-05 14:14:09 +010074 Max_W : constant := 4096;
75 Max_H : constant := 2160;
76 FB_Align : constant := 16#0004_0000#;
Nico Huberfda2d6e2017-07-09 16:47:52 +020077 subtype Screen_Index is Natural range
78 0 .. 3 * (Max_W * Max_H + FB_Align / 4) - 1;
79 type Screen_Type is array (Screen_Index) of Word32;
Nico Huber1d0abe42017-03-05 14:14:09 +010080
Nico Huber34be6542017-12-13 09:26:24 +010081 function Screen_Offset (FB : Framebuffer_Type) return Natural is
82 (Natural (Phys_Offset (FB) / 4));
83
Nico Huberfda2d6e2017-07-09 16:47:52 +020084 package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
Nico Huber1d0abe42017-03-05 14:14:09 +010085
Nico Huber3b654a02017-07-15 22:27:14 +020086 Screen_Backup : Screen_Type;
87
88 procedure Backup_Screen (FB : Framebuffer_Type)
89 is
Nico Huber34be6542017-12-13 09:26:24 +010090 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +020091 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
92 begin
93 for Idx in Screen_Index range First .. Last loop
94 Screen.Read (Screen_Backup (Idx), Idx);
95 end loop;
96 end Backup_Screen;
97
98 procedure Restore_Screen (FB : Framebuffer_Type)
99 is
Nico Huber34be6542017-12-13 09:26:24 +0100100 First : constant Screen_Index := Screen_Offset (FB);
Nico Huber3b654a02017-07-15 22:27:14 +0200101 Last : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
102 begin
103 for Idx in Screen_Index range First .. Last loop
104 Screen.Write (Idx, Screen_Backup (Idx));
105 end loop;
106 end Restore_Screen;
Nico Huber1d0abe42017-03-05 14:14:09 +0100107
Nico Huber244ea7e2017-08-28 11:38:23 +0200108 function Corner_Fill
109 (X, Y : Natural;
110 FB : Framebuffer_Type;
111 Pipe : Pipe_Index)
112 return Pixel_Type
113 is
114 Xrel : constant Integer :=
115 (if X < 32 then X else X - (Natural (FB.Width) - 32));
116 Yrel : constant Integer :=
117 (if Y < 32 then Y else Y - (Natural (FB.Height) - 32));
118
119 function Color (Idx : Natural) return Pixel_Type is
120 (case (Idx + Pipe_Index'Pos (Pipe)) mod 4 is
121 when 0 => Blue, when 1 => Black,
122 when 3 => Green, when others => Red);
123 begin
124 return
125 (if Xrel mod 16 = 0 or Xrel = 31 or Yrel mod 16 = 0 or Yrel = 31 then
126 White
127 elsif Yrel < 16 then
128 (if Xrel < 16 then Color (0) else Color (1))
129 else
130 (if Xrel < 16 then Color (3) else Color (2)));
131 end Corner_Fill;
132
Nico Huber1d0abe42017-03-05 14:14:09 +0100133 function Fill
134 (X, Y : Natural;
135 Framebuffer : Framebuffer_Type;
Nico Huber244ea7e2017-08-28 11:38:23 +0200136 Pipe : Pipe_Index)
Nico Huber1d0abe42017-03-05 14:14:09 +0100137 return Pixel_Type
138 is
139 use type HW.Byte;
140
141 Xp : constant Natural := X * 256 / Natural (Framebuffer.Width);
142 Yp : constant Natural := Y * 256 / Natural (Framebuffer.Height);
143 Xn : constant Natural := 255 - Xp;
144 Yn : constant Natural := 255 - Yp;
145
146 function Map (X, Y : Natural) return Byte is
147 begin
148 return Byte (X * Y / 255);
149 end Map;
150 begin
151 return
152 (case Pipe is
153 when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
154 when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
155 when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
156 end Fill;
157
158 procedure Test_Screen
159 (Framebuffer : Framebuffer_Type;
160 Pipe : GMA.Pipe_Index)
161 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100162 P : Pixel_Type;
163 -- We have pixel offset wheras the framebuffer has a byte offset
Nico Huber34be6542017-12-13 09:26:24 +0100164 Offset_Y : Natural := Screen_Offset (Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100165 Offset : Natural;
Nico Huber9ca69f12017-08-28 14:31:46 +0200166
167 function Top_Test (X, Y : Natural) return Boolean
168 is
169 C : constant Natural := Natural (Framebuffer.Width) / 2;
170 S_Y : constant Natural := 3 * Y / 2;
171 Left : constant Integer := X - C + S_Y;
172 Right : constant Integer := X - C - S_Y;
173 begin
174 return
175 Y < 12 and
176 ((-1 <= Left and Left <= 0) or
177 (0 <= Right and Right <= 1));
178 end Top_Test;
Nico Huber1d0abe42017-03-05 14:14:09 +0100179 begin
180 for Y in 0 .. Natural (Framebuffer.Height) - 1 loop
181 Offset := Offset_Y;
182 for X in 0 .. Natural (Framebuffer.Width) - 1 loop
Nico Huber244ea7e2017-08-28 11:38:23 +0200183 if (X < 32 or X >= Natural (Framebuffer.Width) - 32) and
184 (Y < 32 or Y >= Natural (Framebuffer.Height) - 32)
185 then
186 P := Corner_Fill (X, Y, Framebuffer, Pipe);
Nico Huber9ca69f12017-08-28 14:31:46 +0200187 elsif Framebuffer.Rotation /= No_Rotation and then
188 Top_Test (X, Y)
189 then
190 P := White;
Nico Huber244ea7e2017-08-28 11:38:23 +0200191 elsif Y mod 16 = 0 or X mod 16 = 0 then
192 P := Black;
Nico Huber1d0abe42017-03-05 14:14:09 +0100193 else
194 P := Fill (X, Y, Framebuffer, Pipe);
195 end if;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200196 Screen.Write (Offset, Pixel_To_Word (P));
Nico Huber1d0abe42017-03-05 14:14:09 +0100197 Offset := Offset + 1;
198 end loop;
199 Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
200 end loop;
201 end Test_Screen;
202
203 procedure Calc_Framebuffer
204 (FB : out Framebuffer_Type;
205 Mode : in Mode_Type;
Nico Huber88f3c982017-08-28 13:31:38 +0200206 Rotation : in Rotation_Type;
Nico Huber1d0abe42017-03-05 14:14:09 +0100207 Offset : in out Word32)
208 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100209 begin
210 Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
Nico Huber88f3c982017-08-28 13:31:38 +0200211 if Rotation = Rotated_90 or Rotation = Rotated_270 then
212 FB :=
213 (Width => Width_Type (Mode.V_Visible),
214 Height => Height_Type (Mode.H_Visible),
215 BPC => 8,
216 Stride => Div_Round_Up (Pos32 (Mode.V_Visible), 32) * 32,
217 V_Stride => Div_Round_Up (Pos32 (Mode.H_Visible), 32) * 32,
218 Tiling => Y_Tiled,
219 Rotation => Rotation,
Nico Huber34be6542017-12-13 09:26:24 +0100220 Offset => Offset + Word32 (GTT_Rotation_Offset) * GTT_Page_Size);
Nico Huber88f3c982017-08-28 13:31:38 +0200221 else
222 FB :=
223 (Width => Width_Type (Mode.H_Visible),
224 Height => Width_Type (Mode.V_Visible),
225 BPC => 8,
226 Stride => Div_Round_Up (Pos32 (Mode.H_Visible), 16) * 16,
227 V_Stride => Height_Type (Mode.V_Visible),
228 Tiling => Linear,
229 Rotation => Rotation,
230 Offset => Offset);
231 end if;
Nico Huberb7470492017-11-30 14:48:35 +0100232 Offset := Offset + Word32 (FB_Size (FB));
Nico Huber1d0abe42017-03-05 14:14:09 +0100233 end Calc_Framebuffer;
234
Nico Huber3b654a02017-07-15 22:27:14 +0200235 Pipes : GMA.Pipe_Configs;
236
Nico Huber88f3c982017-08-28 13:31:38 +0200237 procedure Prepare_Configs (Rotation : Rotation_Type)
Nico Huber1d0abe42017-03-05 14:14:09 +0100238 is
239 use type HW.GFX.GMA.Port_Type;
240
Nico Huberfda2d6e2017-07-09 16:47:52 +0200241 Offset : Word32 := 0;
Nico Huber3b654a02017-07-15 22:27:14 +0200242 Success : Boolean;
Nico Huber1d0abe42017-03-05 14:14:09 +0100243 begin
244 GMA.Display_Probing.Scan_Ports (Pipes);
245
246 for Pipe in GMA.Pipe_Index loop
247 if Pipes (Pipe).Port /= GMA.Disabled then
248 Calc_Framebuffer
249 (FB => Pipes (Pipe).Framebuffer,
250 Mode => Pipes (Pipe).Mode,
Nico Huber88f3c982017-08-28 13:31:38 +0200251 Rotation => Rotation,
Nico Huber1d0abe42017-03-05 14:14:09 +0100252 Offset => Offset);
Nico Huber3b654a02017-07-15 22:27:14 +0200253 GMA.Setup_Default_FB
254 (FB => Pipes (Pipe).Framebuffer,
255 Clear => False,
256 Success => Success);
257 if not Success then
258 Pipes (Pipe).Port := GMA.Disabled;
259 end if;
Nico Huber1d0abe42017-03-05 14:14:09 +0100260 end if;
261 end loop;
262
263 GMA.Dump_Configs (Pipes);
264 end Prepare_Configs;
265
Nico Huber3b654a02017-07-15 22:27:14 +0200266 procedure Print_Usage
267 is
268 begin
269 Debug.Put_Line
Nico Huber88f3c982017-08-28 13:31:38 +0200270 ("Usage: " & Ada.Command_Line.Command_Name &
271 " <delay seconds>" &
272 " [(0|90|180|270)]");
Nico Huber3b654a02017-07-15 22:27:14 +0200273 Debug.New_Line;
274 end Print_Usage;
275
Nico Huber1d0abe42017-03-05 14:14:09 +0100276 procedure Main
277 is
Nico Huber1d0abe42017-03-05 14:14:09 +0100278 use type HW.GFX.GMA.Port_Type;
Nico Huberfda2d6e2017-07-09 16:47:52 +0200279 use type HW.Word64;
Nico Huber1d0abe42017-03-05 14:14:09 +0100280 use type Interfaces.C.int;
281
Nico Huberfda2d6e2017-07-09 16:47:52 +0200282 Res_Addr : Word64;
283
Nico Huber3b654a02017-07-15 22:27:14 +0200284 Delay_S : Natural;
Nico Huber88f3c982017-08-28 13:31:38 +0200285 Rotation : Rotation_Type := No_Rotation;
Nico Huber3b654a02017-07-15 22:27:14 +0200286
Nico Huberfda2d6e2017-07-09 16:47:52 +0200287 Dev_Init,
Nico Huber1d0abe42017-03-05 14:14:09 +0100288 Initialized : Boolean;
289
290 function iopl (level : Interfaces.C.int) return Interfaces.C.int;
291 pragma Import (C, iopl, "iopl");
292 begin
Nico Huber88f3c982017-08-28 13:31:38 +0200293 if Ada.Command_Line.Argument_Count < 1 then
Nico Huber3b654a02017-07-15 22:27:14 +0200294 Print_Usage;
295 return;
296 end if;
297
298 Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
299
Nico Huber88f3c982017-08-28 13:31:38 +0200300 if Ada.Command_Line.Argument_Count >= 2 then
301 declare
302 Rotation_Degree : constant String := Ada.Command_Line.Argument (2);
303 begin
304 if Rotation_Degree = "0" then Rotation := No_Rotation;
305 elsif Rotation_Degree = "90" then Rotation := Rotated_90;
306 elsif Rotation_Degree = "180" then Rotation := Rotated_180;
307 elsif Rotation_Degree = "270" then Rotation := Rotated_270;
308 else Print_Usage; return; end if;
309 end;
310 end if;
311
Nico Huber1d0abe42017-03-05 14:14:09 +0100312 if iopl (3) /= 0 then
313 Debug.Put_Line ("Failed to change i/o privilege level.");
314 return;
315 end if;
316
Nico Huberfda2d6e2017-07-09 16:47:52 +0200317 Dev.Initialize (Dev_Init);
318 if not Dev_Init then
319 Debug.Put_Line ("Failed to map PCI config.");
Nico Huber1d0abe42017-03-05 14:14:09 +0100320 return;
321 end if;
322
Nico Huber3b654a02017-07-15 22:27:14 +0200323 Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
324 if Res_Addr = 0 then
325 Debug.Put_Line ("Failed to map PCI resource0.");
326 return;
327 end if;
328 GTT.Set_Base_Address (Res_Addr);
329
Nico Huberfda2d6e2017-07-09 16:47:52 +0200330 Dev.Map (Res_Addr, PCI.Res2, WC => True);
331 if Res_Addr = 0 then
332 Debug.Put_Line ("Failed to map PCI resource2.");
333 return;
334 end if;
335 Screen.Set_Base_Address (Res_Addr);
336
Nico Huber1d0abe42017-03-05 14:14:09 +0100337 GMA.Initialize
Nico Huber2b6f6992017-07-09 18:11:34 +0200338 (Clean_State => True,
Nico Huber1d0abe42017-03-05 14:14:09 +0100339 Success => Initialized);
340
341 if Initialized then
Nico Huber3b654a02017-07-15 22:27:14 +0200342 Backup_GTT;
343
Nico Huber88f3c982017-08-28 13:31:38 +0200344 Prepare_Configs (Rotation);
Nico Huber1d0abe42017-03-05 14:14:09 +0100345
346 GMA.Update_Outputs (Pipes);
347
348 for Pipe in GMA.Pipe_Index loop
349 if Pipes (Pipe).Port /= GMA.Disabled then
Nico Huber3b654a02017-07-15 22:27:14 +0200350 Backup_Screen (Pipes (Pipe).Framebuffer);
Nico Huber1d0abe42017-03-05 14:14:09 +0100351 Test_Screen
352 (Framebuffer => Pipes (Pipe).Framebuffer,
353 Pipe => Pipe);
354 end if;
355 end loop;
Nico Huber3b654a02017-07-15 22:27:14 +0200356
357 Time.M_Delay (Delay_S * 1_000);
358
359 for Pipe in GMA.Pipe_Index loop
360 if Pipes (Pipe).Port /= GMA.Disabled then
361 Restore_Screen (Pipes (Pipe).Framebuffer);
362 end if;
363 end loop;
364 Restore_GTT;
Nico Huber1d0abe42017-03-05 14:14:09 +0100365 end if;
366 end Main;
367
Nico Huberfda2d6e2017-07-09 16:47:52 +0200368end HW.GFX.GMA.GFX_Test;