blob: 2d295f72cd97bdc18594b8296f89cc93e7feef85 [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
6-- the Free Software Foundation; version 2 of the License.
7--
8-- This program is distributed in the hope that it will be useful,
9-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11-- GNU General Public License for more details.
12--
13
14with HW.Time;
15with HW.GFX.GMA.Config;
16with HW.GFX.GMA.PCH.FDI;
17with HW.GFX.GMA.Registers;
18
19with HW.Debug;
20with GNAT.Source_Info;
21
22package body HW.GFX.GMA.Connectors.FDI
23is
24
25 PCH_FDI_CHICKEN_B_AND_C : constant := 1 * 2 ** 12;
26
27 type TX_CTL_Regs is array (GPU_FDI_Port) of Registers.Registers_Index;
28 TX_CTL : constant TX_CTL_Regs :=
29 (DIGI_B => Registers.FDI_TX_CTL_A,
30 DIGI_C => Registers.FDI_TX_CTL_B,
31 DIGI_D => Registers.FDI_TX_CTL_C);
32
33 FDI_TX_CTL_FDI_TX_ENABLE : constant := 1 * 2 ** 31;
34 FDI_TX_CTL_VP_MASK : constant := 16#3f# * 2 ** 22;
35 FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT : constant := 19;
36 FDI_TX_CTL_ENHANCED_FRAMING_ENABLE : constant := 1 * 2 ** 18;
37 FDI_TX_CTL_FDI_PLL_ENABLE : constant := 1 * 2 ** 14;
38 FDI_TX_CTL_COMPOSITE_SYNC_SELECT : constant := 1 * 2 ** 11;
39 FDI_TX_CTL_AUTO_TRAIN_ENABLE : constant := 1 * 2 ** 10;
40 FDI_TX_CTL_AUTO_TRAIN_DONE : constant := 1 * 2 ** 1;
41
42 TP_SHIFT : constant := (if Config.CPU <= Sandybridge then 28 else 8);
43 FDI_TX_CTL_TRAINING_PATTERN_MASK : constant := 3 * 2 ** TP_SHIFT;
44 FDI_TX_CTL_TRAINING_PATTERN_1 : constant := 0 * 2 ** TP_SHIFT;
45 FDI_TX_CTL_TRAINING_PATTERN_2 : constant := 1 * 2 ** TP_SHIFT;
46 FDI_TX_CTL_TRAINING_PATTERN_IDLE : constant := 2 * 2 ** TP_SHIFT;
47 FDI_TX_CTL_TRAINING_PATTERN_NORMAL : constant := 3 * 2 ** TP_SHIFT;
48
49 subtype FDI_TX_CTL_VP_T is Natural range 0 .. 3;
50 type Vswing_Preemph_Values is array (FDI_TX_CTL_VP_T) of Word32;
51 FDI_TX_CTL_VSWING_PREEMPH : constant Vswing_Preemph_Values :=
52 (0 => 16#00# * 2 ** 22,
53 1 => 16#3a# * 2 ** 22,
54 2 => 16#39# * 2 ** 22,
55 3 => 16#38# * 2 ** 22);
56
57 function FDI_TX_CTL_PORT_WIDTH_SEL (Lane_Count : DP_Lane_Count) return Word32
58 is
59 begin
60 return Shift_Left
61 (Word32 (Lane_Count_As_Integer (Lane_Count)) - 1,
62 FDI_TX_CTL_PORT_WIDTH_SEL_SHIFT);
63 end FDI_TX_CTL_PORT_WIDTH_SEL;
64
65 ----------------------------------------------------------------------------
66
67 --
68 -- This is usually used with Ivy Bridge.
69 --
70 procedure Auto_Training
71 (Port_Cfg : in Port_Config;
72 Success : out Boolean)
73 with
74 Pre => Port_Cfg.Port in GPU_FDI_Port
75 is
76 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
77 begin
78 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
79
80 -- try each preemph/voltage pair twice
81 for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
82 loop
83 Registers.Unset_And_Set_Mask
84 (Register => TX_CTL (Port_Cfg.Port),
85 Mask_Unset => FDI_TX_CTL_VP_MASK or
86 FDI_TX_CTL_TRAINING_PATTERN_MASK,
87 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
88 FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
89 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
90 FDI_TX_CTL_TRAINING_PATTERN_1);
91 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
92
93 PCH.FDI.Auto_Train (PCH_FDI_Port);
94
95 -- read at least twice
96 for I in 0 .. 3 loop
97 Registers.Is_Set_Mask
98 (Register => TX_CTL (Port_Cfg.Port),
99 Mask => FDI_TX_CTL_AUTO_TRAIN_DONE,
100 Result => Success);
101 exit when Success or I = 3;
102 Time.U_Delay (1);
103 end loop;
104 exit when Success;
105
106 Registers.Unset_And_Set_Mask
107 (Register => TX_CTL (Port_Cfg.Port),
108 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
109 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
110 FDI_TX_CTL_TRAINING_PATTERN_MASK,
111 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
112
113 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
114 end loop;
115
116 if Success then
117 PCH.FDI.Enable_EC (PCH_FDI_Port);
118 else
119 Registers.Unset_Mask
120 (Register => TX_CTL (Port_Cfg.Port),
121 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
122
123 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
124 end if;
125 end Auto_Training;
126
127 ----------------------------------------------------------------------------
128
129 --
130 -- Used with Sandy Bridge (should work with Ivy Bridge too)
131 --
132 procedure Full_Training
133 (Port_Cfg : in Port_Config;
134 Success : out Boolean)
135 with
136 Pre => Port_Cfg.Port in GPU_FDI_Port
137 is
138 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
139 begin
140 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
141
142 -- try each preemph/voltage pair twice
143 for VP2 in Natural range 0 .. FDI_TX_CTL_VP_T'Last * 2 + 1
144 loop
145 Registers.Unset_And_Set_Mask
146 (Register => TX_CTL (Port_Cfg.Port),
147 Mask_Unset => FDI_TX_CTL_VP_MASK or
148 FDI_TX_CTL_TRAINING_PATTERN_MASK,
149 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
150 FDI_TX_CTL_VSWING_PREEMPH (VP2 / 2) or
151 FDI_TX_CTL_TRAINING_PATTERN_1);
152 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
153
154 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
155
156 if Success then
157 Registers.Unset_And_Set_Mask
158 (Register => TX_CTL (Port_Cfg.Port),
159 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
160 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_2);
161 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
162
163 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
164 end if;
165 exit when Success;
166
167 Registers.Unset_And_Set_Mask
168 (Register => TX_CTL (Port_Cfg.Port),
169 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
170 FDI_TX_CTL_TRAINING_PATTERN_MASK,
171 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
172
173 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
174 end loop;
175
176 if Success then
177 Registers.Unset_And_Set_Mask
178 (Register => TX_CTL (Port_Cfg.Port),
179 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
180 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
181 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
182
183 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
184 else
185 Registers.Unset_Mask
186 (Register => TX_CTL (Port_Cfg.Port),
187 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
188
189 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
190 end if;
191 end Full_Training;
192
193 ----------------------------------------------------------------------------
194
195 --
196 -- Used with original Ironlake (Nehalem CPU)
197 --
198 -- This is close to what Linux' i915 does. A comment in i915_reg.h
199 -- states that it uses only the lowest voltage / pre-emphasis levels
200 -- which is why we leave them at zero here and don't try different
201 -- values.
202 --
203 -- It's actually not clear from i915's code if the values really are
204 -- at zero or if it's just reusing what the Video BIOS set. Some code
205 -- in coreboot sets them to zero explicitly.
206 --
207 procedure Simple_Training
208 (Port_Cfg : in Port_Config;
209 Success : out Boolean)
210 with
211 Pre => Port_Cfg.Port in GPU_FDI_Port
212 is
213 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port_Cfg.Port);
214 begin
215 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
216
217 Registers.Unset_And_Set_Mask
218 (Register => TX_CTL (Port_Cfg.Port),
219 Mask_Unset => FDI_TX_CTL_TRAINING_PATTERN_MASK,
220 Mask_Set => FDI_TX_CTL_FDI_TX_ENABLE or
221 FDI_TX_CTL_TRAINING_PATTERN_1);
222 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
223
224 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_1, Success);
225
226 if Success then
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_TRAINING_PATTERN_2);
231 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
232
233 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_2, Success);
234 end if;
235
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,
240 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_NORMAL);
241 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
242
243 PCH.FDI.Train (PCH_FDI_Port, PCH.FDI.TP_None, Success);
244 else
245 Registers.Unset_And_Set_Mask
246 (Register => TX_CTL (Port_Cfg.Port),
247 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
248 FDI_TX_CTL_TRAINING_PATTERN_MASK,
249 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
250 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
251
252 Registers.Unset_Mask
253 (Register => TX_CTL (Port_Cfg.Port),
254 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
255 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
256 end if;
257 end Simple_Training;
258
259 ----------------------------------------------------------------------------
260
261 procedure Pre_On (Port_Cfg : Port_Config)
262 is
263 Composite_Sel : constant :=
264 (if Config.Has_FDI_Composite_Sel then
265 FDI_TX_CTL_COMPOSITE_SYNC_SELECT else 0);
266 begin
267 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
268
269 -- The PCH_FDI_CHICKEN_B_AND_C bit may not be changed when any of
270 -- both ports is active. Bandwidth calculations before calling us
271 -- should ensure this.
272 if Config.Has_FDI_C then
273 if Port_Cfg.Port = DIGI_D or
274 (Port_Cfg.Port = DIGI_C and
275 Port_Cfg.FDI.Lane_Count <= DP_Lane_Count_2)
276 then
277 Registers.Set_Mask
278 (Register => Registers.PCH_FDI_CHICKEN_B_C,
279 Mask => PCH_FDI_CHICKEN_B_AND_C);
280 elsif Port_Cfg.Port = DIGI_C then
281 Registers.Unset_Mask
282 (Register => Registers.PCH_FDI_CHICKEN_B_C,
283 Mask => PCH_FDI_CHICKEN_B_AND_C);
284 end if;
285 end if;
286
287 PCH.FDI.Pre_Train (PCH_FDIs (Port_Cfg.Port), Port_Cfg);
288
289 Registers.Write
290 (Register => TX_CTL (Port_Cfg.Port),
291 Value => FDI_TX_CTL_PORT_WIDTH_SEL (Port_Cfg.FDI.Lane_Count) or
292 FDI_TX_CTL_ENHANCED_FRAMING_ENABLE or
293 FDI_TX_CTL_FDI_PLL_ENABLE or
294 Composite_Sel or
295 FDI_TX_CTL_TRAINING_PATTERN_1);
296 Registers.Posting_Read (TX_CTL (Port_Cfg.Port));
297 Time.U_Delay (100);
298 end Pre_On;
299
300 ----------------------------------------------------------------------------
301
302 procedure Post_On
303 (Port_Cfg : in Port_Config;
304 Success : out Boolean)
305 is
306 begin
307 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
308
309 case Config.FDI_Training is
310 when GMA.Simple_Training => Simple_Training (Port_Cfg, Success);
311 when GMA.Full_Training => Full_Training (Port_Cfg, Success);
312 when GMA.Auto_Training => Auto_Training (Port_Cfg, Success);
313 end case;
314 end Post_On;
315
316 ----------------------------------------------------------------------------
317
318 procedure Off (Port : GPU_FDI_Port; OT : Off_Type)
319 is
320 PCH_FDI_Port : constant PCH.FDI_Port_Type := PCH_FDIs (Port);
321 begin
322 pragma Debug (Debug.Put_Line (GNAT.Source_Info.Enclosing_Entity));
323
324 Registers.Unset_And_Set_Mask
325 (Register => TX_CTL (Port),
326 Mask_Unset => FDI_TX_CTL_FDI_TX_ENABLE or
327 FDI_TX_CTL_AUTO_TRAIN_ENABLE or
328 FDI_TX_CTL_TRAINING_PATTERN_MASK,
329 Mask_Set => FDI_TX_CTL_TRAINING_PATTERN_1);
330
331 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Rx_Off);
332
333 if OT >= Clock_Off then
334 Registers.Unset_Mask
335 (Register => TX_CTL (Port),
336 Mask => FDI_TX_CTL_FDI_PLL_ENABLE);
337
338 PCH.FDI.Off (PCH_FDI_Port, PCH.FDI.Clock_Off);
339 end if;
340 end Off;
341
342end HW.GFX.GMA.Connectors.FDI;