Add linux user-space app `gfx_test`
This simple program scans all ports and configures pipes for the first
three available displays. It presumes that there is enough framebuffer
mapped for all pipes and fills it with test images.
The only command line parameter ist the path to a PCI-device node in
sysfs. On exit, the hardware is left in the configured state. So the
user has to make sure, that he either can work without the gfx hard-
ware or has another driver to restore a working state.
Change-Id: I2144300589e113e711db7959aa68fa96c3844568
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/18786
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Tested-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/gfxtest/gfx_test.adb b/gfxtest/gfx_test.adb
new file mode 100644
index 0000000..e7fb80c
--- /dev/null
+++ b/gfxtest/gfx_test.adb
@@ -0,0 +1,215 @@
+with System.Storage_Elements;
+with Ada.Command_Line;
+with Interfaces.C;
+
+with HW.File;
+with HW.Debug;
+with HW.GFX.GMA;
+with HW.GFX.GMA.Display_Probing;
+
+use HW;
+use HW.GFX;
+
+package body GFX_Test is
+
+ MMIO_Size : constant := 2 * 1024 * 1024;
+ subtype MMIO_Range is Natural range 0 .. MMIO_Size - 1;
+ subtype MMIO_Buffer is Buffer (MMIO_Range);
+ MMIO_Dummy : MMIO_Buffer
+ with
+ Alignment => 16#1000#,
+ Volatile;
+
+ type Pixel_Type is record
+ Red : Byte;
+ Green : Byte;
+ Blue : Byte;
+ Alpha : Byte;
+ end record;
+
+ for Pixel_Type use record
+ Blue at 0 range 0 .. 7;
+ Green at 1 range 0 .. 7;
+ Red at 2 range 0 .. 7;
+ Alpha at 3 range 0 .. 7;
+ end record;
+
+ Max_W : constant := 4096;
+ Max_H : constant := 2160;
+ FB_Align : constant := 16#0004_0000#;
+ type Screen_Type is
+ array (0 .. 3 * (Max_W * Max_H + FB_Align / 4) - 1) of Pixel_Type;
+
+ Screen : Screen_Type
+ with
+ Alignment => 16#1000#,
+ Volatile;
+
+ Pipes : GMA.Pipe_Configs;
+
+ function Fill
+ (X, Y : Natural;
+ Framebuffer : Framebuffer_Type;
+ Pipe : GMA.Pipe_Index)
+ return Pixel_Type
+ is
+ use type HW.Byte;
+
+ Xp : constant Natural := X * 256 / Natural (Framebuffer.Width);
+ Yp : constant Natural := Y * 256 / Natural (Framebuffer.Height);
+ Xn : constant Natural := 255 - Xp;
+ Yn : constant Natural := 255 - Yp;
+
+ function Map (X, Y : Natural) return Byte is
+ begin
+ return Byte (X * Y / 255);
+ end Map;
+ begin
+ return
+ (case Pipe is
+ when GMA.Primary => (Map (Xn, Yn), Map (Xp, Yn), Map (Xp, Yp), 255),
+ when GMA.Secondary => (Map (Xn, Yp), Map (Xn, Yn), Map (Xp, Yn), 255),
+ when GMA.Tertiary => (Map (Xp, Yp), Map (Xn, Yp), Map (Xn, Yn), 255));
+ end Fill;
+
+ procedure Test_Screen
+ (Framebuffer : Framebuffer_Type;
+ Pipe : GMA.Pipe_Index)
+ is
+ use type HW.Word32;
+ P : Pixel_Type;
+ -- We have pixel offset wheras the framebuffer has a byte offset
+ Offset_Y : Natural := Natural (Framebuffer.Offset / 4);
+ Offset : Natural;
+ begin
+ for Y in 0 .. Natural (Framebuffer.Height) - 1 loop
+ Offset := Offset_Y;
+ for X in 0 .. Natural (Framebuffer.Width) - 1 loop
+ if Y mod 16 = 0 or X mod 16 = 0 then
+ P := (0, 0, 0, 0);
+ else
+ P := Fill (X, Y, Framebuffer, Pipe);
+ end if;
+ Screen (Offset) := P;
+ Offset := Offset + 1;
+ end loop;
+ Offset_Y := Offset_Y + Natural (Framebuffer.Stride);
+ end loop;
+ end Test_Screen;
+
+ procedure Calc_Framebuffer
+ (FB : out Framebuffer_Type;
+ Mode : in Mode_Type;
+ Offset : in out Word32)
+ is
+ use type HW.Int32;
+ use type HW.Word32;
+ begin
+ Offset := (Offset + FB_Align - 1) and not (FB_Align - 1);
+ FB :=
+ (Width => Width_Type (Mode.H_Visible),
+ Height => Height_Type (Mode.V_Visible),
+ BPC => 8,
+ Stride => Width_Type ((Word32 (Mode.H_Visible) + 15) and not 15),
+ Offset => Offset);
+ Offset := Offset + Word32 (FB.Stride * FB.Height * 4);
+ end Calc_Framebuffer;
+
+ procedure Prepare_Configs
+ is
+ use type HW.GFX.GMA.Port_Type;
+
+ Offset : HW.Word32 := 0;
+ begin
+ GMA.Display_Probing.Scan_Ports (Pipes);
+
+ for Pipe in GMA.Pipe_Index loop
+ if Pipes (Pipe).Port /= GMA.Disabled then
+ Calc_Framebuffer
+ (FB => Pipes (Pipe).Framebuffer,
+ Mode => Pipes (Pipe).Mode,
+ Offset => Offset);
+ end if;
+ end loop;
+
+ GMA.Dump_Configs (Pipes);
+ end Prepare_Configs;
+
+ procedure Print_Usage is
+ begin
+ Debug.Put_Line
+ ("Usage: " & Ada.Command_Line.Command_Name & " <sysfs-pci-path>");
+ Debug.New_Line;
+ end Print_Usage;
+
+ procedure Main
+ is
+ use System.Storage_Elements;
+
+ use type HW.GFX.GMA.Port_Type;
+ use type Interfaces.C.int;
+
+ MMIO_Mapped,
+ Screen_Mapped,
+ Initialized : Boolean;
+
+ function iopl (level : Interfaces.C.int) return Interfaces.C.int;
+ pragma Import (C, iopl, "iopl");
+ begin
+ if Ada.Command_Line.Argument_Count /= 1 then
+ Print_Usage;
+ return;
+ end if;
+
+ if iopl (3) /= 0 then
+ Debug.Put_Line ("Failed to change i/o privilege level.");
+ return;
+ end if;
+
+ File.Map
+ (Path => Ada.Command_Line.Argument (1) & "/resource0",
+ Addr => Word64 (To_Integer (MMIO_Dummy'Address)),
+ Len => MMIO_Dummy'Size / 8,
+ Readable => True,
+ Writable => True,
+ Success => MMIO_Mapped);
+ if not MMIO_Mapped then
+ Debug.Put_Line
+ ("Failed to map '" & Ada.Command_Line.Argument (1) & "/resource0'.");
+ return;
+ end if;
+
+ File.Map
+ (Path => Ada.Command_Line.Argument (1) & "/resource2",
+ Addr => Word64 (To_Integer (Screen'Address)),
+ Len => Screen'Size / 8,
+ Readable => True,
+ Writable => True,
+ Success => Screen_Mapped);
+ if not Screen_Mapped then
+ Debug.Put_Line
+ ("Failed to map '" & Ada.Command_Line.Argument (1) & "/resource2'.");
+ return;
+ end if;
+
+ GMA.Initialize
+ (MMIO_Base => Word64 (To_Integer (MMIO_Dummy'Address)),
+ Clean_State => True,
+ Success => Initialized);
+
+ if Initialized then
+ Prepare_Configs;
+
+ GMA.Update_Outputs (Pipes);
+
+ for Pipe in GMA.Pipe_Index loop
+ if Pipes (Pipe).Port /= GMA.Disabled then
+ Test_Screen
+ (Framebuffer => Pipes (Pipe).Framebuffer,
+ Pipe => Pipe);
+ end if;
+ end loop;
+ end if;
+ end Main;
+
+end GFX_Test;