| 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.GFX.GMA.Registers; |
| 15 | |
| 16 | package body HW.GFX.GMA.PLLs.DPLL is |
| 17 | |
| 18 | -- NOTE: Order of DPLLs is twisted => always use named associations! |
| 19 | |
| 20 | type Regs is array (Configurable_DPLLs) of Registers.Registers_Index; |
| 21 | |
| 22 | DPLL_CTL : constant Regs := Regs' |
| 23 | (DPLL1 => Registers.LCPLL2_CTL, |
| 24 | DPLL2 => Registers.WRPLL_CTL_1, |
| 25 | DPLL3 => Registers.WRPLL_CTL_2); |
| 26 | DPLL_CTL_PLL_ENABLE : constant := 1 * 2 ** 31; |
| 27 | |
| 28 | ---------------------------------------------------------------------------- |
| 29 | |
| 30 | DPLL_CFGR1 : constant Regs := Regs' |
| 31 | (DPLL1 => Registers.DPLL1_CFGR1, |
| 32 | DPLL2 => Registers.DPLL2_CFGR1, |
| 33 | DPLL3 => Registers.DPLL3_CFGR1); |
| 34 | DPLL_CFGR1_FREQUENCY_ENABLE : constant := 1 * 2 ** 31; |
| 35 | DPLL_CFGR1_DCO_FRACTION_SHIFT : constant := 9; |
| 36 | DPLL_CFGR1_DCO_FRACTION_MASK : constant := 16#7fff# * 2 ** 9; |
| 37 | DPLL_CFGR1_DCO_INTEGER_MASK : constant := 16#01ff# * 2 ** 0; |
| 38 | |
| 39 | DPLL_CFGR2 : constant Regs := Regs' |
| 40 | (DPLL1 => Registers.DPLL1_CFGR2, |
| 41 | DPLL2 => Registers.DPLL2_CFGR2, |
| 42 | DPLL3 => Registers.DPLL3_CFGR2); |
| 43 | DPLL_CFGR2_QDIV_RATIO_SHIFT : constant := 8; |
| 44 | DPLL_CFGR2_QDIV_RATIO_MASK : constant := 255 * 2 ** 8; |
| 45 | DPLL_CFGR2_QDIV_MODE : constant := 1 * 2 ** 7; |
| 46 | DPLL_CFGR2_KDIV_SHIFT : constant := 5; |
| 47 | DPLL_CFGR2_KDIV_MASK : constant := 3 * 2 ** 5; |
| 48 | DPLL_CFGR2_PDIV_SHIFT : constant := 2; |
| 49 | DPLL_CFGR2_PDIV_MASK : constant := 7 * 2 ** 2; |
| 50 | DPLL_CFGR2_CENTRAL_FREQ_MASK : constant := 3 * 2 ** 0; |
| 51 | DPLL_CFGR2_CENTRAL_FREQ_9600MHZ : constant := 0 * 2 ** 0; |
| 52 | DPLL_CFGR2_CENTRAL_FREQ_9000MHZ : constant := 1 * 2 ** 0; |
| 53 | DPLL_CFGR2_CENTRAL_FREQ_8400MHZ : constant := 3 * 2 ** 0; |
| 54 | |
| 55 | ---------------------------------------------------------------------------- |
| 56 | |
| 57 | HDMI_MODE : constant := 1 * 2 ** 5; |
| 58 | SSC : constant := 1 * 2 ** 4; |
| 59 | LINK_RATE_MASK : constant := 7 * 2 ** 1; |
| 60 | LINK_RATE_2700MHZ : constant := 0 * 2 ** 1; |
| 61 | LINK_RATE_1350MHZ : constant := 1 * 2 ** 1; |
| 62 | LINK_RATE_810MHZ : constant := 2 * 2 ** 1; |
| 63 | LINK_RATE_1620MHZ : constant := 3 * 2 ** 1; |
| 64 | LINK_RATE_1080MHZ : constant := 4 * 2 ** 1; |
| 65 | LINK_RATE_2160MHZ : constant := 5 * 2 ** 1; |
| 66 | OVERRIDE : constant := 1 * 2 ** 0; |
| 67 | |
| 68 | LOCK : constant := 1 * 2 ** 0; |
| 69 | |
| 70 | type Shifts is array (Configurable_DPLLs) of Natural; |
| 71 | DPLL_CTRL1_SHIFT : constant Shifts := |
| 72 | (DPLL1 => 6, DPLL2 => 12, DPLL3 => 18); |
| 73 | DPLL_STATUS_SHIFT : constant Shifts := |
| 74 | (DPLL1 => 8, DPLL2 => 16, DPLL3 => 24); |
| 75 | |
| 76 | function LINK_RATE (Link_Rate : DP_Bandwidth) return Word32 is |
| 77 | begin |
| 78 | return (case Link_Rate is |
| 79 | when DP_Bandwidth_5_4 => LINK_RATE_2700MHZ, |
| 80 | when DP_Bandwidth_2_7 => LINK_RATE_1350MHZ, |
| 81 | when DP_Bandwidth_1_62 => LINK_RATE_810MHZ); |
| 82 | end LINK_RATE; |
| 83 | |
| 84 | function DPLL_CTRL1_DPLLx |
| 85 | (Value : Word32; |
| 86 | PLL : Configurable_DPLLs) |
| 87 | return Word32 is |
| 88 | begin |
| 89 | return Shift_Left (Value, DPLL_CTRL1_SHIFT (PLL)); |
| 90 | end DPLL_CTRL1_DPLLx; |
| 91 | |
| 92 | function DPLL_STATUS_DPLLx_LOCK (PLL : Configurable_DPLLs) return Word32 is |
| 93 | begin |
| 94 | return Shift_Left (LOCK, DPLL_STATUS_SHIFT (PLL)); |
| 95 | end DPLL_STATUS_DPLLx_LOCK; |
| 96 | |
| 97 | ---------------------------------------------------------------------------- |
| 98 | |
| 99 | subtype PDiv_Range is Pos64 range 1 .. 7; |
| 100 | subtype QDiv_Range is Pos64 range 1 .. 255; |
| 101 | subtype KDiv_Range is Pos64 range 1 .. 5; |
| 102 | |
| 103 | type Central_Frequency is (CF_INVALID, CF_9600MHZ, CF_9000MHZ, CF_8400MHZ); |
| 104 | subtype Valid_Central_Freq is |
| 105 | Central_Frequency range CF_9600MHZ .. CF_8400MHZ; |
| 106 | |
| 107 | type CF_Pos is array (Valid_Central_Freq) of Pos64; |
| 108 | CF_Pos64 : constant CF_Pos := CF_Pos' |
| 109 | (CF_9600MHZ => 9_600_000_000, |
| 110 | CF_9000MHZ => 9_000_000_000, |
| 111 | CF_8400MHZ => 8_400_000_000); |
| 112 | |
| 113 | subtype DCO_Frequency is |
| 114 | Pos64 range 1 .. CF_Pos64 (CF_9600MHZ) + CF_Pos64 (CF_9600MHZ) / 100; |
| 115 | |
| 116 | function DPLL_CFGR1_DCO_FRACTION (DCO_Freq : DCO_Frequency) return Word32 |
| 117 | with |
| 118 | Pre => True |
| 119 | is |
| 120 | begin |
| 121 | return Shift_Left |
| 122 | (Word32 ((DCO_Freq * 2 ** 15) / 24_000_000) and 16#7fff#, |
| 123 | DPLL_CFGR1_DCO_FRACTION_SHIFT); |
| 124 | end DPLL_CFGR1_DCO_FRACTION; |
| 125 | |
| 126 | function DPLL_CFGR1_DCO_INTEGER (DCO_Freq : DCO_Frequency) return Word32 |
| 127 | with |
| 128 | Pre => True |
| 129 | is |
| 130 | begin |
| 131 | return Word32 (DCO_Freq / 24_000_000); |
| 132 | end DPLL_CFGR1_DCO_INTEGER; |
| 133 | |
| 134 | function DPLL_CFGR2_PDIV (PDiv : PDiv_Range) return Word32 is |
| 135 | begin |
| 136 | return Shift_Left |
| 137 | ((case PDiv is |
| 138 | when 1 => 0, |
| 139 | when 2 => 1, |
| 140 | when 3 => 2, |
| 141 | when 7 => 4, |
| 142 | when others => 4), |
| 143 | DPLL_CFGR2_PDIV_SHIFT); |
| 144 | end DPLL_CFGR2_PDIV; |
| 145 | |
| 146 | function DPLL_CFGR2_QDIV (QDiv : QDiv_Range) return Word32 is |
| 147 | begin |
| 148 | return Shift_Left (Word32 (QDiv), DPLL_CFGR2_QDIV_RATIO_SHIFT) or |
| 149 | (if QDiv /= 1 then DPLL_CFGR2_QDIV_MODE else 0); |
| 150 | end DPLL_CFGR2_QDIV; |
| 151 | |
| 152 | function DPLL_CFGR2_KDIV (KDiv : KDiv_Range) return Word32 is |
| 153 | begin |
| 154 | return Shift_Left |
| 155 | ((case KDiv is |
| 156 | when 5 => 0, |
| 157 | when 2 => 1, |
| 158 | when 3 => 2, |
| 159 | when 1 => 3, |
| 160 | when others => 0), |
| 161 | DPLL_CFGR2_KDIV_SHIFT); |
| 162 | end DPLL_CFGR2_KDIV; |
| 163 | |
| 164 | function DPLL_CFGR2_CENTRAL_FREQ (CF : Valid_Central_Freq) return Word32 is |
| 165 | begin |
| 166 | return (case CF is |
| 167 | when CF_9600MHZ => DPLL_CFGR2_CENTRAL_FREQ_9600MHZ, |
| 168 | when CF_9000MHZ => DPLL_CFGR2_CENTRAL_FREQ_9000MHZ, |
| 169 | when CF_8400MHZ => DPLL_CFGR2_CENTRAL_FREQ_8400MHZ); |
| 170 | end DPLL_CFGR2_CENTRAL_FREQ; |
| 171 | |
| 172 | ---------------------------------------------------------------------------- |
| 173 | |
| 174 | procedure Calculate_DPLL |
| 175 | (Dotclock : in Frequency_Type; |
| 176 | Central_Freq : out Central_Frequency; |
| 177 | DCO_Freq : out DCO_Frequency; |
| 178 | PDiv : out PDiv_Range; |
| 179 | QDiv : out QDiv_Range; |
| 180 | KDiv : out KDiv_Range) |
| 181 | with |
| 182 | Pre => True |
| 183 | is |
| 184 | Max_Pos_Deviation : constant := 1; |
| 185 | Max_Neg_Deviation : constant := 6; |
| 186 | |
| 187 | subtype Div_Range is Pos64 range 1 .. 98; |
| 188 | subtype Candidate_Index is Positive range 1 .. 36; |
| 189 | type Candidate_Array is array (Candidate_Index) of Div_Range; |
| 190 | type Candidate_List is record |
| 191 | Divs : Candidate_Array; |
| 192 | Count : Candidate_Index; |
| 193 | end record; |
| 194 | type Parity_Type is (Even, Odd); |
| 195 | type Candidates_Type is array (Parity_Type) of Candidate_List; |
| 196 | |
| 197 | Candidates : constant Candidates_Type := Candidates_Type' |
| 198 | (Even => Candidate_List' |
| 199 | (Divs => Candidate_Array' |
| 200 | (4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, |
| 201 | 48, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90, |
| 202 | 92, 96, 98), |
| 203 | Count => 36), |
| 204 | Odd => Candidate_List' |
| 205 | (Divs => Candidate_Array'(3, 5, 7, 9, 15, 21, 35, others => 1), |
| 206 | Count => 7)); |
| 207 | |
| 208 | Temp_Freq, |
| 209 | Allowed_Deviation : Pos64; |
| 210 | Deviation : Int64; |
| 211 | Temp_Central : DCO_Frequency; |
| 212 | Min_Deviation : Int64 := Int64'Last; |
| 213 | Div : Div_Range := Div_Range'Last; |
| 214 | begin |
| 215 | Central_Freq := CF_INVALID; |
| 216 | DCO_Freq := 1; |
| 217 | PDiv := 1; |
| 218 | QDiv := 1; |
| 219 | KDiv := 1; |
| 220 | |
| 221 | for Parity in Parity_Type loop |
| 222 | for CF in Valid_Central_Freq loop |
| 223 | Temp_Central := CF_Pos64 (CF); |
| 224 | for I in Candidate_Index range 1 .. Candidates (Parity).Count loop |
| 225 | Temp_Freq := Candidates (Parity).Divs (I) * 5 * Dotclock; |
| 226 | if Temp_Freq > Temp_Central then |
| 227 | Deviation := Temp_Freq - Temp_Central; |
| 228 | Allowed_Deviation := (Max_Pos_Deviation * Temp_Central) / 100; |
| 229 | else |
| 230 | Deviation := Temp_Central - Temp_Freq; |
| 231 | Allowed_Deviation := (Max_Neg_Deviation * Temp_Central) / 100; |
| 232 | end if; |
| 233 | if Deviation < Min_Deviation and |
| 234 | Deviation < Allowed_Deviation |
| 235 | then |
| 236 | Min_Deviation := Deviation; |
| 237 | Central_Freq := CF; |
| 238 | DCO_Freq := Temp_Freq; |
| 239 | Div := Candidates (Parity).Divs (I); |
| 240 | end if; |
| 241 | end loop; |
| 242 | end loop; |
| 243 | exit when Central_Freq /= CF_INVALID; |
| 244 | end loop; |
| 245 | |
| 246 | if Central_Freq /= CF_INVALID then |
| 247 | if Div mod 2 = 0 then |
| 248 | pragma Assert (Div /= 1); |
| 249 | pragma Assert (Div > 1); |
| 250 | Div := Div / 2; |
| 251 | if Div = 1 or Div = 3 or Div = 5 then |
| 252 | -- 2, 6 and 10 |
| 253 | PDiv := 2; |
| 254 | QDiv := 1; |
| 255 | KDiv := Div; |
| 256 | elsif Div mod 2 = 0 then |
| 257 | -- divisible by 4 |
| 258 | PDiv := 2; |
| 259 | QDiv := Div / 2; |
| 260 | KDiv := 2; |
| 261 | elsif Div mod 3 = 0 then |
| 262 | -- divisible by 6 |
| 263 | PDiv := 3; |
| 264 | QDiv := Div / 3; |
| 265 | KDiv := 2; |
| 266 | elsif Div mod 7 = 0 then |
| 267 | -- divisible by 14 |
| 268 | PDiv := 7; |
| 269 | QDiv := Div / 7; |
| 270 | KDiv := 2; |
| 271 | end if; |
| 272 | elsif Div = 7 or Div = 21 or Div = 35 then |
| 273 | -- 7, 21 and 35 |
| 274 | PDiv := 7; |
| 275 | QDiv := 1; |
| 276 | KDiv := Div / 7; |
| 277 | elsif Div = 3 or Div = 9 or Div = 15 then |
| 278 | -- 3, 9 and 15 |
| 279 | PDiv := 3; |
| 280 | QDiv := 1; |
| 281 | KDiv := Div / 3; |
| 282 | elsif Div = 5 then |
| 283 | -- 5 |
| 284 | PDiv := 5; |
| 285 | QDiv := 1; |
| 286 | KDiv := 1; |
| 287 | end if; |
| 288 | end if; |
| 289 | end Calculate_DPLL; |
| 290 | |
| 291 | ---------------------------------------------------------------------------- |
| 292 | |
| 293 | procedure On |
| 294 | (PLL : in Configurable_DPLLs; |
| 295 | Port_Cfg : in Port_Config; |
| 296 | Success : out Boolean) |
| 297 | is |
| 298 | Central_Freq : Central_Frequency; |
| 299 | DCO_Freq : DCO_Frequency; |
| 300 | PDiv : PDiv_Range; |
| 301 | QDiv : QDiv_Range; |
| 302 | KDiv : KDiv_Range; |
| 303 | begin |
| 304 | if Port_Cfg.Display = DP then |
| 305 | Registers.Unset_And_Set_Mask |
| 306 | (Register => Registers.DPLL_CTRL1, |
| 307 | Mask_Unset => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or |
| 308 | DPLL_CTRL1_DPLLx (SSC, PLL) or |
| 309 | DPLL_CTRL1_DPLLx (LINK_RATE_MASK, PLL), |
| 310 | Mask_Set => DPLL_CTRL1_DPLLx (LINK_RATE |
| 311 | (Port_Cfg.DP.Bandwidth), PLL) or |
| 312 | DPLL_CTRL1_DPLLx (OVERRIDE, PLL)); |
| 313 | Registers.Posting_Read (Registers.DPLL_CTRL1); |
| 314 | Success := True; |
| 315 | else |
| 316 | Calculate_DPLL |
| 317 | (Port_Cfg.Mode.Dotclock, Central_Freq, DCO_Freq, PDiv, QDiv, KDiv); |
| 318 | Success := Central_Freq /= CF_INVALID; |
| 319 | if Success then |
| 320 | Registers.Unset_And_Set_Mask |
| 321 | (Register => Registers.DPLL_CTRL1, |
| 322 | Mask_Unset => DPLL_CTRL1_DPLLx (SSC, PLL), |
| 323 | Mask_Set => DPLL_CTRL1_DPLLx (HDMI_MODE, PLL) or |
| 324 | DPLL_CTRL1_DPLLx (OVERRIDE, PLL)); |
| 325 | Registers.Write |
| 326 | (Register => DPLL_CFGR1 (PLL), |
| 327 | Value => DPLL_CFGR1_FREQUENCY_ENABLE or |
| 328 | DPLL_CFGR1_DCO_FRACTION (DCO_Freq) or |
| 329 | DPLL_CFGR1_DCO_INTEGER (DCO_Freq)); |
| 330 | Registers.Write |
| 331 | (Register => DPLL_CFGR2 (PLL), |
| 332 | Value => DPLL_CFGR2_QDIV (QDiv) or |
| 333 | DPLL_CFGR2_KDIV (KDiv) or |
| 334 | DPLL_CFGR2_PDIV (PDiv) or |
| 335 | DPLL_CFGR2_CENTRAL_FREQ (Central_Freq)); |
| 336 | Registers.Posting_Read (Registers.DPLL_CTRL1); |
| 337 | Registers.Posting_Read (DPLL_CFGR1 (PLL)); |
| 338 | Registers.Posting_Read (DPLL_CFGR2 (PLL)); |
| 339 | end if; |
| 340 | end if; |
| 341 | |
| 342 | if Success then |
| 343 | Registers.Write |
| 344 | (Register => DPLL_CTL (PLL), |
| 345 | Value => DPLL_CTL_PLL_ENABLE); |
| 346 | Registers.Wait_Set_Mask |
| 347 | (Register => Registers.DPLL_STATUS, |
| 348 | Mask => DPLL_STATUS_DPLLx_LOCK (PLL)); |
| 349 | end if; |
| 350 | end On; |
| 351 | |
| 352 | procedure Off (PLL : Configurable_DPLLs) is |
| 353 | begin |
| 354 | Registers.Unset_Mask (DPLL_CTL (PLL), DPLL_CTL_PLL_ENABLE); |
| 355 | end Off; |
| 356 | |
| 357 | end HW.GFX.GMA.PLLs.DPLL; |