mirror of
https://review.haiku-os.org/haiku
synced 2024-11-23 07:18:40 +01:00
intel_extreme: DPLL configuration for Tiger Lake
The DPLL selection registers have changed again somewhere between Skylake and Tiger Lake. Our code was trying to read/write the Skylake registers on hardware where they don't exist anymore. Introduce the new Tiger Lake registers and implement enough of it to get things working on my machine (but probably only on my machine). Also add a bit of specialization of DisplayPort which I think was not done correctly on previous hardware either: for DisplayPort, the link rate is selected from a handful of allowed frequencies, instead of closely matching the pixel clock. Things left TODO: - Write a proper PLL allocation system to ensure each display gets assigned its own PLL (unless multiple displays use the same timings). For now it is hardcoded to what I want on my machine. - Fix the DisplayPort PLL computation to use the values from Intel datasheets, not the ones used by my machine which are somehow different. - Fix the DisplayPort PLL computation to select one of the several available frequencies, allowing resolutions higher than Full HD which require higher clocks. - Fix DisplayPort link training or whatever must happen after the PLL is set up, since changing the PLL results in a non-working display and we don't get it back. Unfortunately this still isn't enough to bring up both displays to life at the same time. I think it is not very far, but the secondary display (as decided by the BIOS) remains off for now even after successfully setting it all up. Early testing on other machines is welcome. Change-Id: I37209bb14f32c99944bdc8ef6eef75e2550e18ed Reviewed-on: https://review.haiku-os.org/c/haiku/+/7367 Reviewed-by: Alexander von Gluck <alex@terarocket.io> Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org> Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
This commit is contained in:
parent
6d1f6cad34
commit
58bfdd0cb5
@ -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)
|
||||
|
||||
|
@ -21,5 +21,6 @@ Addon intel_extreme.accelerant :
|
||||
PanelFitter.cpp
|
||||
Ports.cpp
|
||||
Pipes.cpp
|
||||
TigerLakePLL.cpp
|
||||
: be $(TARGET_LIBSTDC++) libaccelerantscommon.a
|
||||
;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -58,6 +58,7 @@ public:
|
||||
uint32 pixelClock,
|
||||
port_index pllForPort,
|
||||
uint32* pllSel);
|
||||
uint32 TranscoderMode();
|
||||
|
||||
// access to the various parts of the pipe
|
||||
::FDILink* FDI()
|
||||
|
@ -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 <new>
|
||||
|
||||
@ -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
|
||||
|
345
src/add-ons/accelerants/intel_extreme/TigerLakePLL.cpp
Normal file
345
src/add-ons/accelerants/intel_extreme/TigerLakePLL.cpp
Normal file
@ -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 <math.h>
|
||||
|
||||
|
||||
#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;
|
||||
}
|
20
src/add-ons/accelerants/intel_extreme/TigerLakePLL.h
Normal file
20
src/add-ons/accelerants/intel_extreme/TigerLakePLL.h
Normal file
@ -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 <SupportDefs.h>
|
||||
|
||||
|
||||
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 */
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user