gma: Move transcoder setup into own package

Split the transcoder setup out of `Pipe_Setup` into a new package
`Transcoder`. This comes closer to how Intel's manuals describe the
hardware.

Also rework the related constant definitions to make things more
human readable.

Change-Id: Ife0f0d635d87b874d4b713a00ca7a1bec688c672
Signed-off-by: Nico Huber <nico.huber@secunet.com>
Reviewed-on: https://review.coreboot.org/17764
Tested-by: Nico Huber <nico.h@gmx.de>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
diff --git a/common/hw-gfx-gma-transcoder.adb b/common/hw-gfx-gma-transcoder.adb
new file mode 100644
index 0000000..27edf31
--- /dev/null
+++ b/common/hw-gfx-gma-transcoder.adb
@@ -0,0 +1,330 @@
+--
+-- Copyright (C) 2015-2016 secunet Security Networks AG
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+
+with HW.Debug;
+with GNAT.Source_Info;
+
+with HW.GFX.GMA.Config;
+with HW.GFX.GMA.DP_Info;
+
+package body HW.GFX.GMA.Transcoder is
+
+   type Default_Transcoder_Array is array (Pipe_Index) of Transcoder_Index;
+   Default_Transcoder : constant Default_Transcoder_Array :=
+     (Primary     => Trans_A,
+      Secondary   => Trans_B,
+      Tertiary    => Trans_C);
+
+   function Get_Idx (Pipe : Pipe_Index; Port : GPU_Port) return Transcoder_Index
+   is
+   begin
+      return
+        (if Config.Has_EDP_Transcoder and then Port = DIGI_A then
+            Trans_EDP
+         else
+            Default_Transcoder (Pipe));
+   end Get_Idx;
+
+   ----------------------------------------------------------------------------
+
+   TRANS_CLK_SEL_PORT_NONE : constant := 0 * 2 ** 29;
+
+   type TRANS_CLK_SEL_PORT_Array is
+      array (Digital_Port) of Word32;
+   TRANS_CLK_SEL_PORT : constant TRANS_CLK_SEL_PORT_Array :=
+     (DIGI_A => 0 * 2 ** 29,   -- DDI A is not selectable
+      DIGI_B => 2 * 2 ** 29,
+      DIGI_C => 3 * 2 ** 29,
+      DIGI_D => 4 * 2 ** 29,
+      DIGI_E => 5 * 2 ** 29);
+
+   TRANS_CONF_ENABLE          : constant := 1 * 2 ** 31;
+   TRANS_CONF_ENABLED_STATUS  : constant := 1 * 2 ** 30;
+   TRANS_CONF_ENABLE_DITHER   : constant := 1 * 2 **  4;
+
+   type BPC_Array is array (BPC_Type) of Word32;
+   TRANS_CONF_BPC : constant BPC_Array :=
+     (6        => 2 * 2 ** 5,
+      8        => 0 * 2 ** 5,
+      10       => 1 * 2 ** 5,
+      12       => 3 * 2 ** 5,
+      others   => 0 * 2 ** 5);   -- default to 8 BPC
+
+   function BPC_Conf (BPC : BPC_Type; Dither : Boolean) return Word32 is
+   begin
+      return
+        (if Config.Has_Pipeconf_BPC then TRANS_CONF_BPC (BPC) else 0) or
+        (if Dither                  then TRANS_CONF_ENABLE_DITHER else 0);
+   end BPC_Conf;
+
+   ----------------------------------------------------------------------------
+
+   DDI_FUNC_CTL_ENABLE                 : constant := 1 * 2 ** 31;
+   DDI_FUNC_CTL_MODE_SELECT_MASK       : constant := 7 * 2 ** 24;
+   DDI_FUNC_CTL_MODE_SELECT_HDMI       : constant := 0 * 2 ** 24;
+   DDI_FUNC_CTL_MODE_SELECT_DVI        : constant := 1 * 2 ** 24;
+   DDI_FUNC_CTL_MODE_SELECT_DP_SST     : constant := 2 * 2 ** 24;
+   DDI_FUNC_CTL_MODE_SELECT_DP_MST     : constant := 3 * 2 ** 24;
+   DDI_FUNC_CTL_MODE_SELECT_FDI        : constant := 4 * 2 ** 24;
+   DDI_FUNC_CTL_EDP_SELECT_MASK        : constant := 7 * 2 ** 12;
+   DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON   : constant := 0 * 2 ** 12;
+   DDI_FUNC_CTL_EDP_SELECT_A           : constant := 4 * 2 ** 12;
+   DDI_FUNC_CTL_EDP_SELECT_B           : constant := 5 * 2 ** 12;
+   DDI_FUNC_CTL_EDP_SELECT_C           : constant := 6 * 2 ** 12;
+
+   type DDI_Select_Array is array (Digital_Port) of Word32;
+   DDI_FUNC_CTL_DDI_SELECT : constant DDI_Select_Array :=
+     (DIGI_A => 0 * 2 ** 28,
+      DIGI_B => 1 * 2 ** 28,
+      DIGI_C => 2 * 2 ** 28,
+      DIGI_D => 3 * 2 ** 28,
+      DIGI_E => 4 * 2 ** 28);
+
+   type DDI_Mode_Array is array (Display_Type) of Word32;
+   DDI_FUNC_CTL_MODE_SELECT : constant DDI_Mode_Array :=
+     (VGA      => DDI_FUNC_CTL_MODE_SELECT_FDI,
+      HDMI     => DDI_FUNC_CTL_MODE_SELECT_DVI,
+      DP       => DDI_FUNC_CTL_MODE_SELECT_DP_SST,
+      others   => 0);
+
+   type HV_Sync_Array is array (Boolean) of Word32;
+   DDI_FUNC_CTL_VSYNC : constant HV_Sync_Array :=
+     (False => 0 * 2 ** 17,
+      True  => 1 * 2 ** 17);
+   DDI_FUNC_CTL_HSYNC : constant HV_Sync_Array :=
+     (False => 0 * 2 ** 16,
+      True  => 1 * 2 ** 16);
+
+   type EDP_Select_Array is array (Pipe_Index) of Word32;
+   DDI_FUNC_CTL_EDP_SELECT : constant EDP_Select_Array :=
+     (Primary     => DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON,  -- we never use
+                                                         -- panel fitter
+      Secondary   => DDI_FUNC_CTL_EDP_SELECT_B,
+      Tertiary    => DDI_FUNC_CTL_EDP_SELECT_C);
+   DDI_FUNC_CTL_EDP_SELECT_ONOFF : constant EDP_Select_Array :=
+     (Primary     => DDI_FUNC_CTL_EDP_SELECT_A,
+      Secondary   => DDI_FUNC_CTL_EDP_SELECT_B,
+      Tertiary    => DDI_FUNC_CTL_EDP_SELECT_C);
+
+   type Port_Width_Array is array (DP_Lane_Count) of Word32;
+   DDI_FUNC_CTL_PORT_WIDTH : constant Port_Width_Array :=
+     (DP_Lane_Count_1 => 0 * 2 ** 1,
+      DP_Lane_Count_2 => 1 * 2 ** 1,
+      DP_Lane_Count_4 => 3 * 2 ** 1);
+
+   DDI_FUNC_CTL_BPC : constant BPC_Array :=
+     (6        => 2 * 2 ** 20,
+      8        => 0 * 2 ** 20,
+      10       => 1 * 2 ** 20,
+      12       => 3 * 2 ** 20,
+      others   => 0 * 2 ** 20);  -- default to 8 BPC
+
+   ----------------------------------------------------------------------------
+
+   TRANS_MSA_MISC_SYNC_CLK : constant := 1 * 2 ** 0;
+   TRANS_MSA_MISC_BPC      : constant BPC_Array :=
+     (6        => 0 * 2 ** 5,
+      8        => 1 * 2 ** 5,
+      10       => 2 * 2 ** 5,
+      12       => 3 * 2 ** 5,
+      16       => 4 * 2 ** 5,
+      others   => 1 * 2 ** 5);   -- default to 8 BPC
+
+   function TRANS_DATA_M_TU (Transfer_Unit : Positive) return Word32 is
+   begin
+      return Shift_Left (Word32 (Transfer_Unit - 1), 25);
+   end TRANS_DATA_M_TU;
+
+   ----------------------------------------------------------------------------
+
+   function Encode (LSW, MSW : Pos16) return Word32
+   is
+      use type HW.Pos16;
+   begin
+      return Shift_Left (Word32 (MSW - 1), 16) or Word32 (LSW - 1);
+   end Encode;
+
+   ----------------------------------------------------------------------------
+
+   procedure Setup_Link
+     (Trans : Transcoder_Regs;
+      Link  : DP_Link;
+      Mode  : Mode_Type)
+   with
+      Global => (In_Out => Registers.Register_State),
+      Depends => (Registers.Register_State =>+ (Trans, Link, Mode))
+   is
+      Data_M, Link_M : DP_Info.M_Type;
+      Data_N, Link_N : DP_Info.N_Type;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      DP_Info.Calculate_M_N
+        (Link     => Link,
+         Mode     => Mode,
+         Data_M   => Data_M,
+         Data_N   => Data_N,
+         Link_M   => Link_M,
+         Link_N   => Link_N);
+
+      Registers.Write
+        (Register => Trans.DATA_M1,
+         Value    => TRANS_DATA_M_TU (64) or
+                     Word32 (Data_M));
+      Registers.Write
+        (Register => Trans.DATA_N1,
+         Value    => Word32 (Data_N));
+
+      Registers.Write
+        (Register => Trans.LINK_M1,
+         Value    => Word32 (Link_M));
+      Registers.Write
+        (Register => Trans.LINK_N1,
+         Value    => Word32 (Link_N));
+
+      if Config.Has_Pipe_MSA_Misc then
+         Registers.Write
+           (Register => Trans.MSA_MISC,
+            Value    => TRANS_MSA_MISC_SYNC_CLK or
+                        TRANS_MSA_MISC_BPC (Mode.BPC));
+      end if;
+   end Setup_Link;
+
+   ----------------------------------------------------------------------------
+
+   procedure Setup
+     (Pipe     : Pipe_Index;
+      Port_Cfg : Port_Config)
+   is
+      use type HW.GFX.GMA.Registers.Registers_Invalid_Index;
+
+      Trans : Transcoder_Regs renames
+               Transcoders (Get_Idx (Pipe, Port_Cfg.Port));
+      M : constant Mode_Type := Port_Cfg.Mode;
+   begin
+      pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
+
+      if Config.Has_Trans_Clk_Sel and then
+         Trans.CLK_SEL /= Registers.Invalid_Register
+      then
+         Registers.Write
+           (Register => Trans.CLK_SEL,
+            Value    => TRANS_CLK_SEL_PORT (Port_Cfg.Port));
+      end if;
+
+      if Port_Cfg.Is_FDI then
+         Setup_Link (Trans, Port_Cfg.FDI, Port_Cfg.Mode);
+      elsif Port_Cfg.Display = DP then
+         Setup_Link (Trans, Port_Cfg.DP, Port_Cfg.Mode);
+      end if;
+
+      Registers.Write (Trans.HTOTAL,   Encode (M.H_Visible,    M.H_Total));
+      Registers.Write (Trans.HBLANK,   Encode (M.H_Visible,    M.H_Total));
+      Registers.Write (Trans.HSYNC,    Encode (M.H_Sync_Begin, M.H_Sync_End));
+      Registers.Write (Trans.VTOTAL,   Encode (M.V_Visible,    M.V_Total));
+      Registers.Write (Trans.VBLANK,   Encode (M.V_Visible,    M.V_Total));
+      Registers.Write (Trans.VSYNC,    Encode (M.V_Sync_Begin, M.V_Sync_End));
+   end Setup;
+
+   ----------------------------------------------------------------------------
+
+   procedure On
+     (Pipe     : Pipe_Index;
+      Port_Cfg : Port_Config;
+      Dither   : Boolean)
+   is
+      Trans : Transcoder_Regs renames
+               Transcoders (Get_Idx (Pipe, Port_Cfg.Port));
+   begin
+      if Config.Has_Pipe_DDI_Func then
+         Registers.Write
+           (Register => Trans.DDI_FUNC_CTL,
+            Value    => DDI_FUNC_CTL_ENABLE or
+                        DDI_FUNC_CTL_DDI_SELECT (Port_Cfg.Port) or
+                        DDI_FUNC_CTL_MODE_SELECT (Port_Cfg.Display) or
+                        DDI_FUNC_CTL_BPC (Port_Cfg.Mode.BPC) or
+                        DDI_FUNC_CTL_VSYNC (Port_Cfg.Mode.V_Sync_Active_High) or
+                        DDI_FUNC_CTL_HSYNC (Port_Cfg.Mode.H_Sync_Active_High) or
+                        DDI_FUNC_CTL_EDP_SELECT (Pipe) or
+                        DDI_FUNC_CTL_PORT_WIDTH (Port_Cfg.DP.Lane_Count));
+      end if;
+
+      Registers.Write
+        (Register => Trans.CONF,
+         Value    => TRANS_CONF_ENABLE or
+                     (if not Config.Has_Pipeconf_Misc then
+                        BPC_Conf (Port_Cfg.Mode.BPC, Dither) else 0));
+      Registers.Posting_Read (Trans.CONF);
+   end On;
+
+   ----------------------------------------------------------------------------
+
+   procedure Trans_Off (Trans : Transcoder_Regs)
+   is
+      Enabled : Boolean;
+   begin
+      Registers.Is_Set_Mask (Trans.CONF, TRANS_CONF_ENABLE, Enabled);
+
+      if Enabled then
+         Registers.Unset_Mask (Trans.CONF, TRANS_CONF_ENABLE);
+      end if;
+
+      -- Workaround for Broadwell:
+      -- Status may be wrong if pipe hasn't been enabled since reset.
+      if not Config.Pipe_Enabled_Workaround or else Enabled then
+         -- synchronously wait until pipe is truly off
+         Registers.Wait_Unset_Mask
+           (Register => Trans.CONF,
+            Mask     => TRANS_CONF_ENABLED_STATUS,
+            TOut_MS  => 40);
+      end if;
+
+      if Config.Has_Pipe_DDI_Func then
+         Registers.Write (Trans.DDI_FUNC_CTL, 0);
+      end if;
+   end Trans_Off;
+
+   procedure Off (Pipe : Pipe_Index)
+   is
+      DDI_Func_Ctl : Word32;
+   begin
+      if Config.Has_EDP_Transcoder then
+         Registers.Read (Registers.PIPE_EDP_DDI_FUNC_CTL, DDI_Func_Ctl);
+         DDI_Func_Ctl := DDI_Func_Ctl and DDI_FUNC_CTL_EDP_SELECT_MASK;
+
+         if (Pipe = Primary and
+             DDI_Func_Ctl = DDI_FUNC_CTL_EDP_SELECT_ALWAYS_ON) or
+            DDI_Func_Ctl = DDI_FUNC_CTL_EDP_SELECT_ONOFF (Pipe)
+         then
+            Trans_Off (Transcoders (Trans_EDP));
+         end if;
+      end if;
+
+      Trans_Off (Transcoders (Default_Transcoder (Pipe)));
+   end Off;
+
+   procedure Clk_Off (Pipe : Pipe_Index)
+   is
+      use type Registers.Registers_Invalid_Index;
+
+      Trans : Transcoder_Regs renames Transcoders (Default_Transcoder (Pipe));
+   begin
+      if Config.Has_Trans_Clk_Sel and then
+         Trans.CLK_SEL /= Registers.Invalid_Register
+      then
+         Registers.Write (Trans.CLK_SEL, TRANS_CLK_SEL_PORT_NONE);
+      end if;
+   end Clk_Off;
+
+end HW.GFX.GMA.Transcoder;