/* Get DSI clock from pixel clock */ static u32 dsi_clk_from_pclk(u32 pclk, enum mipi_dsi_pixel_format fmt, int lane_count)
{
u32 dsi_clk_khz;
u32 bpp = mipi_dsi_pixel_format_to_bpp(fmt);
/* DSI data rate = pixel clock * bits per pixel / lane count
pixel clock is converted from KHz to Hz */
dsi_clk_khz = DIV_ROUND_CLOSEST(pclk * bpp, lane_count);
return dsi_clk_khz;
}
staticint dsi_calc_mnp(struct intel_display *display, struct intel_crtc_state *config, int target_dsi_clk)
{ unsignedint m_min, m_max, p_min = 2, p_max = 6; unsignedint m, n, p; unsignedint calc_m, calc_p; int delta, ref_clk;
/* target_dsi_clk is expected in kHz */ if (target_dsi_clk < 300000 || target_dsi_clk > 1150000) {
drm_err(display->drm, "DSI CLK Out of Range\n"); return -ECHRNG;
}
for (m = m_min; m <= m_max && delta; m++) { for (p = p_min; p <= p_max && delta; p++) { /* * Find the optimal m and p divisors with minimal delta * +/- the required clock
*/ int calc_dsi_clk = (m * ref_clk) / (p * n); int d = abs(target_dsi_clk - calc_dsi_clk); if (d < delta) {
delta = d;
calc_m = m;
calc_p = p;
}
}
}
/* register has log2(N1), this works fine for powers of two */
config->dsi_pll.ctrl = 1 << (DSI_PLL_P1_POST_DIV_SHIFT + calc_p - 2);
config->dsi_pll.div =
(ffs(n) - 1) << DSI_PLL_N1_DIV_SHIFT |
(u32)lfsr_converts[calc_m - 62] << DSI_PLL_M1_DIV_SHIFT;
return 0;
}
staticint vlv_dsi_pclk(struct intel_encoder *encoder, struct intel_crtc_state *config)
{ struct intel_display *display = to_intel_display(encoder); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); int bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format);
u32 dsi_clock;
u32 pll_ctl, pll_div;
u32 m = 0, p = 0, n; int refclk = display->platform.cherryview ? 100000 : 25000; int i;
/* * XXX: The muxing and gating is hard coded for now. Need to add support for * sharing PLLs with two DSI outputs.
*/ int vlv_dsi_pll_compute(struct intel_encoder *encoder, struct intel_crtc_state *config)
{ struct intel_display *display = to_intel_display(encoder); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); int pclk, dsi_clk, ret;
/* FIXME definitely not right for burst/cmd mode/pixel overlap */
config->hw.adjusted_mode.crtc_clock = pclk; if (intel_dsi->dual_link)
config->hw.adjusted_mode.crtc_clock *= 2;
/* * Dividers must be programmed with valid values. As per BSEPC, for * GEMINLAKE only PORT A divider values are checked while for BXT * both divider values are validated. Check this here for * paranoia, since BIOS is known to misconfigure PLLs in this way at * times, and since accessing DSI registers with invalid dividers * causes a system hang.
*/
val = intel_de_read(display, BXT_DSI_PLL_CTL); if (display->platform.geminilake) { if (!(val & BXT_DSIA_16X_MASK)) {
drm_dbg_kms(display->drm, "Invalid PLL divider (%08x)\n", val);
enabled = false;
}
} else { if (!(val & BXT_DSIA_16X_MASK) || !(val & BXT_DSIC_16X_MASK)) {
drm_dbg_kms(display->drm, "Invalid PLL divider (%08x)\n", val);
enabled = false;
}
}
/* * PLL lock should deassert within 200us. * Wait up to 1ms before timing out.
*/ if (intel_de_wait_for_clear(display, BXT_DSI_PLL_ENABLE,
BXT_DSI_PLL_LOCKED, 1))
drm_err(display->drm, "Timeout waiting for PLL lock deassertion\n");
}
/* Get the current DSI rate(actual) */
pll_ratio = config->dsi_pll.ctrl & BXT_DSI_PLL_RATIO_MASK;
dsi_rate = (BXT_REF_CLOCK_KHZ * pll_ratio) / 2;
/* * tx clock should be <= 20MHz and the div value must be * subtracted by 1 as per bspec
*/
tx_div = DIV_ROUND_UP(dsi_rate, 20000) - 1; /* * rx clock should be <= 150MHz and the div value must be * subtracted by 1 as per bspec
*/
rx_div = DIV_ROUND_UP(dsi_rate, 150000) - 1;
/* * rx divider value needs to be updated in the * two different bit fields in the register hence splitting the * rx divider value accordingly
*/
rx_div_lower = rx_div & RX_DIVIDER_BIT_1_2;
rx_div_upper = (rx_div & RX_DIVIDER_BIT_3_4) >> 2;
/* * From clock diagram, to get PLL ratio divider, divide double of DSI * link rate (i.e., 2*8x=16x frequency value) by ref clock. Make sure to * round 'up' the result
*/
dsi_ratio = DIV_ROUND_UP(dsi_clk * 2, BXT_REF_CLOCK_KHZ);
if (dsi_ratio < dsi_ratio_min || dsi_ratio > dsi_ratio_max) {
drm_err(display->drm, "Can't get a suitable ratio from DSI PLL ratios\n"); return -ECHRNG;
} else
drm_dbg_kms(display->drm, "DSI PLL calculation is Done!!\n");
/* * Program DSI ratio and Select MIPIC and MIPIA PLL output as 8x * Spec says both have to be programmed, even if one is not getting * used. Configure MIPI_CLOCK_CTL dividers in modeset
*/
config->dsi_pll.ctrl = dsi_ratio | BXT_DSIA_16X_BY2 | BXT_DSIC_16X_BY2;
/* As per recommendation from hardware team, * Prog PVD ratio =1 if dsi ratio <= 50
*/ if (display->platform.broxton && dsi_ratio <= 50)
config->dsi_pll.ctrl |= BXT_DSI_PLL_PVD_RATIO_1;
/* FIXME definitely not right for burst/cmd mode/pixel overlap */
config->hw.adjusted_mode.crtc_clock = pclk; if (intel_dsi->dual_link)
config->hw.adjusted_mode.crtc_clock *= 2;
/* Timeout and fail if PLL not locked */ if (intel_de_wait_for_set(display, BXT_DSI_PLL_ENABLE,
BXT_DSI_PLL_LOCKED, 1)) {
drm_err(display->drm, "Timed out waiting for DSI PLL to lock\n"); return;
}
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.