/** * DOC: Display PLLs * * Display PLLs used for driving outputs vary by platform. While some have * per-pipe or per-encoder dedicated PLLs, others allow the use of any PLL * from a pool. In the latter scenario, it is possible that multiple pipes * share a PLL if their configurations match. * * This file provides an abstraction over display PLLs. The function * intel_dpll_init() initializes the PLLs for the given platform. The * users of a PLL are tracked and that tracking is integrated with the atomic * modset interface. During an atomic operation, required PLLs can be reserved * for a given CRTC and encoder configuration by calling * intel_dpll_reserve() and previously reserved PLLs can be released * with intel_dpll_release(). * Changes to the users are first staged in the atomic state, and then made * effective by calling intel_dpll_swap_state() during the atomic * commit phase.
*/
/* platform specific hooks for managing DPLLs */ struct intel_dpll_funcs { /* * Hook for enabling the pll, called from intel_enable_dpll() if * the pll is not already enabled.
*/ void (*enable)(struct intel_display *display, struct intel_dpll *pll, conststruct intel_dpll_hw_state *dpll_hw_state);
/* * Hook for disabling the pll, called from intel_disable_dpll() * only when it is safe to disable the pll, i.e., there are no more * tracked users for it.
*/ void (*disable)(struct intel_display *display, struct intel_dpll *pll);
/* * Hook for reading the values currently programmed to the DPLL * registers. This is used for initial hw state readout and state * verification after a mode set.
*/ bool (*get_hw_state)(struct intel_display *display, struct intel_dpll *pll, struct intel_dpll_hw_state *dpll_hw_state);
/* * Hook for calculating the pll's output frequency based on its passed * in state.
*/ int (*get_freq)(struct intel_display *i915, conststruct intel_dpll *pll, conststruct intel_dpll_hw_state *dpll_hw_state);
};
/** * intel_get_dpll_by_id - get a DPLL given its id * @display: intel_display device instance * @id: pll id * * Returns: * A pointer to the DPLL with @id
*/ struct intel_dpll *
intel_get_dpll_by_id(struct intel_display *display, enum intel_dpll_id id)
{ struct intel_dpll *pll; int i;
/** * intel_dpll_disable - disable a CRTC's shared DPLL * @crtc_state: CRTC, and its state, which has a shared DPLL * * Disable DPLL used by @crtc.
*/ void intel_dpll_disable(conststruct intel_crtc_state *crtc_state)
{ struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); struct intel_dpll *pll = crtc_state->intel_dpll; unsignedint pipe_mask = intel_crtc_joined_pipe_mask(crtc_state);
/* PCH only available on ILK+ */ if (DISPLAY_VER(display) < 5) return;
if (pll == NULL) return;
mutex_lock(&display->dpll.lock); if (drm_WARN(display->drm, !(pll->active_mask & pipe_mask), "%s not used by [CRTC:%d:%s]\n", pll->info->name,
crtc->base.base.id, crtc->base.name)) goto out;
/* Ok no matching timings, maybe there's a free one? */ if (unused_pll) {
drm_dbg_kms(display->drm, "[CRTC:%d:%s] allocated %s\n",
crtc->base.base.id, crtc->base.name,
unused_pll->info->name); return unused_pll;
}
return NULL;
}
/** * intel_dpll_crtc_get - Get a DPLL reference for a CRTC * @crtc: CRTC on which behalf the reference is taken * @pll: DPLL for which the reference is taken * @dpll_state: the DPLL atomic state in which the reference is tracked * * Take a reference for @pll tracking the use of it by @crtc.
*/ staticvoid
intel_dpll_crtc_get(conststruct intel_crtc *crtc, conststruct intel_dpll *pll, struct intel_dpll_state *dpll_state)
{ struct intel_display *display = to_intel_display(crtc);
/** * intel_dpll_crtc_put - Drop a DPLL reference for a CRTC * @crtc: CRTC on which behalf the reference is dropped * @pll: DPLL for which the reference is dropped * @dpll_state: the DPLL atomic state in which the reference is tracked * * Drop a reference for @pll tracking the end of use of it by @crtc.
*/ void
intel_dpll_crtc_put(conststruct intel_crtc *crtc, conststruct intel_dpll *pll, struct intel_dpll_state *dpll_state)
{ struct intel_display *display = to_intel_display(crtc);
/** * intel_dpll_swap_state - make atomic DPLL configuration effective * @state: atomic state * * This is the dpll version of drm_atomic_helper_swap_state() since the * helper does not handle driver-specific global state. * * For consistency with atomic helpers this function does a complete swap, * i.e. it also puts the current state into @state, even though there is no * need for that at this moment.
*/ void intel_dpll_swap_state(struct intel_atomic_state *state)
{ struct intel_display *display = to_intel_display(state); struct intel_dpll_state *dpll_state = state->dpll_state; struct intel_dpll *pll; int i;
val = intel_de_read(display, PCH_DREF_CONTROL);
enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK |
DREF_SUPERSPREAD_SOURCE_MASK));
INTEL_DISPLAY_STATE_WARN(display, !enabled, "PCH refclk assertion failure, should be active but is disabled\n");
}
/* Wait for the clocks to stabilize. */
intel_de_posting_read(display, PCH_DPLL(id));
udelay(150);
/* The pixel multiplier can only be updated once the * DPLL is enabled and the clocks are stable. * * So write it again.
*/
intel_de_write(display, PCH_DPLL(id), hw_state->dpll);
intel_de_posting_read(display, PCH_DPLL(id));
udelay(200);
}
/* * Try to set up the PCH reference clock once all DPLLs * that depend on it have been shut down.
*/ if (display->dpll.pch_ssc_use & BIT(id))
intel_init_pch_refclk(display);
}
/* * Try to set up the PCH reference clock once all DPLLs * that depend on it have been shut down.
*/ if (display->dpll.pch_ssc_use & BIT(id))
intel_init_pch_refclk(display);
}
/* Constraints for PLL good behavior */ #define REF_MIN 48 #define REF_MAX 400 #define VCO_MIN 2400 #define VCO_MAX 4800
struct hsw_wrpll_rnp { unsigned p, n2, r2;
};
staticunsigned hsw_wrpll_get_budget_for_freq(int clock)
{ switch (clock) { case 25175000: case 25200000: case 27000000: case 27027000: case 37762500: case 37800000: case 40500000: case 40541000: case 54000000: case 54054000: case 59341000: case 59400000: case 72000000: case 74176000: case 74250000: case 81000000: case 81081000: case 89012000: case 89100000: case 108000000: case 108108000: case 111264000: case 111375000: case 148352000: case 148500000: case 162000000: case 162162000: case 222525000: case 222750000: case 296703000: case 297000000: return 0; case 233500000: case 245250000: case 247750000: case 253250000: case 298000000: return 1500; case 169128000: case 169500000: case 179500000: case 202000000: return 2000; case 256250000: case 262500000: case 270000000: case 272500000: case 273750000: case 280750000: case 281250000: case 286000000: case 291750000: return 4000; case 267250000: case 268500000: return 5000; default: return 1000;
}
}
/* No best (r,n,p) yet */ if (best->p == 0) {
best->p = p;
best->n2 = n2;
best->r2 = r2; return;
}
/* * Output clock is (LC_FREQ_2K / 2000) * N / (P * R), which compares to * freq2k. * * delta = 1e6 * * abs(freq2k - (LC_FREQ_2K * n2/(p * r2))) / * freq2k; * * and we would like delta <= budget. * * If the discrepancy is above the PPM-based budget, always prefer to * improve upon the previous solution. However, if you're within the * budget, try to maximize Ref * VCO, that is N / (P * R^2).
*/
a = freq2k * budget * p * r2;
b = freq2k * budget * best->p * best->r2;
diff = abs_diff(freq2k * p * r2, LC_FREQ_2K * n2);
diff_best = abs_diff(freq2k * best->p * best->r2,
LC_FREQ_2K * best->n2);
c = 1000000 * diff;
d = 1000000 * diff_best;
if (a < c && b < d) { /* If both are above the budget, pick the closer */ if (best->p * best->r2 * diff < p * r2 * diff_best) {
best->p = p;
best->n2 = n2;
best->r2 = r2;
}
} elseif (a >= c && b < d) { /* If A is below the threshold but B is above it? Update. */
best->p = p;
best->n2 = n2;
best->r2 = r2;
} elseif (a >= c && b >= d) { /* Both are below the limit, so pick the higher n2/(r2*r2) */ if (n2 * best->r2 * best->r2 > best->n2 * r2 * r2) {
best->p = p;
best->n2 = n2;
best->r2 = r2;
}
} /* Otherwise a < c && b >= d, do nothing */
}
/* Special case handling for 540 pixel clock: bypass WR PLL entirely
* and directly pass the LC PLL to it. */ if (freq2k == 5400000) {
*n2_out = 2;
*p_out = 1;
*r2_out = 2; return;
}
/* * Ref = LC_FREQ / R, where Ref is the actual reference input seen by * the WR PLL. * * We want R so that REF_MIN <= Ref <= REF_MAX. * Injecting R2 = 2 * R gives: * REF_MAX * r2 > LC_FREQ * 2 and * REF_MIN * r2 < LC_FREQ * 2 * * Which means the desired boundaries for r2 are: * LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN *
*/ for (r2 = LC_FREQ * 2 / REF_MAX + 1;
r2 <= LC_FREQ * 2 / REF_MIN;
r2++) {
/* * VCO = N * Ref, that is: VCO = N * LC_FREQ / R * * Once again we want VCO_MIN <= VCO <= VCO_MAX. * Injecting R2 = 2 * R and N2 = 2 * N, we get: * VCO_MAX * r2 > n2 * LC_FREQ and * VCO_MIN * r2 < n2 * LC_FREQ) * * Which means the desired boundaries for n2 are: * VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
*/ for (n2 = VCO_MIN * r2 / LC_FREQ + 1;
n2 <= VCO_MAX * r2 / LC_FREQ;
n2++) {
for (p = P_MIN; p <= P_MAX; p += P_INC)
hsw_wrpll_update_rnp(freq2k, budget,
r2, n2, p, &best);
}
}
staticint hsw_ddi_wrpll_get_freq(struct intel_display *display, conststruct intel_dpll *pll, conststruct intel_dpll_hw_state *dpll_hw_state)
{ conststruct hsw_dpll_hw_state *hw_state = &dpll_hw_state->hsw; int refclk; int n, p, r;
u32 wrpll = hw_state->wrpll;
switch (wrpll & WRPLL_REF_MASK) { case WRPLL_REF_SPECIAL_HSW: /* Muxed-SSC for BDW, non-SSC for non-ULT HSW. */ if (display->platform.haswell && !display->platform.haswell_ult) {
refclk = display->dpll.ref_clks.nssc; break;
}
fallthrough; case WRPLL_REF_PCH_SSC: /* * We could calculate spread here, but our checking * code only cares about 5% accuracy, and spread is a max of * 0.5% downspread.
*/
refclk = display->dpll.ref_clks.ssc; break; case WRPLL_REF_LCPLL:
refclk = 2700000; break; default:
MISSING_CASE(wrpll); return 0;
}
r = wrpll & WRPLL_DIVIDER_REF_MASK;
p = (wrpll & WRPLL_DIVIDER_POST_MASK) >> WRPLL_DIVIDER_POST_SHIFT;
n = (wrpll & WRPLL_DIVIDER_FB_MASK) >> WRPLL_DIVIDER_FB_SHIFT;
/* Convert to KHz, p & r have a fixed point portion */ return (refclk * n / 10) / (p * r) * 2;
}
wakeref = intel_display_power_get_if_enabled(display,
POWER_DOMAIN_DISPLAY_CORE); if (!wakeref) returnfalse;
ret = false;
val = intel_de_read(display, regs[id].ctl); if (!(val & LCPLL_PLL_ENABLE)) goto out;
val = intel_de_read(display, DPLL_CTRL1);
hw_state->ctrl1 = (val >> (id * 6)) & 0x3f;
/* avoid reading back stale values if HDMI mode is not enabled */ if (val & DPLL_CTRL1_HDMI_MODE(id)) {
hw_state->cfgcr1 = intel_de_read(display, regs[id].cfgcr1);
hw_state->cfgcr2 = intel_de_read(display, regs[id].cfgcr2);
}
ret = true;
wakeref = intel_display_power_get_if_enabled(display,
POWER_DOMAIN_DISPLAY_CORE); if (!wakeref) returnfalse;
ret = false;
/* DPLL0 is always enabled since it drives CDCLK */
val = intel_de_read(display, regs[id].ctl); if (drm_WARN_ON(display->drm, !(val & LCPLL_PLL_ENABLE))) goto out;
val = intel_de_read(display, DPLL_CTRL1);
hw_state->ctrl1 = (val >> (id * 6)) & 0x3f;
for (d = 0; d < ARRAY_SIZE(dividers); d++) { for (dco = 0; dco < ARRAY_SIZE(dco_central_freq); dco++) { for (i = 0; i < dividers[d].n_dividers; i++) { unsignedint p = dividers[d].list[i];
u64 dco_freq = p * afe_clock;
skl_wrpll_try_divider(&ctx,
dco_central_freq[dco],
dco_freq,
p); /* * Skip the remaining dividers if we're sure to * have found the definitive divider, we can't * improve a 0 deviation.
*/ if (ctx.min_deviation == 0) goto skip_remaining_dividers;
}
}
skip_remaining_dividers: /* * If a solution is found with an even divider, prefer * this one.
*/ if (d == 0 && ctx.p) break;
}
if (!ctx.p) return -EINVAL;
/* * gcc incorrectly analyses that these can be used without being * initialized. To be fair, it's hard to guess.
*/
p0 = p1 = p2 = 0;
skl_wrpll_get_multipliers(ctx.p, &p0, &p1, &p2);
skl_wrpll_params_populate(wrpll_params, afe_clock, ref_clock,
ctx.central_freq, p0, p1, p2);
ret = skl_ddi_calculate_wrpll(crtc_state->port_clock,
display->dpll.ref_clks.nssc, &wrpll_params); if (ret) return ret;
/* * See comment in intel_dpll_hw_state to understand why we always use 0 * as the DPLL id in this function.
*/
hw_state->ctrl1 =
DPLL_CTRL1_OVERRIDE(0) |
DPLL_CTRL1_HDMI_MODE(0);
/* * See comment in intel_dpll_hw_state to understand why we always use 0 * as the DPLL id in this function.
*/
ctrl1 = DPLL_CTRL1_OVERRIDE(0); switch (crtc_state->port_clock / 2) { case 81000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); break; case 135000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0); break; case 270000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0); break; /* eDP 1.4 rates */ case 162000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1620, 0); break; case 108000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0); break; case 216000:
ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2160, 0); break;
}
/* * ctrl1 register is already shifted for each pll, just use 0 to get * the internal shift for each field
*/ if (hw_state->ctrl1 & DPLL_CTRL1_HDMI_MODE(0)) return skl_ddi_wrpll_get_freq(display, pll, dpll_hw_state); else return skl_ddi_lcpll_get_freq(display, pll, dpll_hw_state);
}
if (display->platform.geminilake) {
intel_de_rmw(display, BXT_PORT_PLL_ENABLE(port),
0, PORT_PLL_POWER_ENABLE);
if (wait_for_us((intel_de_read(display, BXT_PORT_PLL_ENABLE(port)) &
PORT_PLL_POWER_STATE), 200))
drm_err(display->drm, "Power state not set for PLL:%d\n", port);
}
/* * While we write to the group register to program all lanes at once we * can read only lane registers and we pick lanes 0/1 for that.
*/
temp = intel_de_read(display, BXT_PORT_PCS_DW12_LN01(phy, ch));
temp &= ~LANE_STAGGER_MASK;
temp &= ~LANESTAGGER_STRAP_OVRD;
temp |= hw_state->pcsdw12;
intel_de_write(display, BXT_PORT_PCS_DW12_GRP(phy, ch), temp);
}
staticvoid bxt_ddi_pll_disable(struct intel_display *display, struct intel_dpll *pll)
{ enum port port = (enum port)pll->info->id; /* 1:1 port->PLL mapping */
if (display->platform.geminilake) {
intel_de_rmw(display, BXT_PORT_PLL_ENABLE(port),
PORT_PLL_POWER_ENABLE, 0);
if (wait_for_us(!(intel_de_read(display, BXT_PORT_PLL_ENABLE(port)) &
PORT_PLL_POWER_STATE), 200))
drm_err(display->drm, "Power state not reset for PLL:%d\n", port);
}
}
/* * While we write to the group register to program all lanes at once we * can read only lane registers. We configure all lanes the same way, so * here just read out lanes 0/1 and output a note if lanes 2/3 differ.
*/
hw_state->pcsdw12 = intel_de_read(display,
BXT_PORT_PCS_DW12_LN01(phy, ch)); if (intel_de_read(display, BXT_PORT_PCS_DW12_LN23(phy, ch)) != hw_state->pcsdw12)
drm_dbg(display->drm, "lane stagger config different for lane 01 (%08x) and 23 (%08x)\n",
hw_state->pcsdw12,
intel_de_read(display,
BXT_PORT_PCS_DW12_LN23(phy, ch)));
hw_state->pcsdw12 &= LANE_STAGGER_MASK | LANESTAGGER_STRAP_OVRD;
/* Calculate HDMI div */ /* * FIXME: tie the following calculation into * i9xx_crtc_compute_clock
*/ if (!bxt_find_best_dpll(crtc_state, clk_div)) return -EINVAL;
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.