blob: 9821f8ebe3c3e9831e76b9b87bfb88d2c67470a3 [file] [log] [blame]
Nico Huber83693c82016-10-08 22:17:55 +02001--
2-- Copyright (C) 2016 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
Nico Huber125a29e2016-10-18 00:23:54 +02006-- the Free Software Foundation; either version 2 of the License, or
7-- (at your option) any later version.
Nico Huber83693c82016-10-08 22:17:55 +02008--
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 HW.Time;
16with HW.GFX.GMA.Config;
17with HW.GFX.GMA.PCH.FDI;
18with HW.GFX.GMA.Registers;
19
20with HW.Debug;
21with GNAT.Source_Info;
22
23package body HW.GFX.GMA.Connectors.FDI
24is
25
Nico Huber63dc9192018-06-09 17:42:19 +020026 use all type PCH.FDI.Training_Pattern;
27
Nico Huber83693c82016-10-08 22:17:55 +020028 PCH_FDI_CHICKEN_B_AND_C : constant := 1 * 2 ** 12;
29
30 type TX_CTL_Regs is array (GPU_FDI_Port) of Registers.Registers_Index;
31 TX_CTL : constant TX_CTL_Regs :=
32 (DIGI_B => Registers.FDI_TX_CTL_A,
33 DIGI_C => Registers.FDI_TX_CTL_B,
34 DIGI_D => Registers.FDI_TX_CTL_C);
35
36 FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
37 FDI_TX_CTL_VP_MASK : constant := 16#3f# * 2 ** 22;
38 FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT : constant := 19;
39 FDI_TX_CTL_ENHANCED_FRAMING_ENABLE : constant := 1 * 2 ** 18;
40 FDI_TX_CTL_FDI_PLL_ENABLE : constant := 1 * 2 ** 14;
41 FDI_TX_CTL_COMPOSITE_SYNC_SELECT : constant := 1 * 2 ** 11;
42 FDI_TX_CTL_AUTO_TRAIN_ENABLE : constant := 1 * 2 ** 10;
43 FDI_TX_CTL_AUTO_TRAIN_DONE : constant := 1 * 2 ** 1;
44
Nico Huber63dc9192018-06-09 17:42:19 +020045 function TP_SHIFT return Natural is
46 (if Config.Has_New_FDI_Source then 8 else 28);
47
48 function FDI_TX_CTL_TRAINING_PATTERN_MASK
49 return Word32 is (Shift_Left (3, TP_SHIFT));
50
51 function FDI_TX_CTL_TRAINING_PATTERN (TP : PCH.FDI.Training_Pattern)
52 return Word32 is
53 (case TP is
54 when TP_1 => Shift_Left (0, TP_SHIFT),
55 when TP_2 => Shift_Left (1, TP_SHIFT),
56 when TP_Idle => Shift_Left (2, TP_SHIFT),
57 when TP_None => Shift_Left (3, TP_SHIFT));
Nico Huber83693c82016-10-08 22:17:55 +020058
59 subtype FDI_TX_CTL_VP_T is Natural range 0 .. 3;
60 type Vswing_Preemph_Values is array (FDI_TX_CTL_VP_T) of Word32;
61 FDI_TX_CTL_VSWING_PREEMPH : constant Vswing_Preemph_Values :=
62 (0 => 16#00# * 2 ** 22,
63 1 => 16#3a# * 2 ** 22,
64 2 => 16#39# * 2 ** 22,
65 3 => 16#38# * 2 ** 22);
66
67 function FDI_TX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
68 is
69 begin
70 return Shift_Left
71 (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
72 FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT);
73 end FDI_TX_CTL_PORT_WIDTH_SEL;
74
75 ----------------------------------------------------------------------------
76
77 --
78 -- This is usually used with Ivy Bridge.
79 --
80 procedure Auto_Training
81 (Port_Cfg : in Port_Config;
82 Success : out Boolean)
83 with
84 Pre => Port_Cfg.Port in GPU_FDI_Port
85 is
86 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
87 begin
88 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
89
90 -- try each preemph/voltage pair twice
91 for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
92 loop
93 Registers.Unset_And_Set_Mask
94 (Register => TX_CTL (Port_Cfg.Port),
95 Mask_Unset => FDI_TX_CTL_VP_MASK or
96 FDI_TX_CTL_TRAINING_PATTERN_MASK,
97 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
98 FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
99 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
Nico Huber63dc9192018-06-09 17:42:19 +0200100 FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200101 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
102
103 PCH.FDI.Auto_Train (PCH_FDI_Port);
104
105 -- read at least twice
106 for I in 0 .. 3 loop
107 Registers.Is_Set_Mask
108 (Register => TX_CTL (Port_Cfg.Port),
109 Mask => FDI_TX_CTL_AUTO_TRAIN_DONE,
110 Result => Success);
111 exit when Success or I = 3;
112 Time.U_Delay (1);
113 end loop;
114 exit when Success;
115
116 Registers.Unset_And_Set_Mask
117 (Register => TX_CTL (Port_Cfg.Port),
118 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
119 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
120 FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200121 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200122
123 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
124 end loop;
125
126 if Success then
127 PCH.FDI.Enable_EC (PCH_FDI_Port);
128 else
129 Registers.Unset_Mask
130 (Register => TX_CTL (Port_Cfg.Port),
131 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
132
133 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
134 end if;
135 end Auto_Training;
136
137 ----------------------------------------------------------------------------
138
139 --
140 -- Used with Sandy Bridge (should work with Ivy Bridge too)
141 --
142 procedure Full_Training
143 (Port_Cfg : in Port_Config;
144 Success : out Boolean)
145 with
146 Pre => Port_Cfg.Port in GPU_FDI_Port
147 is
148 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
149 begin
150 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
151
152 -- try each preemph/voltage pair twice
153 for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
154 loop
155 Registers.Unset_And_Set_Mask
156 (Register => TX_CTL (Port_Cfg.Port),
157 Mask_Unset => FDI_TX_CTL_VP_MASK or
158 FDI_TX_CTL_TRAINING_PATTERN_MASK,
159 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
160 FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
Nico Huber63dc9192018-06-09 17:42:19 +0200161 FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200162 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
163
Nico Huber63dc9192018-06-09 17:42:19 +0200164 PCH.FDI.Train (PCH_FDI_Port, TP_1, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200165
166 if Success then
167 Registers.Unset_And_Set_Mask
168 (Register => TX_CTL (Port_Cfg.Port),
169 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200170 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_2));
Nico Huber83693c82016-10-08 22:17:55 +0200171 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
172
Nico Huber63dc9192018-06-09 17:42:19 +0200173 PCH.FDI.Train (PCH_FDI_Port, TP_2, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200174 end if;
175 exit when Success;
176
177 Registers.Unset_And_Set_Mask
178 (Register => TX_CTL (Port_Cfg.Port),
179 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
180 FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200181 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200182
183 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
184 end loop;
185
186 if Success then
187 Registers.Unset_And_Set_Mask
188 (Register => TX_CTL (Port_Cfg.Port),
189 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200190 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_None));
Nico Huber83693c82016-10-08 22:17:55 +0200191 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
192
Nico Huber63dc9192018-06-09 17:42:19 +0200193 PCH.FDI.Train (PCH_FDI_Port, TP_None, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200194 else
195 Registers.Unset_Mask
196 (Register => TX_CTL (Port_Cfg.Port),
197 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
198
199 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
200 end if;
201 end Full_Training;
202
203 ----------------------------------------------------------------------------
204
205 --
206 -- Used with original Ironlake (Nehalem CPU)
207 --
208 -- This is close to what Linux' i915 does. A comment in i915_reg.h
209 -- states that it uses only the lowest voltage / pre-emphasis levels
210 -- which is why we leave them at zero here and don't try different
211 -- values.
212 --
213 -- It's actually not clear from i915's code if the values really are
214 -- at zero or if it's just reusing what the Video BIOS set. Some code
215 -- in coreboot sets them to zero explicitly.
216 --
217 procedure Simple_Training
218 (Port_Cfg : in Port_Config;
219 Success : out Boolean)
220 with
221 Pre => Port_Cfg.Port in GPU_FDI_Port
222 is
223 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
224 begin
225 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
226
227 Registers.Unset_And_Set_Mask
228 (Register => TX_CTL (Port_Cfg.Port),
229 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
230 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
Nico Huber63dc9192018-06-09 17:42:19 +0200231 FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200232 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
233
Nico Huber63dc9192018-06-09 17:42:19 +0200234 PCH.FDI.Train (PCH_FDI_Port, TP_1, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200235
236 if Success then
237 Registers.Unset_And_Set_Mask
238 (Register => TX_CTL (Port_Cfg.Port),
239 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200240 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_2));
Nico Huber83693c82016-10-08 22:17:55 +0200241 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
242
Nico Huber63dc9192018-06-09 17:42:19 +0200243 PCH.FDI.Train (PCH_FDI_Port, TP_2, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200244 end if;
245
246 if Success then
247 Registers.Unset_And_Set_Mask
248 (Register => TX_CTL (Port_Cfg.Port),
249 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200250 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_None));
Nico Huber83693c82016-10-08 22:17:55 +0200251 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
252
Nico Huber63dc9192018-06-09 17:42:19 +0200253 PCH.FDI.Train (PCH_FDI_Port, TP_None, Success);
Nico Huber83693c82016-10-08 22:17:55 +0200254 else
255 Registers.Unset_And_Set_Mask
256 (Register => TX_CTL (Port_Cfg.Port),
257 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
258 FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200259 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200260 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
261
262 Registers.Unset_Mask
263 (Register => TX_CTL (Port_Cfg.Port),
264 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
265 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
266 end if;
267 end Simple_Training;
268
269 ----------------------------------------------------------------------------
270
271 procedure Pre_On (Port_Cfg : Port_Config)
272 is
Nico Huber63ec8362018-06-09 17:42:19 +0200273 Composite_Sel : constant Word32 :=
Nico Huber83693c82016-10-08 22:17:55 +0200274 (if Config.Has_FDI_Composite_Sel then
275 FDI_TX_CTL_COMPOSITE_SYNC_SELECT else 0);
276 begin
277 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
278
279 -- The PCH_FDI_CHICKEN_B_AND_C bit may not be changed when any of
280 -- both ports is active. Bandwidth calculations before calling us
281 -- should ensure this.
282 if Config.Has_FDI_C then
283 if Port_Cfg.Port = DIGI_D or
284 (Port_Cfg.Port = DIGI_C and
285 Port_Cfg.FDI.Lane_Count <= DP_Lane_Count_2)
286 then
287 Registers.Set_Mask
288 (Register => Registers.PCH_FDI_CHICKEN_B_C,
289 Mask => PCH_FDI_CHICKEN_B_AND_C);
290 elsif Port_Cfg.Port = DIGI_C then
291 Registers.Unset_Mask
292 (Register => Registers.PCH_FDI_CHICKEN_B_C,
293 Mask => PCH_FDI_CHICKEN_B_AND_C);
294 end if;
295 end if;
296
297 PCH.FDI.Pre_Train (PCH_FDIs (Port_Cfg.Port), Port_Cfg);
298
299 Registers.Write
300 (Register => TX_CTL (Port_Cfg.Port),
301 Value => FDI_TX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
302 FDI_TX_CTL_ENHANCED_FRAMING_ENABLE or
303 FDI_TX_CTL_FDI_PLL_ENABLE or
304 Composite_Sel or
Nico Huber63dc9192018-06-09 17:42:19 +0200305 FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200306 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
307 Time.U_Delay (100);
308 end Pre_On;
309
310 ----------------------------------------------------------------------------
311
312 procedure Post_On
313 (Port_Cfg : in Port_Config;
314 Success : out Boolean)
315 is
316 begin
317 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
318
319 case Config.FDI_Training is
320 when GMA.Simple_Training => Simple_Training (Port_Cfg, Success);
321 when GMA.Full_Training => Full_Training (Port_Cfg, Success);
322 when GMA.Auto_Training => Auto_Training (Port_Cfg, Success);
323 end case;
324 end Post_On;
325
326 ----------------------------------------------------------------------------
327
328 procedure Off (Port : GPU_FDI_Port; OT : Off_Type)
329 is
330 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port);
331 begin
332 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
333
334 Registers.Unset_And_Set_Mask
335 (Register => TX_CTL (Port),
336 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
337 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
338 FDI_TX_CTL_TRAINING_PATTERN_MASK,
Nico Huber63dc9192018-06-09 17:42:19 +0200339 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN (TP_1));
Nico Huber83693c82016-10-08 22:17:55 +0200340
341 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
342
343 if OT >= Clock_Off then
344 Registers.Unset_Mask
345 (Register => TX_CTL (Port),
346 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
347
348 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
349 end if;
350 end Off;
351
352end HW.GFX.GMA.Connectors.FDI;