/** * DOC: CDCLK / RAWCLK * * The display engine uses several different clocks to do its work. There * are two main clocks involved that aren't directly related to the actual * pixel clock or any symbol/bit clock of the actual output port. These * are the core display clock (CDCLK) and RAWCLK. * * CDCLK clocks most of the display pipe logic, and thus its frequency * must be high enough to support the rate at which pixels are flowing * through the pipes. Downscaling must also be accounted as that increases * the effective pixel rate. * * On several platforms the CDCLK frequency can be changed dynamically * to minimize power consumption for a given display configuration. * Typically changes to the CDCLK frequency require all the display pipes * to be shut down while the frequency is being changed. * * On SKL+ the DMC will toggle the CDCLK off/on during DC5/6 entry/exit. * DMC will not change the active CDCLK frequency however, so that part * will still be performed by the driver directly. * * There are multiple components involved in the generation of the CDCLK * frequency: * * - We have the CDCLK PLL, which generates an output clock based on a * reference clock and a ratio parameter. * - The CD2X Divider, which divides the output of the PLL based on a * divisor selected from a set of pre-defined choices. * - The CD2X Squasher, which further divides the output based on a * waveform represented as a sequence of bits where each zero * "squashes out" a clock cycle. * - And, finally, a fixed divider that divides the output frequency by 2. * * As such, the resulting CDCLK frequency can be calculated with the * following formula: * * cdclk = vco / cd2x_div / (sq_len / sq_div) / 2 * * , where vco is the frequency generated by the PLL; cd2x_div * represents the CD2X Divider; sq_len and sq_div are the bit length * and the number of high bits for the CD2X Squasher waveform, respectively; * and 2 represents the fixed divider. * * Note that some older platforms do not contain the CD2X Divider * and/or CD2X Squasher, in which case we can ignore their respective * factors in the formula above. * * Several methods exist to change the CDCLK frequency, which ones are * supported depends on the platform: * * - Full PLL disable + re-enable with new VCO frequency. Pipes must be inactive. * - CD2X divider update. Single pipe can be active as the divider update * can be synchronized with the pipe's start of vblank. * - Crawl the PLL smoothly to the new VCO frequency. Pipes can be active. * - Squash waveform update. Pipes can be active. * - Crawl and squash can also be done back to back. Pipes can be active. * * RAWCLK is a fixed frequency clock, often used by various auxiliary * blocks such as AUX CH or backlight PWM. Hence the only thing we * really need to know about RAWCLK is its frequency so that various * dividers can be programmed correctly.
*/
/* * Logical configuration of cdclk (used for all scaling, * watermark, etc. calculations and checks). This is * computed as if all enabled crtcs were active.
*/ struct intel_cdclk_config logical;
/* * Actual configuration of cdclk, can be different from the * logical configuration only when all crtc's are DPMS off.
*/ struct intel_cdclk_config actual;
/* minimum acceptable cdclk to satisfy bandwidth requirements */ int bw_min_cdclk; /* minimum acceptable cdclk for each pipe */ int min_cdclk[I915_MAX_PIPES]; /* minimum acceptable voltage level for each pipe */
u8 min_voltage_level[I915_MAX_PIPES];
/* pipe to which cd2x update is synchronized */ enum pipe pipe;
/* forced minimum cdclk for glk+ audio w/a */ int force_min_cdclk;
/* bitmask of active pipes */
u8 active_pipes;
/* update cdclk with pipes disabled */ bool disable_pipes;
};
/* * 852GM/852GMV only supports 133 MHz and the HPLLCC * encoding is different :( * FIXME is this the right way to detect 852GM/852GMV?
*/ if (pdev->revision == 0x1) {
cdclk_config->cdclk = 133333; return;
}
/* Assume that the hardware is in the high speed state. This * should be the default.
*/ switch (hpllcc & GC_CLOCK_CONTROL_MASK) { case GC_CLOCK_133_200: case GC_CLOCK_133_200_2: case GC_CLOCK_100_200:
cdclk_config->cdclk = 200000; break; case GC_CLOCK_166_250:
cdclk_config->cdclk = 250000; break; case GC_CLOCK_100_133:
cdclk_config->cdclk = 133333; break; case GC_CLOCK_133_266: case GC_CLOCK_133_266_2: case GC_CLOCK_166_266:
cdclk_config->cdclk = 266667; break;
}
}
/* * We seem to get an unstable or solid color picture at 200MHz. * Not sure what's wrong. For now use 200MHz only when all pipes * are off.
*/ if (display->platform.valleyview && min_cdclk > freq_320) return 400000; elseif (min_cdclk > 266667) return freq_320; elseif (min_cdclk > 0) return 266667; else return 200000;
}
if (display->platform.valleyview) { if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */ return 2; elseif (cdclk >= 266667) return 1; else return 0;
} else { /* * Specs are full of misinformation, but testing on actual * hardware has shown that we just need to write the desired * CCK divider into the Punit register.
*/ return DIV_ROUND_CLOSEST(dev_priv->hpll_freq << 1, cdclk) - 1;
}
}
if (display->platform.cherryview)
default_credits = PFI_CREDIT(12); else
default_credits = PFI_CREDIT(8);
if (display->cdclk.hw.cdclk >= dev_priv->czclk_freq) { /* CHV suggested value is 31 or 63 */ if (display->platform.cherryview)
credits = PFI_CREDIT_63; else
credits = PFI_CREDIT(15);
} else {
credits = default_credits;
}
/* * WA - write default credits before re-programming * FIXME: should we also set the resend bit here?
*/
intel_de_write(display, GCI_CONTROL,
VGA_FAST_MODE_DISABLE | default_credits);
/* * FIXME is this guaranteed to clear * immediately or should we poll for it?
*/
drm_WARN_ON(display->drm,
intel_de_read(display, GCI_CONTROL) & PFI_CREDIT_RESEND);
}
switch (cdclk) { case 400000: case 333333: case 320000: case 266667: case 200000: break; default:
MISSING_CASE(cdclk); return;
}
/* There are cases where we can end up here with power domains * off and a CDCLK frequency other than the minimum, like when * issuing a modeset without actually changing any display after * a system suspend. So grab the display core domain, which covers * the HW blocks needed for the following programming.
*/
wakeref = intel_display_power_get(display, POWER_DOMAIN_DISPLAY_CORE);
/* adjust cdclk divider */
val = vlv_cck_read(display->drm, CCK_DISPLAY_CLOCK_CONTROL);
val &= ~CCK_FREQUENCY_VALUES;
val |= divider;
vlv_cck_write(display->drm, CCK_DISPLAY_CLOCK_CONTROL, val);
if (wait_for((vlv_cck_read(display->drm, CCK_DISPLAY_CLOCK_CONTROL) &
CCK_FREQUENCY_STATUS) == (divider << CCK_FREQUENCY_STATUS_SHIFT),
50))
drm_err(display->drm, "timed out waiting for CDclk change\n");
}
/* adjust self-refresh exit latency value */
val = vlv_bunit_read(display->drm, BUNIT_REG_BISOC);
val &= ~0x7f;
/* * For high bandwidth configs, we set a higher latency in the bunit * so that the core display fetch happens in time to avoid underruns.
*/ if (cdclk == 400000)
val |= 4500 / 250; /* 4.5 usec */ else
val |= 3000 / 250; /* 3.0 usec */
vlv_bunit_write(display->drm, BUNIT_REG_BISOC, val);
switch (cdclk) { case 333333: case 320000: case 266667: case 200000: break; default:
MISSING_CASE(cdclk); return;
}
/* There are cases where we can end up here with power domains * off and a CDCLK frequency other than the minimum, like when * issuing a modeset without actually changing any display after * a system suspend. So grab the display core domain, which covers * the HW blocks needed for the following programming.
*/
wakeref = intel_display_power_get(display, POWER_DOMAIN_DISPLAY_CORE);
vlv_punit_get(display->drm);
val = vlv_punit_read(display->drm, PUNIT_REG_DSPSSPM);
val &= ~DSPFREQGUAR_MASK_CHV;
val |= (cmd << DSPFREQGUAR_SHIFT_CHV);
vlv_punit_write(display->drm, PUNIT_REG_DSPSSPM, val); if (wait_for((vlv_punit_read(display->drm, PUNIT_REG_DSPSSPM) &
DSPFREQSTAT_MASK_CHV) == (cmd << DSPFREQSTAT_SHIFT_CHV),
50)) {
drm_err(display->drm, "timed out waiting for CDclk change\n");
}
/* * Can't read this out :( Let's assume it's * at least what the CDCLK frequency requires.
*/
cdclk_config->voltage_level =
bdw_calc_voltage_level(cdclk_config->cdclk);
}
static u32 bdw_cdclk_freq_sel(int cdclk)
{ switch (cdclk) { default:
MISSING_CASE(cdclk);
fallthrough; case 337500: return LCPLL_CLK_FREQ_337_5_BDW; case 450000: return LCPLL_CLK_FREQ_450; case 540000: return LCPLL_CLK_FREQ_54O_BDW; case 675000: return LCPLL_CLK_FREQ_675_BDW;
}
}
staticvoid bdw_set_cdclk(struct intel_display *display, conststruct intel_cdclk_config *cdclk_config, enum pipe pipe)
{ int cdclk = cdclk_config->cdclk; int ret;
if (drm_WARN(display->drm,
(intel_de_read(display, LCPLL_CTL) &
(LCPLL_PLL_DISABLE | LCPLL_PLL_LOCK |
LCPLL_CD_CLOCK_DISABLE | LCPLL_ROOT_CD_CLOCK_DISABLE |
LCPLL_CD2X_CLOCK_DISABLE | LCPLL_POWER_DOWN_ALLOW |
LCPLL_CD_SOURCE_FCLK)) != LCPLL_PLL_LOCK, "trying to change cdclk frequency with cdclk not enabled\n")) return;
ret = intel_pcode_write(display->drm, BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ, 0x0); if (ret) {
drm_err(display->drm, "failed to inform pcode about cdclk change\n"); return;
}
/* * According to the spec, it should be enough to poll for this 1 us. * However, extensive testing shows that this can take longer.
*/ if (wait_for_us(intel_de_read(display, LCPLL_CTL) &
LCPLL_CD_SOURCE_FCLK_DONE, 100))
drm_err(display->drm, "Switching to FCLK failed\n");
if (cdclk_config->vco == 8640000) { switch (cdctl & CDCLK_FREQ_SEL_MASK) { case CDCLK_FREQ_450_432:
cdclk_config->cdclk = 432000; break; case CDCLK_FREQ_337_308:
cdclk_config->cdclk = 308571; break; case CDCLK_FREQ_540:
cdclk_config->cdclk = 540000; break; case CDCLK_FREQ_675_617:
cdclk_config->cdclk = 617143; break; default:
MISSING_CASE(cdctl & CDCLK_FREQ_SEL_MASK); break;
}
} else { switch (cdctl & CDCLK_FREQ_SEL_MASK) { case CDCLK_FREQ_450_432:
cdclk_config->cdclk = 450000; break; case CDCLK_FREQ_337_308:
cdclk_config->cdclk = 337500; break; case CDCLK_FREQ_540:
cdclk_config->cdclk = 540000; break; case CDCLK_FREQ_675_617:
cdclk_config->cdclk = 675000; break; default:
MISSING_CASE(cdctl & CDCLK_FREQ_SEL_MASK); break;
}
}
out: /* * Can't read this out :( Let's assume it's * at least what the CDCLK frequency requires.
*/
cdclk_config->voltage_level =
skl_calc_voltage_level(cdclk_config->cdclk);
}
/* convert from kHz to .1 fixpoint MHz with -1MHz offset */ staticint skl_cdclk_decimal(int cdclk)
{ return DIV_ROUND_CLOSEST(cdclk - 1000, 500);
}
/* * We always enable DPLL0 with the lowest link rate possible, but still * taking into account the VCO required to operate the eDP panel at the * desired frequency. The usual DP link rates operate with a VCO of * 8100 while the eDP 1.4 alternate link rates need a VCO of 8640. * The modeset code is responsible for the selection of the exact link * rate later on, with the constraint of choosing a frequency that * works with vco.
*/ if (vco == 8640000) return DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, SKL_DPLL0); else return DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, SKL_DPLL0);
}
if (intel_de_wait_for_clear(display, LCPLL1_CTL, LCPLL_PLL_LOCK, 1))
drm_err(display->drm, "Couldn't disable DPLL0\n");
display->cdclk.hw.vco = 0;
}
static u32 skl_cdclk_freq_sel(struct intel_display *display, int cdclk, int vco)
{ switch (cdclk) { default:
drm_WARN_ON(display->drm,
cdclk != display->cdclk.hw.bypass);
drm_WARN_ON(display->drm, vco != 0);
fallthrough; case 308571: case 337500: return CDCLK_FREQ_337_308; case 450000: case 432000: return CDCLK_FREQ_450_432; case 540000: return CDCLK_FREQ_540; case 617143: case 675000: return CDCLK_FREQ_675_617;
}
}
staticvoid skl_set_cdclk(struct intel_display *display, conststruct intel_cdclk_config *cdclk_config, enum pipe pipe)
{ int cdclk = cdclk_config->cdclk; int vco = cdclk_config->vco;
u32 freq_select, cdclk_ctl; int ret;
/* * Based on WA#1183 CDCLK rates 308 and 617MHz CDCLK rates are * unsupported on SKL. In theory this should never happen since only * the eDP1.4 2.16 and 4.32Gbps rates require it, but eDP1.4 is not * supported on SKL either, see the above WA. WARN whenever trying to * use the corresponding VCO freq as that always leads to using the * minimum 308MHz CDCLK.
*/
drm_WARN_ON_ONCE(display->drm,
display->platform.skylake && vco == 8640000);
ret = intel_pcode_request(display->drm, SKL_PCODE_CDCLK_CONTROL,
SKL_CDCLK_PREPARE_FOR_CHANGE,
SKL_CDCLK_READY_FOR_CHANGE,
SKL_CDCLK_READY_FOR_CHANGE, 3); if (ret) {
drm_err(display->drm, "Failed to inform PCU about cdclk change (%d)\n", ret); return;
}
/* * check if the pre-os initialized the display * There is SWF18 scratchpad register defined which is set by the * pre-os which can be used by the OS drivers to check the status
*/ if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0) goto sanitize;
/* Is PLL enabled and locked ? */ if (display->cdclk.hw.vco == 0 ||
display->cdclk.hw.cdclk == display->cdclk.hw.bypass) goto sanitize;
/* DPLL okay; verify the cdclock * * Noticed in some instances that the freq selection is correct but * decimal part is programmed wrong from BIOS where pre-os does not * enable display. Verify the same as well.
*/
cdctl = intel_de_read(display, CDCLK_CTL);
expected = (cdctl & CDCLK_FREQ_SEL_MASK) |
skl_cdclk_decimal(display->cdclk.hw.cdclk); if (cdctl == expected) /* All well; nothing to sanitize */ return;
sanitize:
drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n");
/* force cdclk programming */
display->cdclk.hw.cdclk = 0; /* force full PLL disable + enable */
display->cdclk.hw.vco = ~0;
}
if (display->cdclk.hw.cdclk != 0 &&
display->cdclk.hw.vco != 0) { /* * Use the current vco as our initial * guess as to what the preferred vco is.
*/ if (display->cdclk.skl_preferred_vco_freq == 0)
skl_set_preferred_cdclk_vco(display,
display->cdclk.hw.vco); return;
}
static u8 xe3lpd_calc_voltage_level(int cdclk)
{ /* * Starting with xe3lpd power controller does not need the voltage * index when doing the modeset update. This function is best left * defined but returning 0 to the mask.
*/ return 0;
}
val = intel_de_read(display, BXT_DE_PLL_ENABLE); if ((val & BXT_DE_PLL_PLL_ENABLE) == 0 ||
(val & BXT_DE_PLL_LOCK) == 0) { /* * CDCLK PLL is disabled, the VCO/ratio doesn't matter, but * setting it to zero is a way to signal that.
*/
cdclk_config->vco = 0; return;
}
/* * DISPLAY_VER >= 11 have the ratio directly in the PLL enable register, * gen9lp had it in a separate PLL control register.
*/ if (DISPLAY_VER(display) >= 11)
ratio = val & ICL_CDCLK_PLL_RATIO_MASK; else
ratio = intel_de_read(display, BXT_DE_PLL_CTL) & BXT_DE_PLL_RATIO_MASK;
switch (divider) { case BXT_CDCLK_CD2X_DIV_SEL_1:
div = 2; break; case BXT_CDCLK_CD2X_DIV_SEL_1_5:
div = 3; break; case BXT_CDCLK_CD2X_DIV_SEL_2:
div = 4; break; case BXT_CDCLK_CD2X_DIV_SEL_4:
div = 8; break; default:
MISSING_CASE(divider); return;
}
if (HAS_CDCLK_SQUASH(display))
squash_ctl = intel_de_read(display, CDCLK_SQUASH_CTL);
if (squash_ctl & CDCLK_SQUASH_ENABLE) {
u16 waveform; int size;
out: if (DISPLAY_VER(display) >= 20)
cdclk_config->joined_mbus = intel_de_read(display, MBUS_CTL) & MBUS_JOIN; /* * Can't read this out :( Let's assume it's * at least what the CDCLK frequency requires.
*/
cdclk_config->voltage_level =
intel_cdclk_calc_voltage_level(display, cdclk_config->cdclk);
}
staticbool cdclk_pll_is_unknown(unsignedint vco)
{ /* * Ensure driver does not take the crawl path for the * case when the vco is set to ~0 in the * sanitize path.
*/ return vco == ~0;
}
/* Return if Squash only or Crawl only is the desired action */ if (old_cdclk_config->vco == 0 || new_cdclk_config->vco == 0 ||
old_cdclk_config->vco == new_cdclk_config->vco ||
old_waveform == new_waveform) returnfalse;
/* * Should not happen currently. We might need more midpoint * transitions if we need to also change the cd2x divider.
*/ if (drm_WARN_ON(display->drm, old_div != new_div)) returnfalse;
*mid_cdclk_config = *new_cdclk_config;
/* * Populate the mid_cdclk_config accordingly. * - If moving to a higher cdclk, the desired action is squashing. * The mid cdclk config should have the new (squash) waveform. * - If moving to a lower cdclk, the desired action is crawling. * The mid cdclk config should have the new vco.
*/
if (pipe != INVALID_PIPE)
intel_crtc_wait_for_next_vblank(intel_crtc_for_pipe(display, pipe));
}
staticvoid bxt_set_cdclk(struct intel_display *display, conststruct intel_cdclk_config *cdclk_config, enum pipe pipe)
{ struct intel_cdclk_config mid_cdclk_config; int cdclk = cdclk_config->cdclk; int ret = 0;
/* * Inform power controller of upcoming frequency change. * Display versions 14 and beyond do not follow the PUnit * mailbox communication, skip * this step.
*/ if (DISPLAY_VER(display) >= 14 || display->platform.dg2)
; /* NOOP */ elseif (DISPLAY_VER(display) >= 11)
ret = intel_pcode_request(display->drm, SKL_PCODE_CDCLK_CONTROL,
SKL_CDCLK_PREPARE_FOR_CHANGE,
SKL_CDCLK_READY_FOR_CHANGE,
SKL_CDCLK_READY_FOR_CHANGE, 3); else /* * BSpec requires us to wait up to 150usec, but that leads to * timeouts; the 2ms used here is based on experiment.
*/
ret = intel_pcode_write_timeout(display->drm,
HSW_PCODE_DE_WRITE_FREQ_REQ,
0x80000000, 2);
if (ret) {
drm_err(display->drm, "Failed to inform PCU about cdclk change (err %d, freq %d)\n",
ret, cdclk); return;
}
if (DISPLAY_VER(display) >= 20 && cdclk < display->cdclk.hw.cdclk)
xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config);
if (DISPLAY_VER(display) >= 20 && cdclk > display->cdclk.hw.cdclk)
xe2lpd_mdclk_cdclk_ratio_program(display, cdclk_config);
if (DISPLAY_VER(display) >= 14) /* * NOOP - No Pcode communication needed for * Display versions 14 and beyond
*/ elseif (DISPLAY_VER(display) >= 11 && !display->platform.dg2)
ret = intel_pcode_write(display->drm, SKL_PCODE_CDCLK_CONTROL,
cdclk_config->voltage_level); if (DISPLAY_VER(display) < 11) { /* * The timeout isn't specified, the 2ms used here is based on * experiment. * FIXME: Waiting for the request completion could be delayed * until the next PCODE request based on BSpec.
*/
ret = intel_pcode_write_timeout(display->drm,
HSW_PCODE_DE_WRITE_FREQ_REQ,
cdclk_config->voltage_level, 2);
} if (ret) {
drm_err(display->drm, "PCode CDCLK freq set failed, (err %d, freq %d)\n",
ret, cdclk); return;
}
intel_update_cdclk(display);
if (DISPLAY_VER(display) >= 11) /* * Can't read out the voltage level :( * Let's just assume everything is as expected.
*/
display->cdclk.hw.voltage_level = cdclk_config->voltage_level;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.