From efde34c2fcb6375cba7b3daec16159e18680f374 Mon Sep 17 00:00:00 2001 From: Rudolf Cornelissen Date: Mon, 22 Nov 2021 11:14:12 +0000 Subject: [PATCH] intel_extreme: add haswell/skylake PLL calcs, no functional change yet. --- .../graphics/intel_extreme/intel_extreme.h | 13 +- .../accelerants/intel_extreme/Pipes.cpp | 11 + src/add-ons/accelerants/intel_extreme/Pipes.h | 4 + .../accelerants/intel_extreme/Ports.cpp | 30 +- src/add-ons/accelerants/intel_extreme/pll.cpp | 482 ++++++++++++++++++ src/add-ons/accelerants/intel_extreme/pll.h | 13 + 6 files changed, 544 insertions(+), 9 deletions(-) diff --git a/headers/private/graphics/intel_extreme/intel_extreme.h b/headers/private/graphics/intel_extreme/intel_extreme.h index 230a6757c4..71ad2b0330 100644 --- a/headers/private/graphics/intel_extreme/intel_extreme.h +++ b/headers/private/graphics/intel_extreme/intel_extreme.h @@ -512,7 +512,7 @@ struct intel_free_graphics_memory { #define LVDS_18BIT_DITHER (1UL << 25) #define LVDS_PORT_EN (1UL << 31) -// PLL flags +// PLL flags (pre-DDI) #define DISPLAY_PLL_ENABLED (1UL << 31) #define DISPLAY_PLL_2X_CLOCK (1UL << 30) #define DISPLAY_PLL_SYNC_LOCK_ENABLED (1UL << 29) @@ -540,6 +540,17 @@ struct intel_free_graphics_memory { #define DISPLAY_PLL_M2_DIVISOR_SHIFT 0 #define DISPLAY_PLL_PULSE_PHASE_SHIFT 9 +// Skylake PLLs +#define SKL_DPLL1_CFGCR1 (0xc040 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL1_CFGCR2 (0xc044 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL2_CFGCR1 (0xc048 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL2_CFGCR2 (0xc04c | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL3_CFGCR1 (0xc050 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL3_CFGCR2 (0xc054 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL_CTRL1 (0xc058 | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL_CTRL2 (0xc05c | REGS_NORTH_PIPE_AND_PORT) +#define SKL_DPLL_STATUS (0xc060 | REGS_NORTH_PIPE_AND_PORT) + // display #define INTEL_DISPLAY_OFFSET 0x1000 diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.cpp b/src/add-ons/accelerants/intel_extreme/Pipes.cpp index 5b2af12245..08280b7194 100644 --- a/src/add-ons/accelerants/intel_extreme/Pipes.cpp +++ b/src/add-ons/accelerants/intel_extreme/Pipes.cpp @@ -461,6 +461,17 @@ Pipe::ConfigureClocks(const pll_divisors& divisors, uint32 pixelClock, } } +void +Pipe::ConfigureClocksSKL(const skl_wrpll_params& wrpll_params, uint32 pixelClock, + uint32 extraFlags) +{ + CALLED(); + + TRACE("Skylake DPLL_CTRL1: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_CTRL1)); + TRACE("Skylake DPLL_CTRL2: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_CTRL2)); + TRACE("Skylake DPLL_STATUS: 0x%" B_PRIx32 "\n", read32(SKL_DPLL_STATUS)); +} + void Pipe::Enable(bool enable) { diff --git a/src/add-ons/accelerants/intel_extreme/Pipes.h b/src/add-ons/accelerants/intel_extreme/Pipes.h index aa1ae8f288..fcf160b08f 100644 --- a/src/add-ons/accelerants/intel_extreme/Pipes.h +++ b/src/add-ons/accelerants/intel_extreme/Pipes.h @@ -47,6 +47,10 @@ public: const pll_divisors& divisors, uint32 pixelClock, uint32 extraFlags); + void ConfigureClocksSKL( + const skl_wrpll_params& wrpll_params, + uint32 pixelClock, + uint32 extraFlags); // 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 e33c1691a6..f09b8f5531 100644 --- a/src/add-ons/accelerants/intel_extreme/Ports.cpp +++ b/src/add-ons/accelerants/intel_extreme/Ports.cpp @@ -1524,18 +1524,32 @@ DigitalDisplayInterface::SetDisplayMode(display_mode* target, uint32 colorMode) } } - pll_divisors divisors; - compute_pll_divisors(&target->timing, &divisors, false); - - uint32 extraPLLFlags = 0; - if (gInfo->shared_info->device_type.Generation() >= 3) - extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL | DISPLAY_PLL_2X_CLOCK; - // Program general pipe config fPipe->Configure(target); + //pll_divisors divisors; + //compute_pll_divisors(&target->timing, &divisors, false); + + //uint32 extraPLLFlags = 0; + //if (gInfo->shared_info->device_type.Generation() >= 3) + // extraPLLFlags |= DISPLAY_PLL_MODE_NORMAL | DISPLAY_PLL_2X_CLOCK; + // Program pipe PLL's - fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + //fPipe->ConfigureClocks(divisors, target->timing.pixel_clock, extraPLLFlags); + + if (gInfo->shared_info->device_type.Generation() <= 8) { + unsigned int r2_out, n2_out, p_out; + hsw_ddi_calculate_wrpll( + target->timing.pixel_clock * 1000 /* in Hz */, + &r2_out, &n2_out, &p_out); + } else { + skl_wrpll_params wrpll_params; + skl_ddi_calculate_wrpll( + target->timing.pixel_clock * 1000 /* in Hz */, + gInfo->shared_info->pll_info.reference_frequency,//fixme Hz? kHz? + &wrpll_params); + fPipe->ConfigureClocksSKL(wrpll_params, target->timing.pixel_clock, 0); + } // Program target display mode fPipe->ConfigureTimings(target); diff --git a/src/add-ons/accelerants/intel_extreme/pll.cpp b/src/add-ons/accelerants/intel_extreme/pll.cpp index 8e887035dc..e87ad23c31 100644 --- a/src/add-ons/accelerants/intel_extreme/pll.cpp +++ b/src/add-ons/accelerants/intel_extreme/pll.cpp @@ -552,3 +552,485 @@ refclk_activate_ilk(bool hasPanel) } } + +//excerpt (plus modifications) from intel_dpll_mgr.c: + +/* + * Copyright © 2006-2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define LC_FREQ 2700 +#define LC_FREQ_2K (uint64)(LC_FREQ * 2000) + +#define P_MIN 2 +#define P_MAX 64 +#define P_INC 2 + +/* 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; +}; + +static unsigned hsw_wrpll_get_budget_for_freq(int clock) +{ + unsigned budget; + + 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: + budget = 0; + break; + case 233500000: + case 245250000: + case 247750000: + case 253250000: + case 298000000: + budget = 1500; + break; + case 169128000: + case 169500000: + case 179500000: + case 202000000: + budget = 2000; + break; + case 256250000: + case 262500000: + case 270000000: + case 272500000: + case 273750000: + case 280750000: + case 281250000: + case 286000000: + case 291750000: + budget = 4000; + break; + case 267250000: + case 268500000: + budget = 5000; + break; + default: + budget = 1000; + break; + } + + return budget; +} + +static void hsw_wrpll_update_rnp(uint64 freq2k, unsigned int budget, + unsigned int r2, unsigned int n2, + unsigned int p, + struct hsw_wrpll_rnp *best) +{ + uint64 a, b, c, d, diff, diff_best; + + /* 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((uint64)freq2k * p * r2 - LC_FREQ_2K * n2); + diff_best = abs((uint64)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; + } + } else if (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; + } else if (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 */ +} + +void +hsw_ddi_calculate_wrpll(int clock /* in Hz */, + unsigned *r2_out, unsigned *n2_out, unsigned *p_out) +{ + uint64 freq2k; + unsigned p, n2, r2; + struct hsw_wrpll_rnp best = { 0, 0, 0 }; + unsigned budget; + + freq2k = clock / 100; + + budget = hsw_wrpll_get_budget_for_freq(clock); + + /* 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); + } + } + + *n2_out = best.n2; + *p_out = best.p; + *r2_out = best.r2; +} + +struct skl_wrpll_context { + uint64 min_deviation; /* current minimal deviation */ + uint64 central_freq; /* chosen central freq */ + uint64 dco_freq; /* chosen dco freq */ + unsigned int p; /* chosen divider */ +}; + +/* DCO freq must be within +1%/-6% of the DCO central freq */ +#define SKL_DCO_MAX_PDEVIATION 100 +#define SKL_DCO_MAX_NDEVIATION 600 + +static void skl_wrpll_try_divider(struct skl_wrpll_context *ctx, + uint64 central_freq, + uint64 dco_freq, + unsigned int divider) +{ + uint64 deviation; + + deviation = ((uint64)10000 * abs(dco_freq - central_freq) + / (uint64)central_freq); + + /* positive deviation */ + if (dco_freq >= central_freq) { + if (deviation < SKL_DCO_MAX_PDEVIATION && + deviation < ctx->min_deviation) { + ctx->min_deviation = deviation; + ctx->central_freq = central_freq; + ctx->dco_freq = dco_freq; + ctx->p = divider; + } + /* negative deviation */ + } else if (deviation < SKL_DCO_MAX_NDEVIATION && + deviation < ctx->min_deviation) { + ctx->min_deviation = deviation; + ctx->central_freq = central_freq; + ctx->dco_freq = dco_freq; + ctx->p = divider; + } +} + +static void skl_wrpll_get_multipliers(unsigned int p, + unsigned int *p0 /* out */, + unsigned int *p1 /* out */, + unsigned int *p2 /* out */) +{ + /* even dividers */ + if (p % 2 == 0) { + unsigned int half = p / 2; + + if (half == 1 || half == 2 || half == 3 || half == 5) { + *p0 = 2; + *p1 = 1; + *p2 = half; + } else if (half % 2 == 0) { + *p0 = 2; + *p1 = half / 2; + *p2 = 2; + } else if (half % 3 == 0) { + *p0 = 3; + *p1 = half / 3; + *p2 = 2; + } else if (half % 7 == 0) { + *p0 = 7; + *p1 = half / 7; + *p2 = 2; + } + } else if (p == 3 || p == 9) { /* 3, 5, 7, 9, 15, 21, 35 */ + *p0 = 3; + *p1 = 1; + *p2 = p / 3; + } else if (p == 5 || p == 7) { + *p0 = p; + *p1 = 1; + *p2 = 1; + } else if (p == 15) { + *p0 = 3; + *p1 = 1; + *p2 = 5; + } else if (p == 21) { + *p0 = 7; + *p1 = 1; + *p2 = 3; + } else if (p == 35) { + *p0 = 7; + *p1 = 1; + *p2 = 5; + } +} + +static void skl_wrpll_context_init(struct skl_wrpll_context *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->min_deviation = UINT64_MAX; +} + +static void skl_wrpll_params_populate(struct skl_wrpll_params *params, + uint64 afe_clock, + int ref_clock, + uint64 central_freq, + uint32 p0, uint32 p1, uint32 p2) +{ + uint64 dco_freq; + + switch (central_freq) { + case 9600000000ULL: + params->central_freq = 0; + break; + case 9000000000ULL: + params->central_freq = 1; + break; + case 8400000000ULL: + params->central_freq = 3; + } + + switch (p0) { + case 1: + params->pdiv = 0; + break; + case 2: + params->pdiv = 1; + break; + case 3: + params->pdiv = 2; + break; + case 7: + params->pdiv = 4; + break; + default: + TRACE("%s: Incorrect PDiv\n", __func__); + } + + switch (p2) { + case 5: + params->kdiv = 0; + break; + case 2: + params->kdiv = 1; + break; + case 3: + params->kdiv = 2; + break; + case 1: + params->kdiv = 3; + break; + default: + TRACE("%s: Incorrect KDiv\n", __func__); + } + + params->qdiv_ratio = p1; + params->qdiv_mode = (params->qdiv_ratio == 1) ? 0 : 1; + + dco_freq = p0 * p1 * p2 * afe_clock; + + /* + * Intermediate values are in Hz. + * Divide by MHz to match bsepc + */ + params->dco_integer = (uint64)dco_freq / ((uint64)ref_clock * 1000); + params->dco_fraction = ( + (uint64)dco_freq / ((uint64)ref_clock / 1000) - + (uint64)params->dco_integer * 1000000 * 0x8000) / + 1000000; +} + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +bool +skl_ddi_calculate_wrpll(int clock /* in Hz */, + int ref_clock, + struct skl_wrpll_params *wrpll_params) +{ + uint64 afe_clock = clock * 5; /* AFE Clock is 5x Pixel clock */ + uint64 dco_central_freq[3] = { 8400000000ULL, + 9000000000ULL, + 9600000000ULL }; + static const int even_dividers[] = { 4, 6, 8, 10, 12, 14, 16, 18, 20, + 24, 28, 30, 32, 36, 40, 42, 44, + 48, 52, 54, 56, 60, 64, 66, 68, + 70, 72, 76, 78, 80, 84, 88, 90, + 92, 96, 98 }; + static const int odd_dividers[] = { 3, 5, 7, 9, 15, 21, 35 }; + static const struct { + const int *list; + unsigned int n_dividers; + } dividers[] = { + { even_dividers, ARRAY_SIZE(even_dividers) }, + { odd_dividers, ARRAY_SIZE(odd_dividers) }, + }; + struct skl_wrpll_context ctx; + unsigned int dco, d, i; + unsigned int p0, p1, p2; + + skl_wrpll_context_init(&ctx); + + 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++) { + unsigned int p = dividers[d].list[i]; + uint64 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) { + TRACE("%s: No valid divider found for %dHz\n", __func__, clock); + return false; + } + + /* + * 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); + + return true; +} + diff --git a/src/add-ons/accelerants/intel_extreme/pll.h b/src/add-ons/accelerants/intel_extreme/pll.h index 57373925aa..b7012e857e 100644 --- a/src/add-ons/accelerants/intel_extreme/pll.h +++ b/src/add-ons/accelerants/intel_extreme/pll.h @@ -31,6 +31,15 @@ struct pll_limits { uint32 max_vco; }; +struct skl_wrpll_params { + uint32 dco_fraction; + uint32 dco_integer; + uint32 qdiv_ratio; + uint32 qdiv_mode; + uint32 kdiv; + uint32 pdiv; + uint32 central_freq; +}; bool valid_pll_divisors(pll_divisors* divisors, pll_limits* limits); void compute_pll_divisors(display_timing* current, pll_divisors* divisors, @@ -38,5 +47,9 @@ void compute_pll_divisors(display_timing* current, pll_divisors* divisors, void refclk_activate_ilk(bool hasPanel); +void hsw_ddi_calculate_wrpll(int clock /* in Hz */, + unsigned *r2_out, unsigned *n2_out, unsigned *p_out); +bool skl_ddi_calculate_wrpll(int clock /* in Hz */, + int ref_clock, struct skl_wrpll_params *wrpll_params); #endif /* INTEL_EXTREME_PLL_H */