summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tools/Makefile.sources1
-rw-r--r--tools/cnl_compute_wrpll.c526
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, &params[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, &params[1])) {
+ fprintf(stderr, "i915 implementation: Couldn't compute divider for %dHz, reference %dHz\n",
+ clock, params[1].ref_clock*1000);
+ }
+
+ compare_params(clock, "Reference", &params[0],
+ "i915 implementation", &params[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;
+}