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;