diff --git a/topology/Makefile.am b/topology/Makefile.am index 7f77fdd27..40516c6b2 100644 --- a/topology/Makefile.am +++ b/topology/Makefile.am @@ -9,9 +9,23 @@ endif rst2man $< > $@ alsatplg_SOURCES = topology.c pre-processor.c pre-process-class.c pre-process-object.c \ - pre-process-dapm.c pre-process-dai.c - -noinst_HEADERS = topology.h pre-processor.h + pre-process-dapm.c pre-process-dai.c intel/nhlt.c intel/dmic/dmic-nhlt.c \ + intel/ssp/ssp-nhlt.c + +noinst_HEADERS = topology.h pre-processor.h \ + intel/nhlt.h intel/nhlt-internal.h \ + intel/dmic/dmic-nhlt.h intel/dmic/format.h \ + intel/dmic/pdm_decim_fir.h intel/dmic/pdm_decim_table.h \ + intel/dmic/pdm_decim_int32_02_4375_5100_010_095.h \ + intel/dmic/pdm_decim_int32_08_4156_5301_010_090.h \ + intel/dmic/pdm_decim_int32_03_4375_5100_010_095.h \ + intel/dmic/pdm_decim_int32_10_4156_5345_010_090.h \ + intel/dmic/pdm_decim_int32_04_4318_5100_010_095.h \ + intel/dmic/pdm_decim_int32_12_4156_5345_010_090.h \ + intel/dmic/pdm_decim_int32_05_4325_5100_010_095.h \ + intel/dmic/pdm_decim_int32_02_4323_5100_010_095.h \ + intel/dmic/pdm_decim_int32_06_4172_5100_010_095.h \ + intel/ssp/ssp-nhlt.h AM_CPPFLAGS = \ -Wall -I$(top_srcdir)/include diff --git a/topology/intel/dmic/dmic-nhlt.c b/topology/intel/dmic/dmic-nhlt.c new file mode 100644 index 000000000..7d523b38d --- /dev/null +++ b/topology/intel/dmic/dmic-nhlt.c @@ -0,0 +1,1827 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "../nhlt-internal.h" +#include "dmic-nhlt.h" +#include "pdm_decim_table.h" +#include "dmic_macros.h" + +/* structs for dmic internal calculations */ +struct dmic_calc_decim_modes { + int16_t clkdiv[DMIC_MAX_MODES]; + int16_t mcic[DMIC_MAX_MODES]; + int16_t mfir[DMIC_MAX_MODES]; + int num_of_modes; +}; + +struct dmic_calc_matched_modes { + int16_t clkdiv[DMIC_MAX_MODES]; + int16_t mcic[DMIC_MAX_MODES]; + int16_t mfir_a[DMIC_MAX_MODES]; + int16_t mfir_b[DMIC_MAX_MODES]; + int num_of_modes; +}; + +struct dmic_calc_configuration { + struct pdm_decim *fir_a; + struct pdm_decim *fir_b; + int clkdiv; + int mcic; + int mfir_a; + int mfir_b; + int cic_shift; + int fir_a_shift; + int fir_b_shift; + int fir_a_length; + int fir_b_length; + int32_t fir_a_scale; + int32_t fir_b_scale; +}; + +#ifdef DMIC_NHLT_DEBUG + +#define MAX_NHLT_BUFFER_SIZE 25000 + +/* print blob as bytes hex string like: 0x11,0xff,0xff,0xff etc. */ +static void dmic_print_bytes_as_hex(uint8_t *src, size_t size) +{ + tplg_pp_debug("printing dmic vendor blob as bytes:"); + char nhlt_string_buffer[MAX_NHLT_BUFFER_SIZE]; + char *nhlt_p = &nhlt_string_buffer[0]; + int i; + + for (i = 0; i < size; i++) { + snprintf(nhlt_p, 6, "0x%02x,", *src); + nhlt_p += 5; + src++; + } + + /* remove the last comma... */ + nhlt_p--; + *nhlt_p = '\0'; + + printf("%s\n", nhlt_string_buffer); +} + +/* print blob as 32 bit integer hex string like: 0xffffffff,0x00000010 etc. */ +static void dmic_print_integers_as_hex(uint32_t *src, size_t size) +{ + tplg_pp_debug("printing dmic vendor blob as integers:"); + + char nhlt_string_buffer[MAX_NHLT_BUFFER_SIZE]; + char *nhlt_p = &nhlt_string_buffer[0]; + int i; + + for (i = 0; i < size; i++) { + snprintf(nhlt_p, 12, "0x%08x,", *src); + nhlt_p += 11; + src++; + } + + /* remove the last comma... */ + nhlt_p--; + *nhlt_p = '\0'; + + printf("%s\n", nhlt_string_buffer); + + tplg_pp_debug(""); +} + +static void dmic_print_internal(struct intel_nhlt_params *nhlt) +{ + int i, j, line, lines, remind; + + tplg_pp_debug("printing dmic nhlt internal data"); + + /* top level struct */ + tplg_pp_debug("gateway attributes: %u", nhlt->dmic_blob.gateway_attributes); + + tplg_pp_debug("ts_group: %u %u %u %u", nhlt->dmic_blob.ts_group[0], nhlt->dmic_blob.ts_group[1], + nhlt->dmic_blob.ts_group[2], nhlt->dmic_blob.ts_group[3]); + + tplg_pp_debug("clock_on_delay: %u", nhlt->dmic_blob.clock_on_delay); + + tplg_pp_debug("channel_ctrl_mask: %u", nhlt->dmic_blob.channel_ctrl_mask); + + tplg_pp_debug("chan_ctrl_cfg: %u %u", nhlt->dmic_blob.chan_ctrl_cfg[0], + nhlt->dmic_blob.chan_ctrl_cfg[1]); + + tplg_pp_debug("channel_pdm_mask: %u", nhlt->dmic_blob.channel_pdm_mask); + + /* first pdm struct */ + tplg_pp_debug("pdm_ctrl_cfg 0"); + tplg_pp_debug("cic_control: %u", nhlt->dmic_blob_pdm[0].cic_control); + tplg_pp_debug("cic_config: %u", nhlt->dmic_blob_pdm[0].cic_config); + tplg_pp_debug("mic_control: %u", nhlt->dmic_blob_pdm[0].mic_control); + tplg_pp_debug("pdmsm: %u", nhlt->dmic_blob_pdm[0].pdmsm); + tplg_pp_debug("reuse_fir_from_pdm: %u", nhlt->dmic_blob_pdm[0].reuse_fir_from_pdm); + + /* first pdm struct, first fir */ + tplg_pp_debug("fir_config 0"); + tplg_pp_debug("fir_control: %u", nhlt->dmic_blob_fir[0][0].fir_control); + tplg_pp_debug("fir_config: %u", nhlt->dmic_blob_fir[0][0].fir_config); + tplg_pp_debug("dc_offset_left: %u", nhlt->dmic_blob_fir[0][0].dc_offset_left); + tplg_pp_debug("dc_offset_right: %u", nhlt->dmic_blob_fir[0][0].dc_offset_right); + tplg_pp_debug("out_gain_left: %u", nhlt->dmic_blob_fir[0][0].out_gain_left); + tplg_pp_debug("out_gain_right: %u", nhlt->dmic_blob_fir[0][0].out_gain_right); + + /* first pdm struct, second fir */ + tplg_pp_debug("fir_config 1"); + tplg_pp_debug("fir_control: %u", nhlt->dmic_blob_fir[0][1].fir_control); + tplg_pp_debug("fir_config: %u", nhlt->dmic_blob_fir[0][1].fir_config); + tplg_pp_debug("dc_offset_left: %u", nhlt->dmic_blob_fir[0][1].dc_offset_left); + tplg_pp_debug("dc_offset_right: %u", nhlt->dmic_blob_fir[0][1].dc_offset_right); + tplg_pp_debug("out_gain_left: %u", nhlt->dmic_blob_fir[0][1].out_gain_left); + tplg_pp_debug("out_gain_right: %u", nhlt->dmic_blob_fir[0][1].out_gain_right); + + /* first pdm struct, fir coeffs */ + for (j = 0; j < DMIC_HW_CONTROLLERS; j++) { + tplg_pp_debug("fir_coeffs a length %u:", nhlt->dmic_fir_array.fir_len[0]); + lines = nhlt->dmic_fir_array.fir_len[0] / 8; + remind = nhlt->dmic_fir_array.fir_len[0] % 8; + for (i = 0; i < lines; i++) { + line = i * 8; + tplg_pp_debug("%d %u %u %u %u %u %u %u %u", i, + nhlt->dmic_fir_array.fir_coeffs[j][0][line], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 1], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 2], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 3], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 4], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 5], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 6], + nhlt->dmic_fir_array.fir_coeffs[j][0][line + 7]); + } + line += 1; + for (i = 0; i < remind; i++) + tplg_pp_debug("%u ", nhlt->dmic_fir_array.fir_coeffs[j][0][line + i]); + } + + /* second pdm struct */ + tplg_pp_debug("pdm_ctrl_cfg 1"); + tplg_pp_debug("cic_control: %u", nhlt->dmic_blob_pdm[1].cic_control); + tplg_pp_debug("cic_config: %u", nhlt->dmic_blob_pdm[1].cic_config); + tplg_pp_debug("mic_control: %u", nhlt->dmic_blob_pdm[1].mic_control); + tplg_pp_debug("pdmsm: %u", nhlt->dmic_blob_pdm[1].pdmsm); + tplg_pp_debug("reuse_fir_from_pdm: %u", nhlt->dmic_blob_pdm[1].reuse_fir_from_pdm); + + /* second pdm struct, first fir */ + tplg_pp_debug("fir_config 0"); + tplg_pp_debug("fir_control: %u", nhlt->dmic_blob_fir[1][0].fir_control); + tplg_pp_debug("fir_config: %u", nhlt->dmic_blob_fir[1][0].fir_config); + tplg_pp_debug("dc_offset_left: %u", nhlt->dmic_blob_fir[1][0].dc_offset_left); + tplg_pp_debug("dc_offset_right: %u", nhlt->dmic_blob_fir[1][0].dc_offset_right); + tplg_pp_debug("out_gain_left: %u", nhlt->dmic_blob_fir[1][0].out_gain_left); + tplg_pp_debug("out_gain_right: %u", nhlt->dmic_blob_fir[1][0].out_gain_right); + + /* second pdm struct, second fir */ + tplg_pp_debug("fir_config 1"); + tplg_pp_debug("fir_control: %u", nhlt->dmic_blob_fir[1][1].fir_control); + tplg_pp_debug("fir_config: %u", nhlt->dmic_blob_fir[1][1].fir_config); + tplg_pp_debug("dc_offset_left: %u", nhlt->dmic_blob_fir[1][1].dc_offset_left); + tplg_pp_debug("dc_offset_right: %u", nhlt->dmic_blob_fir[1][1].dc_offset_right); + tplg_pp_debug("out_gain_left: %u", nhlt->dmic_blob_fir[1][1].out_gain_left); + tplg_pp_debug("out_gain_right: %u", nhlt->dmic_blob_fir[1][1].out_gain_right); + + for (j = 0; j < DMIC_HW_CONTROLLERS; j++) { + tplg_pp_debug("fir_coeffs b length %u:", nhlt->dmic_fir_array.fir_len[1]); + lines = nhlt->dmic_fir_array.fir_len[1] / 8; + remind = nhlt->dmic_fir_array.fir_len[1] % 8; + for (i = 0; i < lines; i++) { + line = i * 8; + tplg_pp_debug("%d %u %u %u %u %u %u %u %u", i, + nhlt->dmic_fir_array.fir_coeffs[j][1][line], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 1], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 2], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 3], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 4], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 5], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 6], + nhlt->dmic_fir_array.fir_coeffs[j][1][line + 7]); + } + line += 1; + for (i = 0; i < remind; i++) + tplg_pp_debug("%u ", nhlt->dmic_fir_array.fir_coeffs[j][1][line + i]); + } +} +#endif /* DMIC_NHLT_DEBUG */ + +/* This is a divide function that returns ceil of the quotient. + * E.g. ceil_divide(9, 3) returns 3, ceil_divide(10, 3) returns 4. + */ +static int ceil_divide(int a, int b) +{ + int c; + + c = a / b; + + if (!((a ^ b) & (1 << ((sizeof(int) * 8) - 1))) && c * b != a) + c++; + + return c; +} + +/* This function searches from vec[] (of length vec_length) integer values + * of n. The indexes to equal values is returned in idx[]. The function + * returns the number of found matches. The max_results should be set to + * 0 (or negative) or vec_length get all the matches. The max_result can be set + * to 1 to receive only the first match in ascending order. It avoids need + * for an array for idx. + */ +static int find_equal_int16(int16_t idx[], int16_t vec[], int n, int vec_length, + int max_results) +{ + int nresults = 0; + int i; + + for (i = 0; i < vec_length; i++) { + if (vec[i] == n) { + idx[nresults++] = i; + if (nresults == max_results) + break; + } + } + + return nresults; +} + +/* Return the largest absolute value found in the vector. Note that + * smallest negative value need to be saturated to preset as int32_t. + */ +static int32_t find_max_abs_int32(int32_t vec[], int vec_length) +{ + int i; + int64_t amax = (vec[0] > 0) ? vec[0] : -vec[0]; + + for (i = 1; i < vec_length; i++) { + amax = (vec[i] > amax) ? vec[i] : amax; + amax = (-vec[i] > amax) ? -vec[i] : amax; + } + + return SATP_INT32(amax); /* Amax is always a positive value */ +} + +/* Count the left shift amount to normalize a 32 bit signed integer value + * without causing overflow. Input value 0 will result to 31. + */ +static int norm_int32(int32_t val) +{ + int c = 0; + + /* count number of bits c that val can be right-shifted arithmetically + * until there is -1 (if val is negative) or 0 (if val is positive) + * norm of val will be 31-c + */ + for (; val != -1 && val != 0; c++) + val >>= 1; + + return 31 - c; +} + +/* This function returns a raw list of potential microphone clock and decimation + * modes for achieving requested sample rates. The search is constrained by + * decimation HW capabililies and setup parameters. The parameters such as + * microphone clock min/max and duty cycle requirements need be checked from + * used microphone component datasheet. + */ +static void find_modes(struct intel_nhlt_params *nhlt, struct dmic_calc_decim_modes *modes) +{ + int di = nhlt->dmic_dai_index; + uint32_t fs = nhlt->dmic_prm[di].fifo_fs; + int clkdiv_min; + int clkdiv_max; + int clkdiv; + int c1; + int du_min; + int du_max; + int pdmclk; + int osr; + int mfir; + int mcic; + int ioclk_test; + int osr_min = DMIC_MIN_OSR; + int j; + int i = 0; + + /* Defaults, empty result */ + modes->num_of_modes = 0; + + /* The FIFO is not requested if sample rate is set to zero. Just + * return in such case with num_of_modes as zero. + */ + if (fs == 0) { + tplg_pp_debug("find_modes(): fs not set"); + return; + } + + /* Override DMIC_MIN_OSR for very high sample rates, use as minimum + * the nominal clock for the high rates. + */ + if (fs >= DMIC_HIGH_RATE_MIN_FS) + osr_min = DMIC_HIGH_RATE_OSR_MIN; + + /* Check for sane pdm clock, min 100 kHz, max ioclk/2 */ + if (nhlt->dmic_prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN || + nhlt->dmic_prm[di].pdmclk_max > nhlt->dmic_prm[di].io_clk / 2) { + tplg_pp_debug("find_modes(): pdm clock max not in range"); + return; + } + if (nhlt->dmic_prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN || + nhlt->dmic_prm[di].pdmclk_min > nhlt->dmic_prm[di].pdmclk_max) { + tplg_pp_debug("find_modes(): pdm clock min not in range"); + return; + } + + /* Check for sane duty cycle */ + if (nhlt->dmic_prm[di].duty_min > nhlt->dmic_prm[di].duty_max) { + tplg_pp_debug("find_modes(): duty cycle min > max"); + return; + } + if (nhlt->dmic_prm[di].duty_min < DMIC_HW_DUTY_MIN || + nhlt->dmic_prm[di].duty_min > DMIC_HW_DUTY_MAX) { + tplg_pp_debug("find_modes(): pdm clock min not in range"); + return; + } + if (nhlt->dmic_prm[di].duty_max < DMIC_HW_DUTY_MIN || + nhlt->dmic_prm[di].duty_max > DMIC_HW_DUTY_MAX) { + tplg_pp_debug("find_modes(): pdm clock max not in range"); + return; + } + + /* Min and max clock dividers */ + clkdiv_min = ceil_divide(nhlt->dmic_prm[di].io_clk, nhlt->dmic_prm[di].pdmclk_max); + clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN); + clkdiv_max = nhlt->dmic_prm[di].io_clk / nhlt->dmic_prm[di].pdmclk_min; + + tplg_pp_debug("find_modes(): clkdiv_min %u clkdiv_max %u", clkdiv_min, clkdiv_max); + + /* Loop possible clock dividers and check based on resulting + * oversampling ratio that CIC and FIR decimation ratios are + * feasible. The ratios need to be integers. Also the mic clock + * duty cycle need to be within limits. + */ + for (clkdiv = clkdiv_min; clkdiv <= clkdiv_max; clkdiv++) { + /* Calculate duty cycle for this clock divider. Note that + * odd dividers cause non-50% duty cycle. + */ + c1 = clkdiv >> 1; + du_min = 100 * c1 / clkdiv; + du_max = 100 - du_min; + + /* Calculate PDM clock rate and oversampling ratio. */ + pdmclk = nhlt->dmic_prm[di].io_clk / clkdiv; + osr = pdmclk / fs; + + /* Check that OSR constraints is met and clock duty cycle does + * not exceed microphone specification. If exceed proceed to + * next clkdiv. + */ + if (osr < osr_min || du_min < nhlt->dmic_prm[di].duty_min || + du_max > nhlt->dmic_prm[di].duty_max) + continue; + + /* Loop FIR decimation factors candidates. If the + * integer divided decimation factors and clock dividers + * as multiplied with sample rate match the IO clock + * rate the division was exact and such decimation mode + * is possible. Then check that CIC decimation constraints + * are met. The passed decimation modes are added to array. + */ + for (j = 0; fir_list[j]; j++) { + mfir = fir_list[j]->decim_factor; + + /* Skip if previous decimation factor was the same */ + if (j > 1 && fir_list[j - 1]->decim_factor == mfir) + continue; + + mcic = osr / mfir; + ioclk_test = fs * mfir * mcic * clkdiv; + + if (ioclk_test == nhlt->dmic_prm[di].io_clk && + mcic >= DMIC_HW_CIC_DECIM_MIN && + mcic <= DMIC_HW_CIC_DECIM_MAX && + i < DMIC_MAX_MODES) { + modes->clkdiv[i] = clkdiv; + modes->mcic[i] = mcic; + modes->mfir[i] = mfir; + i++; + } + } + } + + tplg_pp_debug("find_modes(): mode %d", i); + modes->num_of_modes = i; +} + +/* The previous raw modes list contains sane configuration possibilities. When + * there is request for both FIFOs A and B operation this function returns + * list of compatible settings. + */ +static void match_modes(struct dmic_calc_matched_modes *c, struct dmic_calc_decim_modes *a, + struct dmic_calc_decim_modes *b) +{ + int16_t idx[DMIC_MAX_MODES]; + int idx_length; + int i; + int n; + int m; + + /* Check if previous search got results. */ + c->num_of_modes = 0; + if (a->num_of_modes == 0 && b->num_of_modes == 0) { + /* Nothing to do */ + return; + } + + /* Ensure that num_of_modes is sane. */ + if (a->num_of_modes > DMIC_MAX_MODES || + b->num_of_modes > DMIC_MAX_MODES) + return; + + /* Check for request only for FIFO A or B. In such case pass list for + * A or B as such. + */ + if (b->num_of_modes == 0) { + c->num_of_modes = a->num_of_modes; + for (i = 0; i < a->num_of_modes; i++) { + c->clkdiv[i] = a->clkdiv[i]; + c->mcic[i] = a->mcic[i]; + c->mfir_a[i] = a->mfir[i]; + c->mfir_b[i] = 0; /* Mark FIR B as non-used */ + } + return; + } + + if (a->num_of_modes == 0) { + c->num_of_modes = b->num_of_modes; + for (i = 0; i < b->num_of_modes; i++) { + c->clkdiv[i] = b->clkdiv[i]; + c->mcic[i] = b->mcic[i]; + c->mfir_b[i] = b->mfir[i]; + c->mfir_a[i] = 0; /* Mark FIR A as non-used */ + } + return; + } + + /* Merge a list of compatible modes */ + i = 0; + for (n = 0; n < a->num_of_modes; n++) { + /* Find all indices of values a->clkdiv[n] in b->clkdiv[] */ + idx_length = find_equal_int16(idx, b->clkdiv, a->clkdiv[n], + b->num_of_modes, 0); + for (m = 0; m < idx_length; m++) { + if (b->mcic[idx[m]] == a->mcic[n]) { + c->clkdiv[i] = a->clkdiv[n]; + c->mcic[i] = a->mcic[n]; + c->mfir_a[i] = a->mfir[n]; + c->mfir_b[i] = b->mfir[idx[m]]; + i++; + } + } + c->num_of_modes = i; + } +} + +/* Finds a suitable FIR decimation filter from the included set */ +static struct pdm_decim *get_fir(struct intel_nhlt_params *nhlt, + struct dmic_calc_configuration *cfg, int mfir) +{ + int i; + int fs; + int cic_fs; + int fir_max_length; + struct pdm_decim *fir = NULL; + int di = nhlt->dmic_dai_index; + + if (mfir <= 0) + return fir; + + cic_fs = nhlt->dmic_prm[di].io_clk / cfg->clkdiv / cfg->mcic; + fs = cic_fs / mfir; + /* FIR max. length depends on available cycles and coef RAM + * length. Exceeding this length sets HW overrun status and + * overwrite of other register. + */ + fir_max_length = MIN(DMIC_HW_FIR_LENGTH_MAX, + nhlt->dmic_prm[di].io_clk / fs / 2 - + DMIC_FIR_PIPELINE_OVERHEAD); + + i = 0; + /* Loop until NULL */ + while (fir_list[i]) { + if (fir_list[i]->decim_factor == mfir) { + if (fir_list[i]->length <= fir_max_length) { + /* Store pointer, break from loop to avoid a + * Possible other mode with lower FIR length. + */ + fir = fir_list[i]; + break; + } + tplg_pp_debug("get_fir(), Note length=%d exceeds max=%d", + fir_list[i]->length, fir_max_length); + } + i++; + } + + return fir; +} + +/* Calculate scale and shift to use for FIR coefficients. Scale is applied + * before write to HW coef RAM. Shift will be programmed to HW register. + */ +static int fir_coef_scale(int32_t *fir_scale, int *fir_shift, int add_shift, + const int32_t coef[], int coef_length, int32_t gain) +{ + int32_t amax; + int32_t new_amax; + int32_t fir_gain; + int shift; + + /* Multiply gain passed from CIC with output full scale. */ + fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q28, + DMIC_FIR_SCALE_Q, 28, DMIC_FIR_SCALE_Q); + + /* Find the largest FIR coefficient value. */ + amax = find_max_abs_int32((int32_t *)coef, coef_length); + + /* Scale max. tap value with FIR gain. */ + new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, + DMIC_FIR_SCALE_Q, DMIC_FIR_SCALE_Q); + if (new_amax <= 0) + return -1; + + /* Get left shifts count to normalize the fractional value as 32 bit. + * We need right shifts count for scaling so need to invert. The + * difference of Q31 vs. used Q format is added to get the correct + * normalization right shift value. + */ + shift = 31 - DMIC_FIR_SCALE_Q - norm_int32(new_amax); + + /* Add to shift for coef raw Q31 format shift and store to + * configuration. Ensure range (fail should not happen with OK + * coefficient set). + */ + *fir_shift = -shift + add_shift; + if (*fir_shift < DMIC_HW_FIR_SHIFT_MIN || + *fir_shift > DMIC_HW_FIR_SHIFT_MAX) + return -1; + + /* Compensate shift into FIR coef scaler and store as Q4.20. */ + if (shift < 0) + *fir_scale = fir_gain << -shift; + else + *fir_scale = fir_gain >> shift; + + return 0; +} + +/* This function selects with a simple criteria one mode to set up the + * decimator. For the settings chosen for FIFOs A and B output a lookup + * is done for FIR coefficients from the included coefficients tables. + * For some decimation factors there may be several length coefficient sets. + * It is due to possible restruction of decimation engine cycles per given + * sample rate. If the coefficients length is exceeded the lookup continues. + * Therefore the list of coefficient set must present the filters for a + * decimation factor in decreasing length order. + * + * Note: If there is no filter available an error is returned. The parameters + * should be reviewed for such case. If still a filter is missing it should be + * added into the included set. FIR decimation with a high factor usually + * needs compromizes into specifications and is not desirable. + */ +static int select_mode(struct intel_nhlt_params *nhlt, struct dmic_calc_configuration *cfg, + struct dmic_calc_matched_modes *modes) +{ + int32_t g_cic; + int32_t fir_in_max; + int32_t cic_out_max; + int32_t gain_to_fir; + int16_t idx[DMIC_MAX_MODES]; + int16_t *mfir; + int mcic; + int bits_cic; + int ret; + int n; + int found = 0; + + /* If there are more than one possibilities select a mode with a preferred + * FIR decimation factor. If there are several select mode with highest + * ioclk divider to minimize microphone power consumption. The highest + * clock divisors are in the end of list so select the last of list. + * The minimum OSR criteria used in previous ensures that quality in + * the candidates should be sufficient. + */ + if (modes->num_of_modes == 0) { + /* dai_err(dai, "select_mode(): no modes available"); */ + return -1; + } + + /* Valid modes presence is indicated with non-zero decimation + * factor in 1st element. If FIR A is not used get decimation factors + * from FIR B instead. + */ + if (modes->mfir_a[0] > 0) + mfir = modes->mfir_a; + else + mfir = modes->mfir_b; + + /* Search fir_list[] decimation factors from start towards end. The found + * last configuration entry with searched decimation factor will be used. + */ + for (n = 0; fir_list[n]; n++) { + found = find_equal_int16(idx, mfir, fir_list[n]->decim_factor, + modes->num_of_modes, 0); + if (found) + break; + } + + if (!found) { + /* dai_err(dai, "select_mode(): No filter for decimation found"); */ + return -1; + } + n = idx[found - 1]; /* Option with highest clock divisor and lowest mic clock rate */ + + /* Get microphone clock and decimation parameters for used mode from + * the list. + */ + cfg->clkdiv = modes->clkdiv[n]; + cfg->mfir_a = modes->mfir_a[n]; + cfg->mfir_b = modes->mfir_b[n]; + cfg->mcic = modes->mcic[n]; + cfg->fir_a = NULL; + cfg->fir_b = NULL; + + /* Find raw FIR coefficients to match the decimation factors of FIR + * A and B. + */ + if (cfg->mfir_a > 0) { + cfg->fir_a = get_fir(nhlt, cfg, cfg->mfir_a); + if (!cfg->fir_a) { + /* dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_a = %u", + * cfg->mfir_a); + */ + return -1; + } + } + + if (cfg->mfir_b > 0) { + cfg->fir_b = get_fir(nhlt, cfg, cfg->mfir_b); + if (!cfg->fir_b) { + /* dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_b = %u", + * cfg->mfir_b); + */ + return -1; + } + } + + /* Calculate CIC shift from the decimation factor specific gain. The + * gain of HW decimator equals decimation factor to power of 5. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic; + if (g_cic < 0) { + /* Erroneous decimation factor and CIC gain */ + /* dai_err(dai, "select_mode(): erroneous decimation factor and CIC gain"); */ + return -1; + } + + bits_cic = 32 - norm_int32(g_cic); + cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT; + + /* Calculate remaining gain to FIR in Q format used for gain + * values. + */ + fir_in_max = INT_MAX(DMIC_HW_BITS_FIR_INPUT); + if (cfg->cic_shift >= 0) + cic_out_max = g_cic >> cfg->cic_shift; + else + cic_out_max = g_cic << -cfg->cic_shift; + + gain_to_fir = (int32_t)((((int64_t)fir_in_max) << DMIC_FIR_SCALE_Q) / + cic_out_max); + + /* Calculate FIR scale and shift */ + if (cfg->mfir_a > 0) { + cfg->fir_a_length = cfg->fir_a->length; + ret = fir_coef_scale(&cfg->fir_a_scale, &cfg->fir_a_shift, + cfg->fir_a->shift, cfg->fir_a->coef, + cfg->fir_a->length, gain_to_fir); + if (ret < 0) { + /* Invalid coefficient set found, should not happen. */ + /* dai_err(dai, "select_mode(): invalid coefficient set found"); */ + return -1; + } + } else { + cfg->fir_a_scale = 0; + cfg->fir_a_shift = 0; + cfg->fir_a_length = 0; + } + + if (cfg->mfir_b > 0) { + cfg->fir_b_length = cfg->fir_b->length; + ret = fir_coef_scale(&cfg->fir_b_scale, &cfg->fir_b_shift, + cfg->fir_b->shift, cfg->fir_b->coef, + cfg->fir_b->length, gain_to_fir); + if (ret < 0) { + /* Invalid coefficient set found, should not happen. */ + /* dai_err(dai, "select_mode(): invalid coefficient set found"); */ + return -1; + } + } else { + cfg->fir_b_scale = 0; + cfg->fir_b_shift = 0; + cfg->fir_b_length = 0; + } + + return 0; +} + +/* The FIFO input packer mode (IPM) settings are somewhat different in + * HW versions. This helper function returns a suitable IPM bit field + * value to use. + */ + +static void ipm_helper1(struct intel_nhlt_params *nhlt, int *ipm) +{ + int di = nhlt->dmic_dai_index; + int pdm[DMIC_HW_CONTROLLERS]; + int i; + + /* Loop number of PDM controllers in the configuration. If mic A + * or B is enabled then a pdm controller is marked as active for + * this DAI. + */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + if (nhlt->dmic_prm[di].pdm[i].enable_mic_a || + nhlt->dmic_prm[di].pdm[i].enable_mic_b) + pdm[i] = 1; + else + pdm[i] = 0; + } + + /* Set IPM to match active pdm controllers. */ + *ipm = 0; + + if (pdm[0] == 0 && pdm[1] > 0) + *ipm = 1; + + if (pdm[0] > 0 && pdm[1] > 0) + *ipm = 2; +} + +static void ipm_helper2(struct intel_nhlt_params *nhlt, int source[], int *ipm) +{ + int di = nhlt->dmic_dai_index; + int pdm[DMIC_HW_CONTROLLERS]; + int i; + int n = 0; + + for (i = 0; i < OUTCONTROLX_IPM_NUMSOURCES; i++) + source[i] = 0; + + /* Loop number of PDM controllers in the configuration. If mic A + * or B is enabled then a pdm controller is marked as active. + * The function returns in array source[] the indice of enabled + * pdm controllers to be used for IPM configuration. + */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + if (nhlt->dmic_prm[di].pdm[i].enable_mic_a || + nhlt->dmic_prm[di].pdm[i].enable_mic_b) { + pdm[i] = 1; + source[n] = i; + n++; + } else { + pdm[i] = 0; + } + } + + /* IPM bit field is set to count of active pdm controllers. */ + *ipm = pdm[0]; + for (i = 1; i < DMIC_HW_CONTROLLERS; i++) + *ipm += pdm[i]; +} + +/* Loop number of PDM controllers in the configuration. The function + * checks if the controller should operate as stereo or mono left (A) + * or mono right (B) mode. Mono right mode is setup as channel + * swapped mono left. + */ +static int stereo_helper(struct intel_nhlt_params *nhlt, int stereo[], int swap[]) +{ + int cnt; + int i; + int swap_check; + int ret = 0; + + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + cnt = 0; + if (nhlt->dmic_prm[0].pdm[i].enable_mic_a || + nhlt->dmic_prm[1].pdm[i].enable_mic_a) + cnt++; + + if (nhlt->dmic_prm[0].pdm[i].enable_mic_b || + nhlt->dmic_prm[1].pdm[i].enable_mic_b) + cnt++; + + /* Set stereo mode if both mic A anc B are enabled. */ + cnt >>= 1; + stereo[i] = cnt; + + /* Swap channels if only mic B is used for mono processing. */ + swap[i] = (nhlt->dmic_prm[0].pdm[i].enable_mic_b || + nhlt->dmic_prm[1].pdm[i].enable_mic_b) && !cnt; + + /* Check that swap does not conflict with other DAI request */ + swap_check = (nhlt->dmic_prm[1].pdm[i].enable_mic_a || + nhlt->dmic_prm[0].pdm[i].enable_mic_a); + + if (swap_check && swap[i]) + ret = -1; + } + return ret; +} + +static int configure_registers(struct intel_nhlt_params *nhlt, struct dmic_calc_configuration *cfg) +{ + int stereo[DMIC_HW_CONTROLLERS]; + int swap[DMIC_HW_CONTROLLERS]; + uint32_t val; + int32_t ci; + uint32_t cu; + int ipm; + int of0; + int of1; + int fir_decim; + int fir_length; + int length; + int edge; + int soft_reset; + int cic_mute; + int fir_mute; + int i; + int j; + int ret; + int mic; + int chmap_bits; + int di = nhlt->dmic_dai_index; + int dccomp = 1; + int array_a = 0; + int array_b = 0; + int bfth = 3; /* Should be 3 for 8 entries, 1 is 2 entries */ + int th = 3; /* Used with TIE=1 */ + int source[OUTCONTROLX_IPM_NUMSOURCES]; + + /* + * ts_group value describes which audio channels in the hw fifo are enabled. A 32 bit + * value is divided into 8 x 4 bit nibbles corresponding to 8 audio channels. Hex value 0xF + * means "not in use", any other value means the channel is enabled. For example 0xFFFFFFFF + * means no channels are enabled, 0xFFFFFF10 means channels 1 and 2 are enabled. + * + * ts_group array index corresponds to dmic hw fifos, that gather audio samples from pdm + * controllers. 1 pdm controller can host 2 mono dmics and usually pdm controllers are + * connected to 2 hw fifos -> we can for example run the dmics simultaneously with different + * sampling rates. + * + * Currently there is no evidence we would ever have more than 2 hw fifos, so ts_group[2] + * and ts_group[3] are not used for anything. Also the nibbles could be used for channel + * mapping the pdm channels arbitrarely into hw fifos, however currently it is used as + * binary not_enabled/enabled setting. + * + * if we have 2 dmics (stereo) it means we are using 1 pdm controller with possibly 2 hw + * fifos: + * mic1 fifo0(2ch) + * \ / + * pdm0 + * / \ + * mic2 fifo1(2ch) + * + * So in this case it makes only sense to control ts_group indexes 0 and 1 and their last 2 + * nibbles (as we have only 2 channels). + * + * if we have 4 dmics, it means we are using 2 pdm controller with possibly 2 x 4 channel hw + * fifos: + * + * mic1 fifo0(4ch) + * \ / / + * pdm0 / + * / \ / + * mic2 \/ + * mic3 /\ + * \ / \ + * pdm1 \ + * / \ \ + * mic4 fifo1(4ch) + * + * So it makes sense to control ts_group indexes 0 and 1 and their last 4 nibbles. + * + * channel_pdm_mask defines which existing pdm controllers will be taken into use. So if + * either of mic a or b is enabled -> that particular pdm controller is in use. For example + * pdm0 in use/not_in_use is defined by setting bit 0 in channel_pdm_mask to 1/0. + * + * channel_ctrl_mask defines what mic channels are available in hw for a pdm controller. in + * theory pdm controller could have only 1 channel enabled, in practice there's always 2 + * channels which are both enabled -> set bits 0 and 1. + */ + + for (i = 0, mic = 0, chmap_bits = 4; i < DMIC_HW_CONTROLLERS; i++) { + /* enable fifo channels (ts_group) based on mic_enable in dai definition */ + if (nhlt->dmic_prm[di].pdm[i].enable_mic_a) { + nhlt->dmic_blob.ts_group[di] &= ~(0xF << (chmap_bits * mic)); + nhlt->dmic_blob.ts_group[di] |= 0x0 << (chmap_bits * mic); + } + mic++; + if (nhlt->dmic_prm[di].pdm[i].enable_mic_b) { + nhlt->dmic_blob.ts_group[di] &= ~(0xF << (chmap_bits * mic)); + nhlt->dmic_blob.ts_group[di] |= 0x1 << (chmap_bits * mic); + } + /* set channel_pdm_mask to describe what pdm controllers are in use */ + if (nhlt->dmic_prm[di].pdm[i].enable_mic_a || + nhlt->dmic_prm[di].pdm[i].enable_mic_b) { + nhlt->dmic_blob.channel_pdm_mask |= 1 << i; + } + } + + /* set always both mic channels enabled */ + nhlt->dmic_blob.channel_ctrl_mask = 0x3; + + /* Normal start sequence */ + soft_reset = 0; + cic_mute = 0; + fir_mute = 0; + + /* OUTCONTROL0 and OUTCONTROL1 */ + of0 = (nhlt->dmic_prm[0].fifo_bits == 32) ? 2 : 0; + of1 = (nhlt->dmic_prm[1].fifo_bits == 32) ? 2 : 0; + + if (nhlt->dmic_prm[di].driver_version == 1 || + nhlt->dmic_prm[di].driver_version == 2) { + if (di == 0) { + ipm_helper1(nhlt, &ipm); + val = OUTCONTROL0_TIE(0) | + OUTCONTROL0_SIP(0) | + OUTCONTROL0_FINIT(0) | + OUTCONTROL0_FCI(0) | + OUTCONTROL0_BFTH(bfth) | + OUTCONTROL0_OF(of0) | + OUTCONTROL0_IPM_VER1(ipm) | + OUTCONTROL0_TH(th); + } else { + ipm_helper1(nhlt, &ipm); + val = OUTCONTROL1_TIE(0) | + OUTCONTROL1_SIP(0) | + OUTCONTROL1_FINIT(0) | + OUTCONTROL1_FCI(0) | + OUTCONTROL1_BFTH(bfth) | + OUTCONTROL1_OF(of1) | + OUTCONTROL1_IPM_VER1(ipm) | + OUTCONTROL1_TH(th); + } + } + + if (nhlt->dmic_prm[di].driver_version == 3) { + if (di == 0) { + ipm_helper2(nhlt, source, &ipm); + val = OUTCONTROL0_TIE(0) | + OUTCONTROL0_SIP(0) | + OUTCONTROL0_FINIT(0) | + OUTCONTROL0_FCI(0) | + OUTCONTROL0_BFTH(bfth) | + OUTCONTROL0_OF(of0) | + OUTCONTROL0_IPM_VER2(ipm) | + OUTCONTROL0_IPM_SOURCE_1(source[0]) | + OUTCONTROL0_IPM_SOURCE_2(source[1]) | + OUTCONTROL0_IPM_SOURCE_3(source[2]) | + OUTCONTROL0_IPM_SOURCE_4(source[3]) | + OUTCONTROL0_TH(th); + } else { + ipm_helper2(nhlt, source, &ipm); + val = OUTCONTROL1_TIE(0) | + OUTCONTROL1_SIP(0) | + OUTCONTROL1_FINIT(0) | + OUTCONTROL1_FCI(0) | + OUTCONTROL1_BFTH(bfth) | + OUTCONTROL1_OF(of1) | + OUTCONTROL1_IPM_VER2(ipm) | + OUTCONTROL1_IPM_SOURCE_1(source[0]) | + OUTCONTROL1_IPM_SOURCE_2(source[1]) | + OUTCONTROL1_IPM_SOURCE_3(source[2]) | + OUTCONTROL1_IPM_SOURCE_4(source[3]) | + OUTCONTROL1_TH(th); + } + } + + nhlt->dmic_blob.chan_ctrl_cfg[di] = val; + + ret = stereo_helper(nhlt, stereo, swap); + if (ret < 0) { + /* dai_err(dai, "configure_registers(): enable conflict"); */ + return ret; + } + + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + /* CIC */ + val = CIC_CONTROL_SOFT_RESET(soft_reset) | + CIC_CONTROL_CIC_START_B(1) | + CIC_CONTROL_CIC_START_A(1) | + CIC_CONTROL_MIC_B_POLARITY(nhlt->dmic_prm[di].pdm[i].polarity_mic_b) | + CIC_CONTROL_MIC_A_POLARITY(nhlt->dmic_prm[di].pdm[i].polarity_mic_a) | + CIC_CONTROL_MIC_MUTE(cic_mute) | + CIC_CONTROL_STEREO_MODE(stereo[i]); + nhlt->dmic_blob_pdm[i].cic_control = val; + + val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | + CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); + nhlt->dmic_blob_pdm[i].cic_config = val; + + /* Mono right channel mic usage requires swap of PDM channels + * since the mono decimation is done with only left channel + * processing active. + */ + edge = nhlt->dmic_prm[di].pdm[i].clk_edge; + if (swap[i]) + edge = !edge; + + val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) | + MIC_CONTROL_PDM_SKEW(nhlt->dmic_prm[di].pdm[i].skew) | + MIC_CONTROL_CLK_EDGE(edge) | + MIC_CONTROL_PDM_EN_B(1) | + MIC_CONTROL_PDM_EN_A(1); + nhlt->dmic_blob_pdm[i].mic_control = val; + + /* if cfg->mfir_a */ + if (di == 0) { + /* FIR A */ + fir_decim = MAX(cfg->mfir_a - 1, 0); + fir_length = MAX(cfg->fir_a_length - 1, 0); + val = FIR_CONTROL_A_START(1) | + FIR_CONTROL_A_ARRAY_START_EN(array_a) | + FIR_CONTROL_A_DCCOMP(dccomp) | + FIR_CONTROL_A_MUTE(fir_mute) | + FIR_CONTROL_A_STEREO(stereo[i]); + nhlt->dmic_blob_fir[i][di].fir_control = val; + + val = FIR_CONFIG_A_FIR_DECIMATION(fir_decim) | + FIR_CONFIG_A_FIR_SHIFT(cfg->fir_a_shift) | + FIR_CONFIG_A_FIR_LENGTH(fir_length); + nhlt->dmic_blob_fir[i][di].fir_config = val; + + val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0); + nhlt->dmic_blob_fir[i][di].dc_offset_left = val; + + val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0); + nhlt->dmic_blob_fir[i][di].dc_offset_right = val; + + val = OUT_GAIN_LEFT_A_GAIN(0); + nhlt->dmic_blob_fir[i][di].out_gain_left = val; + + val = OUT_GAIN_RIGHT_A_GAIN(0); + nhlt->dmic_blob_fir[i][di].out_gain_right = val; + + /* Write coef RAM A with scaled coefficient in reverse order */ + length = cfg->fir_a_length; + for (j = 0; j < length; j++) { + ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_a->coef[j], + cfg->fir_a_scale, 31, + DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_A(ci); + /* blob_pdm[i].fir_coeffs[di][j] = cu; */ + nhlt->dmic_fir_array.fir_coeffs[i][di][j] = cu; + } + tplg_pp_debug("set fir len a %u and b 0", fir_length); + nhlt->dmic_fir_array.fir_len[0] = length; + nhlt->dmic_fir_array.fir_len[1] = 0; + } + + if (di == 1) { + /* FIR B */ + fir_decim = MAX(cfg->mfir_b - 1, 0); + fir_length = MAX(cfg->fir_b_length - 1, 0); + val = FIR_CONTROL_B_START(1) | + FIR_CONTROL_B_ARRAY_START_EN(array_b) | + FIR_CONTROL_B_DCCOMP(dccomp) | + FIR_CONTROL_B_MUTE(fir_mute) | + FIR_CONTROL_B_STEREO(stereo[i]); + nhlt->dmic_blob_fir[i][di].fir_control = val; + + val = FIR_CONFIG_B_FIR_DECIMATION(fir_decim) | + FIR_CONFIG_B_FIR_SHIFT(cfg->fir_b_shift) | + FIR_CONFIG_B_FIR_LENGTH(fir_length); + nhlt->dmic_blob_fir[i][di].fir_config = val; + val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0); + nhlt->dmic_blob_fir[i][di].dc_offset_left = val; + + val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0); + nhlt->dmic_blob_fir[i][di].dc_offset_right = val; + + val = OUT_GAIN_LEFT_B_GAIN(0); + nhlt->dmic_blob_fir[i][di].out_gain_left = val; + + val = OUT_GAIN_RIGHT_B_GAIN(0); + nhlt->dmic_blob_fir[i][di].out_gain_right = val; + + /* Write coef RAM B with scaled coefficient in reverse order */ + length = cfg->fir_b_length; + for (j = 0; j < length; j++) { + ci = (int32_t)Q_MULTSR_32X32((int64_t)cfg->fir_b->coef[j], + cfg->fir_b_scale, 31, + DMIC_FIR_SCALE_Q, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_B(ci); + /* blob_pdm[i].fir_coeffs[di][j] = cu; */ + nhlt->dmic_fir_array.fir_coeffs[i][di][j] = cu; + } + tplg_pp_debug("set fir len b %u", fir_length); + nhlt->dmic_fir_array.fir_len[1] = length; + } + } + + tplg_pp_debug(""); + + return 0; +} + +static int dmic_calculate(struct tplg_pre_processor *tplg_pp) +{ + struct dmic_calc_matched_modes modes_ab; + struct dmic_calc_decim_modes modes_a; + struct dmic_calc_decim_modes modes_b; + struct dmic_calc_configuration cfg; + struct intel_nhlt_params *nhlt; + int32_t unmute_ramp_time_ms; + int ret = 0; + int di; + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + di = nhlt->dmic_dai_index; + + /* Compute unmute ramp gain update coefficient. Use the value from + * topology if it is non-zero, otherwise use default length. + */ + /* unmute_ramp related stuff is not used in nhlt at all */ + if (nhlt->dmic_prm[di].unmute_ramp_time) + unmute_ramp_time_ms = nhlt->dmic_prm[di].unmute_ramp_time; + else + unmute_ramp_time_ms = LOGRAMP_TIME_MS; + + if (unmute_ramp_time_ms < LOGRAMP_TIME_MIN_MS || + unmute_ramp_time_ms > LOGRAMP_TIME_MAX_MS) { + tplg_pp_debug("dmic_set_config(): Illegal ramp time = %d", + unmute_ramp_time_ms); + ret = -1; + goto out; + } + + if (di >= DMIC_HW_FIFOS) { + tplg_pp_debug("dmic_set_config(): dai->index exceeds number of FIFOs"); + ret = -1; + goto out; + } + + if (nhlt->dmic_prm[di].num_pdm_active > DMIC_HW_CONTROLLERS) { + tplg_pp_debug("dmic_set_config(): the requested PDM controllers count exceeds platform capability"); + ret = -1; + goto out; + } + + switch (nhlt->dmic_prm[di].fifo_bits) { + case 0: + case 16: + case 32: + break; + default: + tplg_pp_debug("dmic_set_config(): fifo_bits EINVAL"); + ret = -1; + goto out; + } + + /* Match and select optimal decimators configuration for FIFOs A and B + * paths. This setup phase is still abstract. Successful completion + * points struct cfg to FIR coefficients and contains the scale value + * to use for FIR coefficient RAM write as well as the CIC and FIR + * shift values. + */ + find_modes(nhlt, &modes_a); + if (modes_a.num_of_modes == 0 && nhlt->dmic_prm[0].fifo_fs > 0) { + tplg_pp_debug("dmic_set_config(): No modes found found for FIFO A"); + ret = -1; + goto out; + } + + find_modes(nhlt, &modes_b); + if (modes_b.num_of_modes == 0 && nhlt->dmic_prm[1].fifo_fs > 0) { + tplg_pp_debug("dmic_set_config(): No modes found for FIFO B"); + ret = -1; + goto out; + } + + match_modes(&modes_ab, &modes_a, &modes_b); + ret = select_mode(nhlt, &cfg, &modes_ab); + if (ret < 0) { + tplg_pp_debug("dmic_set_config(): select_mode() failed"); + ret = -1; + goto out; + } + + /* Struct reg contains a mirror of actual HW registers. Determine + * register bits configuration from decimator configuration and the + * requested parameters. + */ + ret = configure_registers(nhlt, &cfg); + if (ret < 0) { + tplg_pp_debug("dmic_set_config(): cannot configure registers"); + ret = -1; + goto out; + } + +#ifdef DMIC_NHLT_DEBUG + dmic_print_internal(nhlt); +#endif + +out: + return ret; +} + +static int set_dmic_data(struct tplg_pre_processor *tplg_pp, + snd_config_t *dai_cfg) +{ + struct intel_nhlt_params *nhlt; + snd_config_iterator_t i, next; + snd_config_t *class_cfg; + int unmute_ramp_time_ms; + int fifo_word_length; + int driver_version; + int num_pdm_active; + int sample_rate; + int dai_index_t; + snd_config_t *n; + const char *id; + long int_val; + int duty_min; + int duty_max; + int clk_min; + int clk_max; + int io_clk; + int ret; + + tplg_pp_debug("set_dmic_data from topology"); + + /* get default values */ + ret = snd_config_search(tplg_pp->input_cfg, "Class.Dai.DMIC", &class_cfg); + if (ret < 0) + return -EINVAL; + + /* set default values */ + snd_config_for_each(i, next, class_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) { + tplg_pp_debug("set_dmic_data no id found"); + continue; + } + + if (!strcmp(id, "driver_version")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + driver_version = int_val; + } + + if (!strcmp(id, "io_clk")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + io_clk = int_val; + } + + if (!strcmp(id, "dai_index")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + dai_index_t = int_val; + } + + if (!strcmp(id, "num_pdm_active")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + num_pdm_active = int_val; + } + + if (!strcmp(id, "fifo_word_length")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + fifo_word_length = int_val; + } + + if (!strcmp(id, "clk_min")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_min = int_val; + } + + if (!strcmp(id, "clk_max")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_max = int_val; + } + + if (!strcmp(id, "duty_min")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + duty_min = int_val; + } + + if (!strcmp(id, "duty_max")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + duty_max = int_val; + } + + if (!strcmp(id, "sample_rate")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + sample_rate = int_val; + } + + if (!strcmp(id, "unmute_ramp_time_ms")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + unmute_ramp_time_ms = int_val; + } + } + + /* set object values */ + snd_config_for_each(i, next, dai_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) { + tplg_pp_debug("set_dmic_data no id found"); + continue; + } + + if (!strcmp(id, "driver_version")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + driver_version = int_val; + } + + if (!strcmp(id, "io_clk")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + io_clk = int_val; + } + + if (!strcmp(id, "dai_index")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + dai_index_t = int_val; + } + + if (!strcmp(id, "num_pdm_active")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + num_pdm_active = int_val; + } + + if (!strcmp(id, "fifo_word_length")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + fifo_word_length = int_val; + } + + if (!strcmp(id, "clk_min")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_min = int_val; + } + + if (!strcmp(id, "clk_max")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_max = int_val; + } + + if (!strcmp(id, "duty_min")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + duty_min = int_val; + } + + if (!strcmp(id, "duty_max")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + duty_max = int_val; + } + + if (!strcmp(id, "sample_rate")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + sample_rate = int_val; + } + + if (!strcmp(id, "unmute_ramp_time_ms")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + unmute_ramp_time_ms = int_val; + } + } + + tplg_pp_debug("driver_version: %d", driver_version); + tplg_pp_debug("io_clk: %d", io_clk); + tplg_pp_debug("dai_index: %d", dai_index_t); + tplg_pp_debug("num_pdm_active: %d", num_pdm_active); + tplg_pp_debug("fifo_word_length: %d", fifo_word_length); + tplg_pp_debug("clk_min: %d", clk_min); + tplg_pp_debug("clk_max: %d", clk_max); + tplg_pp_debug("duty_min: %d", duty_min); + tplg_pp_debug("duty_max: %d", duty_max); + tplg_pp_debug("sample_rate: %d", sample_rate); + tplg_pp_debug("unmute_ramp_time: %d", unmute_ramp_time_ms); + tplg_pp_debug(""); + + if (dai_index_t > DMIC_HW_FIFOS) { + tplg_pp_debug("set_dmic_data illegal dai index"); + return -EINVAL; + } + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + nhlt->dmic_dai_index = dai_index_t; + nhlt->dmic_prm[dai_index_t].driver_version = driver_version; + nhlt->dmic_prm[dai_index_t].io_clk = io_clk; + nhlt->dmic_prm[dai_index_t].num_pdm_active = num_pdm_active; + nhlt->dmic_prm[dai_index_t].fifo_bits = fifo_word_length; + nhlt->dmic_prm[dai_index_t].pdmclk_min = clk_min; + nhlt->dmic_prm[dai_index_t].pdmclk_max = clk_max; + nhlt->dmic_prm[dai_index_t].duty_min = duty_min; + nhlt->dmic_prm[dai_index_t].duty_max = duty_max; + nhlt->dmic_prm[dai_index_t].fifo_fs = sample_rate; + nhlt->dmic_prm[dai_index_t].unmute_ramp_time = unmute_ramp_time_ms; + + return 0; +} + +static int set_pdm_data(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg) +{ + struct intel_nhlt_params *nhlt; + snd_config_iterator_t i, next; + snd_config_t *class_cfg; + int mic_a_enable; + int mic_b_enable; + snd_config_t *n; + const char *id; + int polarity_a; + int polarity_b; + int clk_edge; + long int_val; + int ctrl_id; + int skew; + int ret; + int di; + + tplg_pp_debug("set_pdm_data from topology"); + + /* get default values */ + ret = snd_config_search(tplg_pp->input_cfg, "Class.Base.pdm_config", &class_cfg); + if (ret < 0) + return -EINVAL; + + /* set default values */ + snd_config_for_each(i, next, class_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (!strcmp(id, "ctrl_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + ctrl_id = int_val; + } + + if (!strcmp(id, "mic_a_enable")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mic_a_enable = int_val; + } + + if (!strcmp(id, "mic_b_enable")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mic_b_enable = int_val; + } + + if (!strcmp(id, "polarity_a")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + polarity_a = int_val; + } + + if (!strcmp(id, "polarity_b")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + polarity_b = int_val; + } + + if (!strcmp(id, "clk_edge")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_edge = int_val; + } + + if (!strcmp(id, "skew")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + skew = int_val; + } + } + + /* set object values */ + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (!strcmp(id, "ctrl_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + ctrl_id = int_val; + } + + if (!strcmp(id, "mic_a_enable")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mic_a_enable = int_val; + } + + if (!strcmp(id, "mic_b_enable")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mic_b_enable = int_val; + } + + if (!strcmp(id, "polarity_a")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + polarity_a = int_val; + } + + if (!strcmp(id, "polarity_b")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + polarity_b = int_val; + } + + if (!strcmp(id, "clk_edge")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clk_edge = int_val; + } + + if (!strcmp(id, "skew")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + skew = int_val; + } + } + + tplg_pp_debug("pdm ctrl_id: %ld", ctrl_id); + tplg_pp_debug("pdm mic_a_enable: %ld", mic_a_enable); + tplg_pp_debug("pdm mic_b_enable: %ld", mic_b_enable); + tplg_pp_debug("pdm polarity_a: %ld", polarity_a); + tplg_pp_debug("pdm polarity_b: %ld", polarity_b); + tplg_pp_debug("pdm clk_edge: %ld", clk_edge); + tplg_pp_debug("pdm skew: %ld", skew); + tplg_pp_debug(""); + + if (ctrl_id >= DMIC_HW_CONTROLLERS) { + tplg_pp_debug("set_pdm_data illegal ctrl_id"); + return -EINVAL; + } + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + di = nhlt->dmic_dai_index; + + nhlt->dmic_prm[di].pdm[ctrl_id].enable_mic_a = mic_a_enable; + nhlt->dmic_prm[di].pdm[ctrl_id].enable_mic_b = mic_b_enable; + nhlt->dmic_prm[di].pdm[ctrl_id].polarity_mic_a = polarity_a; + nhlt->dmic_prm[di].pdm[ctrl_id].polarity_mic_b = polarity_b; + nhlt->dmic_prm[di].pdm[ctrl_id].clk_edge = clk_edge; + nhlt->dmic_prm[di].pdm[ctrl_id].skew = skew; + + return 0; +} + +/* Get the size of dynamic vendor blob to reserve proper amount of memory */ +int dmic_nhlt_get_vendor_blob_size(struct tplg_pre_processor *tplg_pp) +{ + struct intel_nhlt_params *nhlt; + int dmic_blob_size = 0; + int i, fir_index_0, fir_index_1; + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + dmic_blob_size += sizeof(struct dmic_intel_config_data); + + /* if either of the fir is 0 length, copy the existing fir twice */ + fir_index_0 = 0; + fir_index_1 = 1; + if (nhlt->dmic_fir_array.fir_len[0] == 0) { + fir_index_0 = 1; + fir_index_1 = 1; + } + if (nhlt->dmic_fir_array.fir_len[1] == 0) { + fir_index_0 = 0; + fir_index_1 = 0; + } + + /* variable amount of pdm's */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + + /* only copy the pdm data if it is enabled */ + if ((nhlt->dmic_blob.channel_pdm_mask & BIT(i)) == 0) + continue; + + dmic_blob_size += sizeof(struct dmic_intel_pdm_ctrl_cfg); + dmic_blob_size += sizeof(struct dmic_intel_fir_config) * DMIC_HW_FIFOS; + + dmic_blob_size += nhlt->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t); + dmic_blob_size += nhlt->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t); + } + + return dmic_blob_size; +} + +/* + * Build dmic vendor blob from calculated parameters. + * + * Supposed to be called after all dmic DAIs are parsed from topology and the final nhlt blob is + * generated. + */ +void dmic_nhlt_get_vendor_blob(struct tplg_pre_processor *tplg_pp, uint8_t *vendor_blob) +{ + struct intel_nhlt_params *nhlt; + int i, j, fir_index_0, fir_index_1; + +#ifdef DMIC_NHLT_DEBUG + uint8_t *orig_blob = vendor_blob; + size_t blob_size; +#endif + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return; + + /* top level struct */ + memcpy(vendor_blob, &nhlt->dmic_blob, sizeof(struct dmic_intel_config_data)); + vendor_blob += sizeof(struct dmic_intel_config_data); + + /* if either of the fir is 0 length, copy the existing fir twice */ + fir_index_0 = 0; + fir_index_1 = 1; + if (nhlt->dmic_fir_array.fir_len[0] == 0) { + fir_index_0 = 1; + fir_index_1 = 1; + } + if (nhlt->dmic_fir_array.fir_len[1] == 0) { + fir_index_0 = 0; + fir_index_1 = 0; + } + + /* variable amount of pdm's */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + + /* only copy the pdm data if it is enabled */ + if ((nhlt->dmic_blob.channel_pdm_mask & BIT(i)) == 0) + continue; + + /* top level struct first pdm data */ + memcpy(vendor_blob, (uint8_t*)&nhlt->dmic_blob_pdm[i], + sizeof(struct dmic_intel_pdm_ctrl_cfg)); + vendor_blob += sizeof(struct dmic_intel_pdm_ctrl_cfg); + + /* top level struct first pdm data first fir */ + memcpy(vendor_blob, (uint8_t*)&nhlt->dmic_blob_fir[i][fir_index_0], + sizeof(struct dmic_intel_fir_config)); + vendor_blob += sizeof(struct dmic_intel_fir_config); + + /* top level struct first pdm data second fir */ + memcpy(vendor_blob, (uint8_t*)&nhlt->dmic_blob_fir[i][fir_index_1], + sizeof(struct dmic_intel_fir_config)); + vendor_blob += sizeof(struct dmic_intel_fir_config); + + /* fir coeffs a */ + memcpy(vendor_blob, (uint8_t*)&nhlt->dmic_fir_array.fir_coeffs[i][fir_index_0][0], + nhlt->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t)); + vendor_blob += nhlt->dmic_fir_array.fir_len[fir_index_0] * sizeof(uint32_t); + + /* fir coeffs b */ + memcpy(vendor_blob, (uint8_t*)&nhlt->dmic_fir_array.fir_coeffs[i][fir_index_1][0], + nhlt->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t)); + vendor_blob += nhlt->dmic_fir_array.fir_len[fir_index_1] * sizeof(uint32_t); + } + +#ifdef DMIC_NHLT_DEBUG + blob_size = dmic_nhlt_get_vendor_blob_size(tplg_pp); + dmic_print_bytes_as_hex((uint8_t *)orig_blob, blob_size); + dmic_print_integers_as_hex((uint32_t *)orig_blob, blob_size / 4); +#endif +} + +/* init dmic parameters, should be called before parsing dais */ +int dmic_nhlt_init_params(struct tplg_pre_processor *tplg_pp) +{ + struct intel_nhlt_params *nhlt; + int i; + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + nhlt->dmic_blob.gateway_attributes = 1; + nhlt->dmic_blob.clock_on_delay = 16; + + for (i = 0; i < 4; i++) + nhlt->dmic_blob.ts_group[i] = 0xFFFFFFFF; /* not enabled */ + + return 0; +} + +/* + * Set dmic parameters from topology for dmic coefficient calculation. + * + * Coefficients are recalculated in case of multiple DAIs in topology and might affect each other. + */ +int dmic_nhlt_set_params(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg, snd_config_t *parent) +{ + snd_config_t *items; + int ret; + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + + tplg_pp_debug("set_blob from topology"); + + set_dmic_data(tplg_pp, cfg); + + ret = snd_config_search(cfg, "Object.Base.pdm_config", &items); + if (ret < 0) + return 0; + + snd_config_for_each(i, next, items) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + tplg_pp_debug("set_pdm_data from topology for id: '%s' ...", id); + + set_pdm_data(tplg_pp, n); + } + + ret = dmic_calculate(tplg_pp); + + return 0; +} diff --git a/topology/intel/dmic/dmic-nhlt.h b/topology/intel/dmic/dmic-nhlt.h new file mode 100644 index 000000000..3bcca993b --- /dev/null +++ b/topology/intel/dmic/dmic-nhlt.h @@ -0,0 +1,32 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __DMIC_NHLT_H +#define __DMIC_NHLT_H + +#include "pre-processor.h" + +int dmic_nhlt_init_params(struct tplg_pre_processor *tplg_pp); +int dmic_nhlt_set_params(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg, + snd_config_t *parent); +int dmic_nhlt_get_vendor_blob_size(struct tplg_pre_processor *tplg_pp); +void dmic_nhlt_get_vendor_blob(struct tplg_pre_processor *tplg_pp, uint8_t *vendor_blob); + +#endif diff --git a/topology/intel/dmic/dmic_macros.h b/topology/intel/dmic/dmic_macros.h new file mode 100644 index 000000000..22d1919b2 --- /dev/null +++ b/topology/intel/dmic/dmic_macros.h @@ -0,0 +1,281 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __DMIC_MACROS_H +#define __DMIC_MACROS_H + +#define MIN(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + __a > __b ? __b : __a; \ +}) +#define MAX(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + __a < __b ? __b : __a; \ +}) + +/* Get max and min signed integer values for N bits word length */ +#define INT_MAX(N) ((int64_t)((1ULL << ((N) - 1)) - 1)) + +/* Fractional multiplication with shift and round + * Note that the parameters px and py must be cast to (int64_t) if other type. + */ +#define Q_MULTSR_32X32(px, py, qx, qy, qp) \ + ((((px) * (py) >> ((qx) + (qy) - (qp) - 1)) + 1) >> 1) + +/* Convert a float number to fractional Qnx.ny format. Note that there is no + * check for nx+ny number of bits to fit the word length of int. The parameter + * qy must be 31 or less. + */ +#define Q_CONVERT_FLOAT(f, qy) \ + ((int32_t)(((const double)f) * ((int64_t)1 << (const int)qy) + 0.5)) + +/* Saturation */ +#define SATP_INT32(x) (((x) > INT32_MAX) ? INT32_MAX : (x)) + +#define BIT(b) (1UL << (b)) + +#define MASK(b_hi, b_lo) \ + (((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo)) +#define SET_BIT(b, x) (((x) & 1) << (b)) +#define SET_BITS(b_hi, b_lo, x) \ + (((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo)) + +#define EXP_FIXED_INPUT_QY 27 +#define EXP_FIXED_OUTPUT_QY 20 +#define DB2LIN_FIXED_INPUT_QY 24 +#define DB2LIN_FIXED_OUTPUT_QY 20 + +#define DMIC_MAX_MODES 50 +#define DMIC_FIR_PIPELINE_OVERHEAD 5 +#define DMIC_UNMUTE_RAMP_US 1000 + +/* Parameters used in modes computation */ +#define DMIC_HW_BITS_CIC 26 +#define DMIC_HW_BITS_FIR_COEF 20 +#define DMIC_HW_BITS_FIR_GAIN 20 +#define DMIC_HW_BITS_FIR_INPUT 22 +#define DMIC_HW_BITS_FIR_OUTPUT 24 +#define DMIC_HW_BITS_FIR_INTERNAL 26 +#define DMIC_HW_BITS_GAIN_OUTPUT 22 +#define DMIC_HW_CIC_SHIFT_MIN -8 +#define DMIC_HW_CIC_SHIFT_MAX 4 +#define DMIC_HW_FIR_SHIFT_MIN 0 +#define DMIC_HW_FIR_SHIFT_MAX 8 +#define DMIC_HW_CIC_DECIM_MIN 5 +#define DMIC_HW_CIC_DECIM_MAX 31 /* Note: Limited by BITS_CIC */ +#define DMIC_HW_FIR_DECIM_MIN 2 +#define DMIC_HW_FIR_DECIM_MAX 20 /* Note: Practical upper limit */ +#define DMIC_HW_SENS_Q28 Q_CONVERT_FLOAT(1.0, 28) /* Q1.28 */ +#define DMIC_HW_PDM_CLK_MIN 100000 /* Note: Practical min value */ +#define DMIC_HW_DUTY_MIN 20 /* Note: Practical min value */ +#define DMIC_HW_DUTY_MAX 80 /* Note: Practical max value */ + +/* OUTCONTROL0 bits */ +#define OUTCONTROL0_TIE_BIT BIT(27) +#define OUTCONTROL0_SIP_BIT BIT(26) +#define OUTCONTROL0_FINIT_BIT BIT(25) +#define OUTCONTROL0_FCI_BIT BIT(24) +#define OUTCONTROL0_TIE(x) SET_BIT(27, x) +#define OUTCONTROL0_SIP(x) SET_BIT(26, x) +#define OUTCONTROL0_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL0_FCI(x) SET_BIT(24, x) +#define OUTCONTROL0_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL0_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL0_TH(x) SET_BITS(5, 0, x) + +/* OUTCONTROL1 bits */ +#define OUTCONTROL1_TIE_BIT BIT(27) +#define OUTCONTROL1_SIP_BIT BIT(26) +#define OUTCONTROL1_FINIT_BIT BIT(25) +#define OUTCONTROL1_FCI_BIT BIT(24) +#define OUTCONTROL1_TIE(x) SET_BIT(27, x) +#define OUTCONTROL1_SIP(x) SET_BIT(26, x) +#define OUTCONTROL1_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL1_FCI(x) SET_BIT(24, x) +#define OUTCONTROL1_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL1_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL1_TH(x) SET_BITS(5, 0, x) + +/* OUTCONTROL0 bits ver1*/ +#define OUTCONTROL0_IPM_VER1(x) SET_BITS(17, 16, x) +/* OUTCONTROL1 bits ver1 */ +#define OUTCONTROL1_IPM_VER1(x) SET_BITS(17, 16, x) + +/* OUTCONTROL0 bits */ +#define OUTCONTROL0_IPM_VER2(x) SET_BITS(17, 15, x) +#define OUTCONTROL0_IPM_SOURCE_1(x) SET_BITS(14, 13, x) +#define OUTCONTROL0_IPM_SOURCE_2(x) SET_BITS(12, 11, x) +#define OUTCONTROL0_IPM_SOURCE_3(x) SET_BITS(10, 9, x) +#define OUTCONTROL0_IPM_SOURCE_4(x) SET_BITS(8, 7, x) + +/* OUTCONTROL1 bits */ +#define OUTCONTROL1_IPM_VER2(x) SET_BITS(17, 15, x) +#define OUTCONTROL1_IPM_SOURCE_1(x) SET_BITS(14, 13, x) +#define OUTCONTROL1_IPM_SOURCE_2(x) SET_BITS(12, 11, x) +#define OUTCONTROL1_IPM_SOURCE_3(x) SET_BITS(10, 9, x) +#define OUTCONTROL1_IPM_SOURCE_4(x) SET_BITS(8, 7, x) + +#define OUTCONTROLX_IPM_NUMSOURCES 4 + +/* CIC_CONTROL bits */ +#define CIC_CONTROL_SOFT_RESET_BIT BIT(16) +#define CIC_CONTROL_CIC_START_B_BIT BIT(15) +#define CIC_CONTROL_CIC_START_A_BIT BIT(14) +#define CIC_CONTROL_MIC_B_POLARITY_BIT BIT(3) +#define CIC_CONTROL_MIC_A_POLARITY_BIT BIT(2) +#define CIC_CONTROL_MIC_MUTE_BIT BIT(1) +#define CIC_CONTROL_STEREO_MODE_BIT BIT(0) + +#define CIC_CONTROL_SOFT_RESET(x) SET_BIT(16, x) +#define CIC_CONTROL_CIC_START_B(x) SET_BIT(15, x) +#define CIC_CONTROL_CIC_START_A(x) SET_BIT(14, x) +#define CIC_CONTROL_MIC_B_POLARITY(x) SET_BIT(3, x) +#define CIC_CONTROL_MIC_A_POLARITY(x) SET_BIT(2, x) +#define CIC_CONTROL_MIC_MUTE(x) SET_BIT(1, x) +#define CIC_CONTROL_STEREO_MODE(x) SET_BIT(0, x) + +/* CIC_CONFIG bits */ +#define CIC_CONFIG_CIC_SHIFT(x) SET_BITS(27, 24, x) +#define CIC_CONFIG_COMB_COUNT(x) SET_BITS(15, 8, x) + +/* CIC_CONFIG masks */ +#define CIC_CONFIG_CIC_SHIFT_MASK MASK(27, 24) +#define CIC_CONFIG_COMB_COUNT_MASK MASK(15, 8) + +/* MIC_CONTROL bits */ +#define MIC_CONTROL_PDM_EN_B_BIT BIT(1) +#define MIC_CONTROL_PDM_EN_A_BIT BIT(0) +#define MIC_CONTROL_PDM_CLKDIV(x) SET_BITS(15, 8, x) +#define MIC_CONTROL_PDM_SKEW(x) SET_BITS(7, 4, x) +#define MIC_CONTROL_CLK_EDGE(x) SET_BIT(3, x) +#define MIC_CONTROL_PDM_EN_B(x) SET_BIT(1, x) +#define MIC_CONTROL_PDM_EN_A(x) SET_BIT(0, x) + +/* MIC_CONTROL masks */ +#define MIC_CONTROL_PDM_CLKDIV_MASK MASK(15, 8) + +/* FIR_CONTROL_A bits */ +#define FIR_CONTROL_A_START_BIT BIT(7) +#define FIR_CONTROL_A_ARRAY_START_EN_BIT BIT(6) +#define FIR_CONTROL_A_MUTE_BIT BIT(1) +#define FIR_CONTROL_A_START(x) SET_BIT(7, x) +#define FIR_CONTROL_A_ARRAY_START_EN(x) SET_BIT(6, x) +#define FIR_CONTROL_A_DCCOMP(x) SET_BIT(4, x) +#define FIR_CONTROL_A_MUTE(x) SET_BIT(1, x) +#define FIR_CONTROL_A_STEREO(x) SET_BIT(0, x) + +/* FIR_CONFIG_A bits */ +#define FIR_CONFIG_A_FIR_DECIMATION(x) SET_BITS(20, 16, x) +#define FIR_CONFIG_A_FIR_SHIFT(x) SET_BITS(11, 8, x) +#define FIR_CONFIG_A_FIR_LENGTH(x) SET_BITS(7, 0, x) + +/* DC offset compensation time constants */ +#define DCCOMP_TC0 0 +#define DCCOMP_TC1 1 +#define DCCOMP_TC2 2 +#define DCCOMP_TC3 3 +#define DCCOMP_TC4 4 +#define DCCOMP_TC5 5 +#define DCCOMP_TC6 6 +#define DCCOMP_TC7 7 + +/* DC_OFFSET_LEFT_A bits */ +#define DC_OFFSET_LEFT_A_DC_OFFS(x) SET_BITS(21, 0, x) + +/* DC_OFFSET_RIGHT_A bits */ +#define DC_OFFSET_RIGHT_A_DC_OFFS(x) SET_BITS(21, 0, x) + +/* OUT_GAIN_LEFT_A bits */ +#define OUT_GAIN_LEFT_A_GAIN(x) SET_BITS(19, 0, x) + +/* OUT_GAIN_RIGHT_A bits */ +#define OUT_GAIN_RIGHT_A_GAIN(x) SET_BITS(19, 0, x) + +/* FIR_CONTROL_B bits */ +#define FIR_CONTROL_B_START_BIT BIT(7) +#define FIR_CONTROL_B_ARRAY_START_EN_BIT BIT(6) +#define FIR_CONTROL_B_MUTE_BIT BIT(1) +#define FIR_CONTROL_B_START(x) SET_BIT(7, x) +#define FIR_CONTROL_B_ARRAY_START_EN(x) SET_BIT(6, x) +#define FIR_CONTROL_B_DCCOMP(x) SET_BIT(4, x) +#define FIR_CONTROL_B_MUTE(x) SET_BIT(1, x) +#define FIR_CONTROL_B_STEREO(x) SET_BIT(0, x) + +/* FIR_CONFIG_B bits */ +#define FIR_CONFIG_B_FIR_DECIMATION(x) SET_BITS(20, 16, x) +#define FIR_CONFIG_B_FIR_SHIFT(x) SET_BITS(11, 8, x) +#define FIR_CONFIG_B_FIR_LENGTH(x) SET_BITS(7, 0, x) + +/* DC_OFFSET_LEFT_B bits */ +#define DC_OFFSET_LEFT_B_DC_OFFS(x) SET_BITS(21, 0, x) + +/* DC_OFFSET_RIGHT_B bits */ +#define DC_OFFSET_RIGHT_B_DC_OFFS(x) SET_BITS(21, 0, x) + +/* OUT_GAIN_LEFT_B bits */ +#define OUT_GAIN_LEFT_B_GAIN(x) SET_BITS(19, 0, x) + +/* OUT_GAIN_RIGHT_B bits */ +#define OUT_GAIN_RIGHT_B_GAIN(x) SET_BITS(19, 0, x) + +/* FIR coefficients */ +#define FIR_COEF_A(x) SET_BITS(19, 0, x) +#define FIR_COEF_B(x) SET_BITS(19, 0, x) + +/* Minimum OSR is always applied for 48 kHz and less sample rates */ +#define DMIC_MIN_OSR 50 + +/* These are used as guideline for configuring > 48 kHz sample rates. The + * minimum OSR can be relaxed down to 40 (use 3.84 MHz clock for 96 kHz). + */ +#define DMIC_HIGH_RATE_MIN_FS 64000 +#define DMIC_HIGH_RATE_OSR_MIN 40 + +/* Used for scaling FIR coefficients for HW */ +#define DMIC_HW_FIR_COEF_MAX ((1 << (DMIC_HW_BITS_FIR_COEF - 1)) - 1) +#define DMIC_HW_FIR_COEF_Q (DMIC_HW_BITS_FIR_COEF - 1) + +/* Internal precision in gains computation, e.g. Q4.28 in int32_t */ +#define DMIC_FIR_SCALE_Q 28 + +/* Used in unmute ramp values calculation */ +#define DMIC_HW_FIR_GAIN_MAX ((1 << (DMIC_HW_BITS_FIR_GAIN - 1)) - 1) + +/* Hardwired log ramp parameters. The first value is the initial gain in + * decibels. The second value is the default ramp time. + */ +#define LOGRAMP_START_DB Q_CONVERT_FLOAT(-90, DB2LIN_FIXED_INPUT_QY) +#define LOGRAMP_TIME_MS 400 /* Default ramp time in milliseconds */ + +/* Limits for ramp time from topology */ +#define LOGRAMP_TIME_MIN_MS 10 /* Min. 10 ms */ +#define LOGRAMP_TIME_MAX_MS 1000 /* Max. 1s */ + +/* Simplify log ramp step calculation equation with this constant term */ +#define LOGRAMP_CONST_TERM ((int32_t) \ + ((int64_t)-LOGRAMP_START_DB * DMIC_UNMUTE_RAMP_US / 1000)) + +/* Fractional shift for gain update. Gain format is Q2.30. */ +#define Q_SHIFT_GAIN_X_GAIN_COEF \ + (Q_SHIFT_BITS_32(30, DB2LIN_FIXED_OUTPUT_QY, 30)) + +#endif /* __DMIC_MACROS_H */ diff --git a/topology/intel/dmic/format.h b/topology/intel/dmic/format.h new file mode 100644 index 000000000..5e004c78a --- /dev/null +++ b/topology/intel/dmic/format.h @@ -0,0 +1,82 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __SOF_AUDIO_FORMAT_H__ +#define __SOF_AUDIO_FORMAT_H__ + +#include + +#if 0 +/* Maximum and minimum values for 24 bit */ +#define INT24_MAXVALUE 8388607 +#define INT24_MINVALUE -8388608 + + +/* Collection of common fractional numbers */ +#define ONE_Q2_30 1073741824 /* Q2.30 1.0 */ +#define ONE_Q1_31 2147483647 /* Q1.31 ~1.0 */ +#define MINUS_3DB_Q1_31 1520301996 /* 10^(-3/20) */ +#define MINUS_6DB_Q1_31 1076291389 /* 10^(-6/20) */ +#define MINUS_10DB_Q1_31 679093957 /* 10^(-10/20) */ +#define MINUS_20DB_Q1_31 214748365 /* 10^(-20/20) */ +#define MINUS_30DB_Q1_31 67909396 /* 10^(-30/20) */ +#define MINUS_40DB_Q1_31 21474836 /* 10^(-40/20) */ +#define MINUS_50DB_Q1_31 6790940 /* 10^(-50/20) */ +#define MINUS_60DB_Q1_31 2147484 /* 10^(-60/20) */ +#define MINUS_70DB_Q1_31 679094 /* 10^(-70/20) */ +#define MINUS_80DB_Q1_31 214748 /* 10^(-80/20) */ +#define MINUS_90DB_Q1_31 67909 /* 10^(-90/20) */ + + +/* Compute the number of shifts + * This will result in a compiler overflow error if shift bits are out of + * range as INT64_MAX/MIN is greater than 32 bit Q shift parameter + */ +#define Q_SHIFT_BITS_64(qx, qy, qz) \ + ((qx + qy - qz) <= 63 ? (((qx + qy - qz) >= 0) ? \ + (qx + qy - qz) : INT64_MIN) : INT64_MAX) + +#define Q_SHIFT_BITS_32(qx, qy, qz) \ + ((qx + qy - qz) <= 31 ? (((qx + qy - qz) >= 0) ? \ + (qx + qy - qz) : INT32_MIN) : INT32_MAX) + + +/* Convert fractional Qnx.ny number x to float */ +#define Q_CONVERT_QTOF(x, ny) ((float)(x) / ((int64_t)1 << (ny))) + +/* A more clever macro for Q-shifts */ +#define Q_SHIFT(x, src_q, dst_q) ((x) >> ((src_q) - (dst_q))) +#define Q_SHIFT_RND(x, src_q, dst_q) \ + ((((x) >> ((src_q) - (dst_q) - 1)) + 1) >> 1) + +/* Alternative version since compiler does not allow (x >> -1) */ +#define Q_SHIFT_LEFT(x, src_q, dst_q) ((x) << ((dst_q) - (src_q))) + +/* Fractional multiplication with shift + * Note that the parameters px and py must be cast to (int64_t) if other type. + */ +#define Q_MULTS_32X32(px, py, qx, qy, qp) \ + ((px) * (py) >> (((qx) + (qy) - (qp)))) + + +#endif + + +#endif /* __SOF_AUDIO_FORMAT_H__ */ diff --git a/topology/intel/dmic/pdm_decim_fir.h b/topology/intel/dmic/pdm_decim_fir.h new file mode 100644 index 000000000..aef3e19ea --- /dev/null +++ b/topology/intel/dmic/pdm_decim_fir.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * + * Copyright(c) 2018 Intel Corporation. All rights reserved. + * + * Author: Seppo Ingalsuo + */ + +/* Format for generated coefficients tables */ + +#ifndef __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__ +#define __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__ + +#include + +struct pdm_decim { + int decim_factor; + int length; + int shift; + int relative_passband; + int relative_stopband; + int passband_ripple; + int stopband_ripple; + const int32_t *coef; +}; + +#endif /* __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_FIR_H__ */ diff --git a/topology/intel/dmic/pdm_decim_int32_02_4323_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_02_4323_5100_010_095.h new file mode 100644 index 000000000..a7afa8ab5 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_02_4323_5100_010_095.h @@ -0,0 +1,124 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_02_4323_5100_010_095[95] = { + 178179, + -158757, + -2195582, + -5296650, + -5737416, + -1057695, + 4405719, + 3336648, + -3249588, + -5061179, + 1984305, + 6895125, + 68826, + -8433396, + -2933479, + 9499107, + 6882087, + -9330152, + -11397510, + 7807097, + 16376076, + -4402338, + -21239788, + -1118085, + 25423993, + 9062534, + -27935015, + -19203927, + 28049586, + 31500423, + -24524863, + -45191501, + 16582731, + 59861920, + -2808306, + -74639091, + -18696113, + 88054673, + 50505898, + -98266320, + -97865783, + 101816481, + 173879965, + -88042495, + -320187025, + -1193013, + 740698712, + 1139586920, + 740698712, + -1193013, + -320187025, + -88042495, + 173879965, + 101816481, + -97865783, + -98266320, + 50505898, + 88054673, + -18696113, + -74639091, + -2808306, + 59861920, + 16582731, + -45191501, + -24524863, + 31500423, + 28049586, + -19203927, + -27935015, + 9062534, + 25423993, + -1118085, + -21239788, + -4402338, + 16376076, + 7807097, + -11397510, + -9330152, + 6882087, + 9499107, + -2933479, + -8433396, + 68826, + 6895125, + 1984305, + -5061179, + -3249588, + 3336648, + 4405719, + -1057695, + -5737416, + -5296650, + -2195582, + -158757, + 178179 + +}; + +struct pdm_decim pdm_decim_int32_02_4323_5100_010_095 = { + 2, 95, 0, 4323, 5100, 10, 95, fir_int32_02_4323_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_02_4375_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_02_4375_5100_010_095.h new file mode 100644 index 000000000..1d9fcdecd --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_02_4375_5100_010_095.h @@ -0,0 +1,130 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_02_4375_5100_010_095[101] = { + -587830, + -2653881, + -5154608, + -4845367, + -226474, + 4220832, + 2571159, + -3184700, + -4043579, + 2206821, + 5554546, + -750495, + -6923897, + -1268580, + 8073364, + 4085184, + -8546479, + -7505366, + 8176184, + 11533751, + -6471060, + -15704257, + 3359705, + 19852407, + 1635592, + -23144509, + -8252640, + 25285011, + 16574477, + -25723227, + -26663303, + 23549736, + 38139662, + -17943368, + -50446982, + 8141045, + 63090266, + 7051348, + -75166961, + -29039893, + 85772628, + 60568976, + -93167361, + -106799777, + 94198977, + 180962817, + -78385599, + -324820256, + -12243140, + 742491464, + 1151461314, + 742491464, + -12243140, + -324820256, + -78385599, + 180962817, + 94198977, + -106799777, + -93167361, + 60568976, + 85772628, + -29039893, + -75166961, + 7051348, + 63090266, + 8141045, + -50446982, + -17943368, + 38139662, + 23549736, + -26663303, + -25723227, + 16574477, + 25285011, + -8252640, + -23144509, + 1635592, + 19852407, + 3359705, + -15704257, + -6471060, + 11533751, + 8176184, + -7505366, + -8546479, + 4085184, + 8073364, + -1268580, + -6923897, + -750495, + 5554546, + 2206821, + -4043579, + -3184700, + 2571159, + 4220832, + -226474, + -4845367, + -5154608, + -2653881, + -587830 + +}; + +struct pdm_decim pdm_decim_int32_02_4375_5100_010_095 = { + 2, 101, 0, 4375, 5100, 10, 95, fir_int32_02_4375_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_03_4375_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_03_4375_5100_010_095.h new file mode 100644 index 000000000..259df7de7 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_03_4375_5100_010_095.h @@ -0,0 +1,186 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_03_4375_5100_010_095[157] = { + 350908, + 1127906, + 2233575, + 3059598, + 2752218, + 818077, + -2252677, + -4944563, + -5550761, + -3466262, + 53093, + 2496787, + 1904133, + -1421749, + -4818616, + -5204506, + -1721103, + 3155344, + 5311508, + 2454543, + -3518663, + -7589567, + -5713379, + 1327838, + 7901439, + 7958184, + 527907, + -8634105, + -11354937, + -4214569, + 7627213, + 13970417, + 8263468, + -5862019, + -16549858, + -13530131, + 2213952, + 17870989, + 19056458, + 2854116, + -18029944, + -24979416, + -9860304, + 16175288, + 30546328, + 18606151, + -11894439, + -35172976, + -28918520, + 4746588, + 38201563, + 40591159, + 5825487, + -38713444, + -53159813, + -20283635, + 35723701, + 66121349, + 39266346, + -27911327, + -78796917, + -63664545, + 13459132, + 90417174, + 95194527, + 10755265, + -99898306, + -137498952, + -51076476, + 105458775, + 200050300, + 124562550, + -101613472, + -313388303, + -297347454, + 53702567, + 639689683, + 1187815441, + 1411068556, + 1187815441, + 639689683, + 53702567, + -297347454, + -313388303, + -101613472, + 124562550, + 200050300, + 105458775, + -51076476, + -137498952, + -99898306, + 10755265, + 95194527, + 90417174, + 13459132, + -63664545, + -78796917, + -27911327, + 39266346, + 66121349, + 35723701, + -20283635, + -53159813, + -38713444, + 5825487, + 40591159, + 38201563, + 4746588, + -28918520, + -35172976, + -11894439, + 18606151, + 30546328, + 16175288, + -9860304, + -24979416, + -18029944, + 2854116, + 19056458, + 17870989, + 2213952, + -13530131, + -16549858, + -5862019, + 8263468, + 13970417, + 7627213, + -4214569, + -11354937, + -8634105, + 527907, + 7958184, + 7901439, + 1327838, + -5713379, + -7589567, + -3518663, + 2454543, + 5311508, + 3155344, + -1721103, + -5204506, + -4818616, + -1421749, + 1904133, + 2496787, + 53093, + -3466262, + -5550761, + -4944563, + -2252677, + 818077, + 2752218, + 3059598, + 2233575, + 1127906, + 350908 + +}; + +struct pdm_decim pdm_decim_int32_03_4375_5100_010_095 = { + 3, 157, 1, 4375, 5100, 10, 95, fir_int32_03_4375_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_04_4318_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_04_4318_5100_010_095.h new file mode 100644 index 000000000..2d9f24461 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_04_4318_5100_010_095.h @@ -0,0 +1,224 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_04_4318_5100_010_095[195] = { + 111466, + 733409, + 1749250, + 3319696, + 5129378, + 6676209, + 7309490, + 6506584, + 4154756, + 734975, + -2729377, + -4998637, + -5126868, + -2945573, + 726080, + 4306371, + 6084832, + 5022201, + 1343898, + -3405897, + -6962146, + -7384707, + -4083477, + 1706504, + 7322247, + 9858386, + 7634375, + 1218279, + -6584983, + -11909667, + -11705999, + -5426216, + 4387589, + 13057254, + 15953274, + 10914694, + -336708, + -12649310, + -19740590, + -17350504, + -5691816, + 10168034, + 22423979, + 24315225, + 13770305, + -5012695, + -23099234, + -30992981, + -23541145, + -3074594, + 21044646, + 36602258, + 34627991, + 14410179, + -15304906, + -39942350, + -46136506, + -28837515, + 5227124, + 39904111, + 57125295, + 46160603, + 9951610, + -35069112, + -66221384, + -65802058, + -30833475, + 23892424, + 71729303, + 86873627, + 57925919, + -4643620, + -71630929, + -108312985, + -91957579, + -25024430, + 63233146, + 128873180, + 134384678, + 69003997, + -42277605, + -146972769, + -188542432, + -135065835, + 625004, + 160544910, + 263771211, + 243538822, + 83417905, + -164654723, + -391259178, + -468017530, + -299941690, + 129616412, + 741218294, + 1378462855, + 1858765025, + 2037199780, + 1858765025, + 1378462855, + 741218294, + 129616412, + -299941690, + -468017530, + -391259178, + -164654723, + 83417905, + 243538822, + 263771211, + 160544910, + 625004, + -135065835, + -188542432, + -146972769, + -42277605, + 69003997, + 134384678, + 128873180, + 63233146, + -25024430, + -91957579, + -108312985, + -71630929, + -4643620, + 57925919, + 86873627, + 71729303, + 23892424, + -30833475, + -65802058, + -66221384, + -35069112, + 9951610, + 46160603, + 57125295, + 39904111, + 5227124, + -28837515, + -46136506, + -39942350, + -15304906, + 14410179, + 34627991, + 36602258, + 21044646, + -3074594, + -23541145, + -30992981, + -23099234, + -5012695, + 13770305, + 24315225, + 22423979, + 10168034, + -5691816, + -17350504, + -19740590, + -12649310, + -336708, + 10914694, + 15953274, + 13057254, + 4387589, + -5426216, + -11705999, + -11909667, + -6584983, + 1218279, + 7634375, + 9858386, + 7322247, + 1706504, + -4083477, + -7384707, + -6962146, + -3405897, + 1343898, + 5022201, + 6084832, + 4306371, + 726080, + -2945573, + -5126868, + -4998637, + -2729377, + 734975, + 4154756, + 6506584, + 7309490, + 6676209, + 5129378, + 3319696, + 1749250, + 733409, + 111466 + +}; + +struct pdm_decim pdm_decim_int32_04_4318_5100_010_095 = { + 4, 195, 2, 4318, 5100, 10, 95, fir_int32_04_4318_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_05_4325_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_05_4325_5100_010_095.h new file mode 100644 index 000000000..9d2565818 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_05_4325_5100_010_095.h @@ -0,0 +1,278 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_05_4325_5100_010_095[249] = { + -207469, + -340409, + -498144, + -558705, + -409384, + 55040, + 891125, + 2067198, + 3439775, + 4759611, + 5714914, + 6006588, + 5438767, + 3999938, + 1904139, + -429386, + -2461501, + -3672469, + -3715731, + -2543409, + -455850, + 1949813, + 3931230, + 4816572, + 4223058, + 2208032, + -705100, + -3649781, + -5664341, + -5997954, + -4379534, + -1155856, + 2760627, + 6138124, + 7803235, + 7038238, + 3865643, + -886573, + -5783539, + -9205487, + -9873081, + -7309023, + -2074085, + 4324112, + 9849851, + 12575185, + 11338304, + 6203771, + -1449001, + -9273272, + -14659745, + -15592551, + -11379919, + -3001447, + 7079011, + 15617750, + 19618936, + 17365380, + 9103870, + -2875883, + -14845719, + -22779047, + -23684744, + -16699479, + -3549687, + 11824128, + 24420688, + 29781202, + 25536278, + 12369331, + -5975020, + -23715313, + -34825066, + -35046780, + -23465500, + -3076561, + 19921841, + 37971240, + 44596949, + 36675890, + 15750350, + -12128842, + -38090537, + -53213360, + -51529430, + -32269554, + -535756, + 33948414, + 59741809, + 67400482, + 52822241, + 19070165, + -24058950, + -62761384, + -83526757, + -77707237, + -44854657, + 6391470, + 60372631, + 99010031, + 107635426, + 80237884, + 22341840, + -49653145, + -112793869, + -144502927, + -130123093, + -68698546, + 25049444, + 123586766, + 193887099, + 207405271, + 149914650, + 28201854, + -128717667, + -274782040, + -357589632, + -332094228, + -173714910, + 112793207, + 491412574, + 900833942, + 1267184732, + 1520519220, + 1610979079, + 1520519220, + 1267184732, + 900833942, + 491412574, + 112793207, + -173714910, + -332094228, + -357589632, + -274782040, + -128717667, + 28201854, + 149914650, + 207405271, + 193887099, + 123586766, + 25049444, + -68698546, + -130123093, + -144502927, + -112793869, + -49653145, + 22341840, + 80237884, + 107635426, + 99010031, + 60372631, + 6391470, + -44854657, + -77707237, + -83526757, + -62761384, + -24058950, + 19070165, + 52822241, + 67400482, + 59741809, + 33948414, + -535756, + -32269554, + -51529430, + -53213360, + -38090537, + -12128842, + 15750350, + 36675890, + 44596949, + 37971240, + 19921841, + -3076561, + -23465500, + -35046780, + -34825066, + -23715313, + -5975020, + 12369331, + 25536278, + 29781202, + 24420688, + 11824128, + -3549687, + -16699479, + -23684744, + -22779047, + -14845719, + -2875883, + 9103870, + 17365380, + 19618936, + 15617750, + 7079011, + -3001447, + -11379919, + -15592551, + -14659745, + -9273272, + -1449001, + 6203771, + 11338304, + 12575185, + 9849851, + 4324112, + -2074085, + -7309023, + -9873081, + -9205487, + -5783539, + -886573, + 3865643, + 7038238, + 7803235, + 6138124, + 2760627, + -1155856, + -4379534, + -5997954, + -5664341, + -3649781, + -705100, + 2208032, + 4223058, + 4816572, + 3931230, + 1949813, + -455850, + -2543409, + -3715731, + -3672469, + -2461501, + -429386, + 1904139, + 3999938, + 5438767, + 6006588, + 5714914, + 4759611, + 3439775, + 2067198, + 891125, + 55040, + -409384, + -558705, + -498144, + -340409, + -207469 + +}; + +struct pdm_decim pdm_decim_int32_05_4325_5100_010_095 = { + 5, 249, 2, 4325, 5100, 10, 95, fir_int32_05_4325_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_06_4172_5100_010_095.h b/topology/intel/dmic/pdm_decim_int32_06_4172_5100_010_095.h new file mode 100644 index 000000000..d33d75ed4 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_06_4172_5100_010_095.h @@ -0,0 +1,276 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_06_4172_5100_010_095[247] = { + -128632, + -59497, + 27046, + 238561, + 615381, + 1180391, + 1925670, + 2802557, + 3718091, + 4541067, + 5118546, + 5302088, + 4979296, + 4105097, + 2725202, + 985340, + -879509, + -2576724, + -3805615, + -4316339, + -3968132, + -2772637, + -910841, + 1284350, + 3378797, + 4918713, + 5524148, + 4978750, + 3294285, + 731418, + -2230825, + -4978575, + -6884637, + -7446661, + -6411304, + -3856078, + -204820, + 3832851, + 7392855, + 9635554, + 9937191, + 8050966, + 4198476, + -936294, + -6318030, + -10761631, + -13186246, + -12869044, + -9641051, + -3974915, + 3065409, + 10013209, + 15296510, + 17584632, + 16110734, + 10893393, + 2799042, + -6583403, + -15242334, + -21164579, + -22791637, + -19410411, + -11383563, + -154458, + 11993186, + 22385766, + 28535081, + 28717126, + 22423602, + 10572914, + -4585854, + -19878539, + -31852959, + -37539801, + -35162895, + -24634855, + -7708109, + 12278651, + 31033082, + 44206560, + 48374146, + 41881953, + 25360406, + 1761722, + -24123604, + -46603137, + -60296121, + -61373509, + -48548023, + -23572693, + 8903015, + 42138419, + 68642074, + 81792054, + 77398921, + 54859810, + 17614341, + -27257528, + -70279853, + -101467545, + -112499312, + -98729567, + -60589492, + -4020371, + 60241948, + 118486175, + 156668601, + 163407087, + 132743110, + 66089855, + -27096695, + -130167425, + -221612366, + -278546163, + -280773282, + -214713860, + -76455494, + 126679457, + 376416772, + 645705969, + 902489232, + 1114466646, + 1254066162, + 1302772250, + 1254066162, + 1114466646, + 902489232, + 645705969, + 376416772, + 126679457, + -76455494, + -214713860, + -280773282, + -278546163, + -221612366, + -130167425, + -27096695, + 66089855, + 132743110, + 163407087, + 156668601, + 118486175, + 60241948, + -4020371, + -60589492, + -98729567, + -112499312, + -101467545, + -70279853, + -27257528, + 17614341, + 54859810, + 77398921, + 81792054, + 68642074, + 42138419, + 8903015, + -23572693, + -48548023, + -61373509, + -60296121, + -46603137, + -24123604, + 1761722, + 25360406, + 41881953, + 48374146, + 44206560, + 31033082, + 12278651, + -7708109, + -24634855, + -35162895, + -37539801, + -31852959, + -19878539, + -4585854, + 10572914, + 22423602, + 28717126, + 28535081, + 22385766, + 11993186, + -154458, + -11383563, + -19410411, + -22791637, + -21164579, + -15242334, + -6583403, + 2799042, + 10893393, + 16110734, + 17584632, + 15296510, + 10013209, + 3065409, + -3974915, + -9641051, + -12869044, + -13186246, + -10761631, + -6318030, + -936294, + 4198476, + 8050966, + 9937191, + 9635554, + 7392855, + 3832851, + -204820, + -3856078, + -6411304, + -7446661, + -6884637, + -4978575, + -2230825, + 731418, + 3294285, + 4978750, + 5524148, + 4918713, + 3378797, + 1284350, + -910841, + -2772637, + -3968132, + -4316339, + -3805615, + -2576724, + -879509, + 985340, + 2725202, + 4105097, + 4979296, + 5302088, + 5118546, + 4541067, + 3718091, + 2802557, + 1925670, + 1180391, + 615381, + 238561, + 27046, + -59497, + -128632 + +}; + +struct pdm_decim pdm_decim_int32_06_4172_5100_010_095 = { + 6, 247, 2, 4172, 5100, 10, 95, fir_int32_06_4172_5100_010_095 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_08_4156_5301_010_090.h b/topology/intel/dmic/pdm_decim_int32_08_4156_5301_010_090.h new file mode 100644 index 000000000..ec9518aaf --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_08_4156_5301_010_090.h @@ -0,0 +1,278 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_08_4156_5301_010_090[249] = { + -436533, + -30097, + 185136, + 599151, + 1249127, + 2156309, + 3316125, + 4690126, + 6201703, + 7736149, + 9146691, + 10266194, + 10924643, + 10970009, + 10291237, + 8839410, + 6645091, + 3827796, + 595625, + -2767301, + -5925074, + -8524488, + -10235869, + -10797193, + -10055131, + -7997758, + -4771108, + -678235, + 3841448, + 8252025, + 11985305, + 14503017, + 15375105, + 14342998, + 11370408, + 6669744, + 697166, + -5883789, + -12270613, + -17614188, + -21126754, + -22191034, + -20457484, + -15913860, + -8916788, + -176408, + 9306801, + 18349873, + 25728753, + 30337331, + 31343834, + 28324134, + 21351665, + 11028693, + -1551253, + -14908738, + -27343830, + -37144407, + -42813431, + -43283070, + -38091954, + -27492154, + -12471488, + 5321162, + 23744416, + 40410795, + 52985294, + 59498920, + 58637630, + 49965561, + 34048834, + 12455105, + -12379340, + -37407904, + -59331472, + -75016381, + -81921345, + -78477412, + -64367569, + -40661743, + -9779029, + 24730505, + 58569248, + 87201632, + 106433026, + 112987295, + 105007254, + 82406938, + 47020690, + 2512109, + -45960354, + -92298012, + -130180961, + -153871591, + -159007197, + -143278921, + -106903512, + -52811885, + 13489989, + 84396518, + 150980417, + 204001696, + 235041565, + 237634873, + 208265018, + 147093489, + 58319704, + -49897266, + -165975625, + -275876513, + -364417203, + -416806765, + -420248734, + -365439450, + -247794421, + -68258347, + 166406038, + 443903891, + 747260153, + 1056170460, + 1348727100, + 1603354585, + 1800763975, + 1925728602, + 1968501522, + 1925728602, + 1800763975, + 1603354585, + 1348727100, + 1056170460, + 747260153, + 443903891, + 166406038, + -68258347, + -247794421, + -365439450, + -420248734, + -416806765, + -364417203, + -275876513, + -165975625, + -49897266, + 58319704, + 147093489, + 208265018, + 237634873, + 235041565, + 204001696, + 150980417, + 84396518, + 13489989, + -52811885, + -106903512, + -143278921, + -159007197, + -153871591, + -130180961, + -92298012, + -45960354, + 2512109, + 47020690, + 82406938, + 105007254, + 112987295, + 106433026, + 87201632, + 58569248, + 24730505, + -9779029, + -40661743, + -64367569, + -78477412, + -81921345, + -75016381, + -59331472, + -37407904, + -12379340, + 12455105, + 34048834, + 49965561, + 58637630, + 59498920, + 52985294, + 40410795, + 23744416, + 5321162, + -12471488, + -27492154, + -38091954, + -43283070, + -42813431, + -37144407, + -27343830, + -14908738, + -1551253, + 11028693, + 21351665, + 28324134, + 31343834, + 30337331, + 25728753, + 18349873, + 9306801, + -176408, + -8916788, + -15913860, + -20457484, + -22191034, + -21126754, + -17614188, + -12270613, + -5883789, + 697166, + 6669744, + 11370408, + 14342998, + 15375105, + 14503017, + 11985305, + 8252025, + 3841448, + -678235, + -4771108, + -7997758, + -10055131, + -10797193, + -10235869, + -8524488, + -5925074, + -2767301, + 595625, + 3827796, + 6645091, + 8839410, + 10291237, + 10970009, + 10924643, + 10266194, + 9146691, + 7736149, + 6201703, + 4690126, + 3316125, + 2156309, + 1249127, + 599151, + 185136, + -30097, + -436533 + +}; + +struct pdm_decim pdm_decim_int32_08_4156_5301_010_090 = { + 8, 249, 3, 4156, 5301, 10, 90, fir_int32_08_4156_5301_010_090 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_10_4156_5345_010_090.h b/topology/intel/dmic/pdm_decim_int32_10_4156_5345_010_090.h new file mode 100644 index 000000000..d889c1f7d --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_10_4156_5345_010_090.h @@ -0,0 +1,279 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_10_4156_5345_010_090[250] = { + 1523665, + 1033186, + 1237912, + 1334775, + 1259136, + 945771, + 330804, + -639430, + -2007230, + -3782603, + -5951062, + -8464725, + -11233139, + -14135396, + -17013193, + -19685639, + -21953913, + -23620153, + -24499971, + -24438406, + -23328862, + -21124651, + -17851431, + -13614282, + -8597813, + -3061758, + 2673313, + 8241850, + 13260214, + 17352951, + 20183225, + 21483036, + 21079871, + 18918865, + 15076270, + 9764507, + 3325641, + -3786831, + -11034844, + -17834361, + -23598518, + -27786005, + -29948197, + -29773031, + -27120493, + -22045821, + -14809184, + -5868041, + 4147420, + 14474606, + 24273606, + 32691772, + 38934943, + 42337380, + 42426833, + 38977193, + 32043932, + 21978645, + 9419218, + -4744590, + -19429341, + -33436251, + -45544284, + -54611932, + -59678078, + -60055175, + -55404949, + -45789928, + -31694490, + -14011800, + 6004287, + 26821132, + 46737621, + 64015881, + 77024918, + 84384267, + 85096503, + 78655977, + 65124057, + 45161629, + 20014065, + -8553289, + -38367257, + -66999907, + -91952963, + -110860336, + -121693302, + -122951962, + -113826153, + -94311463, + -65267325, + -28409399, + 13767679, + 58135590, + 101138413, + 139047421, + 168247614, + 185536046, + 188409622, + 175319624, + 145872050, + 100954938, + 42779310, + -25174601, + -98307054, + -171138928, + -237629824, + -291555223, + -326919157, + -338374861, + -321624361, + -273768560, + -193581379, + -81686484, + 59379271, + 225223665, + 409778420, + 605591661, + 804217132, + 996677146, + 1173970423, + 1327592623, + 1450036054, + 1535235882, + 1578933611, + 1578933611, + 1535235882, + 1450036054, + 1327592623, + 1173970423, + 996677146, + 804217132, + 605591661, + 409778420, + 225223665, + 59379271, + -81686484, + -193581379, + -273768560, + -321624361, + -338374861, + -326919157, + -291555223, + -237629824, + -171138928, + -98307054, + -25174601, + 42779310, + 100954938, + 145872050, + 175319624, + 188409622, + 185536046, + 168247614, + 139047421, + 101138413, + 58135590, + 13767679, + -28409399, + -65267325, + -94311463, + -113826153, + -122951962, + -121693302, + -110860336, + -91952963, + -66999907, + -38367257, + -8553289, + 20014065, + 45161629, + 65124057, + 78655977, + 85096503, + 84384267, + 77024918, + 64015881, + 46737621, + 26821132, + 6004287, + -14011800, + -31694490, + -45789928, + -55404949, + -60055175, + -59678078, + -54611932, + -45544284, + -33436251, + -19429341, + -4744590, + 9419218, + 21978645, + 32043932, + 38977193, + 42426833, + 42337380, + 38934943, + 32691772, + 24273606, + 14474606, + 4147420, + -5868041, + -14809184, + -22045821, + -27120493, + -29773031, + -29948197, + -27786005, + -23598518, + -17834361, + -11034844, + -3786831, + 3325641, + 9764507, + 15076270, + 18918865, + 21079871, + 21483036, + 20183225, + 17352951, + 13260214, + 8241850, + 2673313, + -3061758, + -8597813, + -13614282, + -17851431, + -21124651, + -23328862, + -24438406, + -24499971, + -23620153, + -21953913, + -19685639, + -17013193, + -14135396, + -11233139, + -8464725, + -5951062, + -3782603, + -2007230, + -639430, + 330804, + 945771, + 1259136, + 1334775, + 1237912, + 1033186, + 1523665 + +}; + +struct pdm_decim pdm_decim_int32_10_4156_5345_010_090 = { + 10, 250, 3, 4156, 5345, 10, 90, fir_int32_10_4156_5345_010_090 +}; diff --git a/topology/intel/dmic/pdm_decim_int32_12_4156_5345_010_090.h b/topology/intel/dmic/pdm_decim_int32_12_4156_5345_010_090.h new file mode 100644 index 000000000..64cb4392e --- /dev/null +++ b/topology/intel/dmic/pdm_decim_int32_12_4156_5345_010_090.h @@ -0,0 +1,279 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include + +const int32_t fir_int32_12_4156_5345_010_090[250] = { + 3388064, + 2103678, + 2588621, + 3003201, + 3289311, + 3375616, + 3187681, + 2654590, + 1715972, + 309000, + -1610211, + -4055571, + -7035886, + -10511174, + -14426105, + -18683424, + -23158811, + -27697959, + -32125744, + -36244313, + -39850668, + -42739502, + -44716782, + -45614286, + -45293362, + -43663492, + -40685637, + -36382432, + -30840569, + -24214188, + -16719141, + -8631481, + -273616, + 7996830, + 15802429, + 22766807, + 28531588, + 32779134, + 35250465, + 35763733, + 34227329, + 30653000, + 25158397, + 17970176, + 9416017, + -86479, + -10047026, + -19926252, + -29162149, + -37200047, + -43523733, + -47686853, + -49340539, + -48260145, + -44362718, + -37720992, + -28567244, + -17289156, + -4416600, + 9400589, + 23423041, + 36858656, + 48904650, + 58793401, + 65836995, + 69472264, + 69298737, + 65111461, + 56923398, + 44977350, + 29744904, + 11913285, + -7641844, + -27894994, + -47720356, + -65950400, + -81438916, + -93127908, + -100112283, + -101700449, + -97465921, + -87287745, + -71375413, + -50277965, + -24873838, + 3657557, + 33882414, + 64176403, + 92802029, + 117997525, + 138071930, + 151502587, + 157029100, + 153740023, + 141145505, + 119233461, + 88503690, + 49978361, + 5186664, + -43876910, + -94819563, + -144940463, + -191340721, + -231048840, + -261155504, + -278952672, + -282068987, + -268596392, + -237200378, + -187208906, + -118674579, + -32406655, + 70029801, + 186348178, + 313607557, + 448310730, + 586528751, + 724047761, + 856530828, + 979688864, + 1089452098, + 1182135271, + 1254588364, + 1304326765, + 1329634160, + 1329634160, + 1304326765, + 1254588364, + 1182135271, + 1089452098, + 979688864, + 856530828, + 724047761, + 586528751, + 448310730, + 313607557, + 186348178, + 70029801, + -32406655, + -118674579, + -187208906, + -237200378, + -268596392, + -282068987, + -278952672, + -261155504, + -231048840, + -191340721, + -144940463, + -94819563, + -43876910, + 5186664, + 49978361, + 88503690, + 119233461, + 141145505, + 153740023, + 157029100, + 151502587, + 138071930, + 117997525, + 92802029, + 64176403, + 33882414, + 3657557, + -24873838, + -50277965, + -71375413, + -87287745, + -97465921, + -101700449, + -100112283, + -93127908, + -81438916, + -65950400, + -47720356, + -27894994, + -7641844, + 11913285, + 29744904, + 44977350, + 56923398, + 65111461, + 69298737, + 69472264, + 65836995, + 58793401, + 48904650, + 36858656, + 23423041, + 9400589, + -4416600, + -17289156, + -28567244, + -37720992, + -44362718, + -48260145, + -49340539, + -47686853, + -43523733, + -37200047, + -29162149, + -19926252, + -10047026, + -86479, + 9416017, + 17970176, + 25158397, + 30653000, + 34227329, + 35763733, + 35250465, + 32779134, + 28531588, + 22766807, + 15802429, + 7996830, + -273616, + -8631481, + -16719141, + -24214188, + -30840569, + -36382432, + -40685637, + -43663492, + -45293362, + -45614286, + -44716782, + -42739502, + -39850668, + -36244313, + -32125744, + -27697959, + -23158811, + -18683424, + -14426105, + -10511174, + -7035886, + -4055571, + -1610211, + 309000, + 1715972, + 2654590, + 3187681, + 3375616, + 3289311, + 3003201, + 2588621, + 2103678, + 3388064 + +}; + +struct pdm_decim pdm_decim_int32_12_4156_5345_010_090 = { + 12, 250, 3, 4156, 5345, 10, 90, fir_int32_12_4156_5345_010_090 +}; diff --git a/topology/intel/dmic/pdm_decim_table.h b/topology/intel/dmic/pdm_decim_table.h new file mode 100644 index 000000000..598a838e1 --- /dev/null +++ b/topology/intel/dmic/pdm_decim_table.h @@ -0,0 +1,104 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_TABLE_H__ +#define __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_TABLE_H__ + +#include "pdm_decim_fir.h" + +#include + +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_2 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_3 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_4 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_5 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_6 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_8 1 +#define CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_12 1 + +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_2 +#include "pdm_decim_int32_02_4323_5100_010_095.h" +#include "pdm_decim_int32_02_4375_5100_010_095.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_3 +#include "pdm_decim_int32_03_4375_5100_010_095.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_4 +#include "pdm_decim_int32_04_4318_5100_010_095.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_5 +#include "pdm_decim_int32_05_4325_5100_010_095.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_6 +#include "pdm_decim_int32_06_4172_5100_010_095.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_8 +#include "pdm_decim_int32_08_4156_5301_010_090.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_10 +#include "pdm_decim_int32_10_4156_5345_010_090.h" +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_12 +#include "pdm_decim_int32_12_4156_5345_010_090.h" +#endif + +/* Note 1: Higher spec filter must be before lower spec filter + * if there are multiple filters for a decimation factor. The first + * filter is skipped if the length is too much vs. overrun limit. If + * other order the better filter would be never selected. + * + * Note 2: The introduction order of FIR decimation factors is the selection + * preference order. The decimation factor 5 and 10 (2*5) cause a often less + * compatible output sample rate for CIC so they are not used if there other + * suitable nearby values. + * + * The naming scheme of coefficients set is: + * _____ + */ +struct pdm_decim *fir_list[] = { +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_2 + &pdm_decim_int32_02_4375_5100_010_095, + &pdm_decim_int32_02_4323_5100_010_095, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_3 + &pdm_decim_int32_03_4375_5100_010_095, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_4 + &pdm_decim_int32_04_4318_5100_010_095, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_6 + &pdm_decim_int32_06_4172_5100_010_095, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_5 + &pdm_decim_int32_05_4325_5100_010_095, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_8 + &pdm_decim_int32_08_4156_5301_010_090, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_12 + &pdm_decim_int32_12_4156_5345_010_090, +#endif +#if CONFIG_INTEL_DMIC_FIR_DECIMATE_BY_10 + &pdm_decim_int32_10_4156_5345_010_090, +#endif + NULL, /* This marks the end of coefficients */ +}; + +#endif /* __SOF_AUDIO_COEFFICIENTS_PDM_DECIM_PDM_DECIM_TABLE_H__ */ diff --git a/topology/intel/nhlt-internal.h b/topology/intel/nhlt-internal.h new file mode 100644 index 000000000..20ad74880 --- /dev/null +++ b/topology/intel/nhlt-internal.h @@ -0,0 +1,155 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __DAIS_NHLT_H +#define __DAIS_NHLT_H + +#include + +#define DMIC_HW_CONTROLLERS 2 +#define DMIC_HW_FIFOS 2 +#define DMIC_HW_FIR_LENGTH_MAX 250 +#define SSP_TDM_MAX_SLOT_MAP_COUNT 8 + +/* structs for gathering the parameters from topology */ +struct dmic_config_pdm { + uint16_t id; + uint16_t enable_mic_a; + uint16_t enable_mic_b; + uint16_t polarity_mic_a; + uint16_t polarity_mic_b; + uint16_t clk_edge; + uint16_t skew; +}; + +struct dmic_config_dai { + uint32_t driver_version; + uint32_t io_clk; + uint32_t pdmclk_min; + uint32_t pdmclk_max; + uint32_t fifo_fs; + uint16_t fifo_bits; + uint16_t fifo_bits_b; + uint16_t duty_min; + uint16_t duty_max; + uint32_t num_pdm_active; + uint32_t wake_up_time; + uint32_t min_clock_on_time; + uint32_t unmute_ramp_time; + struct dmic_config_pdm pdm[DMIC_HW_CONTROLLERS]; +}; + +/* every pdm controller has separate fir filter for output fifos */ +struct dmic_calc_fir_coeffs_array { + uint32_t fir_len[DMIC_HW_CONTROLLERS]; + uint32_t fir_coeffs[DMIC_HW_CONTROLLERS][DMIC_HW_FIFOS][DMIC_HW_FIR_LENGTH_MAX]; +}; + +/* structs for intel dmic nhlt vendor specific blob generation */ +struct dmic_intel_fir_config { + uint32_t fir_control; + uint32_t fir_config; + uint32_t dc_offset_left; + uint32_t dc_offset_right; + uint32_t out_gain_left; + uint32_t out_gain_right; + uint32_t reserved2[2]; + uint32_t fir_coeffs[]; +} __attribute__((packed)); + +struct dmic_intel_pdm_ctrl_cfg { + uint32_t cic_control; + uint32_t cic_config; + uint32_t reserved0; + uint32_t mic_control; + uint32_t pdmsm; + uint32_t reuse_fir_from_pdm; + uint32_t reserved1[2]; + struct dmic_intel_fir_config fir_config[]; +} __attribute__((packed)); + +struct dmic_intel_config_data { + uint32_t gateway_attributes; + uint32_t ts_group[4]; + uint32_t clock_on_delay; + uint32_t channel_ctrl_mask; + uint32_t chan_ctrl_cfg[2]; + uint32_t channel_pdm_mask; + struct dmic_intel_pdm_ctrl_cfg pdm_ctrl_cfg[]; +} __attribute__((packed)); + +/* structs for gathering the parameters from topology */ +struct ssp_config_dai { + uint32_t io_clk; + uint16_t mclk_id; + uint32_t mclk_rate; + uint32_t fsync_rate; + uint32_t bclk_rate; + uint32_t tdm_slots; + uint32_t rx_slots; + uint32_t tx_slots; + uint32_t sample_valid_bits; + uint16_t tdm_slot_width; + uint32_t mclk_direction; + uint16_t frame_pulse_width; + uint16_t tdm_per_slot_padding_flag; + uint32_t clks_control; + uint32_t quirks; + uint32_t bclk_delay; + uint32_t format; +}; + +/* structs for intel dmic nhlt vendor specific blob generation */ +typedef struct ssp_intel_config_data { + uint32_t gateway_attributes; + uint32_t ts_group[SSP_TDM_MAX_SLOT_MAP_COUNT]; + uint32_t ssc0; + uint32_t ssc1; + uint32_t sscto; + uint32_t sspsp; + uint32_t sstsa; + uint32_t ssrsa; + uint32_t ssc2; + uint32_t sspsp2; + uint32_t ssc3; + uint32_t ssioc; + uint32_t mdivc; + uint32_t mdivr; +} __attribute__((packed)); + +struct intel_nhlt_params +{ + /* structs to gather dmic params before calculations */ + struct dmic_config_dai dmic_prm[DMIC_HW_FIFOS]; + uint32_t dmic_dai_index; + + /* dmic vendor blob structs */ + struct dmic_intel_config_data dmic_blob; + struct dmic_intel_pdm_ctrl_cfg dmic_blob_pdm[DMIC_HW_CONTROLLERS]; + struct dmic_intel_fir_config dmic_blob_fir[DMIC_HW_CONTROLLERS][DMIC_HW_FIFOS]; + struct dmic_calc_fir_coeffs_array dmic_fir_array; + + struct ssp_config_dai ssp_prm; + struct ssp_intel_config_data ssp_blob; +}; + + + +#endif diff --git a/topology/intel/nhlt.c b/topology/intel/nhlt.c new file mode 100644 index 000000000..e2ee98dff --- /dev/null +++ b/topology/intel/nhlt.c @@ -0,0 +1,460 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include +#include +#include +#include +#include +#include +#include "pre-processor.h" +#include "nhlt.h" +#include "nhlt-internal.h" +#include "dmic/dmic-nhlt.h" +#include "ssp/ssp-nhlt.h" + +#define MAX_NHLT_BUFFER_SIZE 25000 +#define MAX_EPS_COUNT 20 + +static int add_dmic_ep(struct tplg_pre_processor *tplg_pp, struct endpoint_descriptor **eps) +{ + struct endpoint_descriptor ep; + struct mic_array_device_specific_config mic_s_conf; + struct formats_config f_conf; + struct format_config f_conf1; + uint8_t *ep_target; + + /* + * nhlt dmic structure: + * + * endpoint_descriptor, sizeof(struct endpoint_descriptor) + * device_specific_config (mic), sizeof(struct mic_array_device_specific_config) + * formats_config (formats_count), sizeof(struct formats_config) + * format_config (waveex), sizeof(struct format_config) + * vendor_blob sizeof(vendor_blob) + */ + + /* dmic ep */ + ep.link_type = NHLT_LINK_TYPE_PDM; + ep.instance_id = 0; + ep.vendor_id = NHLT_VENDOR_ID_INTEL; + ep.device_id = NHLT_DEVICE_ID_INTEL_PDM_DMIC; + ep.revision_id = 0; + ep.subsystem_id = 0; + ep.device_type = NHLT_DEVICE_TYPE_PDM_CAVS18; + ep.direction = NHLT_ENDPOINT_DIRECTION_CAPTURE; + ep.virtualbus_id = 0; + + /* mic array config */ + mic_s_conf.config.capabilities_size = 3; + mic_s_conf.device_config.virtual_slot = 0; + mic_s_conf.device_config.config_type = NHLT_DEVICE_CONFIG_TYPE_MICARRAY; + mic_s_conf.array_type_ex = 0xA; + + /* formats_config */ + f_conf.formats_count = 1; + + /* format_config, how and where to get these */ + f_conf1.format.wFormatTag = 0; + f_conf1.format.nChannels = 0; + f_conf1.format.nSamplesPerSec = 0; + f_conf1.format.nAvgBytesPerSec = 0; + f_conf1.format.nBlockAlign = 0; + f_conf1.format.wBitsPerSample = 0; + f_conf1.format.cbSize = 0; + f_conf1.format.wValidBitsPerSample = 0; + f_conf1.format.dwChannelMask = 0; + f_conf1.format.SubFormat[4] = 0; /* is here some magic number ?*/ + f_conf1.vendor_blob.capabilities_size = dmic_nhlt_get_vendor_blob_size(tplg_pp); + + ep.length = sizeof(struct endpoint_descriptor) + + sizeof(struct mic_array_device_specific_config) + + sizeof(struct formats_config) + + sizeof(struct format_config) + + dmic_nhlt_get_vendor_blob_size(tplg_pp); + + tplg_pp_debug("dmic ep size %u", ep.length); + + tplg_pp_debug("dmic ep header size %u", + sizeof(struct endpoint_descriptor) + + sizeof(struct mic_array_device_specific_config) + + sizeof(struct formats_config) + + sizeof(struct format_config)); + + tplg_pp_debug("dmic vendor blob size %u", dmic_nhlt_get_vendor_blob_size(tplg_pp)); + + /* allocate the final variable length ep struct */ + ep_target = malloc(ep.length); + if (!ep_target) + return -ENOMEM; + + *eps = (struct endpoint_descriptor*)ep_target; + + /* copy all parsed sub arrays into the top level array */ + memcpy(ep_target, &ep, sizeof(struct endpoint_descriptor)); + + ep_target += sizeof(struct endpoint_descriptor); + + memcpy(ep_target, &mic_s_conf, sizeof(struct mic_array_device_specific_config)); + ep_target += sizeof(struct mic_array_device_specific_config); + + memcpy(ep_target, &f_conf, sizeof(struct formats_config)); + ep_target += sizeof(struct formats_config); + + memcpy(ep_target, &f_conf1, sizeof(struct format_config)); + ep_target += sizeof(struct format_config); + + dmic_nhlt_get_vendor_blob(tplg_pp, ep_target); + + return 0; +} + +static int add_ssp_ep(struct tplg_pre_processor *tplg_pp, struct endpoint_descriptor **eps) +{ + struct endpoint_descriptor ep; + struct ssp_device_specific_config ssp_conf; + struct formats_config f_conf; + struct format_config f_conf1; + uint8_t *ep_target; + + /* + * nhlt ssp structure: + * + * endpoint_descriptor, sizeof(struct endpoint_descriptor) + * device_specific_config (headset), sizeof(struct ssp_device_specific_config) + * formats_config (formats_count), sizeof(struct formats_config) + * format_config (waveex), sizeof(struct format_config) + * vendor_blob sizeof(vendor_blob) + */ + + /* ssp ep */ + ep.link_type = NHLT_LINK_TYPE_SSP; + ep.instance_id = 0; + ep.vendor_id = NHLT_VENDOR_ID_INTEL; + ep.device_id = NHLT_DEVICE_ID_INTEL_I2S_TDM; + ep.revision_id = 0; + ep.subsystem_id = 0; + ep.device_type = NHLT_DEVICE_TYPE_SSP_ANALOG; + ep.direction = NHLT_ENDPOINT_DIRECTION_RENDER; + /* ssp device index */ + ep.virtualbus_id = 0; + + /* ssp config */ + ssp_conf.config.capabilities_size = 2; + ssp_conf.device_config.virtual_slot = 0; + ssp_conf.device_config.config_type = 0; + + /* formats_config */ + f_conf.formats_count = 1; + + /* format_config, how and where to get these */ + f_conf1.format.wFormatTag = 0; + f_conf1.format.nChannels = 0; + f_conf1.format.nSamplesPerSec = 0; + f_conf1.format.nAvgBytesPerSec = 0; + f_conf1.format.nBlockAlign = 0; + f_conf1.format.wBitsPerSample = 0; + f_conf1.format.cbSize = 0; + f_conf1.format.wValidBitsPerSample = 0; + f_conf1.format.dwChannelMask = 0; + f_conf1.format.SubFormat[4] = 0; /* is here some magic number ?*/ + f_conf1.vendor_blob.capabilities_size = ssp_nhlt_get_vendor_blob_size(tplg_pp); + + ep.length = sizeof(struct endpoint_descriptor) + + sizeof(struct ssp_device_specific_config) + + sizeof(struct formats_config) + + sizeof(struct format_config) + + ssp_nhlt_get_vendor_blob_size(tplg_pp); + + tplg_pp_debug("ssp ep size %u", ep.length); + + tplg_pp_debug("ssp ep header size %u", + sizeof(struct endpoint_descriptor) + + sizeof(struct ssp_device_specific_config) + + sizeof(struct formats_config) + + sizeof(struct format_config)); + + tplg_pp_debug("ssp vendor blob size %u", ssp_nhlt_get_vendor_blob_size(tplg_pp)); + + /* allocate the final variable length ep struct */ + ep_target = malloc(ep.length); + if (!ep_target) + return -ENOMEM; + + *eps = (struct endpoint_descriptor*)ep_target; + + /* copy all parsed sub arrays into the top level array */ + memcpy(ep_target, &ep, sizeof(struct endpoint_descriptor)); + + ep_target += sizeof(struct endpoint_descriptor); + + memcpy(ep_target, &ssp_conf, sizeof(struct ssp_device_specific_config)); + ep_target += sizeof(struct ssp_device_specific_config); + + memcpy(ep_target, &f_conf, sizeof(struct formats_config)); + ep_target += sizeof(struct formats_config); + + memcpy(ep_target, &f_conf1, sizeof(struct format_config)); + ep_target += sizeof(struct format_config); + + ssp_nhlt_get_vendor_blob(tplg_pp, ep_target); + + return 0; +} + +static int add_bytes_to_data_section(struct tplg_pre_processor *tplg_pp, char *src) +{ + const char *data_name = "nhlt"; + snd_config_t *top_data_section; + snd_config_t *bytes; + snd_config_t *nhlt; + int ret; + + /* add the nhlt bytes as new config into existing SectionData*/ + ret = snd_config_search(tplg_pp->output_cfg, "SectionData", &top_data_section); + if (ret < 0) + return ret; + + ret = tplg_config_make_add(&nhlt, data_name, SND_CONFIG_TYPE_COMPOUND, top_data_section); + if (ret < 0) + return ret; + + ret = tplg_config_make_add(&bytes, "bytes", SND_CONFIG_TYPE_STRING, nhlt); + if (ret < 0) { + snd_config_delete(nhlt); + return ret; + } + + ret = snd_config_set_string(bytes, src); + if (ret < 0) { + snd_config_delete(nhlt); + snd_config_delete(bytes); + return ret; + } + + return 0; +} + +static int add_to_manifest(struct tplg_pre_processor *tplg_pp, char *src) +{ + const char *data_name = "nhlt"; + snd_config_t *manifest; + snd_config_t *man_inst; + snd_config_t *n; + snd_config_t *cfg_new; + snd_config_t *data; + snd_config_iterator_t i, next; + const char *id; + char *id_name; + int ret, id_num = 0; + + /* find manifest section and its named instance */ + ret = snd_config_search(tplg_pp->output_cfg, "SectionManifest", &manifest); + if (ret < 0) + return 0; + + snd_config_for_each(i, next, manifest) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (!strcmp(id, "sof_manifest")) + man_inst = n; + } + + /* if manifest not found, just exit */ + if (!man_inst) + return 0; + + ret = add_bytes_to_data_section(tplg_pp, src); + if (ret < 0) + return ret; + + /* add the reference to the bytes into manifest data */ + ret = snd_config_search(man_inst, "data", &data); + if (ret < 0) + return ret; + + snd_config_for_each(i, next, data) { + const char *name; + + n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &name) < 0) + continue; + + /* item already exists */ + if (!strcmp(name, data_name)) + return 0; + id_num++; + } + + /* add new item */ + id_name = tplg_snprintf("%d", id_num); + if (!id_name) + return -ENOMEM; + + ret = snd_config_make(&cfg_new, id_name, SND_CONFIG_TYPE_STRING); + free(id_name); + if (ret < 0) + return ret; + + ret = snd_config_set_string(cfg_new, data_name); + if (ret < 0) + return ret; + + ret = snd_config_add(data, cfg_new); + if (ret < 0) + snd_config_delete(cfg_new); + + return 0; +} + +static int print_nhlt_as_hex_bytes(struct nhlt *blob, struct endpoint_descriptor **eps, char *dst) +{ + uint8_t *top_p = (uint8_t *)blob; + struct endpoint_descriptor *ep; + uint8_t *ep_p; + int i, j; + + if (blob->efi_acpi.length > MAX_NHLT_BUFFER_SIZE) + return -ENOMEM; + + for (i = 0; i < sizeof(struct nhlt); i++) { + snprintf(dst, 6, "0x%02x,", *top_p); + dst += 5; + top_p++; + } + + for (i = 0; i < blob->endpoint_count; i++) { + ep = eps[i]; + ep_p = (uint8_t *)ep; + printf("ep %i length is %u\n", i, ep->length); + for (j = 0; j < ep->length; j++) { + snprintf(dst, 6, "0x%02x,", *ep_p); + dst += 5; + ep_p++; + } + } + + /* remove the last comma... */ + dst--; + *dst = '\0'; + + return 0; +} + +/* called at the beginning of topology per-processing */ +int init_nhlt_blob(struct tplg_pre_processor *tplg_pp) +{ + if (!tplg_pp) + return -EINVAL; + + tplg_pp->private_data = malloc(sizeof(struct intel_nhlt_params)); + + if (!tplg_pp->private_data) + return -ENOMEM; + + /* set default values here */ + dmic_nhlt_init_params(tplg_pp); + ssp_nhlt_init_params(tplg_pp); + + return 0; +} + +/* called at the end of topology per-processing */ +int create_nhlt_blob(struct tplg_pre_processor *tplg_pp) +{ + static char top_nhlt_string_buffer[MAX_NHLT_BUFFER_SIZE]; + struct endpoint_descriptor *eps[MAX_EPS_COUNT]; + int eps_count = 0; + struct nhlt blob; + size_t nhlt_size; + int i; + + add_dmic_ep(tplg_pp, &eps[eps_count]); + eps_count++; + + add_ssp_ep(tplg_pp, &eps[eps_count]); + eps_count++; + + blob.efi_acpi.signature = 0; + blob.efi_acpi.length = 0; + blob.efi_acpi.revision = 0; + blob.efi_acpi.checksum = 0; + blob.efi_acpi.oem_id[6] = 0; + blob.efi_acpi.oem_table_id = 0; + blob.efi_acpi.oem_revision = 0; + blob.efi_acpi.creator_id = 0; + blob.efi_acpi.creator_revision = 0; + + blob.endpoint_count = eps_count; + + /* get blob total size */ + nhlt_size = sizeof(struct nhlt); + for (i = 0; i < eps_count; i++) + nhlt_size += eps[i]->length; + + /* add the total length to top level struct */ + blob.efi_acpi.length = nhlt_size; + + tplg_pp_debug("create_nhlt_blob ep count %d total size %u", eps_count, nhlt_size); + + /* here print the whole thing in hex characters */ + print_nhlt_as_hex_bytes(&blob, eps, &top_nhlt_string_buffer[0]); + + add_to_manifest(tplg_pp, &top_nhlt_string_buffer[0]); + + /* remove all enpoints */ + for (i = 0; i < eps_count; i++) { + free(eps[i]); + eps[i] = NULL; + } + eps_count = 0; + + return 0; +} + +/* called from pre-processor deferred dai processing */ +int set_dai_nhlt(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg, snd_config_t *parent) +{ + const char *id; + + tplg_pp_debug("set_dai_nhlt"); + + if (snd_config_get_id(cfg, &id) < 0) + return 0; + + tplg_pp_debug("set_dai_nhlt for comp %s", id); + + if (!strncmp(id, "DMIC", 4)) { + tplg_pp_debug("set_dai_nhlt found DMIC"); + dmic_nhlt_set_params(tplg_pp, cfg, parent); + } + + if (!strncmp(id, "SSP", 3)) { + tplg_pp_debug("set_dai_nhlt found SSP"); + ssp_nhlt_set_params(tplg_pp, cfg, parent); + } + + return 0; +} diff --git a/topology/intel/nhlt.h b/topology/intel/nhlt.h new file mode 100644 index 000000000..532496508 --- /dev/null +++ b/topology/intel/nhlt.h @@ -0,0 +1,166 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __NHLT_H +#define __NHLT_H + +#include +#include "pre-processor.h" + +/* + * Nhlt defines and structs are derived from: + * https://01.org/sites/default/files/595976_intel_sst_nhlt.pdf + * + * Acpi description header for example: + * https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf + * + * Idea is to generate similar blob as you would get from: + * 'cat /sys/firmware/acpi/tables/NHLT' + * + */ +#define NHLT_LINK_TYPE_HDAUDIO 0 +#define NHLT_LINK_TYPE_DSP 1 +#define NHLT_LINK_TYPE_PDM 2 +#define NHLT_LINK_TYPE_SSP 3 +#define NHLT_LINK_TYPE_SLIMBUS 4 +#define NHLT_LINK_TYPE_SOUNDWIRE 5 + +#define NHLT_VENDOR_ID_INTEL 0x8086 + +#define NHLT_DEVICE_ID_INTEL_PDM_DMIC 0xAE20 +#define NHLT_DEVICE_ID_INTEL_BT_SIDEBAND 0xAE30 +#define NHLT_DEVICE_ID_INTEL_I2S_TDM 0xAE34 + +#define NHLT_DEVICE_TYPE_SSP_BT_SIDEBAND 0 +#define NHLT_DEVICE_TYPE_SSP_FM 1 +#define NHLT_DEVICE_TYPE_SSP_MODEM 2 +#define NHLT_DEVICE_TYPE_SSP_ANALOG 4 +#define NHLT_DEVICE_TYPE_PDM_CAVS18 0 +#define NHLT_DEVICE_TYPE_PDM_CAVS15 1 + +#define NHLT_ENDPOINT_DIRECTION_RENDER 0 +#define NHLT_ENDPOINT_DIRECTION_CAPTURE 1 +#define NHLT_ENDPOINT_DIRECTION_RENDER_WITH_LOOPBACK 2 +#define NHLT_ENDPOINT_DIRECTION_FEEDBACK_FOR_RENDER 3 + +#define NHLT_DEVICE_CONFIG_TYPE_GENERIC 0 +#define NHLT_DEVICE_CONFIG_TYPE_MICARRAY 1 +#define NHLT_DEVICE_CONFIG_TYPE_RENDERWITHLOOPBACK 2 +#define NHLT_DEVICE_CONFIG_TYPE_RENDERFEEDBACK 3 + +#define NHLT_MIC_ARRAY_TYPE_LINEAR_2_ELEMENT_SMALL 0xA +#define NHLT_MIC_ARRAY_TYPE_LINEAR_2_ELEMENT_BIG 0xB +#define NHLT_MIC_ARRAY_TYPE_LINEAR_4_ELEMENT_1ST_GEOMETRY 0xC +#define NHLT_MIC_ARRAY_TYPE_PLANAR_4_ELEMENT_L_SHAPED 0xD +#define NHLT_MIC_ARRAY_TYPE_PLANAR_4_ELEMENT_2ND_GEOMETRY 0xE +#define NHLT_MIC_ARRAY_TYPE_VENDOR_DEFINED 0xF + +#define NHLT_MIC_ARRAY_NO_EXTENSION 0x0 +#define NHLT_MIC_ARRAY_SNR_AND_SENSITIVITY_EXTENSION 0x1 + +struct specific_config +{ + uint32_t capabilities_size; /* does not include size of this field */ + uint8_t capabilities[0]; +} __attribute__((packed)); + +struct device_specific_config +{ + uint8_t virtual_slot; + uint8_t config_type; +} __attribute__((packed)); + +struct ssp_device_specific_config +{ + struct specific_config config; + struct device_specific_config device_config; +} __attribute__((packed)); + +struct mic_array_device_specific_config +{ + struct specific_config config; + struct device_specific_config device_config; + uint8_t array_type_ex; +} __attribute__((packed)); + +struct WAVEFORMATEXTENSIBLE { + uint16_t wFormatTag; + uint16_t nChannels; + uint32_t nSamplesPerSec; + uint32_t nAvgBytesPerSec; + uint16_t nBlockAlign; + uint16_t wBitsPerSample; + uint16_t cbSize; + uint16_t wValidBitsPerSample; + uint32_t dwChannelMask; + uint32_t SubFormat[4]; +} __attribute__((packed)); + +struct format_config +{ + struct WAVEFORMATEXTENSIBLE format; + struct specific_config vendor_blob; +} __attribute__((packed)); + +struct formats_config +{ + uint8_t formats_count; + uint8_t f_configs[0]; +} __attribute__((packed)); + +struct endpoint_descriptor +{ + uint32_t length; /* includes the length of this field also */ + uint8_t link_type; + uint8_t instance_id; + uint16_t vendor_id; + uint16_t device_id; + uint16_t revision_id; + uint32_t subsystem_id; + uint8_t device_type; + uint8_t direction; + uint8_t virtualbus_id; +} __attribute__((packed)); + +struct efi_acpi_description_header { + uint32_t signature; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint64_t oem_table_id; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +} __attribute__((packed)); + +struct nhlt +{ + struct efi_acpi_description_header efi_acpi; + uint8_t endpoint_count; + uint8_t endpoints[0]; +} __attribute__((packed)); + +int set_dai_nhlt(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg, snd_config_t *parent); +int init_nhlt_blob(struct tplg_pre_processor *tplg_pp); +int create_nhlt_blob(struct tplg_pre_processor *tplg_pp); + +#endif diff --git a/topology/intel/ssp/ssp-nhlt.c b/topology/intel/ssp/ssp-nhlt.c new file mode 100644 index 000000000..5e597920e --- /dev/null +++ b/topology/intel/ssp/ssp-nhlt.c @@ -0,0 +1,1062 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "../nhlt-internal.h" +#include "pre-processor.h" +#include "ssp-nhlt.h" +#include "ssp_macros.h" + +static int popcount(uint32_t value) +{ + int bits_set = 0; + + while (value) { + bits_set += value & 1; + value >>= 1; + } + + return bits_set; +} + +static int ssp_calculate(struct tplg_pre_processor *tplg_pp) +{ + struct intel_nhlt_params *nhlt; + bool inverted_frame = false; + bool inverted_bclk = false; + bool start_delay = false; + bool cfs = false; + uint32_t frame_end_padding; + uint32_t slot_end_padding; + uint32_t active_tx_slots; + uint32_t active_rx_slots; + uint32_t sample_width; + uint32_t frame_len = 0; + uint32_t data_size; + uint32_t bdiv_min; + uint32_t clk_div; + uint32_t bdiv; + uint32_t tft; + uint32_t rft; + int ret = 0; + int i; + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + /* should be eventually the lp_mode defined in pipeline */ + nhlt->ssp_blob.gateway_attributes = 0; + + for (i = 0; i < nhlt->ssp_prm.tdm_slots; i++) + nhlt->ssp_blob.ts_group[0] |= (i << (i * 4)); + for (; i < SSP_TDM_MAX_SLOT_MAP_COUNT; i++) + nhlt->ssp_blob.ts_group[0] |= (0xF << (i * 4)); + + /* reset SSP settings */ + /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ + nhlt->ssp_blob.ssc0 = SSCR0_PSP | SSCR0_RIM | SSCR0_TIM; + + /* sscr1 dynamic settings are SFRMDIR, SCLKDIR, SCFR */ + nhlt->ssp_blob.ssc1 = SSCR1_TTE | SSCR1_TTELP | SSCR1_TRAIL | SSCR1_RSRE | SSCR1_TSRE; + + /* sscr2 dynamic setting is LJDFD */ + nhlt->ssp_blob.ssc2 = SSCR2_SDFD | SSCR2_TURM1; + + /* sscr3 dynamic settings are TFT, RFT */ + nhlt->ssp_blob.ssc3 = 0; + + /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ + nhlt->ssp_blob.sspsp = 0; + + /* sspsp2 no dynamic setting */ + nhlt->ssp_blob.sspsp2 = 0x0; + + /* ssioc dynamic setting is SFCR */ + nhlt->ssp_blob.ssioc = SSIOC_SCOE; + + /* ssto no dynamic setting */ + nhlt->ssp_blob.sscto = 0x0; + + /* sstsa dynamic setting is TTSA, default 2 slots */ + nhlt->ssp_blob.sstsa = SSTSA_SSTSA(nhlt->ssp_prm.tx_slots); + + /* ssrsa dynamic setting is RTSA, default 2 slots */ + nhlt->ssp_blob.ssrsa = SSRSA_SSRSA(nhlt->ssp_prm.rx_slots); + + switch (nhlt->ssp_prm.format & SOF_DAI_FMT_CLOCK_PROVIDER_MASK) { + case SOF_DAI_FMT_CBP_CFP: + nhlt->ssp_blob.ssc1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; + break; + case SOF_DAI_FMT_CBC_CFC: + nhlt->ssp_blob.ssc1 |= SSCR1_SCFR; + cfs = true; + break; + case SOF_DAI_FMT_CBP_CFC: + nhlt->ssp_blob.ssc1 |= SSCR1_SCLKDIR; + /* FIXME: this mode has not been tested */ + + cfs = true; + break; + case SOF_DAI_FMT_CBC_CFP: + nhlt->ssp_blob.ssc1 |= SSCR1_SCFR | SSCR1_SFRMDIR; + /* FIXME: this mode has not been tested */ + break; + default: + tplg_pp_debug("ssp_calculate(): format & PROVIDER_MASK EINVAL"); + ret = -EINVAL; + goto out; + } + + /* clock signal polarity */ + switch (nhlt->ssp_prm.format & SOF_DAI_FMT_INV_MASK) { + case SOF_DAI_FMT_NB_NF: + break; + case SOF_DAI_FMT_NB_IF: + inverted_frame = true; /* handled later with format */ + break; + case SOF_DAI_FMT_IB_IF: + inverted_bclk = true; /* handled later with bclk idle */ + inverted_frame = true; /* handled later with format */ + break; + case SOF_DAI_FMT_IB_NF: + inverted_bclk = true; /* handled later with bclk idle */ + break; + default: + tplg_pp_debug("ssp_calcualte: format & INV_MASK EINVAL"); + ret = -EINVAL; + goto out; + } + + /* supporting bclk idle state */ + if (nhlt->ssp_prm.clks_control & + SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH) { + /* bclk idle state high */ + nhlt->ssp_blob.sspsp |= SSPSP_SCMODE((inverted_bclk ^ 0x3) & 0x3); + } else { + /* bclk idle state low */ + nhlt->ssp_blob.sspsp |= SSPSP_SCMODE(inverted_bclk); + } + + nhlt->ssp_blob.ssc0 |= SSCR0_MOD | SSCR0_ACS; + + /* Additional hardware settings */ + + /* Receiver Time-out Interrupt Disabled/Enabled */ + nhlt->ssp_blob.ssc1 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_TINTE) ? + SSCR1_TINTE : 0; + + /* Peripheral Trailing Byte Interrupts Disable/Enable */ + nhlt->ssp_blob.ssc1 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_PINTE) ? + SSCR1_PINTE : 0; + + /* Enable/disable internal loopback. Output of transmit serial + * shifter connected to input of receive serial shifter, internally. + */ + nhlt->ssp_blob.ssc1 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_LBM) ? + SSCR1_LBM : 0; + + /* Transmit data are driven at the same/opposite clock edge specified + * in SSPSP.SCMODE[1:0] + */ + nhlt->ssp_blob.ssc2 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_SMTATF) ? + SSCR2_SMTATF : 0; + + /* Receive data are sampled at the same/opposite clock edge specified + * in SSPSP.SCMODE[1:0] + */ + nhlt->ssp_blob.ssc2 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_MMRATF) ? + SSCR2_MMRATF : 0; + + /* Enable/disable the fix for PSP consumer mode TXD wait for frame + * de-assertion before starting the second channel + */ + nhlt->ssp_blob.ssc2 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD) ? + SSCR2_PSPSTWFDFD : 0; + + /* Enable/disable the fix for PSP provider mode FSRT with dummy stop & + * frame end padding capability + */ + nhlt->ssp_blob.ssc2 |= (nhlt->ssp_prm.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD) ? + SSCR2_PSPSRWFDFD : 0; + +#if 0 + if (!nhlt->ssp_prm.mclk_rate || + nhlt->ssp_prm.mclk_rate > ssp_freq[MAX_SSP_FREQ_INDEX].freq) { + tplg_pp_debug("ssp_calculate(): invalid MCLK = %d Hz (valid < %d)", + nhlt->ssp_prm.mclk_rate, + ssp_freq[MAX_SSP_FREQ_INDEX].freq); + ret = -EINVAL; + goto out; + } +#endif + if (!nhlt->ssp_prm.bclk_rate || + nhlt->ssp_prm.bclk_rate > nhlt->ssp_prm.mclk_rate) { + tplg_pp_debug("ssp_calculate(): BCLK %d Hz = 0 or > MCLK %d Hz", + nhlt->ssp_prm.bclk_rate, nhlt->ssp_prm.mclk_rate); + ret = -EINVAL; + goto out; + } + + /* calc frame width based on BCLK and rate - must be divisable */ + if (nhlt->ssp_prm.bclk_rate % nhlt->ssp_prm.fsync_rate) { + tplg_pp_debug("ssp_calculate(): BCLK %d is not divisable by rate %d", + nhlt->ssp_prm.bclk_rate, nhlt->ssp_prm.fsync_rate); + ret = -EINVAL; + goto out; + } + + /* must be enough BCLKs for data */ + bdiv = nhlt->ssp_prm.bclk_rate / nhlt->ssp_prm.fsync_rate; + if (bdiv < nhlt->ssp_prm.tdm_slot_width * nhlt->ssp_prm.tdm_slots) { + tplg_pp_debug("ssp_calculate(): not enough BCLKs need %d", + nhlt->ssp_prm.tdm_slot_width * + nhlt->ssp_prm.tdm_slots); + ret = -EINVAL; + goto out; + } + + /* tdm_slot_width must be <= 38 for SSP */ + if (nhlt->ssp_prm.tdm_slot_width > 38) { + tplg_pp_debug("ssp_calculate(): tdm_slot_width %d > 38", + nhlt->ssp_prm.tdm_slot_width); + ret = -EINVAL; + goto out; + } + + bdiv_min = nhlt->ssp_prm.tdm_slots * + (nhlt->ssp_prm.tdm_per_slot_padding_flag ? + nhlt->ssp_prm.tdm_slot_width : nhlt->ssp_prm.sample_valid_bits); + if (bdiv < bdiv_min) { + tplg_pp_debug("ssp_calculate(): bdiv(%d) < bdiv_min(%d)", + bdiv, bdiv_min); + ret = -EINVAL; + goto out; + } + + frame_end_padding = bdiv - bdiv_min; + if (frame_end_padding > SSPSP2_FEP_MASK) { + tplg_pp_debug("ssp_calculate(): frame_end_padding too big: %u", + frame_end_padding); + ret = -EINVAL; + goto out; + } + + /* format */ + switch (nhlt->ssp_prm.format & SOF_DAI_FMT_FORMAT_MASK) { + case SOF_DAI_FMT_I2S: + + start_delay = true; + + nhlt->ssp_blob.ssc0 |= SSCR0_FRDC(nhlt->ssp_prm.tdm_slots); + + if (bdiv % 2) { + tplg_pp_debug("ssp_calculate(): bdiv %d is not divisible by 2", + bdiv); + ret = -EINVAL; + goto out; + } + + /* set asserted frame length to half frame length */ + frame_len = bdiv / 2; + + /* + * handle frame polarity, I2S default is falling/active low, + * non-inverted(inverted_frame=0) -- active low(SFRMP=0), + * inverted(inverted_frame=1) -- rising/active high(SFRMP=1), + * so, we should set SFRMP to inverted_frame. + */ + nhlt->ssp_blob.sspsp |= SSPSP_SFRMP(inverted_frame); + + /* + * for I2S/LEFT_J, the padding has to happen at the end + * of each slot + */ + if (frame_end_padding % 2) { + tplg_pp_debug("ssp_calculate(): frame_end_padding %d is not divisible by 2", + frame_end_padding); + ret = -EINVAL; + goto out; + } + + slot_end_padding = frame_end_padding / 2; + + if (slot_end_padding > SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { + /* too big padding */ + tplg_pp_debug("ssp_calculate(): slot_end_padding > %d", + SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX); + ret = -EINVAL; + goto out; + } + + nhlt->ssp_blob.sspsp |= SSPSP_DMYSTOP(slot_end_padding); + slot_end_padding >>= SSPSP_DMYSTOP_BITS; + nhlt->ssp_blob.sspsp |= SSPSP_EDMYSTOP(slot_end_padding); + + break; + + case SOF_DAI_FMT_LEFT_J: + + /* default start_delay value is set to false */ + + nhlt->ssp_blob.ssc0 |= SSCR0_FRDC(nhlt->ssp_prm.tdm_slots); + + /* LJDFD enable */ + nhlt->ssp_blob.ssc2 &= ~SSCR2_LJDFD; + + if (bdiv % 2) { + tplg_pp_debug("ssp_calculate(): bdiv %d is not divisible by 2", + bdiv); + ret = -EINVAL; + goto out; + } + + /* set asserted frame length to half frame length */ + frame_len = bdiv / 2; + + /* + * handle frame polarity, LEFT_J default is rising/active high, + * non-inverted(inverted_frame=0) -- active high(SFRMP=1), + * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), + * so, we should set SFRMP to !inverted_frame. + */ + nhlt->ssp_blob.sspsp |= SSPSP_SFRMP(!inverted_frame); + + /* + * for I2S/LEFT_J, the padding has to happen at the end + * of each slot + */ + if (frame_end_padding % 2) { + tplg_pp_debug("ssp_set_config(): frame_end_padding %d is not divisible by 2", + frame_end_padding); + ret = -EINVAL; + goto out; + } + + slot_end_padding = frame_end_padding / 2; + + if (slot_end_padding > 15) { + /* can't handle padding over 15 bits */ + tplg_pp_debug("ssp_set_config(): slot_end_padding %d > 15 bits", + slot_end_padding); + ret = -EINVAL; + goto out; + } + + nhlt->ssp_blob.sspsp |= SSPSP_DMYSTOP(slot_end_padding); + slot_end_padding >>= SSPSP_DMYSTOP_BITS; + nhlt->ssp_blob.sspsp |= SSPSP_EDMYSTOP(slot_end_padding); + + break; + case SOF_DAI_FMT_DSP_A: + + start_delay = true; + + /* fallthrough */ + + case SOF_DAI_FMT_DSP_B: + + /* default start_delay value is set to false */ + + nhlt->ssp_blob.ssc0 |= SSCR0_MOD | SSCR0_FRDC(nhlt->ssp_prm.tdm_slots); + + /* set asserted frame length */ + frame_len = 1; /* default */ + + if (cfs && nhlt->ssp_prm.frame_pulse_width > 0 && + nhlt->ssp_prm.frame_pulse_width <= + SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { + frame_len = nhlt->ssp_prm.frame_pulse_width; + } + + /* frame_pulse_width must less or equal 38 */ + if (nhlt->ssp_prm.frame_pulse_width > + SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX) { + tplg_pp_debug("ssp_set_config(): frame_pulse_width > %d", + SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX); + ret = -EINVAL; + goto out; + } + /* + * handle frame polarity, DSP_B default is rising/active high, + * non-inverted(inverted_frame=0) -- active high(SFRMP=1), + * inverted(inverted_frame=1) -- falling/active low(SFRMP=0), + * so, we should set SFRMP to !inverted_frame. + */ + nhlt->ssp_blob.sspsp |= SSPSP_SFRMP(!inverted_frame); + + active_tx_slots = popcount(nhlt->ssp_prm.tx_slots); + active_rx_slots = popcount(nhlt->ssp_prm.rx_slots); + + /* + * handle TDM mode, TDM mode has padding at the end of + * each slot. The amount of padding is equal to result of + * subtracting slot width and valid bits per slot. + */ + if (nhlt->ssp_prm.tdm_per_slot_padding_flag) { + frame_end_padding = bdiv - nhlt->ssp_prm.tdm_slots * + nhlt->ssp_prm.tdm_slot_width; + + slot_end_padding = nhlt->ssp_prm.tdm_slot_width - + nhlt->ssp_prm.sample_valid_bits; + + if (slot_end_padding > + SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { + tplg_pp_debug("ssp_set_config(): slot_end_padding > %d", + SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX); + ret = -EINVAL; + goto out; + } + + nhlt->ssp_blob.sspsp |= SSPSP_DMYSTOP(slot_end_padding); + slot_end_padding >>= SSPSP_DMYSTOP_BITS; + nhlt->ssp_blob.sspsp |= SSPSP_EDMYSTOP(slot_end_padding); + } + + nhlt->ssp_blob.sspsp2 |= (frame_end_padding & SSPSP2_FEP_MASK); + + break; + default: + tplg_pp_debug("ssp_set_config(): invalid format 0x%04x", + nhlt->ssp_prm.format); + ret = -EINVAL; + goto out; + } + + if (start_delay) + nhlt->ssp_blob.sspsp |= SSPSP_FSRT; + + nhlt->ssp_blob.sspsp |= SSPSP_SFRMWDTH(frame_len); + + data_size = nhlt->ssp_prm.sample_valid_bits; + + if (data_size > 16) + nhlt->ssp_blob.ssc0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); + else + nhlt->ssp_blob.ssc0 |= SSCR0_DSIZE(data_size); + + /* setting TFT and RFT */ + switch (nhlt->ssp_prm.sample_valid_bits) { + case 16: + /* use 2 bytes for each slot */ + sample_width = 2; + break; + case 24: + case 32: + /* use 4 bytes for each slot */ + sample_width = 4; + break; + default: + tplg_pp_debug("ssp_set_config(): sample_valid_bits %d", + nhlt->ssp_prm.sample_valid_bits); + ret = -EINVAL; + goto out; + } + + tft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, + sample_width * active_tx_slots); + rft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, + sample_width * active_rx_slots); + + nhlt->ssp_blob.ssc3 |= SSCR3_TX(tft) | SSCR3_RX(rft); + + clk_div = nhlt->ssp_prm.io_clk / nhlt->ssp_prm.mclk_rate; + if (clk_div > 1) + clk_div -= 2; + else + clk_div = 0xFFF; /* bypass clk divider */ + + /* use clock source 0 */ + nhlt->ssp_blob.mdivc = BIT(0); + nhlt->ssp_blob.mdivr = clk_div; + + tplg_pp_debug("ssp_calculate(), ssc0 = 0x%08x, ssc1 = 0x%08x, sscto = 0x%08x, sspsp = 0x%0x", + nhlt->ssp_blob.ssc0, nhlt->ssp_blob.ssc1, nhlt->ssp_blob.sscto, + nhlt->ssp_blob.sspsp); + tplg_pp_debug("ssp_calcualte(), ssc2 = 0x%08x, sspsp2 = 0x%08x, ssc3 = 0x%08x, ssioc = 0x%08x", + nhlt->ssp_blob.ssc2, nhlt->ssp_blob.sspsp2, nhlt->ssp_blob.ssc3, + nhlt->ssp_blob.ssioc); + tplg_pp_debug("ssp_calculate(), ssrsa = 0x%08x, sstsa = 0x%08x", + nhlt->ssp_blob.ssrsa, nhlt->ssp_blob.sstsa); + +out: + return ret; +} + +int ssp_nhlt_init_params(struct tplg_pre_processor *tplg_pp) +{ + return 0; +} + +int ssp_nhlt_get_vendor_blob_size(struct tplg_pre_processor *tplg_pp) +{ + return sizeof(struct ssp_intel_config_data); +} + +void ssp_nhlt_get_vendor_blob(struct tplg_pre_processor *tplg_pp, uint8_t *vendor_blob) +{ + struct intel_nhlt_params *nhlt; + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return; + + /* top level struct */ + memcpy(vendor_blob, &nhlt->ssp_blob, sizeof(struct ssp_intel_config_data)); +} + +static int set_ssp_data(struct tplg_pre_processor *tplg_pp, snd_config_t *dai_cfg) +{ + struct intel_nhlt_params *nhlt; + snd_config_iterator_t i, next; + snd_config_t *class_cfg; + snd_config_t *n; + const char *id; + const char *direction = NULL; + const char *quirks = NULL; + long int_val = 0; + int bclk_delay = 0; + int mclk_id = 0; + int default_hw_config_id = 0; + int clks_control = 0; + int frame_pulse_width = 0; + int sample_bits = 0; + const char *tdm_padding_per_slot; + int ret; + + tplg_pp_debug("set_ssp_data from topology"); + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + /* get default values from class definition */ + ret = snd_config_search(tplg_pp->input_cfg, "Class.Dai.SSP", &class_cfg); + if (ret < 0) + return -EINVAL; + + snd_config_for_each(i, next, class_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) { + tplg_pp_debug("set_ssp_data no id found"); + continue; + } + + tplg_pp_debug("set_ssp_data default id %s", id); + + if (!strcmp(id, "direction")) { + if (snd_config_get_string(n, &direction)) + return -EINVAL; + } + + if (!strcmp(id, "quirks")) { + if (snd_config_get_string(n, &quirks)) + return -EINVAL; + } + + if (!strcmp(id, "sample_bits")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + sample_bits = int_val; + } + + if (!strcmp(id, "bclk_delay")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + bclk_delay = int_val; + } + + if (!strcmp(id, "mclk_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mclk_id = int_val; + } + + if (!strcmp(id, "default_hw_config_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + default_hw_config_id = int_val; + } + + if (!strcmp(id, "clks_control")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clks_control = int_val; + } + + if (!strcmp(id, "frame_pulse_width")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + frame_pulse_width = int_val; + } + + if (!strcmp(id, "tdm_padding_per_slot")) { + if (snd_config_get_string(n, &tdm_padding_per_slot)) + return -EINVAL; + } + } + + /* check default values? */ + if (direction) + tplg_pp_debug("default direction: %s", direction); + if (quirks) + tplg_pp_debug("default quirks: %s", quirks); + tplg_pp_debug("default sample_bits: %d", sample_bits); + tplg_pp_debug("default bclk_delay: %d", bclk_delay); + tplg_pp_debug("default mclk_id: %d", mclk_id); + tplg_pp_debug("default default_hw_config_id: %d", default_hw_config_id); + tplg_pp_debug("default clks_control: %d", clks_control); + tplg_pp_debug("default frame_pulse_width: %d", frame_pulse_width); + tplg_pp_debug("default tdm_padding_per_slot: %s", tdm_padding_per_slot); + tplg_pp_debug(""); + + /* set instance soecific values */ + snd_config_for_each(i, next, dai_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) { + tplg_pp_debug("set_ssp_data no id found"); + continue; + } + + tplg_pp_debug("set_ssp_data id %s", id); + + if (!strcmp(id, "direction")) { + if (snd_config_get_string(n, &direction)) + return -EINVAL; + } + + if (!strcmp(id, "quirks")) { + if (snd_config_get_string(n, &quirks)) + return -EINVAL; + } + + if (!strcmp(id, "sample_bits")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + sample_bits = int_val; + } + + if (!strcmp(id, "bclk_delay")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + bclk_delay = int_val; + } + + if (!strcmp(id, "mclk_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mclk_id = int_val; + } + + if (!strcmp(id, "default_hw_config_id")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + default_hw_config_id = int_val; + } + + if (!strcmp(id, "clks_control")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + clks_control = int_val; + } + + if (!strcmp(id, "frame_pulse_width")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + frame_pulse_width = int_val; + } + + if (!strcmp(id, "tdm_padding_per_slot")) { + if (snd_config_get_string(n, &tdm_padding_per_slot)) + return -EINVAL; + } + } + + if (direction) + tplg_pp_debug("direction: %s", direction); + if (quirks) + tplg_pp_debug("quirks: %s", quirks); + tplg_pp_debug("sample_bits: %d", sample_bits); + tplg_pp_debug("bclk_delay: %d", bclk_delay); + tplg_pp_debug("mclk_id: %d", mclk_id); + tplg_pp_debug("default_hw_config_id: %d", default_hw_config_id); + tplg_pp_debug("clks_control: %d", clks_control); + tplg_pp_debug("frame_pulse_width: %d", frame_pulse_width); + tplg_pp_debug("tdm_padding_per_slot: %s", tdm_padding_per_slot); + tplg_pp_debug(""); + + nhlt->ssp_prm.bclk_delay = bclk_delay; + nhlt->ssp_prm.sample_valid_bits = sample_bits; + nhlt->ssp_prm.mclk_id = mclk_id; + nhlt->ssp_prm.clks_control = clks_control; + nhlt->ssp_prm.frame_pulse_width = frame_pulse_width; + if (!strcmp(tdm_padding_per_slot, "true")) + nhlt->ssp_prm.tdm_per_slot_padding_flag = 1; + else + nhlt->ssp_prm.tdm_per_slot_padding_flag = 0; + + return 0; +} + +static int set_hw_config(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg) +{ + struct intel_nhlt_params *nhlt; + snd_config_iterator_t i, next;; + snd_config_t *class_cfg; + snd_config_t *n; + const char *id; + long int_val = 0; + int ret; + const char *format = NULL; + const char *mclk = NULL; + const char *bclk = NULL; + const char *bclk_invert = NULL; + const char *fsync = NULL; + const char *fsync_invert = NULL; + int mclk_freq; + int bclk_freq; + int fsync_freq; + int tdm_slots; + int tdm_slot_width; + int tx_slots; + int rx_slots; + + tplg_pp_debug("set_hw_config from topology"); + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -EINVAL; + + /* get default values */ + ret = snd_config_search(tplg_pp->input_cfg, "Class.Base.hw_config", &class_cfg); + if (ret < 0) + return -EINVAL; + + /* set default values */ + snd_config_for_each(i, next, class_cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + if (!strcmp(id, "format")) { + if (snd_config_get_string(n, &format)) + return -EINVAL; + } + + if (!strcmp(id, "mclk")) { + if (snd_config_get_string(n, &mclk)) + return -EINVAL; + } + + if (!strcmp(id, "bclk")) { + if (snd_config_get_string(n, &bclk)) + return -EINVAL; + } + + if (!strcmp(id, "fsync")) { + if (snd_config_get_string(n, &fsync)) + return -EINVAL; + } + + if (!strcmp(id, "bclk_invert")) { + if (snd_config_get_string(n, &bclk_invert)) + return -EINVAL; + } + + if (!strcmp(id, "fsync_invert")) { + if (snd_config_get_string(n, &fsync_invert)) + return -EINVAL; + } + + if (!strcmp(id, "fsync_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + fsync_freq = int_val; + } + + if (!strcmp(id, "bclk_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + bclk_freq = int_val; + } + + if (!strcmp(id, "mclk_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mclk_freq = int_val; + } + + if (!strcmp(id, "tdm_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tdm_slots = int_val; + } + + if (!strcmp(id, "tdm_slot_width")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tdm_slot_width = int_val; + } + + if (!strcmp(id, "tx_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tx_slots = int_val; + } + + if (!strcmp(id, "rx_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + rx_slots = int_val; + } + } + + /* set object values */ + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + tplg_pp_debug("set_hw_config id %s", id); + + if (!strcmp(id, "format")) { + if (snd_config_get_string(n, &format)) + return -EINVAL; + } + + if (!strcmp(id, "mclk")) { + if (snd_config_get_string(n, &mclk)) + return -EINVAL; + } + + if (!strcmp(id, "bclk")) { + if (snd_config_get_string(n, &bclk)) + return -EINVAL; + } + + if (!strcmp(id, "fsync")) { + if (snd_config_get_string(n, &fsync)) + return -EINVAL; + } + + if (!strcmp(id, "bclk_invert")) { + if (snd_config_get_string(n, &bclk_invert)) + return -EINVAL; + } + + if (!strcmp(id, "fsync_invert")) { + if (snd_config_get_string(n, &fsync_invert)) + return -EINVAL; + } + + if (!strcmp(id, "fsync_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + fsync_freq = int_val; + } + + if (!strcmp(id, "bclk_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + bclk_freq = int_val; + } + + if (!strcmp(id, "mclk_freq")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + mclk_freq = int_val; + } + + if (!strcmp(id, "tdm_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tdm_slots = int_val; + } + + if (!strcmp(id, "tdm_slot_width")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tdm_slot_width = int_val; + } + + if (!strcmp(id, "tx_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + tx_slots = int_val; + } + + if (!strcmp(id, "rx_slots")) { + if (snd_config_get_integer(n, &int_val)) + return -EINVAL; + rx_slots = int_val; + } + } + + /* check that the strings are defined*/ + + tplg_pp_debug("hw_config format: %s", format); + tplg_pp_debug("hw_config mclk: %s", mclk); + tplg_pp_debug("hw_config mclk_freq: %ld", mclk_freq); + tplg_pp_debug("hw_config bclk: %s", bclk); + tplg_pp_debug("hw_config bclk_freq: %ld", bclk_freq); + tplg_pp_debug("hw_config fsync: %s", fsync); + tplg_pp_debug("hw_config fsync_freq: %ld", fsync_freq); + tplg_pp_debug("hw_config tdm_slots: %ld", tdm_slots); + tplg_pp_debug("hw_config tdm_slot_width: %ld", tdm_slot_width); + tplg_pp_debug("hw_config tx_slots: %ld", tx_slots); + tplg_pp_debug("hw_config rx_slots: %ld", rx_slots); + tplg_pp_debug(""); + + /* compose format out of clock related string variables */ + + if (!strcmp(format, "I2S")) + nhlt->ssp_prm.format = SOF_DAI_FMT_I2S; + else if (!strcmp(format, "RIGHT_J")) + nhlt->ssp_prm.format = SOF_DAI_FMT_RIGHT_J; + else if (!strcmp(format, "LEFT_J")) + nhlt->ssp_prm.format = SOF_DAI_FMT_LEFT_J; + else if (!strcmp(format, "DSP_A")) + nhlt->ssp_prm.format = SOF_DAI_FMT_DSP_A; + else if (!strcmp(format, "DSP_B")) + nhlt->ssp_prm.format = SOF_DAI_FMT_DSP_B; + else + tplg_pp_debug("no valid format specifide for ssp: %s", format); + + /* clock directions wrt codec */ + if (bclk && !strcmp(bclk, "coded_provider")) { + /* codec is bclk provider */ + if (fsync && !strcmp(fsync, "coded_provider")) + nhlt->ssp_prm.format |= SOF_DAI_FMT_CBP_CFP; + else + nhlt->ssp_prm.format |= SOF_DAI_FMT_CBP_CFC; + } else { + /* codec is bclk consumer */ + if (fsync && !strcmp(fsync, "coded_provider")) + nhlt->ssp_prm.format |= SOF_DAI_FMT_CBC_CFP; + else + nhlt->ssp_prm.format |= SOF_DAI_FMT_CBC_CFC; + } + + if (!bclk_invert) + tplg_pp_debug("bclk_invert is NULL"); + else + tplg_pp_debug("bclk_invert is not NULL"); + + /* inverted clocks ? */ + if (bclk_invert && !strcmp(bclk_invert, "true")) { + if (fsync_invert && !strcmp(fsync_invert, "true")) + nhlt->ssp_prm.format |= SOF_DAI_FMT_IB_IF; + else + nhlt->ssp_prm.format |= SOF_DAI_FMT_IB_NF; + } else { + if (fsync_invert && !strcmp(fsync_invert, "true")) + nhlt->ssp_prm.format |= SOF_DAI_FMT_NB_IF; + else + nhlt->ssp_prm.format |= SOF_DAI_FMT_NB_NF; + } + + nhlt->ssp_prm.mclk_rate = mclk_freq; + nhlt->ssp_prm.bclk_rate = bclk_freq; + nhlt->ssp_prm.fsync_rate = fsync_freq; + nhlt->ssp_prm.tdm_slots = tdm_slots; + nhlt->ssp_prm.tdm_slot_width = tdm_slot_width; + nhlt->ssp_prm.tx_slots = tx_slots; + nhlt->ssp_prm.rx_slots = rx_slots; + + return 0; +} + +int ssp_nhlt_set_params(struct tplg_pre_processor *tplg_pp, + snd_config_t *cfg, snd_config_t *parent) +{ + struct intel_nhlt_params *nhlt; + snd_config_t *items; + int ret; + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + + tplg_pp_debug("set_ssp_vendor_blob from topology"); + + set_ssp_data(tplg_pp, cfg); + + ret = snd_config_search(cfg, "Object.Base.hw_config", &items); + if (ret < 0) + return 0; + + snd_config_for_each(i, next, items) { + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + tplg_pp_debug("set_hw_config for id: '%s' ...", id); + + set_hw_config(tplg_pp, n); + } + + ssp_calculate(tplg_pp); + + nhlt = (struct intel_nhlt_params *)tplg_pp->private_data; + + if (!nhlt) + return -1; + + tplg_pp_debug("io_clk %u", nhlt->ssp_prm.io_clk); + tplg_pp_debug("mclk_id %u", nhlt->ssp_prm.mclk_id); + tplg_pp_debug("mclk_rate %u", nhlt->ssp_prm.mclk_rate); + tplg_pp_debug("fsync_rate %u", nhlt->ssp_prm.fsync_rate); + tplg_pp_debug("bclk_rate %u", nhlt->ssp_prm.bclk_rate); + tplg_pp_debug("tdm_slots %u", nhlt->ssp_prm.tdm_slots); + tplg_pp_debug("rx_slots %u", nhlt->ssp_prm.rx_slots); + tplg_pp_debug("tx_slots %u", nhlt->ssp_prm.tx_slots); + tplg_pp_debug("sample_valid_bits %u", nhlt->ssp_prm.sample_valid_bits); + tplg_pp_debug("tdm_slot_width %u", nhlt->ssp_prm.tdm_slot_width); + tplg_pp_debug("mclk_direction %u", nhlt->ssp_prm.mclk_direction); + tplg_pp_debug("frame_pulse_width %u", nhlt->ssp_prm.frame_pulse_width); + tplg_pp_debug("tdm_per_slot_padding_flag %u", nhlt->ssp_prm.tdm_per_slot_padding_flag); + tplg_pp_debug("clks_control %u", nhlt->ssp_prm.clks_control); + tplg_pp_debug("quirks %u", nhlt->ssp_prm.quirks); + tplg_pp_debug("bclk_delay %u", nhlt->ssp_prm.bclk_delay); + tplg_pp_debug("format %u", nhlt->ssp_prm.format); + + + return 0; +} diff --git a/topology/intel/ssp/ssp-nhlt.h b/topology/intel/ssp/ssp-nhlt.h new file mode 100644 index 000000000..57eb5efff --- /dev/null +++ b/topology/intel/ssp/ssp-nhlt.h @@ -0,0 +1,32 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#ifndef __SSP_NHLT_H +#define __SSP_NHLT_H + +#include + +int ssp_nhlt_init_params(struct tplg_pre_processor *tplg_pp); +int ssp_nhlt_set_params(struct tplg_pre_processor *tplg_pp, snd_config_t *cfg, + snd_config_t *parent); +int ssp_nhlt_get_vendor_blob_size(struct tplg_pre_processor *tplg_pp); +void ssp_nhlt_get_vendor_blob(struct tplg_pre_processor *tplg_pp, uint8_t *vendor_blob); + +#endif diff --git a/topology/intel/ssp/ssp_macros.h b/topology/intel/ssp/ssp_macros.h new file mode 100644 index 000000000..f21693f8d --- /dev/null +++ b/topology/intel/ssp/ssp_macros.h @@ -0,0 +1,234 @@ +/* + Copyright(c) 2021 Intel Corporation + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. +*/ + +#define MIN(a, b) ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + __a > __b ? __b : __a; \ +}) + +#define BIT(b) (1UL << (b)) + +#define MASK(b_hi, b_lo) \ + (((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo)) +#define SET_BIT(b, x) (((x) & 1) << (b)) +#define SET_BITS(b_hi, b_lo, x) \ + (((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo)) + + +#define SOF_DAI_FMT_I2S 1 /**< I2S mode */ +#define SOF_DAI_FMT_RIGHT_J 2 /**< Right Justified mode */ +#define SOF_DAI_FMT_LEFT_J 3 /**< Left Justified mode */ +#define SOF_DAI_FMT_DSP_A 4 /**< L data MSB after FRM LRC */ +#define SOF_DAI_FMT_DSP_B 5 /**< L data MSB during FRM LRC */ +#define SOF_DAI_FMT_PDM 6 /**< Pulse density modulation */ + +#define SOF_DAI_FMT_CONT (1 << 4) /**< continuous clock */ +#define SOF_DAI_FMT_GATED (0 << 4) /**< clock is gated */ + +#define SOF_DAI_FMT_NB_NF (0 << 8) /**< normal bit clock + frame */ +#define SOF_DAI_FMT_NB_IF (2 << 8) /**< normal BCLK + inv FRM */ +#define SOF_DAI_FMT_IB_NF (3 << 8) /**< invert BCLK + nor FRM */ +#define SOF_DAI_FMT_IB_IF (4 << 8) /**< invert BCLK + FRM */ + +#define SOF_DAI_FMT_CBP_CFP (0 << 12) /**< codec bclk provider & frame provider */ +#define SOF_DAI_FMT_CBC_CFP (2 << 12) /**< codec bclk consumer & frame provider */ +#define SOF_DAI_FMT_CBP_CFC (3 << 12) /**< codec bclk provider & frame consumer */ +#define SOF_DAI_FMT_CBC_CFC (4 << 12) /**< codec bclk consumer & frame consumer */ + +#define SOF_DAI_FMT_FORMAT_MASK 0x000f +#define SOF_DAI_FMT_CLOCK_MASK 0x00f0 +#define SOF_DAI_FMT_INV_MASK 0x0f00 +#define SOF_DAI_FMT_CLOCK_PROVIDER_MASK 0xf000 + +/* SSCR0 bits */ +#define SSCR0_DSIZE(x) SET_BITS(3, 0, (x) - 1) +#define SSCR0_FRF MASK(5, 4) +#define SSCR0_MOT SET_BITS(5, 4, 0) +#define SSCR0_TI SET_BITS(5, 4, 1) +#define SSCR0_NAT SET_BITS(5, 4, 2) +#define SSCR0_PSP SET_BITS(5, 4, 3) +#define SSCR0_ECS BIT(6) +#define SSCR0_SSE BIT(7) +#define SSCR0_SCR_MASK MASK(19, 8) +#define SSCR0_SCR(x) SET_BITS(19, 8, x) +#define SSCR0_EDSS BIT(20) +#define SSCR0_NCS BIT(21) +#define SSCR0_RIM BIT(22) +#define SSCR0_TIM BIT(23) +#define SSCR0_FRDC(x) SET_BITS(26, 24, (x) - 1) +#define SSCR0_ACS BIT(30) +#define SSCR0_MOD BIT(31) + +/* SSCR1 bits */ +#define SSCR1_RIE BIT(0) +#define SSCR1_TIE BIT(1) +#define SSCR1_LBM BIT(2) +#define SSCR1_SPO BIT(3) +#define SSCR1_SPH BIT(4) +#define SSCR1_MWDS BIT(5) +#define SSCR1_TFT_MASK MASK(9, 6) +#define SSCR1_TFT(x) SET_BITS(9, 6, (x) - 1) +#define SSCR1_RFT_MASK MASK(13, 10) +#define SSCR1_RFT(x) SET_BITS(13, 10, (x) - 1) +#define SSCR1_EFWR BIT(14) +#define SSCR1_STRF BIT(15) +#define SSCR1_IFS BIT(16) +#define SSCR1_PINTE BIT(18) +#define SSCR1_TINTE BIT(19) +#define SSCR1_RSRE BIT(20) +#define SSCR1_TSRE BIT(21) +#define SSCR1_TRAIL BIT(22) +#define SSCR1_RWOT BIT(23) +#define SSCR1_SFRMDIR BIT(24) +#define SSCR1_SCLKDIR BIT(25) +#define SSCR1_ECRB BIT(26) +#define SSCR1_ECRA BIT(27) +#define SSCR1_SCFR BIT(28) +#define SSCR1_EBCEI BIT(29) +#define SSCR1_TTE BIT(30) +#define SSCR1_TTELP BIT(31) + +/* SSCR2 bits */ +#define SSCR2_URUN_FIX0 BIT(0) +#define SSCR2_URUN_FIX1 BIT(1) +#define SSCR2_SLV_EXT_CLK_RUN_EN BIT(2) +#define SSCR2_CLK_DEL_EN BIT(3) +#define SSCR2_UNDRN_FIX_EN BIT(6) +#define SSCR2_FIFO_EMPTY_FIX_EN BIT(7) +#define SSCR2_ASRC_CNTR_EN BIT(8) +#define SSCR2_ASRC_CNTR_CLR BIT(9) +#define SSCR2_ASRC_FRM_CNRT_EN BIT(10) +#define SSCR2_ASRC_INTR_MASK BIT(11) +#define SSCR2_TURM1 BIT(1) +#define SSCR2_PSPSRWFDFD BIT(3) +#define SSCR2_PSPSTWFDFD BIT(4) +#define SSCR2_SDFD BIT(14) +#define SSCR2_SDPM BIT(16) +#define SSCR2_LJDFD BIT(17) +#define SSCR2_MMRATF BIT(18) +#define SSCR2_SMTATF BIT(19) + +/* SSR bits */ +#define SSSR_TNF BIT(2) +#define SSSR_RNE BIT(3) +#define SSSR_BSY BIT(4) +#define SSSR_TFS BIT(5) +#define SSSR_RFS BIT(6) +#define SSSR_ROR BIT(7) +#define SSSR_TUR BIT(21) + +/* SSPSP bits */ +#define SSPSP_SCMODE(x) SET_BITS(1, 0, x) +#define SSPSP_SFRMP(x) SET_BIT(2, x) +#define SSPSP_ETDS BIT(3) +#define SSPSP_STRTDLY(x) SET_BITS(6, 4, x) +#define SSPSP_DMYSTRT(x) SET_BITS(8, 7, x) +#define SSPSP_SFRMDLY(x) SET_BITS(15, 9, x) +#define SSPSP_SFRMWDTH(x) SET_BITS(21, 16, x) +#define SSPSP_DMYSTOP(x) SET_BITS(24, 23, x) +#define SSPSP_DMYSTOP_BITS 2 +#define SSPSP_DMYSTOP_MASK MASK(SSPSP_DMYSTOP_BITS - 1, 0) +#define SSPSP_FSRT BIT(25) +#define SSPSP_EDMYSTOP(x) SET_BITS(28, 26, x) + +#define SSPSP2 0x44 +#define SSPSP2_FEP_MASK 0xff + +#if CONFIG_CAVS +#define SSCR3 0x48 +#define SSIOC 0x4C + +#define SSP_REG_MAX SSIOC +#endif + +/* SSTSA bits */ +#define SSTSA_SSTSA(x) SET_BITS(7, 0, x) +#define SSTSA_TXEN BIT(8) + +/* SSRSA bits */ +#define SSRSA_SSRSA(x) SET_BITS(7, 0, x) +#define SSRSA_RXEN BIT(8) + +/* SSCR3 bits */ +#define SSCR3_FRM_MST_EN BIT(0) +#define SSCR3_I2S_MODE_EN BIT(1) +#define SSCR3_I2S_FRM_POL(x) SET_BIT(2, x) +#define SSCR3_I2S_TX_SS_FIX_EN BIT(3) +#define SSCR3_I2S_RX_SS_FIX_EN BIT(4) +#define SSCR3_I2S_TX_EN BIT(9) +#define SSCR3_I2S_RX_EN BIT(10) +#define SSCR3_CLK_EDGE_SEL BIT(12) +#define SSCR3_STRETCH_TX BIT(14) +#define SSCR3_STRETCH_RX BIT(15) +#define SSCR3_MST_CLK_EN BIT(16) +#define SSCR3_SYN_FIX_EN BIT(17) + +/* SSCR4 bits */ +#define SSCR4_TOT_FRM_PRD(x) ((x) << 7) + +/* SSCR5 bits */ +#define SSCR5_FRM_ASRT_CLOCKS(x) (((x) - 1) << 1) +#define SSCR5_FRM_POLARITY(x) SET_BIT(0, x) + +/* SFIFOTT bits */ +#define SFIFOTT_TX(x) ((x) - 1) +#define SFIFOTT_RX(x) (((x) - 1) << 16) + +/* SFIFOL bits */ +#define SFIFOL_TFL(x) ((x) & 0xFFFF) +#define SFIFOL_RFL(x) ((x) >> 16) + +#define SSTSA_TSEN BIT(8) +#define SSRSA_RSEN BIT(8) + +#define SSCR3_TFL_MASK MASK(5, 0) +#define SSCR3_RFL_MASK MASK(13, 8) +#define SSCR3_TFL_VAL(scr3_val) (((scr3_val) >> 0) & MASK(5, 0)) +#define SSCR3_RFL_VAL(scr3_val) (((scr3_val) >> 8) & MASK(5, 0)) +#define SSCR3_TX(x) SET_BITS(21, 16, (x) - 1) +#define SSCR3_RX(x) SET_BITS(29, 24, (x) - 1) + +#define SSIOC_TXDPDEB BIT(1) +#define SSIOC_SFCR BIT(4) +#define SSIOC_SCOE BIT(5) + +#define MAX_SSP_COUNT 8 +#define SSP_FIFO_DEPTH 16 +#define SSP_FIFO_WATERMARK 8 + +#define SOF_DAI_INTEL_SSP_QUIRK_TINTE (1 << 0) +#define SOF_DAI_INTEL_SSP_QUIRK_PINTE (1 << 1) +#define SOF_DAI_INTEL_SSP_QUIRK_SMTATF (1 << 2) +#define SOF_DAI_INTEL_SSP_QUIRK_MMRATF (1 << 3) +#define SOF_DAI_INTEL_SSP_QUIRK_PSPSTWFDFD (1 << 4) +#define SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD (1 << 5) +#define SOF_DAI_INTEL_SSP_QUIRK_LBM (1 << 6) + +#define SOF_DAI_INTEL_SSP_FRAME_PULSE_WIDTH_MAX 38 +#define SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX 31 + +/* SSP clocks control settings */ +#define SOF_DAI_INTEL_SSP_MCLK_0_DISABLE BIT(0) +#define SOF_DAI_INTEL_SSP_MCLK_1_DISABLE BIT(1) +#define SOF_DAI_INTEL_SSP_CLKCTRL_MCLK_KA BIT(2) +#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_KA BIT(3) +#define SOF_DAI_INTEL_SSP_CLKCTRL_FS_KA BIT(4) +#define SOF_DAI_INTEL_SSP_CLKCTRL_BCLK_IDLE_HIGH BIT(5) diff --git a/topology/pre-process-object.c b/topology/pre-process-object.c index 89cdad0b9..54d6b9867 100644 --- a/topology/pre-process-object.c +++ b/topology/pre-process-object.c @@ -29,6 +29,7 @@ #include "gettext.h" #include "topology.h" #include "pre-processor.h" +#include "intel/nhlt.h" /* Parse VendorToken object, create the "SectionVendorToken" and save it */ int tplg_build_vendor_token_object(struct tplg_pre_processor *tplg_pp, @@ -937,6 +938,23 @@ static int tplg_build_generic_object(struct tplg_pre_processor *tplg_pp, snd_con return ret; } +static int tplg_update_dai_auto_attr(struct tplg_pre_processor *tplg_pp, + snd_config_t *dai_cfg, snd_config_t *parent) +{ + const char *id; + int ret; + + ret = snd_config_get_id(dai_cfg, &id); + if (ret < 0) + return ret; + + tplg_pp_debug("entering dai auto attr id %s", id); + + set_dai_nhlt(tplg_pp, dai_cfg, parent); + + return 0; +} + const struct config_template_items pcm_caps_config = { .int_config_ids = {"rate_min", "rate_max", "channels_min", "channels_max", "periods_min", "periods_max", "period_size_min", "period_size_max", "buffer_size_min", @@ -1024,7 +1042,7 @@ const struct build_function_map object_build_map[] = { &mixer_control_config}, {"Control", "bytes", "SectionControlBytes", &tplg_build_bytes_control, NULL, &bytes_control_config}, - {"Dai", "", "SectionBE", &tplg_build_generic_object, NULL, &be_dai_config}, + {"Dai", "", "SectionBE", &tplg_build_generic_object, tplg_update_dai_auto_attr, &be_dai_config}, {"PCM", "pcm", "SectionPCM", &tplg_build_generic_object, NULL, &pcm_config}, {"PCM", "pcm_caps", "SectionPCMCapabilities", &tplg_build_pcm_caps_object, NULL, &pcm_caps_config}, @@ -1054,7 +1072,8 @@ static const struct build_function_map *tplg_object_get_map(struct tplg_pre_proc return &object_build_map[i]; if (!strcmp(class_type, "Dai") && - !strcmp(object_build_map[i].class_type, "Dai")) + !strcmp(object_build_map[i].class_type, "Dai") && + !strcmp(object_build_map[i].class_name, "")) return &object_build_map[i]; /* for other type objects, also match the object class_name */ diff --git a/topology/pre-processor.c b/topology/pre-processor.c index 8bb3f7419..d3c9c8103 100644 --- a/topology/pre-processor.c +++ b/topology/pre-processor.c @@ -30,6 +30,7 @@ #include "gettext.h" #include "topology.h" #include "pre-processor.h" +#include "intel/nhlt.h" /* * Helper function to find config by id. @@ -132,6 +133,9 @@ static int pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t * return -EINVAL; } + /* allocate nhlt private data */ + init_nhlt_blob(tplg_pp); + /* parse topology objects */ snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); @@ -164,6 +168,9 @@ static int pre_process_config(struct tplg_pre_processor *tplg_pp, snd_config_t * } } + /* create nhlt blob and add it to manifest */ + create_nhlt_blob(tplg_pp); + return 0; } diff --git a/topology/pre-processor.h b/topology/pre-processor.h index c534ead65..00d89d143 100644 --- a/topology/pre-processor.h +++ b/topology/pre-processor.h @@ -71,6 +71,8 @@ int tplg_build_bytes_control(struct tplg_pre_processor *tplg_pp, snd_config_t *o snd_config_t *parent); int tplg_build_dapm_route_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent); +int tplg_build_pdm_cfg_object(struct tplg_pre_processor *tplg_pp, + snd_config_t *obj_cfg, snd_config_t *parent); int tplg_build_hw_cfg_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, snd_config_t *parent); int tplg_build_fe_dai_object(struct tplg_pre_processor *tplg_pp, snd_config_t *obj_cfg, diff --git a/topology/topology.h b/topology/topology.h index 494612b2d..80cd5ccd7 100644 --- a/topology/topology.h +++ b/topology/topology.h @@ -25,6 +25,7 @@ struct tplg_pre_processor { snd_config_t *output_cfg; snd_output_t *output; snd_output_t *dbg_output; + void *private_data; }; int pre_process(struct tplg_pre_processor *tplg_pp, char *config, size_t config_size);