| Nico Huber | 83693c8 | 2016-10-08 22:17:55 +0200 | [diff] [blame^] | 1 | -- |
| 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 | |
| 14 | with HW.Time; |
| 15 | with HW.GFX.GMA.Config; |
| 16 | with HW.GFX.GMA.PCH.FDI; |
| 17 | with HW.GFX.GMA.Registers; |
| 18 | |
| 19 | with HW.Debug; |
| 20 | with GNAT.Source_Info; |
| 21 | |
| 22 | package body HW.GFX.GMA.Connectors.FDI |
| 23 | is |
| 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 | |
| 342 | end HW.GFX.GMA.Connectors.FDI; |