gma: Configure panel fitter / pipe scaler

If the framebuffer size is smaller than the display mode's resolution,
enable the panel fitter or pipe scaler (on Skylake+).

Change-Id: I0a648a7e7bf495a80636a589a74b698ecba7e7d5
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/17263
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: Nico Huber <nico.h@gmx.de>
diff --git a/common/hw-gfx-gma-config.ads.template b/common/hw-gfx-gma-config.ads.template
index 7a83e77..7289dff 100644
--- a/common/hw-gfx-gma-config.ads.template
+++ b/common/hw-gfx-gma-config.ads.template
@@ -48,6 +48,7 @@
    Has_Pipeconf_BPC        : constant Boolean := CPU /= Haswell;
    Has_Plane_Control       : constant Boolean := CPU >= Skylake;
    Has_DSP_Linoff          : constant Boolean := CPU <= Ivybridge;
+   Has_PF_Pipe_Select      : constant Boolean := CPU in Ivybridge .. Haswell;
 
    ----- Panel power: -----
    Has_PP_Write_Protection : constant Boolean := CPU <= Ivybridge;
diff --git a/common/hw-gfx-gma-pipe_setup.adb b/common/hw-gfx-gma-pipe_setup.adb
index 2d13a01..b902b77 100644
--- a/common/hw-gfx-gma-pipe_setup.adb
+++ b/common/hw-gfx-gma-pipe_setup.adb
@@ -64,11 +64,13 @@
    PIPECONF_ENABLE_DITHER   : constant := 1 * 2 **  4;
    PIPECONF_DITHER_TEMPORAL : constant := 1 * 2 **  2;
 
-   PF_CTL_1_ENABLE : constant Word32 := 1 * 2 ** 31;
+   PF_CTRL_ENABLE                         : constant := 1 * 2 ** 31;
+   PF_CTRL_PIPE_SELECT_MASK               : constant := 3 * 2 ** 29;
+   PF_CTRL_FILTER_MED                     : constant := 1 * 2 ** 23;
 
-   PS_CTRL_ENABLE_SCALER               : constant Word32 := 1 * 2 ** 31;
-   PS_CTRL_SCALER_MODE_7X5_EXTENDED    : constant Word32 := 1 * 2 ** 28;
-   PS_CTRL_FILTER_SELECT_MEDIUM_2      : constant Word32 := 1 * 2 ** 23;
+   PS_CTRL_ENABLE_SCALER                  : constant := 1 * 2 ** 31;
+   PS_CTRL_SCALER_MODE_7X5_EXTENDED       : constant := 1 * 2 ** 28;
+   PS_CTRL_FILTER_SELECT_MEDIUM_2         : constant := 1 * 2 ** 23;
 
    PIPE_DDI_FUNC_CTL_ENABLE               : constant := 1 * 2 ** 31;
    PIPE_DDI_FUNC_CTL_DDI_SELECT_MASK      : constant := 7 * 2 ** 28;
@@ -385,6 +387,148 @@
 
    ----------------------------------------------------------------------------
 
+   procedure Scale_Keep_Aspect
+     (Width       :    out Pos32;
+      Height      :    out Pos32;
+      Max_Width   : in     Pos32;
+      Max_Height  : in     Pos32;
+      Framebuffer : in     Framebuffer_Type)
+   with
+      Pre =>
+         Max_Width <= Pos32 (Pos16'Last) and
+         Max_Height <= Pos32 (Pos16'Last) and
+         Framebuffer.Width <= Max_Width and
+         Framebuffer.Height <= Max_Height,
+      Post =>
+         Width <= Max_Width and Height <= Max_Height
+   is
+   begin
+      if (Max_Width * Framebuffer.Height) / Framebuffer.Width <= Max_Height then
+         Width  := Max_Width;
+         Height := (Max_Width * Framebuffer.Height) / Framebuffer.Width;
+      else
+         Height := Max_Height;
+         Width  := Pos32'Min (Max_Width,  -- could prove, it's <= Max_Width
+            (Max_Height * Framebuffer.Width) / Framebuffer.Height);
+      end if;
+   end Scale_Keep_Aspect;
+
+   procedure Setup_Skylake_Pipe_Scaler
+     (Controller  : in     Controller_Type;
+      Mode        : in     HW.GFX.Mode_Type;
+      Framebuffer : in     HW.GFX.Framebuffer_Type)
+   with
+      Pre =>
+         Framebuffer.Width <= Pos32 (Mode.H_Visible) and
+         Framebuffer.Height <= Pos32 (Mode.V_Visible)
+   is
+      -- Enable 7x5 extended mode where possible:
+      Scaler_Mode : constant Word32 :=
+        (if Controller.PS_CTRL_2 /= Registers.Invalid_Register then
+            PS_CTRL_SCALER_MODE_7X5_EXTENDED else 0);
+
+      -- We can scale up to 2.99x horizontally:
+      Horizontal_Limit : constant Pos32 := ((Framebuffer.Width * 299) / 100);
+      -- The third scaler is limited to 1.99x
+      -- vertical scaling for source widths > 2048:
+      Vertical_Limit : constant Pos32 :=
+        (Framebuffer.Height *
+           (if Controller.PS_CTRL_2 = Registers.Invalid_Register and
+               Framebuffer.Width > 2048
+            then
+               199
+            else
+               299)) / 100;
+
+      Width, Height : Pos32;
+   begin
+      -- Writes to WIN_SZ arm the PS registers.
+
+      Scale_Keep_Aspect
+        (Width       => Width,
+         Height      => Height,
+         Max_Width   => Pos32'Min (Horizontal_Limit, Pos32 (Mode.H_Visible)),
+         Max_Height  => Pos32'Min (Vertical_Limit, Pos32 (Mode.V_Visible)),
+         Framebuffer => Framebuffer);
+
+      Registers.Write
+        (Register => Controller.PS_CTRL_1,
+         Value    => PS_CTRL_ENABLE_SCALER or Scaler_Mode);
+      Registers.Write
+        (Register => Controller.PS_WIN_POS_1,
+         Value    =>
+            Shift_Left (Word32 (Pos32 (Mode.H_Visible) - Width) / 2, 16) or
+            Word32 (Pos32 (Mode.V_Visible) - Height) / 2);
+      Registers.Write
+        (Register => Controller.PS_WIN_SZ_1,
+         Value    => Shift_Left (Word32 (Width), 16) or Word32 (Height));
+   end Setup_Skylake_Pipe_Scaler;
+
+   procedure Setup_Ironlake_Panel_Fitter
+     (Controller  : in     Controller_Type;
+      Mode        : in     HW.GFX.Mode_Type;
+      Framebuffer : in     HW.GFX.Framebuffer_Type)
+   with
+      Pre =>
+         Framebuffer.Width <= Pos32 (Mode.H_Visible) and
+         Framebuffer.Height <= Pos32 (Mode.V_Visible)
+   is
+      -- Force 1:1 mapping of panel fitter:pipe
+      PF_Ctrl_Pipe_Sel : constant Word32 :=
+        (if Config.Has_PF_Pipe_Select then
+           (case Controller.PF_CTRL is
+               when Registers.PFA_CTL_1 => 0 * 2 ** 29,
+               when Registers.PFB_CTL_1 => 1 * 2 ** 29,
+               when Registers.PFC_CTL_1 => 2 * 2 ** 29,
+               when others              => 0) else 0);
+
+      Width, Height : Pos32;
+   begin
+      -- Writes to WIN_SZ arm the PF registers.
+
+      Scale_Keep_Aspect
+        (Width       => Width,
+         Height      => Height,
+         Max_Width   => Pos32 (Mode.H_Visible),
+         Max_Height  => Pos32 (Mode.V_Visible),
+         Framebuffer => Framebuffer);
+
+      Registers.Write
+        (Register => Controller.PF_CTRL,
+         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 (Pos32 (Mode.H_Visible) - Width) / 2, 16) or
+            Word32 (Pos32 (Mode.V_Visible) - Height) / 2);
+      Registers.Write
+        (Register => Controller.PF_WIN_SZ,
+         Value    => Shift_Left (Word32 (Width), 16) or Word32 (Height));
+   end Setup_Ironlake_Panel_Fitter;
+
+   procedure Setup_Scaling
+     (Controller  : in     Controller_Type;
+      Mode        : in     HW.GFX.Mode_Type;
+      Framebuffer : in     HW.GFX.Framebuffer_Type)
+   with
+      Pre =>
+         Framebuffer.Width <= Pos32 (Mode.H_Visible) and
+         Framebuffer.Height <= Pos32 (Mode.V_Visible)
+   is
+   begin
+      if Framebuffer.Width /= Pos32 (Mode.H_Visible) or
+         Framebuffer.Height /= Pos32 (Mode.V_Visible)
+      then
+         if Config.Has_Plane_Control then
+            Setup_Skylake_Pipe_Scaler (Controller, Mode, Framebuffer);
+         else
+            Setup_Ironlake_Panel_Fitter (Controller, Mode, Framebuffer);
+         end if;
+      end if;
+   end Setup_Scaling;
+
+   ----------------------------------------------------------------------------
+
    procedure Setup_Head
      (Controller  : Controller_Type;
       Head        : Head_Type;
@@ -456,6 +600,8 @@
 
       Setup_Display (Controller, Head, Port_Cfg.Mode, Framebuffer);
 
+      Setup_Scaling (Controller, Port_Cfg.Mode, Framebuffer);
+
       Setup_Head (Controller, Head, Port_Cfg, Framebuffer);
    end On;
 
@@ -511,7 +657,7 @@
             Registers.Write (Controller.PS_WIN_SZ_2, 16#0000_0000#);
          end if;
       else
-         Registers.Unset_Mask (Controller.PF_CTL_1, PF_CTL_1_ENABLE);
+         Registers.Unset_Mask (Controller.PF_CTRL, PF_CTRL_ENABLE);
          Registers.Write (Controller.PF_WIN_SZ, 16#0000_0000#);
       end if;
    end Panel_Fitter_Off;
diff --git a/common/hw-gfx-gma-pipe_setup.ads b/common/hw-gfx-gma-pipe_setup.ads
index b696934..c485f1a 100644
--- a/common/hw-gfx-gma-pipe_setup.ads
+++ b/common/hw-gfx-gma-pipe_setup.ads
@@ -65,7 +65,7 @@
          Kind              : Controller_Kind;
          PIPESRC           : Registers.Registers_Index;
          PIPEMISC          : Registers.Registers_Index;
-         PF_CTL_1          : Registers.Registers_Index;
+         PF_CTRL           : Registers.Registers_Index;
          PF_WIN_POS        : Registers.Registers_Index;
          PF_WIN_SZ         : Registers.Registers_Index;
          DSPCNTR           : Registers.Registers_Index;
@@ -83,8 +83,9 @@
          PLANE_STRIDE      : Registers.Registers_Index;
          PLANE_SURF        : Registers.Registers_Index;
          PS_CTRL_1         : Registers.Registers_Index;
-         PS_CTRL_2         : Registers.Registers_Invalid_Index;
+         PS_WIN_POS_1      : Registers.Registers_Index;
          PS_WIN_SZ_1       : Registers.Registers_Index;
+         PS_CTRL_2         : Registers.Registers_Invalid_Index;
          PS_WIN_SZ_2       : Registers.Registers_Invalid_Index;
          WM_LINETIME       : Registers.Registers_Index;
          PLANE_BUF_CFG     : Registers.Registers_Index;
@@ -114,7 +115,7 @@
         (Kind              => A,
          PIPESRC           => Registers.PIPEASRC,
          PIPEMISC          => Registers.PIPEAMISC,
-         PF_CTL_1          => Registers.PFA_CTL_1,
+         PF_CTRL           => Registers.PFA_CTL_1,
          PF_WIN_POS        => Registers.PFA_WIN_POS,
          PF_WIN_SZ         => Registers.PFA_WIN_SZ,
          DSPCNTR           => Registers.DSPACNTR,
@@ -131,8 +132,9 @@
          PLANE_STRIDE      => Registers.DSPASTRIDE,
          PLANE_SURF        => Registers.DSPASURF,
          PS_CTRL_1         => Registers.PS_CTRL_1_A,
-         PS_CTRL_2         => Registers.PS_CTRL_2_A,
+         PS_WIN_POS_1      => Registers.PS_WIN_POS_1_A,
          PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_A,
+         PS_CTRL_2         => Registers.PS_CTRL_2_A,
          PS_WIN_SZ_2       => Registers.PS_WIN_SZ_2_A,
          WM_LINETIME       => Registers.WM_LINETIME_A,
          PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_A,
@@ -149,7 +151,7 @@
         (Kind              => B,
          PIPESRC           => Registers.PIPEBSRC,
          PIPEMISC          => Registers.PIPEBMISC,
-         PF_CTL_1          => Registers.PFB_CTL_1,
+         PF_CTRL           => Registers.PFB_CTL_1,
          PF_WIN_POS        => Registers.PFB_WIN_POS,
          PF_WIN_SZ         => Registers.PFB_WIN_SZ,
          DSPCNTR           => Registers.DSPBCNTR,
@@ -166,8 +168,9 @@
          PLANE_STRIDE      => Registers.DSPBSTRIDE,
          PLANE_SURF        => Registers.DSPBSURF,
          PS_CTRL_1         => Registers.PS_CTRL_1_B,
-         PS_CTRL_2         => Registers.PS_CTRL_2_B,
+         PS_WIN_POS_1      => Registers.PS_WIN_POS_1_B,
          PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_B,
+         PS_CTRL_2         => Registers.PS_CTRL_2_B,
          PS_WIN_SZ_2       => Registers.PS_WIN_SZ_2_B,
          WM_LINETIME       => Registers.WM_LINETIME_B,
          PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_B,
@@ -184,7 +187,7 @@
         (Kind              => C,
          PIPESRC           => Registers.PIPECSRC,
          PIPEMISC          => Registers.PIPECMISC,
-         PF_CTL_1          => Registers.PFC_CTL_1,
+         PF_CTRL           => Registers.PFC_CTL_1,
          PF_WIN_POS        => Registers.PFC_WIN_POS,
          PF_WIN_SZ         => Registers.PFC_WIN_SZ,
          DSPCNTR           => Registers.DSPCCNTR,
@@ -201,8 +204,9 @@
          PLANE_STRIDE      => Registers.DSPCSTRIDE,
          PLANE_SURF        => Registers.DSPCSURF,
          PS_CTRL_1         => Registers.PS_CTRL_1_C,
-         PS_CTRL_2         => Registers.Invalid_Register,
+         PS_WIN_POS_1      => Registers.PS_WIN_POS_1_C,
          PS_WIN_SZ_1       => Registers.PS_WIN_SZ_1_C,
+         PS_CTRL_2         => Registers.Invalid_Register,
          PS_WIN_SZ_2       => Registers.Invalid_Register,
          WM_LINETIME       => Registers.WM_LINETIME_C,
          PLANE_BUF_CFG     => Registers.PLANE_BUF_CFG_1_C,