1 /* 2 * Copyright 2024, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "TigerLakePLL.h" 7 8 #include "accelerant.h" 9 10 #include <math.h> 11 12 13 #undef TRACE 14 #define TRACE_PLL 15 #ifdef TRACE_PLL 16 # define TRACE(x...) _sPrintf("intel_extreme: " x) 17 #else 18 # define TRACE(x...) 19 #endif 20 21 #define ERROR(x...) _sPrintf("intel_extreme: " x) 22 #define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) 23 24 25 /** 26 * Compute the best PLL parameters for a given symbol clock frequency for a DVI or HDMI port. 27 * 28 * This is the algorithm documented in Intel Documentation: IHD-OS-TGL-Vol 12-12.21, page 182 29 * 30 * The clock generation on Tiger Lake is in two steps: first, a DCO generates a fractional 31 * multiplication of the reference clock (in the GHz range). Then, 3 dividers bring this back into 32 * the symbol clock frequency range. 33 * 34 * Reference clock (24 or 19.2MHz, as defined in DSSM Reference Frequency register) 35 * || 36 * vv 37 * DCO (multiply by non-integer value defined in DPLL_CFGCR0 register) 38 * || 39 * vv 40 * "DCO frequency" in the range 7998 - 10000 MHz 41 * || 42 * vv 43 * Divide by P, Q, and K 44 * || 45 * vv 46 * AFE clock (PLL output) 47 * || 48 * vv 49 * Divide by 5 (fixed) 50 * || 51 * vv 52 * Symbol clock (same as Pixel clock for 24-bit RGB) 53 * 54 * The algorithm to configure this is: 55 * - Iterate over all allowed values for the divider obtained by P, Q and K 56 * - Determine the one that results in the DCO frequency being as close as possible to 8999MHz 57 * - Compute the corresponding values for P, Q and K and the DCO multiplier 58 * 59 * Since the DCO is a fractional multiplier (it can multiply by non-integer values), it will always 60 * be possible to set the DCO to a "close enough" value in its available range. The only constraint 61 * is getting it as close as possible to the midpoint (8999MHz), and at least have it in the 62 * allowed range (7998 to 10000MHz). If this is not possible (too low or too high pixel clock), a 63 * different video mode or setup will be needed (for example, enable dual link DVI to divide the 64 * clock by two). 65 * 66 * This also means that this algorithm is independant of the initial reference frequency: there 67 * will always be a way to setup the DCO so that it outputs the frequency computed here, no matter 68 * what the input clock is. 69 * 70 * Unlinke in previous hardware generations, there is no need to satisfy multiple constraints at 71 * the same time because of several stages of dividers and multipliers each with their own 72 * frequency range. 73 * 74 * DCO multiplier = DCO integer + DCO fraction / 2^15 75 * Symbol clock frequency = DCO multiplier * RefFreq in MHz / (5 * Pdiv * Qdiv * Kdiv) 76 * 77 * The symbol clock is the clock of the DVI/HDMI port. It defines how much time is needed to send 78 * one "symbol", which corresponds to 8 bits of useful data for each channel (Red, Green and Blue). 79 * 80 * In our case (8 bit RGB videomode), the symbol clock is equal to the pixel rate. It would need 81 * to be adjusted for 10 and 12-bit modes (more bits per pixel) as well as for YUV420 modes (the U 82 * and V parts are sent only for some pixels, reducing the total bandwidth). 83 * 84 * @param[in] freq Desired symbol clock frequency in kHz 85 * @param[out] Pdiv, Qdiv, Kdiv: dividers for the PLL 86 * @param[out] bestdco Required DCO frequency, in the range 7998 to 10000, in MHz 87 */ 88 bool 89 ComputeHdmiDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco) 90 { 91 int bestdiv = 0; 92 float dco = 0, dcocentrality = 0; 93 float bestdcocentrality = 999999; 94 95 // The allowed values for the divider depending on the allowed values for P, Q, and K: 96 // - P can be 2, 3, 5 or 7 97 // - K can be 1, 2, or 3 98 // - Q can be 1 to 255 if K = 2. Otherwise, Q must be 1. 99 // Not all possible combinations are listed here, more can be added if needed to reach lower 100 // resolutions and refresh rates (probably not so interesting, this already allows to reach 101 // frequencies low enough for all practical uses in a standard setup). 102 const int dividerlist[] = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, 103 40, 42, 44, 48, 50, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90, 92, 104 96, 98, 100, 102, 3, 5, 7, 9, 15, 21 }; 105 const float dcomin = 7998; 106 const float dcomax = 10000; 107 const float dcomid = (dcomin + dcomax) / 2; 108 109 float afeclk = (5 * freq) / 1000; 110 111 for (size_t i = 0; i < B_COUNT_OF(dividerlist); i++) { 112 int div = dividerlist[i]; 113 dco = afeclk * div; 114 if (dco <= dcomax && dco >= dcomin) { 115 dcocentrality = fabs(dco - dcomid); 116 if (dcocentrality < bestdcocentrality) { 117 bestdcocentrality = dcocentrality; 118 bestdiv = div; 119 *bestdco = dco; 120 } 121 } 122 } 123 124 if (bestdiv != 0) { 125 // Good divider found 126 if (bestdiv % 2 == 0) { 127 // Divider is even 128 if (bestdiv == 2) { 129 *Pdiv = 2; 130 *Qdiv = 1; 131 *Kdiv = 1; 132 } else if (bestdiv % 4 == 0) { 133 *Pdiv = 2; 134 *Qdiv = bestdiv / 4; 135 *Kdiv = 2; 136 } else if (bestdiv % 6 == 0) { 137 *Pdiv = 3; 138 *Qdiv = bestdiv / 6; 139 *Kdiv = 2; 140 } else if (bestdiv % 5 == 0) { 141 *Pdiv = 5; 142 *Qdiv = bestdiv / 10; 143 *Kdiv = 2; 144 } else if (bestdiv % 14 == 0) { 145 *Pdiv = 7; 146 *Qdiv = bestdiv / 14; 147 *Kdiv = 2; 148 } 149 } else { 150 // Divider is odd 151 if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) { 152 *Pdiv = bestdiv; 153 *Qdiv = 1; 154 *Kdiv = 1; 155 } else { 156 // Divider is 9, 15, or 21 157 *Pdiv = bestdiv / 3; 158 *Qdiv = 1; 159 *Kdiv = 3; 160 } 161 } 162 163 // SUCCESS 164 return true; 165 } else { 166 // No good divider found 167 // FAIL, try a different frequency (different video mode) 168 return false; 169 } 170 } 171 172 173 /*! In the case of DisplayPort, the interface between the computer and the display is not just a 174 * stream of pixels, but instead a packetized link. This means the interface does not need to be 175 * running in sync with the pixel clock. Instead, a selection of well-defined frequencies are used. 176 * 177 * This also would allow to use a "spread spectrum" clock, reducing interferences without degrading 178 * picture quality. 179 * 180 * Here we just set it to the isecond lowest predefined frequency of 2.7GHz, which will be enough 181 * for displays up to full HD, and a little more. 182 * 183 * TODO decide when we have to use one of the higher frequencies. See "DisplayPort Mode PLL values" 184 * in IHD-OS-TGL-Vol 12-12.21, page 178. However, my machine uses a slightly different value than 185 * what's in Intel datasheets (with Intel values, bestdco should be 8100 and not 8090). I'm not 186 * sure why that is so, possibly they shift the fractional value in the CFGR0 register by 9 bits 187 * instead of 10? But replicating what my BIOS does here allows me to skip the PLL 188 * programming, a good idea, because it seems we don't yet know how to properly disable and 189 * re-train displayport once it is switched off. 190 */ 191 bool 192 ComputeDisplayPortDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco) 193 { 194 *Pdiv = 3; 195 *Qdiv = 1; 196 *Kdiv = 2; 197 *bestdco = 8090; 198 199 return true; 200 } 201 202 203 /*! Actually program the computed values (from the functions above) into the PLL, and start it. 204 * 205 * TODO: detect if the PLL is already running at the right frequency, and in that case, skip 206 * reprogramming it altogether. In the case of DisplayPort, most often there is no need to change 207 * anything once the clock has been initially set. 208 */ 209 status_t 210 ProgramPLL(int which, int Pdiv, int Qdiv, int Kdiv, float dco) 211 { 212 // Set up the registers for PLL access for the requested PLL 213 uint32 DPLL_CFGCR0; 214 uint32 DPLL_CFGCR1; 215 uint32 DPLL_ENABLE; 216 uint32 DPLL_SPREAD_SPECTRUM; 217 218 switch (which) { 219 case 0: 220 DPLL_ENABLE = TGL_DPLL0_ENABLE; 221 DPLL_SPREAD_SPECTRUM = TGL_DPLL0_SPREAD_SPECTRUM; 222 DPLL_CFGCR0 = TGL_DPLL0_CFGCR0; 223 DPLL_CFGCR1 = TGL_DPLL0_CFGCR1; 224 break; 225 case 1: 226 DPLL_ENABLE = TGL_DPLL1_ENABLE; 227 DPLL_SPREAD_SPECTRUM = TGL_DPLL1_SPREAD_SPECTRUM; 228 DPLL_CFGCR0 = TGL_DPLL1_CFGCR0; 229 DPLL_CFGCR1 = TGL_DPLL1_CFGCR1; 230 break; 231 case 4: 232 DPLL_ENABLE = TGL_DPLL4_ENABLE; 233 DPLL_SPREAD_SPECTRUM = TGL_DPLL4_SPREAD_SPECTRUM; 234 DPLL_CFGCR0 = TGL_DPLL4_CFGCR0; 235 DPLL_CFGCR1 = TGL_DPLL4_CFGCR1; 236 break; 237 default: 238 return B_BAD_VALUE; 239 } 240 241 // Find the reference frequency (24 or 19.2MHz) 242 int ref_khz = gInfo->shared_info->pll_info.reference_frequency; 243 244 // There is an automatic divide-by-two in this case 245 if (ref_khz == 38400) 246 ref_khz = 19200; 247 248 float ref = ref_khz / 1000.0f; 249 250 // Compute the DCO divider integer and fractional parts 251 uint32 dco_int = (uint32)floorf(dco / ref); 252 uint32 dco_frac = (uint32)ceilf((dco / ref - dco_int) * (1 << 15)); 253 254 int32 dco_reg = dco_int | (dco_frac << TGL_DPLL_DCO_FRACTION_SHIFT); 255 256 int32 dividers = 0; 257 switch (Pdiv) { 258 case 2: 259 dividers |= TGL_DPLL_PDIV_2; 260 break; 261 case 3: 262 dividers |= TGL_DPLL_PDIV_3; 263 break; 264 case 5: 265 dividers |= TGL_DPLL_PDIV_5; 266 break; 267 case 7: 268 dividers |= TGL_DPLL_PDIV_7; 269 break; 270 default: 271 return B_BAD_VALUE; 272 } 273 switch (Kdiv) { 274 case 1: 275 dividers |= TGL_DPLL_KDIV_1; 276 break; 277 case 2: 278 dividers |= TGL_DPLL_KDIV_2; 279 break; 280 case 3: 281 dividers |= TGL_DPLL_KDIV_3; 282 break; 283 default: 284 return B_BAD_VALUE; 285 } 286 if (Qdiv != 1) 287 dividers |= (Qdiv << TGL_DPLL_QDIV_RATIO_SHIFT) | TGL_DPLL_QDIV_ENABLE; 288 289 int32 initialState = read32(DPLL_ENABLE); 290 TRACE("DPLL_ENABLE(%" B_PRIx32 ") initial value = %" B_PRIx32 "\n", DPLL_ENABLE, initialState); 291 292 if (initialState & TGL_DPLL_LOCK) { 293 int32 oldDCO = read32(DPLL_CFGCR0); 294 int32 oldDividers = read32(DPLL_CFGCR1); 295 TRACE("DPLL already locked, checking current settings: DCO %" B_PRIx32 " -> %" B_PRIx32 296 ", dividers %" B_PRIx32 " -> %" B_PRIx32 "\n", 297 oldDCO, dco_reg, oldDividers, dividers); 298 299 if ((oldDCO == dco_reg) && (oldDividers == dividers)) { 300 TRACE("DPLL already configured at the right frequency, no changes needed\n"); 301 return B_OK; 302 } 303 } 304 305 // Before we start, disable the PLL 306 write32(DPLL_ENABLE, read32(DPLL_ENABLE) & ~TGL_DPLL_ENABLE); 307 while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) != 0); 308 TRACE("PLL is unlocked\n"); 309 310 // Enable PLL power 311 write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_POWER_ENABLE); 312 313 // Wait for PLL to be powered up 314 while ((read32(DPLL_ENABLE) & TGL_DPLL_POWER_STATE) == 0); 315 TRACE("PLL is powered on\n"); 316 317 // Deactivate spread spectrum 318 write32(DPLL_SPREAD_SPECTRUM, read32(DPLL_SPREAD_SPECTRUM) & ~TGL_DPLL_SSC_ENABLE); 319 320 // Configure DCO 321 write32(DPLL_CFGCR0, dco_reg); 322 323 // Configure dividers 324 write32(DPLL_CFGCR1, dividers); 325 TRACE("DFGCR0(%" B_PRIx32 ") = %" B_PRIx32 ", CFGCR1(%" B_PRIx32 ") = %" B_PRIx32 326 " (int = %" B_PRId32 ", frac = %" B_PRId32 ")\n", DPLL_CFGCR0, dco_reg, 327 DPLL_CFGCR1, dividers, dco_int, dco_frac); 328 329 // Read to make sure all writes are flushed to the hardware 330 read32(DPLL_CFGCR1); 331 332 // TODO Display voltage frequency switching? 333 334 // Enable PLL 335 write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_ENABLE); 336 TRACE("DPLL_ENABLE(%" B_PRIx32 ") = %" B_PRIx32 "\n", DPLL_ENABLE, read32(DPLL_ENABLE)); 337 338 // Wait for PLL to be enabled 339 while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) == 0); 340 TRACE("PLL is locked\n"); 341 342 // TODO Display voltage frequency switching? 343 344 return B_OK; 345 } 346