[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