| 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 |
| Nico Huber | 125a29e | 2016-10-18 00:23:54 +0200 | [diff] [blame] | 6 | -- the Free Software Foundation; either version 2 of the License, or |
| 7 | -- (at your option) any later version. |
| Nico Huber | 83693c8 | 2016-10-08 22:17:55 +0200 | [diff] [blame] | 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 | |
| 15 | with HW.Time; |
| 16 | with HW.GFX.GMA.Config; |
| 17 | with HW.GFX.GMA.PCH.FDI; |
| 18 | with HW.GFX.GMA.Registers; |
| 19 | |
| 20 | with HW.Debug; |
| 21 | with GNAT.Source_Info; |
| 22 | |
| 23 | package body HW.GFX.GMA.Connectors.FDI |
| 24 | is |
| 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 | |
| Nico Huber | ef3b093 | 2018-06-09 18:26:38 +0200 | [diff] [blame] | 43 | TP_SHIFT : constant := (if Config.Has_New_FDI_Source then 8 else 28); |
| Nico Huber | 83693c8 | 2016-10-08 22:17:55 +0200 | [diff] [blame] | 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 | |
| 343 | end HW.GFX.GMA.Connectors.FDI; |