[igt-dev] [PATCH i-g-t 1/6] tools/intel_scaler_coef: Add a tool for calculating scaler coefficients
Ville Syrjala
ville.syrjala at linux.intel.com
Tue Mar 10 14:18:25 UTC 2020
From: Ville Syrjälä <ville.syrjala at linux.intel.com>
Some of our scalers have programmable coefficients. Add support for
the format originally defined for the gen2/3/4 video overlay. The
same format (or slight variants) has been reused in later platforms
for the panel fitter and pipe scaler.
The full list of hardware that supports this stuff:
- gen2/3/4 video overlay
- ilk/snb panel fitters
- ivb panel fitter 0
- cnl+ pipe scalers
The tool can calculate the filter coefficients for a random smattering
of filter and window functions.
Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
tools/Makefile.sources | 1 +
tools/intel_scaler_coef.c | 1021 +++++++++++++++++++++++++++++++++++++
tools/meson.build | 1 +
3 files changed, 1023 insertions(+)
create mode 100644 tools/intel_scaler_coef.c
diff --git a/tools/Makefile.sources b/tools/Makefile.sources
index b7a43d47e5df..e6937bb5c478 100644
--- a/tools/Makefile.sources
+++ b/tools/Makefile.sources
@@ -28,6 +28,7 @@ tools_prog_lists = \
intel_panel_fitter \
intel_reg_checker \
intel_residency \
+ intel_scaler_coef \
intel_stepping \
intel_vbt_decode \
intel_watermark \
diff --git a/tools/intel_scaler_coef.c b/tools/intel_scaler_coef.c
new file mode 100644
index 000000000000..19e247ab266a
--- /dev/null
+++ b/tools/intel_scaler_coef.c
@@ -0,0 +1,1021 @@
+/*
+ * Copyright © 2020 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 <getopt.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "igt_aux.h"
+#include "drmtest.h"
+
+struct filter;
+
+/*
+ * x = [0:ntaps-1] with an additional offset of
+ * [-0.5:0.5[ depending on the phase.
+ */
+typedef double (*func)(const struct filter *f, double x);
+
+struct function {
+ const char *name;
+ func func;
+};
+
+struct phase {
+ double *coefs;
+};
+
+enum coef_format {
+ FORMAT_OVL_Y_HORZ,
+ FORMAT_OVL_UV_HORZ,
+ FORMAT_OVL_Y_VERT,
+ FORMAT_OVL_UV_VERT,
+ FORMAT_ILK_PF_HORZ,
+ FORMAT_ILK_PF_VERT_5TAP,
+ FORMAT_ILK_PF_VERT_3TAP,
+ FORMAT_CNL_PS,
+ FORMAT_INVALID,
+};
+
+static const char * const coef_formats[] = {
+ [FORMAT_OVL_Y_HORZ] = "ovl_y_horz",
+ [FORMAT_OVL_UV_HORZ] = "ovl_uv_horz",
+ [FORMAT_OVL_Y_VERT] = "ovl_y_vert",
+ [FORMAT_OVL_UV_VERT] = "ovl_uv_vert",
+ [FORMAT_ILK_PF_HORZ] = "ilk_pf_horz",
+ [FORMAT_ILK_PF_VERT_5TAP] = "ilk_pf_vert_5tap",
+ [FORMAT_ILK_PF_VERT_3TAP] = "ilk_pf_vert_3tap",
+ [FORMAT_CNL_PS] = "cnl_ps",
+};
+
+enum filter_mode {
+ MODE_LOWPASS,
+ MODE_HIGHPASS,
+ MODE_BANDPASS,
+ MODE_BANDSTOP,
+ MODE_INVALID,
+};
+
+static const char * const filter_modes[] = {
+ [MODE_LOWPASS] = "lowpass",
+ [MODE_HIGHPASS] = "highpass",
+ [MODE_BANDPASS] = "bandpass",
+ [MODE_BANDSTOP] = "bandstop",
+};
+
+struct filter_config {
+ int ntaps;
+ int nphases;
+ int center_mantissa_bits;
+ int other_mantissa_bits;
+ enum coef_format format;
+ bool high_prec_taps;
+ int nhwtaps;
+ const int *hwtaps;
+};
+
+struct filter {
+ struct filter_config config;
+ enum filter_mode mode;
+ bool strict;
+ double cutoff, cutoff_high;
+ double phase_offset;
+ const struct function *filter;
+ const struct function *window;
+ struct phase *phases;
+};
+
+static double _sinc(double x)
+{
+ if (x == 0.0)
+ return 1.0;
+ else
+ return sin(x) / x;
+}
+
+/* return the index of the center tap */
+static int center_tap(const struct filter *f)
+{
+ return (f->config.ntaps - 1) / 2;
+}
+
+/* filter functions */
+
+static double nearest(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+ double t = 0.5;
+
+ x -= n;
+
+ if (x >= -t && x < t)
+ return 1.0;
+ else
+ return 0.0;
+}
+
+static double linear(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+ double t = 1.0;
+
+ x -= n;
+
+ if (x >= -t && x < t)
+ return 1.0 - fabs(x) / t;
+ else
+ return 0.0;
+}
+
+static double nearest_box(const struct filter *f, double x)
+{
+ return 1.0;
+}
+
+static double linear_box(const struct filter *f, double x)
+{
+ int i = x + 0.5;
+
+ x -= i;
+
+ if (i == 0)
+ return 0.5 - fabs(x);
+ else if (i == f->config.ntaps - 1)
+ return 0.5 + fabs(x);
+ else
+ return 1.0;
+}
+
+static double tent(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+ double t = f->config.ntaps / 2;
+
+ x -= n;
+
+ if (x >= -t && x < t)
+ return 1.0 - fabs(x) / t;
+ else
+ return 0.0;
+}
+
+static double sinc(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+
+ x = f->cutoff * 2.0 * M_PI * (x - n);
+
+ return _sinc(x);
+}
+
+static double sharp(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+ int i = x + 0.5;
+
+ if (i == n)
+ return 5.0;
+ else if (i == n-1 || i==n+1)
+ return -2.0;
+ else
+ return 0.0;
+}
+
+static double edge(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+ int i = x + 0.5;
+
+ if (i == n)
+ return -3.0;
+ else if (i == n-1 || i==n+1)
+ return 1.0;
+ else if (i == n-2 || i==n+2)
+ return -0.25;
+ else
+ return 0.0;
+}
+
+static const struct function filter_funcs[] = {
+ { .name = "nearest", .func = nearest, },
+ { .name = "linear", .func = linear, },
+ { .name = "nearest_box", .func = nearest_box, },
+ { .name = "linear_box", .func = linear_box, },
+ { .name = "tent", .func = tent, },
+ { .name = "sinc", .func = sinc, },
+ { .name = "sharp", .func = sharp, },
+ { .name = "edge", .func = edge, },
+};
+
+/* window functions */
+
+static double rect(const struct filter *f, double x)
+{
+ return 1.0;
+}
+
+static double hann(const struct filter *f, double x)
+{
+ x = 2.0 * M_PI * x / (f->config.ntaps - 1);
+
+ return 0.5 - 0.5 * cos(x);
+}
+
+static double hann_wide(const struct filter *f, double x)
+{
+ x = 2.0 * M_PI * (x + 0.5) / f->config.ntaps;
+
+ return 0.5 - 0.5 * cos(x);
+}
+
+static double hamming(const struct filter *f, double x)
+{
+ x = 2.0 * M_PI * x / (f->config.ntaps - 1);
+
+ return 0.54 - 0.46 * cos(x);
+}
+
+static double blackman(const struct filter *f, double x)
+{
+ x = 2.0 * M_PI * x / (f->config.ntaps - 1);
+
+ return 0.42 - 0.5 * cos(x) + 0.08 * cos(2.0 * x);
+}
+
+static double lanczos2(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+
+ x = M_PI * (x - n) / 2.0;
+
+ return _sinc(x);
+}
+
+static double lanczos3(const struct filter *f, double x)
+{
+ int n = center_tap(f);
+
+ x = M_PI * (x - n) / 3.0;
+
+ return _sinc(x);
+}
+
+static const struct function window_funcs[] = {
+ { .name = "rect", .func = rect, },
+ { .name = "bartlett", .func = tent, },
+ { .name = "hann", .func = hann, },
+ { .name = "hann_wide", .func = hann_wide, },
+ { .name = "hamming", .func = hamming, },
+ { .name = "blackman", .func = blackman, },
+ { .name = "lanczos2", .func = lanczos2, },
+ { .name = "lanczos3", .func = lanczos3, },
+};
+
+/* */
+
+static double sum_taps(const struct filter *f, int p)
+{
+ const double *coefs = f->phases[p].coefs;
+ double sum = 0.0;
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ sum += coefs[t];
+
+ return sum;
+}
+
+static double calc_phase(const struct filter *f, int p)
+{
+ /* [-0.5:0.5[ + phase_offset */
+ return (double) p / f->config.nphases - 0.5 + f->phase_offset;
+}
+
+static void create_phase(struct filter *f, int p, double *coefs)
+{
+ double phase = calc_phase(f, p);
+ func filter = f->filter->func;
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ coefs[t] = filter(f, t + phase);
+}
+
+static void invert_phase(struct filter *f, double *coefs)
+{
+ int n = center_tap(f);
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ coefs[t] = -coefs[t];
+
+ coefs[n] += 1.0;
+}
+
+static void add_phase(struct filter *f, int p,
+ const double *coefs2)
+{
+ double *coefs = f->phases[p].coefs;
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ coefs[t] += coefs2[t];
+}
+
+static void mul_phase(struct filter *f, int p, double mul)
+{
+ double *coefs = f->phases[p].coefs;
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ coefs[t] *= mul;
+}
+
+static void window_phase(struct filter *f, int p)
+{
+ double phase = calc_phase(f, p);
+ double *coefs = f->phases[p].coefs;
+ func window = f->window->func;
+
+ for (int t = 0; t < f->config.ntaps; t++)
+ coefs[t] *= window(f, t + phase);
+}
+
+static void generate_filter_phase(struct filter *f, int p)
+{
+ double *coefs = f->phases[p].coefs;
+
+ /* apply the filter function */
+ create_phase(f, p, coefs);
+
+ if (f->mode == MODE_HIGHPASS)
+ invert_phase(f, coefs);
+
+ if (f->mode == MODE_BANDPASS || f->mode == MODE_BANDSTOP) {
+ double coefs_high[f->config.ntaps];
+
+ igt_swap(f->cutoff, f->cutoff_high);
+
+ create_phase(f, p, coefs_high);
+ invert_phase(f, coefs_high);
+ add_phase(f, p, coefs_high);
+
+ igt_swap(f->cutoff, f->cutoff_high);
+
+ if (f->mode == MODE_BANDPASS)
+ invert_phase(f, coefs);
+ }
+
+ /* apply window function */
+ window_phase(f, p);
+
+ /* normalize */
+ mul_phase(f, p, 1.0 / sum_taps(f, p));
+}
+
+static void generate_filter(struct filter *f)
+{
+ int p;
+
+ f->phases = calloc(f->config.nphases/2+1, sizeof f->phases[0]);
+ assert(f->phases);
+
+ for (p = 0; p < f->config.nphases/2+1; p++) {
+ f->phases[p].coefs = calloc(f->config.ntaps, sizeof f->phases[p].coefs[0]);
+ assert(f->phases[p].coefs);
+
+ generate_filter_phase(f, p);
+ }
+}
+
+static int nhwtaps(const struct filter *f)
+{
+ return f->config.nhwtaps ?: f->config.ntaps;
+}
+
+static int hwtap_to_tap(const struct filter *f, int _t)
+{
+ if (!f->config.hwtaps)
+ return _t;
+
+ for (int t = 0; t < f->config.nhwtaps; t++) {
+ if (f->config.hwtaps[t] == _t)
+ return t;
+ }
+
+ return -1;
+}
+
+#if 0
+static int tap_to_hwtap(const struct filter *f, int t)
+{
+ if (!f->config.hwtaps)
+ return t;
+
+ return f->config.hwtaps[t];
+}
+#endif
+
+static const int ilk_pf_vert_3tap_hwtaps[] = { 0, 2, 4 };
+static const int cnl_ps_hwtaps[] = { 6, 5, 4, 3, 2, 1, 0 };
+
+static const struct filter_config configs[] = {
+ [FORMAT_OVL_Y_HORZ] = {
+ .ntaps = 5, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 7,
+ .format = FORMAT_OVL_Y_HORZ,
+ },
+ [FORMAT_OVL_UV_HORZ] = {
+ .ntaps = 3, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 7,
+ .format = FORMAT_OVL_UV_HORZ,
+ },
+ [FORMAT_OVL_Y_VERT] = {
+ .ntaps = 3, .nphases = 32,
+ .center_mantissa_bits = 8,
+ .other_mantissa_bits = 6,
+ .format = FORMAT_OVL_Y_VERT,
+ },
+ [FORMAT_OVL_UV_VERT] = {
+ .ntaps = 3, .nphases = 32,
+ .center_mantissa_bits = 6,
+ .other_mantissa_bits = 6,
+ .format = FORMAT_OVL_UV_VERT,
+ },
+ [FORMAT_ILK_PF_HORZ] = {
+ .ntaps = 7, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 7,
+ .high_prec_taps = true,
+ .format = FORMAT_ILK_PF_HORZ,
+ },
+ [FORMAT_ILK_PF_VERT_5TAP] = {
+ .ntaps = 5, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 7,
+ .high_prec_taps = true,
+ .format = FORMAT_ILK_PF_VERT_5TAP,
+ },
+ [FORMAT_ILK_PF_VERT_3TAP] = {
+ .ntaps = 3, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 7,
+ .high_prec_taps = true,
+ .format = FORMAT_ILK_PF_VERT_3TAP,
+ .hwtaps = ilk_pf_vert_3tap_hwtaps, .nhwtaps = 5,
+ },
+ [FORMAT_CNL_PS] = {
+ .ntaps = 7, .nphases = 32,
+ .center_mantissa_bits = 9,
+ .other_mantissa_bits = 9,
+ .format = FORMAT_CNL_PS,
+ .hwtaps = cnl_ps_hwtaps, .nhwtaps = 7,
+ },
+};
+
+/*
+ * Filter coefficient format used by:
+ * - gen2/3/4 video overlay
+ * - ilk/snb/ivb 7x5 panel fitter
+ * - cnl+ pipe scaler
+ */
+union gen2_coef_reg
+{
+ struct {
+ uint16_t mantissa:12;
+ uint16_t exponent:3;
+ uint16_t sign:1;
+ };
+ uint16_t reg;
+};
+
+static int exp_to_hw(const struct filter *f, int t, int exp)
+{
+ /*
+ * On some hw the non-center taps trade
+ * away >= 1.0 values for more precision.
+ */
+ if (f->config.high_prec_taps && t != center_tap(f) && exp == 4)
+ exp = 0;
+
+ assert(exp >= 0 && exp <= 3);
+
+ return exp;
+}
+
+static int exp_from_hw(const struct filter *f, int t, int exp)
+{
+ assert(exp >= 0 && exp <= 3);
+
+ /*
+ * On some hw the non-center taps trade
+ * away >= 1.0 values for more precision.
+ */
+ if (f->config.high_prec_taps && t != center_tap(f) && exp == 0)
+ exp = 4;
+
+ return exp;
+}
+
+static int sign_to_int(bool sign)
+{
+ return sign ? -1 : 1;
+}
+
+static double gen2_reg_to_coef(const struct filter *f, int t,
+ const union gen2_coef_reg *r)
+{
+ return (sign_to_int(r->sign) * (double) r->mantissa) /
+ (1 << (11 + exp_from_hw(f, t, r->exponent)));
+}
+
+
+static int gen2_mantissa_bits(const struct filter *f, int t)
+{
+ if (t == center_tap(f))
+ return f->config.center_mantissa_bits;
+ else
+ return f->config.other_mantissa_bits;
+}
+
+static void gen2_coef_to_reg(const struct filter *f,
+ int t, double coef,
+ union gen2_coef_reg *r)
+{
+ int mantissa_bits = gen2_mantissa_bits(f, t);
+ int max = (1 << mantissa_bits) - 1;
+ int exp, exp_max, exp_min;
+
+ if (f->config.high_prec_taps && t != center_tap(f)) {
+ exp_min = 1;
+ exp_max = 4;
+ } else {
+ exp_min = 0;
+ exp_max = 3;
+ }
+
+ if (coef < 0.0) {
+ coef = -coef;
+ r->sign = 1;
+ } else {
+ r->sign = 0;
+ }
+
+ /* find the exponent that gives the best precision */
+ for (exp = exp_max; exp >= exp_min; exp--) {
+ int c = coef * (1 << (mantissa_bits + exp - 1)) + 0.5;
+
+ if (!f->strict && exp == exp_min)
+ c = min(c, max-1);
+
+ if (c <= max) {
+ r->exponent = exp_to_hw(f, t, exp);
+ r->mantissa = c << (12 - mantissa_bits);
+ return;
+ }
+ }
+
+ fprintf(stderr, "Unable to represent %f in hardware format\n",
+ sign_to_int(r->sign) * coef);
+ assert(0);
+}
+
+/*
+ * Calculate the hardware specific register format for the tap
+ * and translate that back to a floating point value. So afterwards
+ * the floating point value will be limited by the precision of
+ * the hardware.
+ */
+static void gen2_update_tap(struct filter *f, int p, int t)
+{
+ double *coefs = f->phases[p].coefs;
+ union gen2_coef_reg r;
+
+ gen2_coef_to_reg(f, t, coefs[t], &r);
+
+ coefs[t] = gen2_reg_to_coef(f, t, &r);
+}
+
+static void update_tap(struct filter *f, int p, int t)
+{
+ switch (f->config.format) {
+ default:
+ gen2_update_tap(f, p, t);
+ break;
+ }
+}
+
+/*
+ * Convert the coefficients into the hardware format and back
+ */
+static void update_taps(struct filter *f, int p)
+{
+ for (int t = 0; t < f->config.ntaps; t++)
+ update_tap(f, p, t);
+}
+
+/*
+ * Make the taps sum to 1.0 by adding the remaining error
+ * to each tap in turn starting from the center tap.
+ */
+static void fixup_taps(struct filter *f, int p)
+{
+ double *coefs = f->phases[p].coefs;
+ double sum;
+ int i;
+
+ sum = sum_taps(f, p);
+
+ while (sum != 1.0) {
+ for (i = 0; i < f->config.ntaps; i++) {
+ int n = center_tap(f);
+ int t;
+
+ if (i & 1)
+ t = n + (i + 1) / 2;
+ else
+ t = n - i / 2;
+
+ coefs[t] += 1.0 - sum;
+
+ update_tap(f, p, t);
+
+ sum = sum_taps(f, p);
+ if (sum == 1.0)
+ break;
+ }
+ }
+}
+
+/*
+ * Convert the filter coefficients into the hardware specific
+ * format and back.
+ */
+static void update_filter(struct filter *f)
+{
+ for (int p = 0; p < f->config.nphases/2+1; p++)
+ update_taps(f, p);
+}
+
+/*
+ * Make sure the taps for each phase sum up to 1.0 and adjust
+ * if necessary to guarantee that.
+ */
+static void fixup_filter(struct filter *f)
+{
+ for (int p = 0; p < f->config.nphases/2+1; p++)
+ fixup_taps(f, p);
+}
+
+/* */
+
+/*
+ * Print out the filter information in floating point format.
+ */
+static void print_filter(const struct filter *f)
+{
+ int p, t;
+
+ printf(" format = %s\n"
+ " ntaps = %d\n"
+ " nphases = %d\n"
+ " cutoff = %f\n"
+ " cutoff_high = %f\n"
+ " filter = %s\n"
+ " window = %s\n"
+ " mode = %s\n"
+ " coefs = {\n",
+ coef_formats[f->config.format],
+ f->config.ntaps, f->config.nphases,
+ f->cutoff, f->cutoff_high,
+ f->filter->name, f->window->name,
+ filter_modes[f->mode]);
+
+ for (p = 0; p < f->config.nphases/2+1; p++) {
+ const double *coefs = f->phases[p].coefs;
+ double sum = 0.0;
+
+ printf(" %4d: {", p);
+ for (t = 0; t < f->config.ntaps; t++) {
+ printf(" % f", coefs[t]);
+ sum += coefs[t];
+ }
+ printf(" } sum = %.10f\n", sum);
+ }
+ printf(" }\n");
+}
+
+/*
+ * Print out the filter information in hardware specific format.
+ */
+static void hw_print_filter(const struct filter *f)
+{
+ printf(" format = %s\n"
+ " ntaps = %d\n"
+ " nphases = %d\n"
+ " cutoff = %f\n"
+ " cutoff_high = %f\n"
+ " filter = %s\n"
+ " window = %s\n"
+ " mode = %s\n"
+ " coefs = {\n",
+ coef_formats[f->config.format],
+ f->config.ntaps, f->config.nphases,
+ f->cutoff, f->cutoff_high,
+ f->filter->name, f->window->name,
+ filter_modes[f->mode]);
+
+ for (int p = 0; p < f->config.nphases/2+1; p++) {
+ const double *coefs = f->phases[p].coefs;
+ int sum = 0;
+
+ printf(" %4d: {", p);
+ for (int t = 0; t < f->config.ntaps; t++) {
+ switch (f->config.format) {
+ default: {
+ union gen2_coef_reg r;
+ int exp;
+
+ gen2_coef_to_reg(f, t, coefs[t], &r);
+
+ exp = exp_from_hw(f, t, r.exponent);
+
+ /* sum in .16 binary fixed point */
+ sum += sign_to_int(r.sign) * r.mantissa << (5 - exp);
+
+ printf(" %04x", r.reg);
+ }
+ break;
+ }
+ }
+ printf(" } sum = 0x%x\n", sum);
+ }
+ printf(" }\n");
+}
+
+/*
+ * Print out the filter coefs as a C structure..
+ */
+static void print_c_filter(const struct filter *f)
+{
+ printf("struct %s = {\n", coef_formats[f->config.format]);
+
+ for (int p = 0; p < f->config.nphases/2+1; p++) {
+ const double *coefs = f->phases[p].coefs;
+
+ printf("\t[%2d] = {", p);
+
+ for (int _t = 0; _t < nhwtaps(f); _t++) {
+ int t = hwtap_to_tap(f, _t);
+ double coef;
+
+ if (t >= 0)
+ coef = coefs[t];
+ else
+ coef = 0.0;
+
+ switch (f->config.format) {
+ default: {
+ union gen2_coef_reg r;
+
+ gen2_coef_to_reg(f, t, coef, &r);
+
+ printf(" 0x%04x,", r.reg);
+ }
+ break;
+ }
+ }
+ printf(" },\n");
+ }
+ printf("};\n");
+}
+
+/* */
+
+static void usage(FILE *stream, const char *name)
+{
+ int i;
+
+ fprintf(stream, "Usage: %s [-h|--help] [-s|--strict]"
+ " [-t|--taps <taps>] [-p|--phases <phases>]"
+ " [-c|--cutoff <cutoff>] [-C|--high-cutoff <cutoff>]"
+ " [-f|--filter <filter>] [-w|--window <window>]"
+ " [-m|--mantissa <bits>] [-F|--format <format>]"
+ " [-M|--mode <mode>]"
+ "\n"
+ " -h|--help: Show this help text\n"
+ " -s|--strict: Fail if any coefficient is out of range for the hardware, otherwise clamp it\n"
+ " -t|--taps <taps>: number of filter taps [1:1024]\n"
+ " -p|--phases <phases>: number of filter phases [1:1024]\n"
+ " -c|--cutoff <cutoff>: cutoff frequency for sinc filter [0.0:0.5]\n"
+ " -C|--high-cutoff <cutoff>: high cutoff frequency for bandpass/bandstop [0.0:0.5]\n"
+ " -f|--filter <filter>: filter function\n"
+ " -w|--window <window>: window function\n"
+ " -m|--mantissa <bits>: number of mantissa bits in the coefficients [1:12]\n"
+ " -F|--format <format>: hardware coefficient format\n"
+ " -M|--mode <mode>: filter mode\n"
+ , name);
+
+ fprintf(stream, "Possible filter functions: ");
+ for (i = 0; i < ARRAY_SIZE(filter_funcs); i++)
+ fprintf(stream, "%s%c", filter_funcs[i].name, i == ARRAY_SIZE(filter_funcs) - 1 ? '\n' : ',');
+
+ fprintf(stream, "Possible window functions: ");
+ for (i = 0; i < ARRAY_SIZE(window_funcs); i++)
+ fprintf(stream, "%s%c", window_funcs[i].name, i == ARRAY_SIZE(window_funcs) - 1 ? '\n' : ',');
+
+ fprintf(stream, "Possible hardware coefficient formats: ");
+ for (i = 0; i < ARRAY_SIZE(coef_formats); i++)
+ fprintf(stream, "%s%c", coef_formats[i], i == FORMAT_INVALID - 1 ? '\n' : ',');
+
+ fprintf(stream, "Possible filter modes: ");
+ for (i = 0; i < ARRAY_SIZE(filter_modes); i++)
+ fprintf(stream, "%s%c", filter_modes[i], i == MODE_INVALID - 1 ? '\n' : ',');
+}
+
+int main(int argc, char *argv[])
+{
+ const struct option opts[] = {
+ { .name = "help", .val = 'h' },
+ { .name = "strict", .val = 's' },
+ { .name = "taps", .has_arg = true, .val = 't' },
+ { .name = "phases", .has_arg = true, .val = 'p' },
+ { .name = "cutoff", .has_arg = true, .val = 'c' },
+ { .name = "high-cutoff", .has_arg = true, .val = 'C' },
+ { .name = "filter", .has_arg = true, .val = 'f' },
+ { .name = "window", .has_arg = true, .val = 'w' },
+ { .name = "mantissa-center", .has_arg = true, .val = 'B' },
+ { .name = "mantissa-other", .has_arg = true, .val = 'b' },
+ { .name = "format", .has_arg = true, .val = 'F' },
+ { .name = "mode", .has_arg = true, .val = 'M' },
+ {}
+ };
+ struct filter f = {
+ .config = configs[FORMAT_CNL_PS],
+ .mode = MODE_LOWPASS,
+ .strict = false,
+ .cutoff = 0.5,
+ .cutoff_high = 0.5,
+ .filter = &filter_funcs[0],
+ .window = &window_funcs[0],
+ };
+ bool error = false, help = false;
+
+ for (;;) {
+ int c = getopt_long(argc, argv, "hst:p:c:C:f:w:F:M:B:b:", opts, NULL);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ int i;
+ case 'h':
+ help = true;
+ break;
+ case 's':
+ f.strict = true;
+ break;
+ case 't':
+ f.config.ntaps = atoi(optarg);
+ if (f.config.ntaps < 1 || f.config.ntaps > 1024) {
+ fprintf(stderr, "Invalid number of taps: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'p':
+ f.config.nphases = atoi(optarg);
+ if (f.config.nphases < 1 || f.config.nphases > 1024) {
+ fprintf(stderr, "Invalid number of phases: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'c':
+ f.cutoff = strtod(optarg, NULL);
+ if (f.cutoff < 0.0 || f.cutoff > 0.5) {
+ fprintf(stderr, "Invalid cutoff frequency: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'C':
+ f.cutoff_high = strtod(optarg, NULL);
+ if (f.cutoff_high < 0.0 || f.cutoff_high > 0.5) {
+ fprintf(stderr, "Invalid high cutoff frequency: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'f':
+ f.filter = NULL;
+ for (i = 0; i < ARRAY_SIZE(filter_funcs); i++) {
+ if (!strcmp(optarg, filter_funcs[i].name)) {
+ f.filter = &filter_funcs[i];
+ break;
+ }
+ }
+ if (f.filter == NULL) {
+ fprintf(stderr, "Unknwon filter function: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'w':
+ f.window = NULL;
+ for (i = 0; i < ARRAY_SIZE(window_funcs); i++) {
+ if (!strcmp(optarg, window_funcs[i].name)) {
+ f.window = &window_funcs[i];
+ break;
+ }
+ }
+ if (f.window == NULL) {
+ fprintf(stderr, "Unknwon window function: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'B':
+ f.config.center_mantissa_bits = atoi(optarg);
+ if (f.config.center_mantissa_bits < 1 || f.config.center_mantissa_bits > 12) {
+ fprintf(stderr, "Invalid center tap mantissa bits: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'b':
+ f.config.other_mantissa_bits = atoi(optarg);
+ if (f.config.other_mantissa_bits < 1 || f.config.other_mantissa_bits > 12) {
+ fprintf(stderr, "Invalid other tap mantissa bits: %s\n", optarg);
+ error = true;
+ }
+ break;
+ case 'F':
+ for (f.config.format = 0;
+ f.config.format < ARRAY_SIZE(coef_formats);
+ f.config.format++) {
+ if (!strcmp(optarg, coef_formats[f.config.format]))
+ break;
+ }
+ if (f.config.format == FORMAT_INVALID) {
+ fprintf(stderr, "Unknown hardware coefficient format: %s\n", optarg);
+ error = true;
+ }
+ f.config = configs[f.config.format];
+ break;
+ case 'M':
+ for (f.mode = 0; f.mode < ARRAY_SIZE(filter_modes); f.mode++) {
+ if (!strcmp(optarg, filter_modes[f.mode]))
+ break;
+ }
+ if (f.mode == MODE_INVALID) {
+ fprintf(stderr, "Unknown filter mode: %s\n", optarg);
+ error = true;
+ }
+ break;
+ default:
+ error = true;
+ }
+ }
+
+ if (help) {
+ usage(stdout, argv[0]);
+ return 0;
+ } else if (error) {
+ usage(stderr, argv[0]);
+ return 1;
+ }
+
+ generate_filter(&f);
+ print_filter(&f);
+ hw_print_filter(&f);
+
+ update_filter(&f);
+ print_filter(&f);
+ hw_print_filter(&f);
+
+ fixup_filter(&f);
+ print_filter(&f);
+ hw_print_filter(&f);
+
+ print_c_filter(&f);
+
+ return 0;
+}
diff --git a/tools/meson.build b/tools/meson.build
index 59b56d5dedb9..a47badebe1b0 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -30,6 +30,7 @@ tools_progs = [
'intel_panel_fitter',
'intel_reg_checker',
'intel_residency',
+ 'intel_scaler_coef',
'intel_stepping',
'intel_vbt_decode',
'intel_watermark',
--
2.24.1
More information about the igt-dev
mailing list