gfx_test: Set our own framebuffers up, update README

Use GMA.Setup_Default_FB() to allocate framebuffers in the stolen
memory. To help with setups where we can't unload the i915 driver,
back up and restore the current GTT setup and framebuffer contents.

Also add a wrapper script and update the README.

Change-Id: I10790d35d38b7b211f41b2452f6d2baf17372e31
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/20604
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/gfxtest/hw-gfx-gma-gfx_test.adb b/gfxtest/hw-gfx-gma-gfx_test.adb
index ad396fd..d870401 100644
--- a/gfxtest/hw-gfx-gma-gfx_test.adb
+++ b/gfxtest/hw-gfx-gma-gfx_test.adb
@@ -2,10 +2,12 @@
 with Ada.Command_Line;
 with Interfaces.C;
 
+with HW.Time;
 with HW.Debug;
 with HW.PCI.Dev;
 with HW.MMIO_Range;
 with HW.GFX.GMA;
+with HW.GFX.GMA.Config;
 with HW.GFX.GMA.Display_Probing;
 
 package body HW.GFX.GMA.GFX_Test
@@ -14,6 +16,32 @@
 
    package Dev is new PCI.Dev (PCI.Address'(0, 2, 0));
 
+   type GTT_PTE_Type is mod 2 ** (Config.GTT_PTE_Size * 8);
+   type GTT_Registers_Type is array (GTT_Range) of GTT_PTE_Type;
+   package GTT is new MMIO_Range
+     (Base_Addr   => 0,
+      Element_T   => GTT_PTE_Type,
+      Index_T     => GTT_Range,
+      Array_T     => GTT_Registers_Type);
+
+   GTT_Backup : GTT_Registers_Type;
+
+   procedure Backup_GTT
+   is
+   begin
+      for Idx in GTT_Range loop
+         GTT.Read (GTT_Backup (Idx), Idx);
+      end loop;
+   end Backup_GTT;
+
+   procedure Restore_GTT
+   is
+   begin
+      for Idx in GTT_Range loop
+         GTT.Write (Idx, GTT_Backup (Idx));
+      end loop;
+   end Restore_GTT;
+
    type Pixel_Type is record
       Red   : Byte;
       Green : Byte;
@@ -46,7 +74,27 @@
 
    package Screen is new MMIO_Range (0, Word32, Screen_Index, Screen_Type);
 
-   Pipes : GMA.Pipe_Configs;
+   Screen_Backup : Screen_Type;
+
+   procedure Backup_Screen (FB : Framebuffer_Type)
+   is
+      First : constant Screen_Index := Natural (FB.Offset) / 4;
+      Last  : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+   begin
+      for Idx in Screen_Index range First .. Last loop
+         Screen.Read (Screen_Backup (Idx), Idx);
+      end loop;
+   end Backup_Screen;
+
+   procedure Restore_Screen (FB : Framebuffer_Type)
+   is
+      First : constant Screen_Index := Natural (FB.Offset) / 4;
+      Last  : constant Screen_Index := First + Natural (FB_Size (FB)) / 4 - 1;
+   begin
+      for Idx in Screen_Index range First .. Last loop
+         Screen.Write (Idx, Screen_Backup (Idx));
+      end loop;
+   end Restore_Screen;
 
    function Fill
      (X, Y        : Natural;
@@ -113,11 +161,14 @@
       Offset := Offset + Word32 (FB.Stride * FB.Height * 4);
    end Calc_Framebuffer;
 
+   Pipes : GMA.Pipe_Configs;
+
    procedure Prepare_Configs
    is
       use type HW.GFX.GMA.Port_Type;
 
       Offset : Word32 := 0;
+      Success : Boolean;
    begin
       GMA.Display_Probing.Scan_Ports (Pipes);
 
@@ -127,12 +178,27 @@
               (FB       => Pipes (Pipe).Framebuffer,
                Mode     => Pipes (Pipe).Mode,
                Offset   => Offset);
+            GMA.Setup_Default_FB
+              (FB       => Pipes (Pipe).Framebuffer,
+               Clear    => False,
+               Success  => Success);
+            if not Success then
+               Pipes (Pipe).Port := GMA.Disabled;
+            end if;
          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 & " <delay seconds>");
+      Debug.New_Line;
+   end Print_Usage;
+
    procedure Main
    is
       use type HW.GFX.GMA.Port_Type;
@@ -141,12 +207,21 @@
 
       Res_Addr : Word64;
 
+      Delay_S : Natural;
+
       Dev_Init,
       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;
+
+      Delay_S := Natural'Value (Ada.Command_Line.Argument (1));
+
       if iopl (3) /= 0 then
          Debug.Put_Line ("Failed to change i/o privilege level.");
          return;
@@ -158,6 +233,13 @@
          return;
       end if;
 
+      Dev.Map (Res_Addr, PCI.Res0, Offset => Config.GTT_Offset);
+      if Res_Addr = 0 then
+         Debug.Put_Line ("Failed to map PCI resource0.");
+         return;
+      end if;
+      GTT.Set_Base_Address (Res_Addr);
+
       Dev.Map (Res_Addr, PCI.Res2, WC => True);
       if Res_Addr = 0 then
          Debug.Put_Line ("Failed to map PCI resource2.");
@@ -170,17 +252,29 @@
          Success     => Initialized);
 
       if Initialized then
+         Backup_GTT;
+
          Prepare_Configs;
 
          GMA.Update_Outputs (Pipes);
 
          for Pipe in GMA.Pipe_Index loop
             if Pipes (Pipe).Port /= GMA.Disabled then
+               Backup_Screen (Pipes (Pipe).Framebuffer);
                Test_Screen
                  (Framebuffer => Pipes (Pipe).Framebuffer,
                   Pipe        => Pipe);
             end if;
          end loop;
+
+         Time.M_Delay (Delay_S * 1_000);
+
+         for Pipe in GMA.Pipe_Index loop
+            if Pipes (Pipe).Port /= GMA.Disabled then
+               Restore_Screen (Pipes (Pipe).Framebuffer);
+            end if;
+         end loop;
+         Restore_GTT;
       end if;
    end Main;