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