gma pipe_setup: Handle framebuffer `Scaling` and `Alignment`

Change-Id: If4ce7d4ee057fd37a328a41cf2bf3369fe883f77
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.sourcearcade.org/c/libgfxinit/+/519
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Thomas Heijligen <src@posteo.de>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
index 11aac58..3fcdc4d 100644
--- a/common/hw-gfx-gma-pipe_setup.adb
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -567,6 +567,59 @@
       end case;
    end Scale_Keep_Aspect;
 
+   procedure Scale
+     (Width       :    out Width_Type;
+      Height      :    out Height_Type;
+      Scaling     : in     GMA.Scaling;
+      Max_Width   : in     Width_Type;
+      Max_Height  : in     Height_Type;
+      Framebuffer : in     Framebuffer_Type)
+   with
+      Pre =>
+         Rotated_Width (Framebuffer) <= Max_Width and
+         Rotated_Height (Framebuffer) <= Max_Height,
+      Post =>
+         Width <= Max_Width and Height <= Max_Height
+   is
+   begin
+      case Scaling is
+         when None =>
+            Width  := Rotated_Width (Framebuffer);
+            Height := Rotated_Height (Framebuffer);
+         when Fit =>
+            Scale_Keep_Aspect (Width, Height, Max_Width, Max_Height, Framebuffer);
+         when Stretch =>
+            Width  := Max_Width;
+            Height := Max_Height;
+      end case;
+   end Scale;
+
+   procedure Align_Framebuffer
+     (Pos_X    :    out Position_Type;
+      Pos_Y    :    out Position_Type;
+      Align    : in     Alignment;
+      Width    : in     Width_Type;
+      Height   : in     Height_Type;
+      Mode     : in     Mode_Type)
+   with
+      Pre => Width <= Mode.H_Visible and Height <= Mode.V_Visible
+   is
+      Gap_X : constant Position_Type := Mode.H_Visible - Width;
+      Gap_Y : constant Position_Type := Mode.V_Visible - Height;
+   begin
+      case Align is
+         when Top_Left     => Pos_X := 0;          Pos_Y := 0;
+         when Top          => Pos_X := Gap_X / 2;  Pos_Y := 0;
+         when Top_Right    => Pos_X := Gap_X;      Pos_Y := 0;
+         when Left         => Pos_X := 0;          Pos_Y := Gap_Y / 2;
+         when Center       => Pos_X := Gap_X / 2;  Pos_Y := Gap_Y / 2;
+         when Right        => Pos_X := Gap_X;      Pos_Y := Gap_Y / 2;
+         when Bottom_Left  => Pos_X := 0;          Pos_Y := Gap_Y;
+         when Bottom       => Pos_X := Gap_X / 2;  Pos_Y := Gap_Y;
+         when Bottom_Right => Pos_X := Gap_X;      Pos_Y := Gap_Y;
+      end case;
+   end Align_Framebuffer;
+
    type Pipe_Scaler_Limit_Config is record
       Control     : Word32;
       Horizontal  : Pos32;
@@ -638,24 +691,32 @@
 
       Width : Width_Type;
       Height : Height_Type;
+      Pos_X, Pos_Y : Position_Type;
    begin
       -- Writes to WIN_SZ arm the PS registers.
 
-      Scale_Keep_Aspect
+      Scale
         (Width       => Width,
          Height      => Height,
+         Scaling     => Pipe_Cfg.Scaling,
          Max_Width   => Pos32'Min (Limits.Horizontal, Mode.H_Visible),
          Max_Height  => Pos32'Min (Limits.Vertical, Mode.V_Visible),
          Framebuffer => Pipe_Cfg.Framebuffer);
 
+      Align_Framebuffer
+        (Pos_X  => Pos_X,
+         Pos_Y  => Pos_Y,
+         Align  => Pipe_Cfg.Alignment,
+         Width  => Width,
+         Height => Height,
+         Mode   => Mode);
+
       Registers.Write
         (Register => Controller.PS_CTRL_1,
          Value    => PS_CTRL_ENABLE_SCALER or Limits.Control);
       Registers.Write
         (Register => Controller.PS_WIN_POS_1,
-         Value    =>
-            Shift_Left (Word32 (Mode.H_Visible - Width) / 2, 16) or
-            Word32 (Mode.V_Visible - Height) / 2);
+         Value    => Shift_Left (Word32 (Pos_X), 16) or Word32 (Pos_Y));
       Registers.Write
         (Register => Controller.PS_WIN_SZ_1,
          Value    => Shift_Left (Word32 (Width), 16) or Word32 (Height));
@@ -681,13 +742,14 @@
 
       Width : Width_Type;
       Height : Height_Type;
-      X, Y : Int32;
+      Pos_X, Pos_Y : Position_Type;
    begin
       -- Writes to WIN_SZ arm the PF registers.
 
-      Scale_Keep_Aspect
+      Scale
         (Width       => Width,
          Height      => Height,
+         Scaling     => Pipe_Cfg.Scaling,
          Max_Width   => Mode.H_Visible,
          Max_Height  => Mode.V_Visible,
          Framebuffer => Pipe_Cfg.Framebuffer);
@@ -701,13 +763,18 @@
          Height := Height + 1;
       end if;
 
-      X := (Mode.H_Visible - Width) / 2;
-      Y := (Mode.V_Visible - Height) / 2;
+      Align_Framebuffer
+        (Pos_X  => Pos_X,
+         Pos_Y  => Pos_Y,
+         Align  => Pipe_Cfg.Alignment,
+         Width  => Width,
+         Height => Height,
+         Mode   => Mode);
 
       -- Hardware is picky about minimal horizontal gaps.
       if Mode.H_Visible - Width <= 3 then
          Width := Mode.H_Visible;
-         X := 0;
+         Pos_X := 0;
       end if;
 
       Registers.Write
@@ -715,7 +782,7 @@
          Value    => PF_CTRL_ENABLE or PF_Ctrl_Pipe_Sel or PF_CTRL_FILTER_MED);
       Registers.Write
         (Register => Controller.PF_WIN_POS,
-         Value    => Shift_Left (Word32 (X), 16) or Word32 (Y));
+         Value    => Shift_Left (Word32 (Pos_X), 16) or Word32 (Pos_Y));
       Registers.Write
         (Register => Controller.PF_WIN_SZ,
          Value    => Shift_Left (Word32 (Width), 16) or Word32 (Height));
diff --git a/common/hw-gfx.ads b/common/hw-gfx.ads
index f09f9dc..c32e00e 100644
--- a/common/hw-gfx.ads
+++ b/common/hw-gfx.ads
@@ -25,7 +25,7 @@
    -- Allow same range for width and height (for rotated framebuffers)
    subtype Width_Type      is Pos32 range 1 .. 8192;
    subtype Height_Type     is Pos32 range 1 .. 8192;
-   subtype Position_Type   is Int32 range 0 .. 4095;
+   subtype Position_Type   is Int32 range 0 .. 8191;
 
    Auto_BPC : constant := 5;
    subtype BPC_Type    is Int64 range Auto_BPC .. 16;