diff options
-rw-r--r-- | tools/Makefile.sources | 1 | ||||
-rw-r--r-- | tools/cnl_compute_wrpll.c | 526 |
2 files changed, 527 insertions, 0 deletions
diff --git a/tools/Makefile.sources b/tools/Makefile.sources index c49ab8f0..abd23a0f 100644 --- a/tools/Makefile.sources +++ b/tools/Makefile.sources @@ -2,6 +2,7 @@ noinst_PROGRAMS = \ hsw_compute_wrpll \ skl_compute_wrpll \ skl_ddb_allocation \ + cnl_compute_wrpll \ $(NULL) tools_prog_lists = \ diff --git a/tools/cnl_compute_wrpll.c b/tools/cnl_compute_wrpll.c new file mode 100644 index 00000000..c7b7bd72 --- /dev/null +++ b/tools/cnl_compute_wrpll.c @@ -0,0 +1,526 @@ +/* + * Copyright © 2017 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. + */ + +#include <assert.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#define U32_MAX ((uint32_t)~0ULL) +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) + +static inline uint64_t div_u64(uint64_t dividend, uint32_t divisor) +{ + return dividend / divisor; +} + +struct skl_wrpll_params { + uint32_t dco_fraction; + uint32_t dco_integer; + uint32_t qdiv_ratio; + uint32_t qdiv_mode; + uint32_t kdiv; + uint32_t pdiv; + + /* for this test code only */ + unsigned int ref_clock; +} __attribute__((packed)); + +static void dump_params(const char *name, struct skl_wrpll_params *params) +{ + printf("%s:\n", name); + printf("Pdiv: %d\n", params->pdiv); + printf("Qdiv: %d\n", params->qdiv_ratio); + printf("Kdiv: %d\n", params->kdiv); + printf("qdiv mode: %d\n", params->qdiv_mode); + printf("dco integer: %d\n", params->dco_integer); + printf("dco fraction: %d\n", params->dco_fraction); +} + +static void compare_params(unsigned int clock, + const char *name1, struct skl_wrpll_params *p1, + const char *name2, struct skl_wrpll_params *p2) +{ + if (memcmp(p1, p2, sizeof(struct skl_wrpll_params)) == 0) + return; + + printf("=======================================\n"); + printf("Difference with clock: %10.6f MHz\n", clock/1000000.0); + printf("Reference clock: %10.6f MHz\n\n", p1->ref_clock/1000.0); + dump_params(name1, p1); + printf("\n"); + dump_params(name2, p2); + printf("=======================================\n"); +} + +static void cnl_wrpll_params_populate(struct skl_wrpll_params *params, + uint32_t dco_freq, uint32_t ref_freq, + uint32_t pdiv, uint32_t qdiv, + uint32_t kdiv) +{ + uint32_t dco; + + params->qdiv_ratio = qdiv; + params->qdiv_mode = (qdiv == 1) ? 0 : 1; + params->pdiv = pdiv; + params->kdiv = kdiv; + + if (kdiv != 2 && qdiv != 1) + printf("kdiv != 2 and qdiv != 1\n"); + + dco = div_u64((uint64_t)dco_freq << 15, ref_freq); + + params->dco_integer = dco >> 15; + params->dco_fraction = dco & 0x7fff; +} + +static void cnl_wrpll_get_multipliers(int bestdiv, + int *pdiv, + int *qdiv, + int *kdiv) +{ + /* even dividers */ + if (bestdiv % 2 == 0) { + 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 { + if (bestdiv == 3 || bestdiv == 5 || bestdiv == 7) { + *pdiv = bestdiv; + *qdiv = 1; + *kdiv = 1; + } else { /* 9, 15, 21 */ + *pdiv = bestdiv / 3; + *qdiv = 1; + *kdiv = 3; + } + } +} + +static bool +cnl_ddi_calculate_wrpll1(int clock /* in Hz */, + struct skl_wrpll_params *params) +{ + double afe_clock = (clock/1000000.0) * 5; /* clocks in MHz */ + double dco_min = 7998; + double dco_max = 10000; + double dco_mid = (dco_min + dco_max) / 2; + static const int dividers[] = { 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 }; + double dco, dco_centrality = 0; + double best_dco_centrality = 999999; + int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0; + double ref_clock = params->ref_clock/1000.0; /* MHz */ + uint32_t dco_int, dco_frac; + + for (d = 0; d < ARRAY_SIZE(dividers); d++) { + dco = afe_clock * dividers[d]; + + if ((dco <= dco_max) && (dco >= dco_min)) { + dco_centrality = fabs(dco - dco_mid); + + if (dco_centrality < best_dco_centrality) { + best_dco_centrality = dco_centrality; + best_div = dividers[d]; + dco_int = (uint32_t)(dco/ref_clock); + dco_frac = round((dco/ref_clock - dco_int) * (1<<15)); + } + } + } + + if (best_div != 0) { + cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv); + + params->qdiv_ratio = qdiv; + params->qdiv_mode = (qdiv == 1) ? 0 : 1; + params->pdiv = pdiv; + params->kdiv = kdiv; + params->dco_integer = dco_int; + params->dco_fraction = dco_frac; + } else { + return false; + } + + return true; +} + +static bool +cnl_ddi_calculate_wrpll2(int clock, + struct skl_wrpll_params *params) +{ + uint32_t afe_clock = clock * 5 / 1000; /* clock in kHz */ + uint32_t dco_min = 7998000; + uint32_t dco_max = 10000000; + uint32_t dco_mid = (dco_min + dco_max) / 2; + static const int dividers[] = { 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 }; + uint32_t dco, best_dco = 0, dco_centrality = 0; + uint32_t best_dco_centrality = U32_MAX; /* Spec meaning of 999999 MHz */ + int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0; + uint32_t ref_clock = params->ref_clock; + + for (d = 0; d < ARRAY_SIZE(dividers); d++) { + dco = afe_clock * dividers[d]; + + if ((dco <= dco_max) && (dco >= dco_min)) { + dco_centrality = abs(dco - dco_mid); + + if (dco_centrality < best_dco_centrality) { + best_dco_centrality = dco_centrality; + best_div = dividers[d]; + best_dco = dco; + } + } + } + + if (best_div == 0) + return false; + + cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv); + + cnl_wrpll_params_populate(params, best_dco, ref_clock, + pdiv, qdiv, kdiv); + + return true; +} + +static void test_multipliers(unsigned int clock) +{ + uint64_t afe_clock = clock * 5 / 1000; /* clocks in kHz */ + unsigned int dco_min = 7998000; + unsigned int dco_max = 10000000; + unsigned int dco_mid = (dco_min + dco_max) / 2; + + static 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 }; + unsigned int dco, dco_centrality = 0; + unsigned int best_dco_centrality = U32_MAX; + int d, best_div = 0, pdiv = 0, qdiv = 0, kdiv = 0; + + for (d = 0; d < ARRAY_SIZE(dividerlist); d++) { + dco = afe_clock * dividerlist[d]; + + if ((dco <= dco_max) && (dco >= dco_min)) { + dco_centrality = abs(dco - dco_mid); + + if (dco_centrality < best_dco_centrality) { + best_dco_centrality = dco_centrality; + best_div = dividerlist[d]; + } + } + + if (best_div != 0) { + cnl_wrpll_get_multipliers(best_div, &pdiv, &qdiv, &kdiv); + + if ((kdiv != 2) && (qdiv == 1)) + continue; + else + break; + } + } + + assert(pdiv); + assert(qdiv); + assert(kdiv); + + if (kdiv != 2) + assert(qdiv == 1); +} + +static const struct { + uint32_t clock; /* in Hz */ +} modes[] = { + {19750000}, + {23500000}, + {23750000}, + {25175000}, + {25200000}, + {26000000}, + {27000000}, + {27027000}, + {27500000}, + {28750000}, + {29750000}, + {30750000}, + {31500000}, + {35000000}, + {35500000}, + {36750000}, + {37000000}, + {37088000}, + {37125000}, + {37762500}, + {37800000}, + {38250000}, + {40500000}, + {40541000}, + {40750000}, + {41000000}, + {41500000}, + {42500000}, + {45250000}, + {46360000}, + {46406000}, + {46750000}, + {49000000}, + {50500000}, + {52000000}, + {54000000}, + {54054000}, + {54500000}, + {55632000}, + {55688000}, + {56000000}, + {56750000}, + {58250000}, + {58750000}, + {59341000}, + {59400000}, + {60500000}, + {62250000}, + {63500000}, + {64000000}, + {65250000}, + {65500000}, + {66750000}, + {67750000}, + {68250000}, + {69000000}, + {72000000}, + {74176000}, + {74250000}, + {74500000}, + {75250000}, + {76000000}, + {79500000}, + {81000000}, + {81081000}, + {82000000}, + {83000000}, + {84750000}, + {85250000}, + {85750000}, + {88500000}, + {89012000}, + {89100000}, + {91000000}, + {92719800}, + {92812500}, + {94500000}, + {95750000}, + {97750000}, + {99000000}, + {99750000}, + {100000000}, + {100500000}, + {101000000}, + {101250000}, + {102250000}, + {107892000}, + {108000000}, + {108108000}, + {109000000}, + {110250000}, + {110500000}, + {111264000}, + {111375000}, + {112500000}, + {117500000}, + {119000000}, + {119500000}, + {121250000}, + {121750000}, + {125250000}, + {125750000}, + {127250000}, + {130000000}, + {130250000}, + {131000000}, + {131500000}, + {132750000}, + {135250000}, + {138500000}, + {138750000}, + {141500000}, + {146250000}, + {148250000}, + {148352000}, + {148500000}, + {154000000}, + {155250000}, + {155750000}, + {156000000}, + {158250000}, + {159500000}, + {161000000}, + {162000000}, + {162162000}, + {162500000}, + {169500000}, + {172750000}, + {173000000}, + {175000000}, + {178500000}, + {179500000}, + {184750000}, + {185440000}, + {185625000}, + {187000000}, + {192250000}, + {193250000}, + {197750000}, + {198500000}, + {204750000}, + {207500000}, + {209250000}, + {213750000}, + {214750000}, + {216000000}, + {218750000}, + {219000000}, + {220750000}, + {222525000}, + {222750000}, + {227000000}, + {230250000}, + {233500000}, + {235000000}, + {238000000}, + {241500000}, + {243000000}, + {245250000}, + {247750000}, + {253250000}, + {256250000}, + {262500000}, + {267250000}, + {268500000}, + {270000000}, + {272500000}, + {273750000}, + {280750000}, + {281250000}, + {286000000}, + {291750000}, + {296703000}, + {297000000}, + {298000000}, + {303750000}, + {322250000}, + {324000000}, + {337750000}, + {370878750}, + {371250000}, + {373250000}, + {414500000}, + {432000000}, + {445054500}, + {445500000}, + {497750000}, + {533250000}, + {540000000}, + {592500000}, + {594000000}, + {648000000}, + {810000000}, +}; + +static void test_run(unsigned int ref_clock) +{ + unsigned int m; + struct skl_wrpll_params params[2]; + + for (m = 0; m < ARRAY_SIZE(modes); m++) { + int clock = modes[m].clock; + bool skip = false; + + params[0].ref_clock = params[1].ref_clock = ref_clock; + + if (!cnl_ddi_calculate_wrpll1(clock, ¶ms[0])) { + fprintf(stderr, "Reference: Couldn't compute divider for %dHz, reference %dHz\n", + clock, params[0].ref_clock*1000); + skip = true; + } + + if (!skip) { + if (!cnl_ddi_calculate_wrpll2(clock, ¶ms[1])) { + fprintf(stderr, "i915 implementation: Couldn't compute divider for %dHz, reference %dHz\n", + clock, params[1].ref_clock*1000); + } + + compare_params(clock, "Reference", ¶ms[0], + "i915 implementation", ¶ms[1]); + } + } +} + +int main(int argc, char **argv) +{ + unsigned int m; + unsigned int f; + unsigned int ref_clocks[] = {19200, 24000}; /* in kHz */ + + for (m = 0; m < ARRAY_SIZE(modes); m++) + test_multipliers(modes[m].clock); + + for (f = 0; f < ARRAY_SIZE(ref_clocks); f++) { + printf("=== Testing with ref clock %d kHz\n", ref_clocks[f]); + test_run(ref_clocks[f]); + } + + return 0; +} |