blob: b1e8344a529ef141bbb0944a49b7facfd0aa83a6 [file] [log] [blame]
Nico Huberf6266002017-02-03 12:17:28 +01001--
2-- Copyright (C) 2017 secunet Security Networks AG
3--
4-- This program is free software; you can redistribute it and/or modify
5-- it under the terms of the GNU General Public License as published by
6-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
8--
9-- This program is distributed in the hope that it will be useful,
10-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-- GNU General Public License for more details.
13--
14
15with GNAT.Source_Info;
16with HW.Debug;
17
18with HW.GFX.GMA.Registers;
19
20use HW.GFX.GMA.Registers;
21
22package body HW.GFX.GMA.DDI_Phy is
23
24 subtype Dual_Channel is T range BC .. BC;
25
26 type DDI_Range is record
27 First : DDI_Phy_Port;
28 Last : DDI_Phy_Port;
29 end record;
30 type DDI_Range_Array is array (T) of DDI_Range;
31 DDIs : constant DDI_Range_Array :=
32 (A => (DIGI_A, DIGI_A),
33 BC => (DIGI_B, DIGI_C));
34
35 ----------------------------------------------------------------------------
36
37 type CL1CM is record
38 DW0 : Registers_Index;
39 DW9 : Registers_Index;
40 DW10 : Registers_Index;
41 DW28 : Registers_Index;
42 DW30 : Registers_Index;
43 end record;
44 type CL1CM_Array is array (T) of CL1CM;
45 PORT_CL1CM : constant CL1CM_Array :=
46 (A =>
47 (DW0 => BXT_PORT_CL1CM_DW0_A,
48 DW9 => BXT_PORT_CL1CM_DW9_A,
49 DW10 => BXT_PORT_CL1CM_DW10_A,
50 DW28 => BXT_PORT_CL1CM_DW28_A,
51 DW30 => BXT_PORT_CL1CM_DW30_A),
52 BC =>
53 (DW0 => BXT_PORT_CL1CM_DW0_BC,
54 DW9 => BXT_PORT_CL1CM_DW9_BC,
55 DW10 => BXT_PORT_CL1CM_DW10_BC,
56 DW28 => BXT_PORT_CL1CM_DW28_BC,
57 DW30 => BXT_PORT_CL1CM_DW30_BC));
58
59 type CL2CM is record
60 DW6 : Registers_Index;
61 end record;
62 type CL2CM_Array is array (Dual_Channel) of CL2CM;
63 PORT_CL2CM : constant CL2CM_Array :=
64 (BC => (DW6 => BXT_PORT_CL2CM_DW6_BC));
65
66 type Port_Ref_Regs is record
67 DW3 : Registers_Index;
68 DW6 : Registers_Index;
69 DW8 : Registers_Index;
70 end record;
71 type Port_Ref_Array is array (T) of Port_Ref_Regs;
72 PORT_REF : constant Port_Ref_Array :=
73 (A =>
74 (DW3 => BXT_PORT_REF_DW3_A,
75 DW6 => BXT_PORT_REF_DW6_A,
76 DW8 => BXT_PORT_REF_DW8_A),
77 BC =>
78 (DW3 => BXT_PORT_REF_DW3_BC,
79 DW6 => BXT_PORT_REF_DW6_BC,
80 DW8 => BXT_PORT_REF_DW8_BC));
81
82 type Regs is array (T) of Registers_Index;
83 PHY_CTL_FAMILY : constant Regs :=
84 (A => BXT_PHY_CTL_FAM_EDP, BC => BXT_PHY_CTL_FAM_DDI);
85
86 type DDI_Regs is array (DDI_Phy_Port) of Registers_Index;
87 PHY_CTL : constant DDI_Regs :=
88 (DIGI_A => BXT_PHY_CTL_A,
89 DIGI_B => BXT_PHY_CTL_B,
90 DIGI_C => BXT_PHY_CTL_C);
91
92 ----------------------------------------------------------------------------
93
94 type Values is array (T) of Word32;
95 GT_DISPLAY_POWER_ON : constant Values :=
96 (A => 1 * 2 ** 1,
97 BC => 1 * 2 ** 0);
98
99 PORT_CL1CM_PHY_POWER_GOOD : constant := 1 * 2 ** 16;
100 PORT_CL1CM_PHY_RESERVED : constant := 1 * 2 ** 7;
101
102 PORT_CL1CM_IREFxRC_OFFSET_SHIFT : constant := 8;
103 PORT_CL1CM_IREFxRC_OFFSET_MASK : constant := 16#ff# * 2 ** 8;
104
105 PORT_CL1CM_OCL1_POWER_DOWN_EN : constant := 1 * 2 ** 23;
106 PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 22;
107 PORT_CL1CM_SUS_CLK_CONFIG : constant := 3 * 2 ** 0;
108
109 PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN : constant := 1 * 2 ** 28;
110
111 PORT_REF_GRC_DONE : constant := 1 * 2 ** 22;
112
113 PORT_REF_GRC_CODE_SHIFT : constant := 24;
114 PORT_REF_GRC_FAST_SHIFT : constant := 16;
115 PORT_REF_GRC_SLOW_SHIFT : constant := 8;
116
117 PORT_REF_GRC_DISABLE : constant := 1 * 2 ** 15;
118 PORT_REF_GRC_READY_OVERRIDE : constant := 1 * 2 ** 1;
119
120 PHY_CTL_FAM_CMN_RESET_DIS : constant := 1 * 2 ** 31;
121
122 PHY_CTL_CMNLANE_POWERDOWN_ACK : constant := 1 * 2 ** 10;
123
124 ----------------------------------------------------------------------------
125
126 procedure Is_Enabled (Phy : in T; Enabled : out Boolean)
127 is
128 Phy_Pwr : Word32;
129 begin
130 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
131
132 Is_Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy), Enabled);
133
134 if Enabled then
135 Read (PORT_CL1CM (Phy).DW0, Phy_Pwr);
136 Enabled :=
137 (Phy_Pwr and (PORT_CL1CM_PHY_POWER_GOOD or PORT_CL1CM_PHY_RESERVED))
138 = PORT_CL1CM_PHY_POWER_GOOD;
139 pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY power unsettled"));
140 end if;
141
142 if Enabled then
143 Is_Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS, Enabled);
144 pragma Debug (not Enabled, Debug.Put_Line ("DDI PHY still in reset"));
145 end if;
146
147 for DDI in DDIs (Phy).First .. DDIs (Phy).Last loop
148 if Enabled then
149 declare
150 Common_Lane_Powerdown : Boolean;
151 begin
152 Is_Set_Mask
153 (Register => PHY_CTL (DDI),
154 Mask => PHY_CTL_CMNLANE_POWERDOWN_ACK,
155 Result => Common_Lane_Powerdown);
156 Enabled := not Common_Lane_Powerdown;
157 pragma Debug
158 (not Enabled, Debug.Put_Line ("Common lane powered down"));
159 end;
160 end if;
161 end loop;
162 end Is_Enabled;
163
164 procedure Power_On_Phy (Phy : T)
165 with
166 Pre => True
167 is
168 begin
169 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
170
171 Set_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
172
173 Wait
174 (Register => PORT_CL1CM (Phy).DW0,
175 Mask => PORT_CL1CM_PHY_POWER_GOOD or
176 PORT_CL1CM_PHY_RESERVED,
177 Value => PORT_CL1CM_PHY_POWER_GOOD,
178 TOut_MS => 1); -- 50~100us
179
180 Unset_And_Set_Mask
181 (Register => PORT_CL1CM (Phy).DW9,
182 Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
183 Mask_Set => Shift_Left
184 (16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
185 Unset_And_Set_Mask
186 (Register => PORT_CL1CM (Phy).DW10,
187 Mask_Unset => PORT_CL1CM_IREFxRC_OFFSET_MASK,
188 Mask_Set => Shift_Left
189 (16#e4#, PORT_CL1CM_IREFxRC_OFFSET_SHIFT));
190
191 Set_Mask
192 (Register => PORT_CL1CM (Phy).DW28,
193 Mask => PORT_CL1CM_OCL1_POWER_DOWN_EN or
194 PORT_CL1CM_OLDO_DYN_POWER_DOWN_EN or
195 PORT_CL1CM_SUS_CLK_CONFIG);
196
197 if Phy in Dual_Channel then
198 Set_Mask (PORT_CL2CM (Phy).DW6, PORT_CL2CM_OLDO_DYN_POWER_DOWN_EN);
199 end if;
200
201 if Phy = BC then
202 declare
203 GRC_Val : Word32;
204 begin
205 -- take RCOMP calibration result from A
206
207 Wait_Set_Mask (PORT_REF (A).DW3, PORT_REF_GRC_DONE, TOut_MS => 10);
208 Read (PORT_REF (A).DW6, GRC_Val);
209 GRC_Val := Shift_Right (GRC_Val, PORT_REF_GRC_CODE_SHIFT);
210
211 -- use it for BC too
212 GRC_Val := Shift_Left (GRC_Val, PORT_REF_GRC_FAST_SHIFT) or
213 Shift_Left (GRC_Val, PORT_REF_GRC_SLOW_SHIFT) or
214 GRC_Val;
215 Write (PORT_REF (Phy).DW6, GRC_Val);
216
217 Set_Mask
218 (Register => PORT_REF (Phy).DW8,
219 Mask => PORT_REF_GRC_DISABLE or
220 PORT_REF_GRC_READY_OVERRIDE);
221 end;
222 end if;
223
224 Set_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
225 end Power_On_Phy;
226
227 procedure Power_On (Phy : T)
228 is
229 Phy_A_Enabled : Boolean := False;
230 Enabled : Boolean;
231 begin
232 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
233
234 Is_Enabled (Phy, Enabled);
235 pragma Debug (Enabled, Debug.Put_Line ("DDI PHY already enabled"));
236
237 if not Enabled then
238 if Phy = BC then
239 -- PHY BC needs RCOMP calibration results from PHY A
240 Is_Enabled (A, Phy_A_Enabled);
241 if not Phy_A_Enabled then
242 Power_On_Phy (A);
243 end if;
244 end if;
245
246 Power_On_Phy (Phy);
247
248 if Phy = BC and then not Phy_A_Enabled then
249 Power_Off (A);
250 end if;
251 end if;
252 end Power_On;
253
254 procedure Power_Off (Phy : T) is
255 begin
256 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
257
258 Unset_Mask (PHY_CTL_FAMILY (Phy), PHY_CTL_FAM_CMN_RESET_DIS);
259 Unset_Mask (BXT_P_CR_GT_DISP_PWRON, GT_DISPLAY_POWER_ON (Phy));
260 end Power_Off;
261
Nico Huberafadcac2017-02-08 13:41:38 +0100262 ----------------------------------------------------------------------------
263
264 type Lanes is range 0 .. 3;
265 type Lane_Reg_Array is array (Lanes) of Registers_Index;
266
267 type Port_TX_Regs is record
268 DW14_LN : Lane_Reg_Array;
269 end record;
270 type Port_TX_Array is array (DDI_Phy_Port) of Port_TX_Regs;
271
272 PORT_TX : constant Port_TX_Array :=
273 (DIGI_A =>
274 (DW14_LN =>
275 (BXT_PORT_TX_DW14_LN0_A,
276 BXT_PORT_TX_DW14_LN1_A,
277 BXT_PORT_TX_DW14_LN2_A,
278 BXT_PORT_TX_DW14_LN3_A)),
279 DIGI_B =>
280 (DW14_LN =>
281 (BXT_PORT_TX_DW14_LN0_B,
282 BXT_PORT_TX_DW14_LN1_B,
283 BXT_PORT_TX_DW14_LN2_B,
284 BXT_PORT_TX_DW14_LN3_B)),
285 DIGI_C =>
286 (DW14_LN =>
287 (BXT_PORT_TX_DW14_LN0_C,
288 BXT_PORT_TX_DW14_LN1_C,
289 BXT_PORT_TX_DW14_LN2_C,
290 BXT_PORT_TX_DW14_LN3_C)));
291
292 PORT_TX_DW14_LN_LATENCY_OPTIM : constant := 1 * 2 ** 30;
293
294 procedure Pre_PLL (Port_Cfg : Port_Config)
295 is
296 type Lane_Values is array (Lanes) of Word32;
297 Lane_Optim : constant Lane_Values :=
298 (if Port_Cfg.Display = HDMI or
299 Port_Cfg.DP.Lane_Count = DP_Lane_Count_4
300 then
301 (0 => PORT_TX_DW14_LN_LATENCY_OPTIM,
302 1 => 0,
303 2 => PORT_TX_DW14_LN_LATENCY_OPTIM,
304 3 => PORT_TX_DW14_LN_LATENCY_OPTIM)
305 elsif Port_Cfg.DP.Lane_Count = DP_Lane_Count_2 then
306 (0 => PORT_TX_DW14_LN_LATENCY_OPTIM,
307 1 => 0,
308 2 => PORT_TX_DW14_LN_LATENCY_OPTIM,
309 3 => 0)
310 else
311 (Lanes => 0));
312 begin
313 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
314
315 if Port_Cfg.Port in DDI_Phy_Port then
316 for Lane in Lanes loop
317 Unset_And_Set_Mask
318 (Register => PORT_TX (Port_Cfg.Port).DW14_LN (Lane),
319 Mask_Unset => PORT_TX_DW14_LN_LATENCY_OPTIM,
320 Mask_Set => Lane_Optim (Lane));
321 end loop;
322 end if;
323 end Pre_PLL;
324
Nico Huberf6266002017-02-03 12:17:28 +0100325end HW.GFX.GMA.DDI_Phy;