diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 9595b1df47..bc58da1a2d 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -929,6 +929,85 @@ struct intel_brightness_legacy { #define LCPLL_CLK_FREQ_337_5_BDW (2 << 26) #define LCPLL_CD_SOURCE_FCLK (1 << 21) +// Tigerlake PLLs +#define TGL_DPCLKA_CFGCR0 0x164280 +#define TGL_DPCLKA_DDIC_CLOCK_OFF (1 << 24) +#define TGL_DPCLKA_TC6_CLOCK_OFF (1 << 23) +#define TGL_DPCLKA_TC5_CLOCK_OFF (1 << 22) +#define TGL_DPCLKA_TC4_CLOCK_OFF (1 << 21) +#define TGL_DPCLKA_TC3_CLOCK_OFF (1 << 14) +#define TGL_DPCLKA_TC2_CLOCK_OFF (1 << 13) +#define TGL_DPCLKA_TC1_CLOCK_OFF (1 << 12) +#define TGL_DPCLKA_DDIB_CLOCK_OFF (1 << 11) +#define TGL_DPCLKA_DDIA_CLOCK_OFF (1 << 10) +#define TGL_DPCLKA_DDIC_CLOCK_SELECT (3 << 4) +#define TGL_DPCLKA_DDIB_CLOCK_SELECT (3 << 2) +#define TGL_DPCLKA_DDIB_CLOCK_SELECT_SHIFT 2 +#define TGL_DPCLKA_DDIA_CLOCK_SELECT (3 << 0) + +#define TGL_DPLL0_CFGCR0 0x164284 +#define TGL_DPLL1_CFGCR0 0x16428C +#define TGL_TBTPLL_CFGCR0 0x16429C +#define TGL_DPLL4_CFGCR0 0x164294 +#define TGL_DPLL_DCO_FRACTION (0x7FFF << 10) +#define TGL_DPLL_DCO_FRACTION_SHIFT 10 +#define TGL_DPLL_DCO_INTEGER (0x3FF << 0) + +#define TGL_DPLL0_CFGCR1 0x164288 +#define TGL_DPLL1_CFGCR1 0x164290 +#define TGL_TBTPLL_CFGCR1 0x1642A0 +#define TGL_DPLL4_CFGCR1 0x164298 +#define TGL_DPLL_QDIV_RATIO (0xFF << 10) +#define TGL_DPLL_QDIV_RATIO_SHIFT 10 +#define TGL_DPLL_QDIV_ENABLE (1 << 9) +#define TGL_DPLL_KDIV (7 << 6) +#define TGL_DPLL_KDIV_1 (1 << 6) +#define TGL_DPLL_KDIV_2 (2 << 6) +#define TGL_DPLL_KDIV_3 (4 << 6) +#define TGL_DPLL_PDIV (0xF << 2) +#define TGL_DPLL_PDIV_2 (1 << 2) +#define TGL_DPLL_PDIV_3 (2 << 2) +#define TGL_DPLL_PDIV_5 (4 << 2) +#define TGL_DPLL_PDIV_7 (8 << 2) +#define TGL_DPLL_CFSELOVRD (3 << 0) + +#define TGL_DPLL0_DIV0 0x164B00 +#define TGL_DPLL1_DIV0 0x164C00 +#define TGL_DPLL4_DIV0 0x164E00 +#define TGL_DPLL_I_TRUELOCK_CRITERIA (3 << 30) +#define TGL_DPLL_I_EARLYLOCK_CRITERIA (3 << 28) +#define TGL_DPLL_I_AFC_STARTUP (7 << 25) +#define TGL_DPLL_I_DIV_RETIMER_EN (1 << 24) +#define TGL_DPLL_I_GAIN_CTRL (7 << 21) +#define TGL_DPLL_I_INTEGRAL_COEFF (0xF << 16) +#define TGL_DPLL_I_PROPORTIONAL_COEFF (0xF << 12) +#define TGL_DPLL_I_FB_PREDIVIDER (0xF << 8) +#define TGL_DPLL_I_FB_DIVIDER_M2 (0xFF << 0) + +#define TGL_DPLL0_ENABLE 0x46010 +#define TGL_DPLL1_ENABLE 0x46014 +#define TGL_DPLL4_ENABLE 0x46018 +#define TGL_DPLL_ENABLE (1 << 31) +#define TGL_DPLL_LOCK (1 << 30) +#define TGL_DPLL_POWER_ENABLE (1 << 27) +#define TGL_DPLL_POWER_STATE (1 << 26) + +#define TGL_DPLL0_SPREAD_SPECTRUM 0x164B10 +#define TGL_DPLL1_SPREAD_SPECTRUM 0x164C10 +#define TGL_DPLL4_SPREAD_SPECTRUM 0x164E10 +#define TGL_DPLL_IREF_NDIVRATIO (3 << 29) +#define TGL_DPLL_SSC_STEP_NUMBER_OFFSET (3 << 26) +#define TGL_DPLL_SSC_INJECTION_ADAPTIVE_GAIN_CHANGE_ENABLE (1 << 25) +#define TGL_DPLL_SSC_INJECTION_ENABLE (1 << 24) +#define TGL_DPLL_SSC_STEP_LENGTH (0xFF << 16) +#define TGL_DPLL_SSC_FLL_UPDATE (3 << 14) +#define TGL_DPLL_SSC_STEP_NUMBER (7 << 11) +#define TGL_DPLL_SSC_OPENLOOP (1 << 10) +#define TGL_DPLL_SSC_ENABLE (1 << 9) +#define TGL_DPLL_SSC_FLL_ENABLE (1 << 8) +#define TGL_DPLL_SSC_BIAS_GUARD_BAND (3 << 6) +#define TGL_DPLL_SSC_INIT_DCO_AMP (0x3F << 0) + #define FUSE_STRAP 0x42014 #define HSW_CDCLK_LIMIT (1 << 24) diff --git a/src/add-ons/accelerants/intel_extreme/Jamfile b/src/add-ons/accelerants/intel_extreme/Jamfile index 14ef36f55e..7405470e42 100644 --- a/src/add-ons/accelerants/intel_extreme/Jamfile +++ b/src/add-ons/accelerants/intel_extreme/Jamfile @@ -21,5 +21,6 @@ Addon intel_extreme.accelerant : PanelFitter.cpp Ports.cpp Pipes.cpp + TigerLakePLL.cpp : be $(TARGET_LIBSTDC++) libaccelerantscommon.a ; diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.cpp b/src/add-ons/accelerants/intel_extreme/Pipes.cpp index e079147ba0..21cf52ea5e 100644 --- a/src/add-ons/accelerants/intel_extreme/Pipes.cpp +++ b/src/add-ons/accelerants/intel_extreme/Pipes.cpp @@ -193,31 +193,43 @@ Pipe::_ConfigureTranscoder(display_mode* target) | ((uint32)target->timing.v_display - 1)); #endif } else { - //on Skylake timing is already done in ConfigureTimings() - - TRACE("%s: trans conf reg: 0x%" B_PRIx32"\n", __func__, - read32(DDI_SKL_TRANS_CONF_A + fPipeOffset)); - TRACE("%s: trans DDI func ctl reg: 0x%" B_PRIx32"\n", __func__, - read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset)); - switch ((read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset) & PIPE_DDI_MODESEL_MASK) - >> PIPE_DDI_MODESEL_SHIFT) { - case PIPE_DDI_MODE_DVI: - TRACE("%s: Transcoder uses DVI mode\n", __func__); - break; - case PIPE_DDI_MODE_DP_SST: - TRACE("%s: Transcoder uses DP SST mode\n", __func__); - break; - case PIPE_DDI_MODE_DP_MST: - TRACE("%s: Transcoder uses DP MST mode\n", __func__); - break; - default: - TRACE("%s: Transcoder uses HDMI mode\n", __func__); - break; - } + // on Skylake and later, timing is already done in ConfigureTimings() } } +uint32 +Pipe::TranscoderMode() +{ + if (gInfo->shared_info->device_type.Generation() < 9) { + ERROR("%s: Don't know how to get transcoder mode on older generations\n", __func__); + return 0; + } + + TRACE("%s: trans conf reg: 0x%" B_PRIx32 "\n", __func__, + read32(DDI_SKL_TRANS_CONF_A + fPipeOffset)); + TRACE("%s: trans DDI func ctl reg: 0x%" B_PRIx32 "\n", __func__, + read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset)); + uint32 value = (read32(PIPE_DDI_FUNC_CTL_A + fPipeOffset) & PIPE_DDI_MODESEL_MASK) + >> PIPE_DDI_MODESEL_SHIFT; + switch (value) { + case PIPE_DDI_MODE_DVI: + TRACE("%s: Transcoder uses DVI mode\n", __func__); + break; + case PIPE_DDI_MODE_DP_SST: + TRACE("%s: Transcoder uses DP SST mode\n", __func__); + break; + case PIPE_DDI_MODE_DP_MST: + TRACE("%s: Transcoder uses DP MST mode\n", __func__); + break; + default: + TRACE("%s: Transcoder uses HDMI mode\n", __func__); + break; + } + return value; +} + + status_t Pipe::SetFDILink(const display_timing& timing, uint32 linkBandwidth, uint32 lanes, uint32 bitsPerPixel) { diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.h b/src/add-ons/accelerants/intel_extreme/Pipes.h index de97a33309..023f01f17c 100644 --- a/src/add-ons/accelerants/intel_extreme/Pipes.h +++ b/src/add-ons/accelerants/intel_extreme/Pipes.h @@ -58,6 +58,7 @@ public: uint32 pixelClock, port_index pllForPort, uint32* pllSel); + uint32 TranscoderMode(); // access to the various parts of the pipe ::FDILink* FDI() diff --git a/src/add-ons/accelerants/intel_extreme/Ports.cpp b/src/add-ons/accelerants/intel_extreme/Ports.cpp index 974a41d0e9..eb6de752e4 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.cpp +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -21,9 +21,11 @@ #include "accelerant.h" #include "accelerant_protos.h" -#include "FlexibleDisplayInterface.h" #include "intel_extreme.h" + +#include "FlexibleDisplayInterface.h" #include "PanelFitter.h" +#include "TigerLakePLL.h" #include @@ -1675,13 +1677,13 @@ DisplayPort::SetPipe(Pipe* pipe) CALLED(); if (pipe == NULL) { - ERROR("%s: Invalid pipe provided!\n", __func__); + ERROR("%s: Invalid pipe provided!\n", __PRETTY_FUNCTION__); return B_ERROR; } // TODO: UnAssignPipe? This likely needs reworked a little if (fPipe != NULL) { - ERROR("%s: Can't reassign display pipe (yet)\n", __func__); + ERROR("%s: Can't reassign display pipe (yet)\n", __PRETTY_FUNCTION__); return B_ERROR; } @@ -1690,7 +1692,8 @@ DisplayPort::SetPipe(Pipe* pipe) // generation 7 gfx IvyBridge/IVB uses 2 bits (b29-30) on the eDP port. // on all other DP ports pipe selections works differently (indirect). // fixme: implement.. - TRACE("%s: Assuming pipe is assigned by BIOS (fixme)\n", __func__); + TRACE("%s: Assuming pipe %d is assigned by BIOS to port %d (fixme)\n", __PRETTY_FUNCTION__, + pipe->Index(), PortIndex()); fPipe = pipe; @@ -2258,19 +2261,20 @@ DigitalDisplayInterface::SetPipe(Pipe* pipe) CALLED(); if (pipe == NULL) { - ERROR("%s: Invalid pipe provided!\n", __func__); + ERROR("%s: Invalid pipe provided!\n", __PRETTY_FUNCTION__); return B_ERROR; } // TODO: UnAssignPipe? This likely needs reworked a little if (fPipe != NULL) { - ERROR("%s: Can't reassign display pipe (yet)\n", __func__); + ERROR("%s: Can't reassign display pipe (yet)\n", __PRETTY_FUNCTION__); return B_ERROR; } // all DDI ports pipe selections works differently than on the old port types (indirect). // fixme: implement.. - TRACE("%s: Assuming pipe is assigned by BIOS (fixme)\n", __func__); + TRACE("%s: Assuming pipe %d is assigned by BIOS to port %d (fixme)\n", __PRETTY_FUNCTION__, + pipe->Index(), PortIndex()); fPipe = pipe; @@ -2467,7 +2471,10 @@ DigitalDisplayInterface::_SetPortLinkGen8(const display_timing& timing, uint32 p //fixme: always so on pre gen 9? uint32 linkBandwidth = 270000; //khz - if (gInfo->shared_info->device_type.Generation() >= 9) { + if (gInfo->shared_info->device_type.Generation() >= 11) { + ERROR("%s: DDI PLL selection not implemented for Gen11, " + "assuming default DP-link reference\n", __func__); + } else if (gInfo->shared_info->device_type.Generation() >= 9) { if (pllSel != 0xff) { linkBandwidth = (read32(SKL_DPLL_CTRL1) >> (1 + 6 * pllSel)) & SKL_DPLL_DP_LINKRATE_MASK; switch (linkBandwidth) { @@ -2686,13 +2693,16 @@ DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode) // Program general pipe config fPipe->Configure(target); + // TODO create a PLL class that can abstract the details of setting up the clock generation, + // and instanciate the correct type depending on the card generation uint32 pllSel = 0xff; // no PLL selected if (gInfo->shared_info->device_type.Generation() <= 8) { unsigned int r2_out, n2_out, p_out; hsw_ddi_calculate_wrpll( hardwareTarget.pixel_clock * 1000 /* in Hz */, &r2_out, &n2_out, &p_out); - } else { + // TODO the computed PLL values are not used for anything? + } else if (gInfo->shared_info->device_type.Generation() <= 11) { skl_wrpll_params wrpll_params; skl_ddi_calculate_wrpll( hardwareTarget.pixel_clock * 1000 /* in Hz */, @@ -2702,6 +2712,49 @@ DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode) hardwareTarget.pixel_clock, PortIndex(), &pllSel); + } else { + // Tiger Lake + // See volume 12, page 180, "HDMI Mode Combo PHY Programming" + int p, q, k; + float dco; + uint32 mode = fPipe->TranscoderMode(); + if ((mode == PIPE_DDI_MODE_DVI || mode == PIPE_DDI_MODE_HDMI) + && ComputeHdmiDpll(hardwareTarget.pixel_clock, &p, &q, &k, &dco)) { + TRACE("PLL settings: DCO=%f, P,Q,K=%d,%d,%d\n", dco, p, q, k); + } else if ((mode == PIPE_DDI_MODE_DP_SST || mode == PIPE_DDI_MODE_DP_MST) + && ComputeDisplayPortDpll(hardwareTarget.pixel_clock, &p, &q, &k, &dco)) { + TRACE("PLL settings: DCO=%f, P,Q,K=%d,%d,%d\n", dco, p, q, k); + } else { + ERROR("%s: Could not find a matching PLL setting\n", __func__); + return B_ERROR; + } + + // TODO write a proper way to assign PLLs to pipes and ports. + int chosenPLL = 0; + if (PortIndex() == 7) + chosenPLL = 1; + TRACE("Using DPLL %d for port %d. PLL settings: DCO=%f, P,Q,K=%d,%d,%d\n", chosenPLL, + PortIndex(), dco, p, q, k); + ProgramPLL(chosenPLL, p, q, k, dco); + + // Configure DPLL mapping to port then turn on DPLL clock + uint32 config = read32(TGL_DPCLKA_CFGCR0); + TRACE("PLL configuration before changes: %" B_PRIx32 "\n", config); + if (chosenPLL == 0) { + config |= TGL_DPCLKA_DDIA_CLOCK_OFF; + config &= TGL_DPCLKA_DDIA_CLOCK_SELECT; + write32(TGL_DPCLKA_CFGCR0, config); + config &= ~TGL_DPCLKA_DDIA_CLOCK_OFF; + write32(TGL_DPCLKA_CFGCR0, config); + } else { + config |= TGL_DPCLKA_DDIB_CLOCK_OFF; + config &= TGL_DPCLKA_DDIB_CLOCK_SELECT; + config |= 1 << TGL_DPCLKA_DDIB_CLOCK_SELECT_SHIFT; + write32(TGL_DPCLKA_CFGCR0, config); + config &= ~TGL_DPCLKA_DDIB_CLOCK_OFF; + write32(TGL_DPCLKA_CFGCR0, config); + } + TRACE("PLL configuration after changes: %" B_PRIx32 "\n", config); } // Program target display mode diff --git a/src/add-ons/accelerants/intel_extreme/TigerLakePLL.cpp b/src/add-ons/accelerants/intel_extreme/TigerLakePLL.cpp new file mode 100644 index 0000000000..8fef3fabb7 --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/TigerLakePLL.cpp @@ -0,0 +1,345 @@ +/* + * Copyright 2024, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#include "TigerLakePLL.h" + +#include "accelerant.h" + +#include + + +#undef TRACE +#define TRACE_PLL +#ifdef TRACE_PLL +# define TRACE(x...) _sPrintf("intel_extreme: " x) +#else +# define TRACE(x...) +#endif + +#define ERROR(x...) _sPrintf("intel_extreme: " x) +#define CALLED(x...) TRACE("CALLED %s\n", __PRETTY_FUNCTION__) + + +/** + * Compute the best PLL parameters for a given symbol clock frequency for a DVI or HDMI port. + * + * This is the algorithm documented in Intel Documentation: IHD-OS-TGL-Vol 12-12.21, page 182 + * + * The clock generation on Tiger Lake is in two steps: first, a DCO generates a fractional + * multiplication of the reference clock (in the GHz range). Then, 3 dividers bring this back into + * the symbol clock frequency range. + * + * Reference clock (24 or 19.2MHz, as defined in DSSM Reference Frequency register) + * || + * vv + * DCO (multiply by non-integer value defined in DPLL_CFGCR0 register) + * || + * vv + * "DCO frequency" in the range 7998 - 10000 MHz + * || + * vv + * Divide by P, Q, and K + * || + * vv + * AFE clock (PLL output) + * || + * vv + * Divide by 5 (fixed) + * || + * vv + * Symbol clock (same as Pixel clock for 24-bit RGB) + * + * The algorithm to configure this is: + * - Iterate over all allowed values for the divider obtained by P, Q and K + * - Determine the one that results in the DCO frequency being as close as possible to 8999MHz + * - Compute the corresponding values for P, Q and K and the DCO multiplier + * + * Since the DCO is a fractional multiplier (it can multiply by non-integer values), it will always + * be possible to set the DCO to a "close enough" value in its available range. The only constraint + * is getting it as close as possible to the midpoint (8999MHz), and at least have it in the + * allowed range (7998 to 10000MHz). If this is not possible (too low or too high pixel clock), a + * different video mode or setup will be needed (for example, enable dual link DVI to divide the + * clock by two). + * + * This also means that this algorithm is independant of the initial reference frequency: there + * will always be a way to setup the DCO so that it outputs the frequency computed here, no matter + * what the input clock is. + * + * Unlinke in previous hardware generations, there is no need to satisfy multiple constraints at + * the same time because of several stages of dividers and multipliers each with their own + * frequency range. + * + * DCO multiplier = DCO integer + DCO fraction / 2^15 + * Symbol clock frequency = DCO multiplier * RefFreq in MHz / (5 * Pdiv * Qdiv * Kdiv) + * + * The symbol clock is the clock of the DVI/HDMI port. It defines how much time is needed to send + * one "symbol", which corresponds to 8 bits of useful data for each channel (Red, Green and Blue). + * + * In our case (8 bit RGB videomode), the symbol clock is equal to the pixel rate. It would need + * to be adjusted for 10 and 12-bit modes (more bits per pixel) as well as for YUV420 modes (the U + * and V parts are sent only for some pixels, reducing the total bandwidth). + * + * @param[in] freq Desired symbol clock frequency in kHz + * @param[out] Pdiv, Qdiv, Kdiv: dividers for the PLL + * @param[out] bestdco Required DCO frequency, in the range 7998 to 10000, in MHz + */ +bool +ComputeHdmiDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco) +{ + int bestdiv = 0; + float dco = 0, dcocentrality = 0; + float bestdcocentrality = 999999; + + // The allowed values for the divider depending on the allowed values for P, Q, and K: + // - P can be 2, 3, 5 or 7 + // - K can be 1, 2, or 3 + // - Q can be 1 to 255 if K = 2. Otherwise, Q must be 1. + // Not all possible combinations are listed here, more can be added if needed to reach lower + // resolutions and refresh rates (probably not so interesting, this already allows to reach + // frequencies low enough for all practical uses in a standard setup). + const int dividerlist[] = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 30, 32, 36, + 40, 42, 44, 48, 50, 52, 54, 56, 60, 64, 66, 68, 70, 72, 76, 78, 80, 84, 88, 90, 92, + 96, 98, 100, 102, 3, 5, 7, 9, 15, 21 }; + const float dcomin = 7998; + const float dcomax = 10000; + const float dcomid = (dcomin + dcomax) / 2; + + float afeclk = (5 * freq) / 1000; + + for (size_t i = 0; i < B_COUNT_OF(dividerlist); i++) { + int div = dividerlist[i]; + dco = afeclk * div; + if (dco <= dcomax && dco >= dcomin) { + dcocentrality = fabs(dco - dcomid); + if (dcocentrality < bestdcocentrality) { + bestdcocentrality = dcocentrality; + bestdiv = div; + *bestdco = dco; + } + } + } + + if (bestdiv != 0) { + // Good divider found + if (bestdiv % 2 == 0) { + // Divider is even + if (bestdiv == 2) { + *Pdiv = 2; + *Qdiv = 1; + *Kdiv = 1; + } else if (bestdiv % 4 == 0) { + *Pdiv = 2; + *Qdiv = bestdiv / 4; + *Kdiv = 2; + } else if (bestdiv % 6 == 0) { + *Pdiv = 3; + *Qdiv = bestdiv / 6; + *Kdiv = 2; + } else if (bestdiv % 5 == 0) { + *Pdiv = 5; + *Qdiv = bestdiv / 10; + *Kdiv = 2; + } else if (bestdiv % 14 == 0) { + *Pdiv = 7; + *Qdiv = bestdiv / 14; + *Kdiv = 2; + } + } else { + // Divider is odd + if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) { + *Pdiv = bestdiv; + *Qdiv = 1; + *Kdiv = 1; + } else { + // Divider is 9, 15, or 21 + *Pdiv = bestdiv / 3; + *Qdiv = 1; + *Kdiv = 3; + } + } + + // SUCCESS + return true; + } else { + // No good divider found + // FAIL, try a different frequency (different video mode) + return false; + } +} + + +/*! In the case of DisplayPort, the interface between the computer and the display is not just a + * stream of pixels, but instead a packetized link. This means the interface does not need to be + * running in sync with the pixel clock. Instead, a selection of well-defined frequencies are used. + * + * This also would allow to use a "spread spectrum" clock, reducing interferences without degrading + * picture quality. + * + * Here we just set it to the isecond lowest predefined frequency of 2.7GHz, which will be enough + * for displays up to full HD, and a little more. + * + * TODO decide when we have to use one of the higher frequencies. See "DisplayPort Mode PLL values" + * in IHD-OS-TGL-Vol 12-12.21, page 178. However, my machine uses a slightly different value than + * what's in Intel datasheets (with Intel values, bestdco should be 8100 and not 8090). I'm not + * sure why that is so, possibly they shift the fractional value in the CFGR0 register by 9 bits + * instead of 10? But replicating what my BIOS does here allows me to skip the PLL + * programming, a good idea, because it seems we don't yet know how to properly disable and + * re-train displayport once it is switched off. + */ +bool +ComputeDisplayPortDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco) +{ + *Pdiv = 3; + *Qdiv = 1; + *Kdiv = 2; + *bestdco = 8090; + + return true; +} + + +/*! Actually program the computed values (from the functions above) into the PLL, and start it. + * + * TODO: detect if the PLL is already running at the right frequency, and in that case, skip + * reprogramming it altogether. In the case of DisplayPort, most often there is no need to change + * anything once the clock has been initially set. + */ +status_t +ProgramPLL(int which, int Pdiv, int Qdiv, int Kdiv, float dco) +{ + // Set up the registers for PLL access for the requested PLL + uint32 DPLL_CFGCR0; + uint32 DPLL_CFGCR1; + uint32 DPLL_ENABLE; + uint32 DPLL_SPREAD_SPECTRUM; + + switch (which) { + case 0: + DPLL_ENABLE = TGL_DPLL0_ENABLE; + DPLL_SPREAD_SPECTRUM = TGL_DPLL0_SPREAD_SPECTRUM; + DPLL_CFGCR0 = TGL_DPLL0_CFGCR0; + DPLL_CFGCR1 = TGL_DPLL0_CFGCR1; + break; + case 1: + DPLL_ENABLE = TGL_DPLL1_ENABLE; + DPLL_SPREAD_SPECTRUM = TGL_DPLL1_SPREAD_SPECTRUM; + DPLL_CFGCR0 = TGL_DPLL1_CFGCR0; + DPLL_CFGCR1 = TGL_DPLL1_CFGCR1; + break; + case 4: + DPLL_ENABLE = TGL_DPLL4_ENABLE; + DPLL_SPREAD_SPECTRUM = TGL_DPLL4_SPREAD_SPECTRUM; + DPLL_CFGCR0 = TGL_DPLL4_CFGCR0; + DPLL_CFGCR1 = TGL_DPLL4_CFGCR1; + break; + default: + return B_BAD_VALUE; + } + + // Find the reference frequency (24 or 19.2MHz) + int ref_khz = gInfo->shared_info->pll_info.reference_frequency; + + // There is an automatic divide-by-two in this case + if (ref_khz == 38400) + ref_khz = 19200; + + float ref = ref_khz / 1000.0f; + + // Compute the DCO divider integer and fractional parts + uint32 dco_int = (uint32)floorf(dco / ref); + uint32 dco_frac = (uint32)ceilf((dco / ref - dco_int) * (1 << 15)); + + int32 dco_reg = dco_int | (dco_frac << TGL_DPLL_DCO_FRACTION_SHIFT); + + int32 dividers = 0; + switch (Pdiv) { + case 2: + dividers |= TGL_DPLL_PDIV_2; + break; + case 3: + dividers |= TGL_DPLL_PDIV_3; + break; + case 5: + dividers |= TGL_DPLL_PDIV_5; + break; + case 7: + dividers |= TGL_DPLL_PDIV_7; + break; + default: + return B_BAD_VALUE; + } + switch (Kdiv) { + case 1: + dividers |= TGL_DPLL_KDIV_1; + break; + case 2: + dividers |= TGL_DPLL_KDIV_2; + break; + case 3: + dividers |= TGL_DPLL_KDIV_3; + break; + default: + return B_BAD_VALUE; + } + if (Qdiv != 1) + dividers |= (Qdiv << TGL_DPLL_QDIV_RATIO_SHIFT) | TGL_DPLL_QDIV_ENABLE; + + int32 initialState = read32(DPLL_ENABLE); + TRACE("DPLL_ENABLE(%" B_PRIx32 ") initial value = %" B_PRIx32 "\n", DPLL_ENABLE, initialState); + + if (initialState & TGL_DPLL_LOCK) { + int32 oldDCO = read32(DPLL_CFGCR0); + int32 oldDividers = read32(DPLL_CFGCR1); + TRACE("DPLL already locked, checking current settings: DCO %" B_PRIx32 " -> %" B_PRIx32 + ", dividers %" B_PRIx32 " -> %" B_PRIx32 "\n", + oldDCO, dco_reg, oldDividers, dividers); + + if ((oldDCO == dco_reg) && (oldDividers == dividers)) { + TRACE("DPLL already configured at the right frequency, no changes needed\n"); + return B_OK; + } + } + + // Before we start, disable the PLL + write32(DPLL_ENABLE, read32(DPLL_ENABLE) & ~TGL_DPLL_ENABLE); + while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) != 0); + TRACE("PLL is unlocked\n"); + + // Enable PLL power + write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_POWER_ENABLE); + + // Wait for PLL to be powered up + while ((read32(DPLL_ENABLE) & TGL_DPLL_POWER_STATE) == 0); + TRACE("PLL is powered on\n"); + + // Deactivate spread spectrum + write32(DPLL_SPREAD_SPECTRUM, read32(DPLL_SPREAD_SPECTRUM) & ~TGL_DPLL_SSC_ENABLE); + + // Configure DCO + write32(DPLL_CFGCR0, dco_reg); + + // Configure dividers + write32(DPLL_CFGCR1, dividers); + TRACE("DFGCR0(%" B_PRIx32 ") = %" B_PRIx32 ", CFGCR1(%" B_PRIx32 ") = %" B_PRIx32 + " (int = %" B_PRId32 ", frac = %" B_PRId32 ")\n", DPLL_CFGCR0, dco_reg, + DPLL_CFGCR1, dividers, dco_int, dco_frac); + + // Read to make sure all writes are flushed to the hardware + read32(DPLL_CFGCR1); + + // TODO Display voltage frequency switching? + + // Enable PLL + write32(DPLL_ENABLE, read32(DPLL_ENABLE) | TGL_DPLL_ENABLE); + TRACE("DPLL_ENABLE(%" B_PRIx32 ") = %" B_PRIx32 "\n", DPLL_ENABLE, read32(DPLL_ENABLE)); + + // Wait for PLL to be enabled + while ((read32(DPLL_ENABLE) & TGL_DPLL_LOCK) == 0); + TRACE("PLL is locked\n"); + + // TODO Display voltage frequency switching? + + return B_OK; +} diff --git a/src/add-ons/accelerants/intel_extreme/TigerLakePLL.h b/src/add-ons/accelerants/intel_extreme/TigerLakePLL.h new file mode 100644 index 0000000000..595a27824c --- /dev/null +++ b/src/add-ons/accelerants/intel_extreme/TigerLakePLL.h @@ -0,0 +1,20 @@ +/* + * Copyright 2024, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ + +#ifndef TIGERLAKEPLL_H +#define TIGERLAKEPLL_H + +#include "intel_extreme.h" + +#include + + +bool ComputeHdmiDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco); +bool ComputeDisplayPortDpll(int freq, int* Pdiv, int* Qdiv, int* Kdiv, float* bestdco); + +status_t ProgramPLL(int which, int Pdiv, int Qdiv, int Kdiv, float dco); + + +#endif /* !TIGERLAKEPLL_H */ diff --git a/src/add-ons/accelerants/intel_extreme/pll.cpp b/src/add-ons/accelerants/intel_extreme/pll.cpp index b8e08e7f63..1e73b40db0 100644 --- a/src/add-ons/accelerants/intel_extreme/pll.cpp +++ b/src/add-ons/accelerants/intel_extreme/pll.cpp @@ -472,8 +472,6 @@ refclk_activate_ilk(bool hasPanel) { CALLED(); - // aka, our engineers hate you - bool wantsSSC; bool hasCK505; if (gInfo->shared_info->pch_info == INTEL_PCH_IBX) {