From c5f50f533ff876600bc0899822e9763c7ab75cae Mon Sep 17 00:00:00 2001 From: Seppo Ingalsuo Date: Mon, 12 Apr 2021 17:58:44 +0300 Subject: [PATCH] Drivers: Intel: DMIC: Separate decimation filters setup computation This patch moves the run-time decimation factors search and filter coefficients scaling code to separate module as preparation to add more functionality to the driver. There are no changes to functionality. Signed-off-by: Seppo Ingalsuo --- src/drivers/intel/dmic/CMakeLists.txt | 2 +- src/drivers/intel/dmic/dmic.c | 1071 +----------------------- src/drivers/intel/dmic/dmic_computed.c | 1002 ++++++++++++++++++++++ src/include/sof/drivers/dmic.h | 80 ++ zephyr/CMakeLists.txt | 4 + 5 files changed, 1100 insertions(+), 1059 deletions(-) create mode 100644 src/drivers/intel/dmic/dmic_computed.c diff --git a/src/drivers/intel/dmic/CMakeLists.txt b/src/drivers/intel/dmic/CMakeLists.txt index 14b33ab5d..2fb9eae08 100644 --- a/src/drivers/intel/dmic/CMakeLists.txt +++ b/src/drivers/intel/dmic/CMakeLists.txt @@ -1,3 +1,3 @@ # SPDX-License-Identifier: BSD-3-Clause -add_local_sources(sof dmic.c) +add_local_sources(sof dmic.c dmic_computed.c) diff --git a/src/drivers/intel/dmic/dmic.c b/src/drivers/intel/dmic/dmic.c index e4236071a..7763aa8bc 100644 --- a/src/drivers/intel/dmic/dmic.c +++ b/src/drivers/intel/dmic/dmic.c @@ -4,15 +4,13 @@ // // Author: Seppo Ingalsuo -#include - -#if defined DMIC_HW_VERSION +#if CONFIG_INTEL_DMIC #include -#include #include #include #include +#include #include #include #include @@ -47,92 +45,13 @@ DECLARE_TR_CTX(dmic_tr, SOF_UUID(dmic_uuid), LOG_LEVEL_INFO); DECLARE_SOF_UUID("dmic-work", dmic_work_task_uuid, 0x59c87728, 0xd8f9, 0x42f6, 0xb8, 0x9d, 0x58, 0x70, 0xa8, 0x7b, 0x0e, 0x1e); -#define DMIC_MAX_MODES 50 - -/* HW FIR pipeline needs 5 additional cycles per channel for internal - * operations. This is used in MAX filter length check. - */ -#define DMIC_FIR_PIPELINE_OVERHEAD 5 - -struct 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 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_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; -}; - /* Configuration ABI version, increment if not compatible with previous * version. */ #define DMIC_IPC_VERSION 1 -/* 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)) - /* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3}; -static const uint32_t coef_base_a[4] = {PDM0_COEFFICIENT_A, PDM1_COEFFICIENT_A, - PDM2_COEFFICIENT_A, PDM3_COEFFICIENT_A}; -static const uint32_t coef_base_b[4] = {PDM0_COEFFICIENT_B, PDM1_COEFFICIENT_B, - PDM2_COEFFICIENT_B, PDM3_COEFFICIENT_B}; /* Global configuration request and state for DMIC */ static SHARED_DATA struct dmic_global_shared dmic_global; @@ -214,894 +133,11 @@ static enum task_state dmic_work(void *data) return gval ? SOF_TASK_STATE_RESCHEDULE : SOF_TASK_STATE_COMPLETED; } -/* 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 dai *dai, - struct decim_modes *modes, uint32_t fs, int di) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - 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) - 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 (dmic->global->prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN || - dmic->global->prm[di].pdmclk_max > DMIC_HW_IOCLK / 2) { - dai_err(dai, "find_modes(): pdm clock max not in range"); - return; - } - if (dmic->global->prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN || - dmic->global->prm[di].pdmclk_min > dmic->global->prm[di].pdmclk_max) { - dai_err(dai, "find_modes(): pdm clock min not in range"); - return; - } - - /* Check for sane duty cycle */ - if (dmic->global->prm[di].duty_min > dmic->global->prm[di].duty_max) { - dai_err(dai, "find_modes(): duty cycle min > max"); - return; - } - if (dmic->global->prm[di].duty_min < DMIC_HW_DUTY_MIN || - dmic->global->prm[di].duty_min > DMIC_HW_DUTY_MAX) { - dai_err(dai, "find_modes(): pdm clock min not in range"); - return; - } - if (dmic->global->prm[di].duty_max < DMIC_HW_DUTY_MIN || - dmic->global->prm[di].duty_max > DMIC_HW_DUTY_MAX) { - dai_err(dai, "find_modes(): pdm clock max not in range"); - return; - } - - /* Min and max clock dividers */ - clkdiv_min = ceil_divide(DMIC_HW_IOCLK, dmic->global->prm[di].pdmclk_max); - clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN); - clkdiv_max = DMIC_HW_IOCLK / dmic->global->prm[di].pdmclk_min; - - /* 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 = DMIC_HW_IOCLK / 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 < dmic->global->prm[di].duty_min || - du_max > dmic->global->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 == DMIC_HW_IOCLK && - 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++; - } - } - } - - 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 matched_modes *c, struct decim_modes *a, - struct 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 dai *dai, - struct dmic_configuration *cfg, int mfir) -{ - int i; - int fs; - int cic_fs; - int fir_max_length; - struct pdm_decim *fir = NULL; - - if (mfir <= 0) - return fir; - - cic_fs = DMIC_HW_IOCLK / 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, - DMIC_HW_IOCLK / 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; - } - dai_info(dai, "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 -EINVAL; - - /* 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 -EINVAL; - - /* 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 dai *dai, - struct dmic_configuration *cfg, - struct 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 -EINVAL; - } - - /* 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 -EINVAL; - } - 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(dai, cfg, cfg->mfir_a); - if (!cfg->fir_a) { - dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_a = %u", - cfg->mfir_a); - return -EINVAL; - } - } - - if (cfg->mfir_b > 0) { - cfg->fir_b = get_fir(dai, cfg, cfg->mfir_b); - if (!cfg->fir_b) { - dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_b = %u", - cfg->mfir_b); - return -EINVAL; - } - } - - /* 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 -EINVAL; - } - - 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 -EINVAL; - } - } 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 -EINVAL; - } - } 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 inline void ipm_helper1(struct dmic_pdata *dmic, int *ipm, int di) -{ - 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 (dmic->global->prm[di].pdm[i].enable_mic_a || - dmic->global->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; -} - -#if DMIC_HW_VERSION >= 2 - -static inline void ipm_helper2(struct dmic_pdata *dmic, int source[], int *ipm, int di) -{ - 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 (dmic->global->prm[di].pdm[i].enable_mic_a || - dmic->global->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]; -} -#endif - -/* 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 dmic_pdata *dmic, 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 (dmic->global->prm[0].pdm[i].enable_mic_a || - dmic->global->prm[1].pdm[i].enable_mic_a) - cnt++; - - if (dmic->global->prm[0].pdm[i].enable_mic_b || - dmic->global->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] = (dmic->global->prm[0].pdm[i].enable_mic_b || - dmic->global->prm[1].pdm[i].enable_mic_b) && !cnt; - - /* Check that swap does not conflict with other DAI request */ - swap_check = dmic->global->prm[1].pdm[i].enable_mic_a || - dmic->global->prm[0].pdm[i].enable_mic_a; - - if (swap_check && swap[i]) - ret = -EINVAL; - } - return ret; -} - -static int configure_registers(struct dai *dai, - struct dmic_configuration *cfg) -{ - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int stereo[DMIC_HW_CONTROLLERS]; - int swap[DMIC_HW_CONTROLLERS]; - uint32_t val; - uint32_t ref; - 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 pol_a; - int pol_b; - int di = 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 = 0; /* Used with TIE=1 */ - - /* Normal start sequence */ - soft_reset = 1; - cic_mute = 1; - fir_mute = 1; - -#if (DMIC_HW_VERSION == 2 && DMIC_HW_CONTROLLERS > 2) || DMIC_HW_VERSION == 3 - int source[OUTCONTROLX_IPM_NUMSOURCES]; -#endif - - /* pdata is set by dmic_probe(), error if it has not been set */ - if (!dmic) { - dai_err(dai, "configure_registers(): pdata not set"); - return -EINVAL; - } - - dai_info(dai, "configuring registers"); - - /* OUTCONTROL0 and OUTCONTROL1 */ - of0 = (dmic->global->prm[0].fifo_bits == 32) ? 2 : 0; - -#if DMIC_HW_FIFOS > 1 - of1 = (dmic->global->prm[1].fifo_bits == 32) ? 2 : 0; -#else - of1 = 0; -#endif - -#if DMIC_HW_VERSION == 1 || (DMIC_HW_VERSION == 2 && DMIC_HW_CONTROLLERS <= 2) - if (di == 0) { - ipm_helper1(dmic, &ipm, 0); - val = OUTCONTROL0_TIE(0) | - OUTCONTROL0_SIP(0) | - OUTCONTROL0_FINIT(1) | - OUTCONTROL0_FCI(0) | - OUTCONTROL0_BFTH(bfth) | - OUTCONTROL0_OF(of0) | - OUTCONTROL0_IPM(ipm) | - OUTCONTROL0_TH(th); - dai_write(dai, OUTCONTROL0, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); - } else { - ipm_helper1(dmic, &ipm, 1); - val = OUTCONTROL1_TIE(0) | - OUTCONTROL1_SIP(0) | - OUTCONTROL1_FINIT(1) | - OUTCONTROL1_FCI(0) | - OUTCONTROL1_BFTH(bfth) | - OUTCONTROL1_OF(of1) | - OUTCONTROL1_IPM(ipm) | - OUTCONTROL1_TH(th); - dai_write(dai, OUTCONTROL1, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); - } -#endif - -#if DMIC_HW_VERSION == 3 || (DMIC_HW_VERSION == 2 && DMIC_HW_CONTROLLERS > 2) - if (di == 0) { - ipm_helper2(dmic, source, &ipm, 0); - val = OUTCONTROL0_TIE(0) | - OUTCONTROL0_SIP(0) | - OUTCONTROL0_FINIT(1) | - OUTCONTROL0_FCI(0) | - OUTCONTROL0_BFTH(bfth) | - OUTCONTROL0_OF(of0) | - OUTCONTROL0_IPM(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); - dai_write(dai, OUTCONTROL0, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); - } else { - ipm_helper2(dmic, source, &ipm, 1); - val = OUTCONTROL1_TIE(0) | - OUTCONTROL1_SIP(0) | - OUTCONTROL1_FINIT(1) | - OUTCONTROL1_FCI(0) | - OUTCONTROL1_BFTH(bfth) | - OUTCONTROL1_OF(of1) | - OUTCONTROL1_IPM(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); - dai_write(dai, OUTCONTROL1, val); - dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); - } -#endif - - /* Mark enabled microphones into private data to be later used - * for starting correct parts of the HW. - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - dmic->enable[i] = (dmic->global->prm[di].pdm[i].enable_mic_b << 1) | - dmic->global->prm[di].pdm[i].enable_mic_a; - } - - - ret = stereo_helper(dmic, stereo, swap); - if (ret < 0) { - dai_err(dai, "configure_registers(): enable conflict"); - return ret; - } - - /* Note about accessing dmic_active_fifos_mask: the dai spinlock has been set in - * the calling function dmic_set_config(). - */ - for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { - if (dmic->global->active_fifos_mask == 0) { - /* CIC */ - pol_a = dmic->global->prm[di].pdm[i].polarity_mic_a; - pol_b = dmic->global->prm[di].pdm[i].polarity_mic_b; - val = CIC_CONTROL_SOFT_RESET(soft_reset) | - CIC_CONTROL_CIC_START_B(0) | - CIC_CONTROL_CIC_START_A(0) | - CIC_CONTROL_MIC_B_POLARITY(pol_b) | - CIC_CONTROL_MIC_A_POLARITY(pol_a) | - CIC_CONTROL_MIC_MUTE(cic_mute) | - CIC_CONTROL_STEREO_MODE(stereo[i]); - dai_write(dai, base[i] + CIC_CONTROL, val); - dai_dbg(dai, "configure_registers(), CIC_CONTROL = %08x", val); - - val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | - CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); - dai_write(dai, base[i] + CIC_CONFIG, val); - dai_dbg(dai, "configure_registers(), CIC_CONFIG = %08x", val); - - /* Mono right channel mic usage requires swap of PDM channels - * since the mono decimation is done with only left channel - * processing active. - */ - edge = dmic->global->prm[di].pdm[i].clk_edge; - if (swap[i]) - edge = !edge; - - val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) | - MIC_CONTROL_PDM_SKEW(dmic->global->prm[di].pdm[i].skew) | - MIC_CONTROL_CLK_EDGE(edge) | - MIC_CONTROL_PDM_EN_B(0) | - MIC_CONTROL_PDM_EN_A(0); - dai_write(dai, base[i] + MIC_CONTROL, val); - dai_dbg(dai, "configure_registers(), MIC_CONTROL = %08x", val); - } else { - /* Check that request is compatible with running configuration: - * CIC decimation factor and shift value check - */ - val = dai_read(dai, base[i] + CIC_CONFIG); - ref = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | - CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); - if ((val & - (CIC_CONFIG_CIC_SHIFT_MASK | CIC_CONFIG_COMB_COUNT_MASK)) != ref) { - dai_err(dai, "configure_registers(): CIC_CONFIG %08x block", val); - return -EINVAL; - } - - /* Clock divider check */ - val = dai_read(dai, base[i] + MIC_CONTROL); - ref = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2); - if ((val & MIC_CONTROL_PDM_CLKDIV_MASK) != ref) { - dai_err(dai, "configure_registers(): MIC_CONTROL %08x block", val); - return -EINVAL; - } - } - - 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(0) | - 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]); - dai_write(dai, base[i] + FIR_CONTROL_A, val); - dai_dbg(dai, "configure_registers(), FIR_CONTROL_A = %08x", 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); - dai_write(dai, base[i] + FIR_CONFIG_A, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_A = %08x", val); - - val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_LEFT_A, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_A = %08x", val); - - val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_RIGHT_A, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_A = %08x", val); - - val = OUT_GAIN_LEFT_A_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_LEFT_A, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_A = %08x", val); - - val = OUT_GAIN_RIGHT_A_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_A, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_A = %08x", 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); - dai_write(dai, coef_base_a[i] + ((length - j - 1) << 2), cu); - } - } - - 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(0) | - 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]); - dai_write(dai, base[i] + FIR_CONTROL_B, val); - dai_dbg(dai, "configure_registers(), FIR_CONTROL_B = %08x", 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); - dai_write(dai, base[i] + FIR_CONFIG_B, val); - dai_dbg(dai, "configure_registers(), FIR_CONFIG_B = %08x", val); - - val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_LEFT_B, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_B = %08x", val); - - val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0); - dai_write(dai, base[i] + DC_OFFSET_RIGHT_B, val); - dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_B = %08x", val); - - val = OUT_GAIN_LEFT_B_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_LEFT_B, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_B = %08x", val); - - val = OUT_GAIN_RIGHT_B_GAIN(0); - dai_write(dai, base[i] + OUT_GAIN_RIGHT_B, val); - dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_B = %08x", 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); - dai_write(dai, coef_base_b[i] + ((length - j - 1) << 2), cu); - } - } - } - - return 0; -} - /* get DMIC hw params */ static int dmic_get_hw_params(struct dai *dai, struct sof_ipc_stream_params *params, int dir) { - struct dmic_pdata *dmic = dai_get_drvdata(dai); - int di = dai->index; - - if (!dmic) { - dai_err(dai, "dmic_get_hw_params(): dai %d not configured!", di); - return -EINVAL; - } - params->rate = dmic->global->prm[di].fifo_fs; - params->buffer_fmt = 0; - - switch (dmic->global->prm[di].num_pdm_active) { - case 1: - params->channels = 2; - break; - case 2: - params->channels = 4; - break; - default: - dai_info(dai, "dmic_get_hw_params(): not supported PDM active count %d", - dmic->global->prm[di].num_pdm_active); - return -EINVAL; - } - - switch (dmic->global->prm[di].fifo_bits) { - case 16: - params->frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case 32: - params->frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - default: - dai_err(dai, "dmic_get_hw_params(): not supported format"); - return -EINVAL; - } - - return 0; + return dmic_get_hw_params_computed(dai, params, dir); } static int dmic_set_config(struct dai *dai, struct ipc_config_dai *common_config, @@ -1109,13 +145,11 @@ static int dmic_set_config(struct dai *dai, struct ipc_config_dai *common_config { struct dmic_pdata *dmic = dai_get_drvdata(dai); struct sof_ipc_dai_config *config = spec_config; - struct matched_modes modes_ab; - struct dmic_configuration cfg; - struct decim_modes modes_a; - struct decim_modes modes_b; int32_t unmute_ramp_time_ms; int32_t step_db; - int i, j, ret = 0; + int i; + int j; + int ret = 0; int di = dai->index; dai_info(dai, "dmic_set_config()"); @@ -1137,8 +171,7 @@ static int dmic_set_config(struct dai *dai, struct ipc_config_dai *common_config if (unmute_ramp_time_ms < LOGRAMP_TIME_MIN_MS || unmute_ramp_time_ms > LOGRAMP_TIME_MAX_MS) { - dai_err(dai, "dmic_set_config(): Illegal ramp time = %d", - unmute_ramp_time_ms); + dai_err(dai, "dmic_set_config(): Illegal ramp time = %d", unmute_ramp_time_ms); ret = -EINVAL; goto out; } @@ -1189,81 +222,11 @@ static int dmic_set_config(struct dai *dai, struct ipc_config_dai *common_config } } - dai_info(dai, "dmic_set_config(), prm config->dmic.num_pdm_active = %u", - config->dmic.num_pdm_active); - dai_info(dai, "dmic_set_config(), prm pdmclk_min = %u, pdmclk_max = %u", - dmic->global->prm[di].pdmclk_min, dmic->global->prm[di].pdmclk_max); - dai_info(dai, "dmic_set_config(), prm duty_min = %u, duty_max = %u", - dmic->global->prm[di].duty_min, dmic->global->prm[di].duty_max); - dai_info(dai, "dmic_set_config(), prm fifo_fs = %u, fifo_bits = %u", - dmic->global->prm[di].fifo_fs, dmic->global->prm[di].fifo_bits); - - switch (dmic->global->prm[di].fifo_bits) { - case 0: - case 16: - case 32: - break; - default: - dai_err(dai, "dmic_set_config(): fifo_bits EINVAL"); - ret = -EINVAL; - 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(dai, &modes_a, dmic->global->prm[0].fifo_fs, di); - if (modes_a.num_of_modes == 0 && dmic->global->prm[0].fifo_fs > 0) { - dai_err(dai, "dmic_set_config(): No modes found found for FIFO A"); - ret = -EINVAL; - goto out; - } - - find_modes(dai, &modes_b, dmic->global->prm[1].fifo_fs, di); - if (modes_b.num_of_modes == 0 && dmic->global->prm[1].fifo_fs > 0) { - dai_err(dai, "dmic_set_config(): No modes found for FIFO B"); - ret = -EINVAL; - goto out; - } - - match_modes(&modes_ab, &modes_a, &modes_b); - ret = select_mode(dai, &cfg, &modes_ab); - if (ret < 0) { - dai_err(dai, "dmic_set_config(): select_mode() failed"); - ret = -EINVAL; - goto out; - } - - dai_info(dai, "dmic_set_config(), cfg clkdiv = %u, mcic = %u", - cfg.clkdiv, cfg.mcic); - dai_info(dai, "dmic_set_config(), cfg mfir_a = %u, mfir_b = %u", - cfg.mfir_a, cfg.mfir_b); - dai_info(dai, "dmic_set_config(), cfg cic_shift = %u", - cfg.cic_shift); - dai_info(dai, "dmic_set_config(), cfg fir_a_shift = %u, cfg.fir_b_shift = %u", - cfg.fir_a_shift, cfg.fir_b_shift); - dai_info(dai, "dmic_set_config(), cfg fir_a_length = %u, fir_b_length = %u", - cfg.fir_a_length, cfg.fir_b_length); - - /* Struct reg contains a mirror of actual HW registers. Determine - * register bits configuration from decimator configuration and the - * requested parameters. - */ - ret = configure_registers(dai, &cfg); - if (ret < 0) { - dai_err(dai, "dmic_set_config(): cannot configure registers"); - ret = -EINVAL; - goto out; - } - + ret = dmic_set_config_computed(dai); dmic->state = COMP_STATE_PREPARE; out: spin_unlock(&dai->lock); - return ret; } @@ -1308,21 +271,13 @@ static void dmic_start(struct dai *dai) for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { mic_a = dmic->enable[i] & 1; mic_b = (dmic->enable[i] & 2) >> 1; - if (dmic->global->prm[0].fifo_fs > 0) - fir_a = (dmic->enable[i] > 0) ? 1 : 0; - else - fir_a = 0; - + fir_a = (dmic->enable[i] > 0) ? 1 : 0; #if DMIC_HW_FIFOS > 1 - if (dmic->global->prm[1].fifo_fs > 0) - fir_b = (dmic->enable[i] > 0) ? 1 : 0; - else - fir_b = 0; + fir_b = (dmic->enable[i] > 0) ? 1 : 0; #else fir_b = 0; #endif - dai_info(dai, "dmic_start(), mic_a = %u, mic_b = %u, fir_a = %u, fir_b = %u", - mic_a, mic_b, fir_a, fir_b); + dai_info(dai, "dmic_start(), pdm%d mic_a = %u, mic_b = %u", i, mic_a, mic_b); /* If both microphones are needed start them simultaneously * to start them in sync. The reset may be cleared for another @@ -1544,8 +499,7 @@ static void dmic_irq_handler(void *data) /* Trace OUTSTAT0 register */ val0 = dai_read(dai, OUTSTAT0); val1 = dai_read(dai, OUTSTAT1); - dai_info(dai, "dmic_irq_handler(), OUTSTAT0 = 0x%x, OUTSTAT1 = 0x%x", - val0, val1); + dai_info(dai, "dmic_irq_handler(), OUTSTAT0 = 0x%x, OUTSTAT1 = 0x%x", val0, val1); if (val0 & OUTSTAT0_ROR_BIT) { dai_err(dai, "dmic_irq_handler(): full fifo A or PDM overrun"); @@ -1606,6 +560,7 @@ static int dmic_probe(struct dai *dai) /* Enable DMIC power */ pm_runtime_get_sync(DMIC_POW, dai->index); + /* Disable dynamic clock gating for dmic before touching any reg */ pm_runtime_get_sync(DMIC_CLK, dai->index); interrupt_enable(dmic->irq, dai); diff --git a/src/drivers/intel/dmic/dmic_computed.c b/src/drivers/intel/dmic/dmic_computed.c new file mode 100644 index 000000000..9d6010df2 --- /dev/null +++ b/src/drivers/intel/dmic/dmic_computed.c @@ -0,0 +1,1002 @@ +// SPDX-License-Identifier: BSD-3-Clause +// +// Copyright(c) 2017-2021 Intel Corporation. All rights reserved. +// +// Author: Seppo Ingalsuo + +#include +#include +#include +#include +#include + +/* Decimation filter struct */ +#include + +/* Decimation filters */ +#include + +/* OUTCONTROLx IPM bit fields style */ +#if DMIC_HW_VERSION == 1 || (DMIC_HW_VERSION == 2 && DMIC_HW_CONTROLLERS <= 2) +#define DMIC_IPM_VER1 +#elif DMIC_HW_VERSION == 3 || (DMIC_HW_VERSION == 2 && DMIC_HW_CONTROLLERS > 2) +#define DMIC_IPM_VER2 +#else +#error Not supported HW version +#endif + +/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */ +static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3}; +static const uint32_t coef_base_a[4] = {PDM0_COEFFICIENT_A, PDM1_COEFFICIENT_A, + PDM2_COEFFICIENT_A, PDM3_COEFFICIENT_A}; +static const uint32_t coef_base_b[4] = {PDM0_COEFFICIENT_B, PDM1_COEFFICIENT_B, + PDM2_COEFFICIENT_B, PDM3_COEFFICIENT_B}; + +/* 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 dai *dai, + struct decim_modes *modes, uint32_t fs, int di) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + 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) + 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 (dmic->global->prm[di].pdmclk_max < DMIC_HW_PDM_CLK_MIN || + dmic->global->prm[di].pdmclk_max > DMIC_HW_IOCLK / 2) { + dai_err(dai, "find_modes(): pdm clock max not in range"); + return; + } + if (dmic->global->prm[di].pdmclk_min < DMIC_HW_PDM_CLK_MIN || + dmic->global->prm[di].pdmclk_min > dmic->global->prm[di].pdmclk_max) { + dai_err(dai, "find_modes(): pdm clock min not in range"); + return; + } + + /* Check for sane duty cycle */ + if (dmic->global->prm[di].duty_min > dmic->global->prm[di].duty_max) { + dai_err(dai, "find_modes(): duty cycle min > max"); + return; + } + if (dmic->global->prm[di].duty_min < DMIC_HW_DUTY_MIN || + dmic->global->prm[di].duty_min > DMIC_HW_DUTY_MAX) { + dai_err(dai, "find_modes(): pdm clock min not in range"); + return; + } + if (dmic->global->prm[di].duty_max < DMIC_HW_DUTY_MIN || + dmic->global->prm[di].duty_max > DMIC_HW_DUTY_MAX) { + dai_err(dai, "find_modes(): pdm clock max not in range"); + return; + } + + /* Min and max clock dividers */ + clkdiv_min = ceil_divide(DMIC_HW_IOCLK, dmic->global->prm[di].pdmclk_max); + clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN); + clkdiv_max = DMIC_HW_IOCLK / dmic->global->prm[di].pdmclk_min; + + /* 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 = DMIC_HW_IOCLK / 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 < dmic->global->prm[di].duty_min || + du_max > dmic->global->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 == DMIC_HW_IOCLK && + 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++; + } + } + } + + 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 matched_modes *c, struct decim_modes *a, + struct 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 dai *dai, + struct dmic_configuration *cfg, int mfir) +{ + int i; + int fs; + int cic_fs; + int fir_max_length; + struct pdm_decim *fir = NULL; + + if (mfir <= 0) + return fir; + + cic_fs = DMIC_HW_IOCLK / 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, + DMIC_HW_IOCLK / 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; + } + dai_info(dai, "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 -EINVAL; + + /* 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 -EINVAL; + + /* 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 dai *dai, + struct dmic_configuration *cfg, + struct 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 -EINVAL; + } + + /* 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 -EINVAL; + } + 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(dai, cfg, cfg->mfir_a); + if (!cfg->fir_a) { + dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_a = %u", + cfg->mfir_a); + return -EINVAL; + } + } + + if (cfg->mfir_b > 0) { + cfg->fir_b = get_fir(dai, cfg, cfg->mfir_b); + if (!cfg->fir_b) { + dai_err(dai, "select_mode(): cannot find FIR coefficients, mfir_b = %u", + cfg->mfir_b); + return -EINVAL; + } + } + + /* 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 -EINVAL; + } + + 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 -EINVAL; + } + } 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 -EINVAL; + } + } 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. + */ +#ifdef DMIC_IPM_VER1 + +static void ipm_helper1(struct dmic_pdata *dmic, int *ipm, int di) +{ + 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 (dmic->global->prm[di].pdm[i].enable_mic_a || + dmic->global->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; +} + +#endif + +#ifdef DMIC_IPM_VER2 + +static inline void ipm_helper2(struct dmic_pdata *dmic, int source[], int *ipm, int di) +{ + 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 (dmic->global->prm[di].pdm[i].enable_mic_a || + dmic->global->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]; +} +#endif + +/* 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 dmic_pdata *dmic, 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 (dmic->global->prm[0].pdm[i].enable_mic_a || + dmic->global->prm[1].pdm[i].enable_mic_a) + cnt++; + + if (dmic->global->prm[0].pdm[i].enable_mic_b || + dmic->global->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] = (dmic->global->prm[0].pdm[i].enable_mic_b || + dmic->global->prm[1].pdm[i].enable_mic_b) && !cnt; + + /* Check that swap does not conflict with other DAI request */ + swap_check = dmic->global->prm[1].pdm[i].enable_mic_a || + dmic->global->prm[0].pdm[i].enable_mic_a; + + if (swap_check && swap[i]) + ret = -EINVAL; + } + return ret; +} + +static int configure_registers(struct dai *dai, + struct dmic_configuration *cfg) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int stereo[DMIC_HW_CONTROLLERS]; + int swap[DMIC_HW_CONTROLLERS]; + uint32_t val; + uint32_t ref; + 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 pol_a; + int pol_b; + int di = 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 = 0; /* Used with TIE=1 */ + + /* Normal start sequence */ + soft_reset = 1; + cic_mute = 1; + fir_mute = 1; + +#ifdef DMIC_IPM_VER2 + int source[OUTCONTROLX_IPM_NUMSOURCES]; +#endif + + /* pdata is set by dmic_probe(), error if it has not been set */ + if (!dmic) { + dai_err(dai, "configure_registers(): pdata not set"); + return -EINVAL; + } + + dai_info(dai, "configuring registers"); + + /* OUTCONTROL0 and OUTCONTROL1 */ + of0 = (dmic->global->prm[0].fifo_bits == 32) ? 2 : 0; + +#if DMIC_HW_FIFOS > 1 + of1 = (dmic->global->prm[1].fifo_bits == 32) ? 2 : 0; +#else + of1 = 0; +#endif + +#ifdef DMIC_IPM_VER1 + if (di == 0) { + ipm_helper1(dmic, &ipm, 0); + val = OUTCONTROL0_TIE(0) | + OUTCONTROL0_SIP(0) | + OUTCONTROL0_FINIT(1) | + OUTCONTROL0_FCI(0) | + OUTCONTROL0_BFTH(bfth) | + OUTCONTROL0_OF(of0) | + OUTCONTROL0_IPM(ipm) | + OUTCONTROL0_TH(th); + dai_write(dai, OUTCONTROL0, val); + dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); + } else { + ipm_helper1(dmic, &ipm, 1); + val = OUTCONTROL1_TIE(0) | + OUTCONTROL1_SIP(0) | + OUTCONTROL1_FINIT(1) | + OUTCONTROL1_FCI(0) | + OUTCONTROL1_BFTH(bfth) | + OUTCONTROL1_OF(of1) | + OUTCONTROL1_IPM(ipm) | + OUTCONTROL1_TH(th); + dai_write(dai, OUTCONTROL1, val); + dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); + } +#endif + +#ifdef DMIC_IPM_VER2 + if (di == 0) { + ipm_helper2(dmic, source, &ipm, 0); + val = OUTCONTROL0_TIE(0) | + OUTCONTROL0_SIP(0) | + OUTCONTROL0_FINIT(1) | + OUTCONTROL0_FCI(0) | + OUTCONTROL0_BFTH(bfth) | + OUTCONTROL0_OF(of0) | + OUTCONTROL0_IPM(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); + dai_write(dai, OUTCONTROL0, val); + dai_dbg(dai, "configure_registers(), OUTCONTROL0 = %08x", val); + } else { + ipm_helper2(dmic, source, &ipm, 1); + val = OUTCONTROL1_TIE(0) | + OUTCONTROL1_SIP(0) | + OUTCONTROL1_FINIT(1) | + OUTCONTROL1_FCI(0) | + OUTCONTROL1_BFTH(bfth) | + OUTCONTROL1_OF(of1) | + OUTCONTROL1_IPM(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); + dai_write(dai, OUTCONTROL1, val); + dai_dbg(dai, "configure_registers(), OUTCONTROL1 = %08x", val); + } +#endif + + /* Mark enabled microphones into private data to be later used + * for starting correct parts of the HW. + */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + dmic->enable[i] = (dmic->global->prm[di].pdm[i].enable_mic_b << 1) | + dmic->global->prm[di].pdm[i].enable_mic_a; + } + + + ret = stereo_helper(dmic, stereo, swap); + if (ret < 0) { + dai_err(dai, "configure_registers(): enable conflict"); + return ret; + } + + /* Note about accessing dmic_active_fifos_mask: the dai spinlock has been set in + * the calling function dmic_set_config(). + */ + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + if (dmic->global->active_fifos_mask == 0) { + /* CIC */ + pol_a = dmic->global->prm[di].pdm[i].polarity_mic_a; + pol_b = dmic->global->prm[di].pdm[i].polarity_mic_b; + val = CIC_CONTROL_SOFT_RESET(soft_reset) | + CIC_CONTROL_CIC_START_B(0) | + CIC_CONTROL_CIC_START_A(0) | + CIC_CONTROL_MIC_B_POLARITY(pol_b) | + CIC_CONTROL_MIC_A_POLARITY(pol_a) | + CIC_CONTROL_MIC_MUTE(cic_mute) | + CIC_CONTROL_STEREO_MODE(stereo[i]); + dai_write(dai, base[i] + CIC_CONTROL, val); + dai_dbg(dai, "configure_registers(), CIC_CONTROL = %08x", val); + + val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | + CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); + dai_write(dai, base[i] + CIC_CONFIG, val); + dai_dbg(dai, "configure_registers(), CIC_CONFIG = %08x", val); + + /* Mono right channel mic usage requires swap of PDM channels + * since the mono decimation is done with only left channel + * processing active. + */ + edge = dmic->global->prm[di].pdm[i].clk_edge; + if (swap[i]) + edge = !edge; + + val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) | + MIC_CONTROL_PDM_SKEW(dmic->global->prm[di].pdm[i].skew) | + MIC_CONTROL_CLK_EDGE(edge) | + MIC_CONTROL_PDM_EN_B(0) | + MIC_CONTROL_PDM_EN_A(0); + dai_write(dai, base[i] + MIC_CONTROL, val); + dai_dbg(dai, "configure_registers(), MIC_CONTROL = %08x", val); + } else { + /* Check that request is compatible with running configuration: + * CIC decimation factor and shift value check + */ + val = dai_read(dai, base[i] + CIC_CONFIG); + ref = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | + CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); + if ((val & + (CIC_CONFIG_CIC_SHIFT_MASK | CIC_CONFIG_COMB_COUNT_MASK)) != ref) { + dai_err(dai, "configure_registers(): CIC_CONFIG %08x block", val); + return -EINVAL; + } + + /* Clock divider check */ + val = dai_read(dai, base[i] + MIC_CONTROL); + ref = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2); + if ((val & MIC_CONTROL_PDM_CLKDIV_MASK) != ref) { + dai_err(dai, "configure_registers(): MIC_CONTROL %08x block", val); + return -EINVAL; + } + } + + 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(0) | + 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]); + dai_write(dai, base[i] + FIR_CONTROL_A, val); + dai_dbg(dai, "configure_registers(), FIR_CONTROL_A = %08x", 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); + dai_write(dai, base[i] + FIR_CONFIG_A, val); + dai_dbg(dai, "configure_registers(), FIR_CONFIG_A = %08x", val); + + val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0); + dai_write(dai, base[i] + DC_OFFSET_LEFT_A, val); + dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_A = %08x", val); + + val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0); + dai_write(dai, base[i] + DC_OFFSET_RIGHT_A, val); + dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_A = %08x", val); + + val = OUT_GAIN_LEFT_A_GAIN(0); + dai_write(dai, base[i] + OUT_GAIN_LEFT_A, val); + dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_A = %08x", val); + + val = OUT_GAIN_RIGHT_A_GAIN(0); + dai_write(dai, base[i] + OUT_GAIN_RIGHT_A, val); + dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_A = %08x", 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); + dai_write(dai, coef_base_a[i] + ((length - j - 1) << 2), cu); + } + } + + 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(0) | + 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]); + dai_write(dai, base[i] + FIR_CONTROL_B, val); + dai_dbg(dai, "configure_registers(), FIR_CONTROL_B = %08x", 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); + dai_write(dai, base[i] + FIR_CONFIG_B, val); + dai_dbg(dai, "configure_registers(), FIR_CONFIG_B = %08x", val); + + val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0); + dai_write(dai, base[i] + DC_OFFSET_LEFT_B, val); + dai_dbg(dai, "configure_registers(), DC_OFFSET_LEFT_B = %08x", val); + + val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0); + dai_write(dai, base[i] + DC_OFFSET_RIGHT_B, val); + dai_dbg(dai, "configure_registers(), DC_OFFSET_RIGHT_B = %08x", val); + + val = OUT_GAIN_LEFT_B_GAIN(0); + dai_write(dai, base[i] + OUT_GAIN_LEFT_B, val); + dai_dbg(dai, "configure_registers(), OUT_GAIN_LEFT_B = %08x", val); + + val = OUT_GAIN_RIGHT_B_GAIN(0); + dai_write(dai, base[i] + OUT_GAIN_RIGHT_B, val); + dai_dbg(dai, "configure_registers(), OUT_GAIN_RIGHT_B = %08x", 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); + dai_write(dai, coef_base_b[i] + ((length - j - 1) << 2), cu); + } + } + } + + return 0; +} + +/* get DMIC hw params */ +int dmic_get_hw_params_computed(struct dai *dai, struct sof_ipc_stream_params *params, int dir) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int di = dai->index; + + if (!dmic) { + dai_err(dai, "dmic_get_hw_params(): dai %d not configured!", di); + return -EINVAL; + } + params->rate = dmic->global->prm[di].fifo_fs; + params->buffer_fmt = 0; + + switch (dmic->global->prm[di].num_pdm_active) { + case 1: + params->channels = 2; + break; + case 2: + params->channels = 4; + break; + default: + dai_info(dai, "dmic_get_hw_params(): not supported PDM active count %d", + dmic->global->prm[di].num_pdm_active); + return -EINVAL; + } + + switch (dmic->global->prm[di].fifo_bits) { + case 16: + params->frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case 32: + params->frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + default: + dai_err(dai, "dmic_get_hw_params(): not supported format"); + return -EINVAL; + } + + return 0; +} + +int dmic_set_config_computed(struct dai *dai) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + struct matched_modes modes_ab; + struct dmic_configuration cfg; + struct decim_modes modes_a; + struct decim_modes modes_b; + int ret; + int di = dai->index; + + dai_info(dai, "dmic_set_config(), prm config->dmic.num_pdm_active = %u", + dmic->global->prm[di].num_pdm_active); + dai_info(dai, "dmic_set_config(), prm pdmclk_min = %u, pdmclk_max = %u", + dmic->global->prm[di].pdmclk_min, dmic->global->prm[di].pdmclk_max); + dai_info(dai, "dmic_set_config(), prm duty_min = %u, duty_max = %u", + dmic->global->prm[di].duty_min, dmic->global->prm[di].duty_max); + dai_info(dai, "dmic_set_config(), prm fifo_fs = %u, fifo_bits = %u", + dmic->global->prm[di].fifo_fs, dmic->global->prm[di].fifo_bits); + + switch (dmic->global->prm[di].fifo_bits) { + case 0: + case 16: + case 32: + break; + default: + dai_err(dai, "dmic_set_config_computed(): invalid fifo_bits"); + return -EINVAL; + } + + /* 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(dai, &modes_a, dmic->global->prm[0].fifo_fs, di); + if (modes_a.num_of_modes == 0 && dmic->global->prm[0].fifo_fs > 0) { + dai_err(dai, "dmic_set_config(): No modes found found for FIFO A"); + return -EINVAL; + } + + find_modes(dai, &modes_b, dmic->global->prm[1].fifo_fs, di); + if (modes_b.num_of_modes == 0 && dmic->global->prm[1].fifo_fs > 0) { + dai_err(dai, "dmic_set_config(): No modes found for FIFO B"); + return -EINVAL; + } + + match_modes(&modes_ab, &modes_a, &modes_b); + ret = select_mode(dai, &cfg, &modes_ab); + if (ret < 0) { + dai_err(dai, "dmic_set_config(): select_mode() failed"); + return -EINVAL; + } + + dai_info(dai, "dmic_set_config(), cfg clkdiv = %u, mcic = %u", + cfg.clkdiv, cfg.mcic); + dai_info(dai, "dmic_set_config(), cfg mfir_a = %u, mfir_b = %u", + cfg.mfir_a, cfg.mfir_b); + dai_info(dai, "dmic_set_config(), cfg cic_shift = %u", + cfg.cic_shift); + dai_info(dai, "dmic_set_config(), cfg fir_a_shift = %u, cfg.fir_b_shift = %u", + cfg.fir_a_shift, cfg.fir_b_shift); + dai_info(dai, "dmic_set_config(), cfg fir_a_length = %u, fir_b_length = %u", + cfg.fir_a_length, cfg.fir_b_length); + + /* Determine register bits configuration from decimator configuration and + * the requested parameters. + */ + ret = configure_registers(dai, &cfg); + if (ret < 0) { + dai_err(dai, "dmic_set_config(): cannot configure registers"); + ret = -EINVAL; + } + + return ret; +} diff --git a/src/include/sof/drivers/dmic.h b/src/include/sof/drivers/dmic.h index c0640ddd8..14ef6d76a 100644 --- a/src/include/sof/drivers/dmic.h +++ b/src/include/sof/drivers/dmic.h @@ -10,6 +10,23 @@ #if CONFIG_INTEL_DMIC +/* Let find up to 50 mode candidates to choose from */ +#define DMIC_MAX_MODES 50 + +/* 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 + +/* HW FIR pipeline needs 5 additional cycles per channel for internal + * operations. This is used in MAX filter length check. + */ +#define DMIC_FIR_PIPELINE_OVERHEAD 5 + /* The microphones create a low frequecy thump sound when clock is enabled. * The unmute linear gain ramp chacteristic is defined here. * NOTE: Do not set any of these to 0. @@ -313,9 +330,38 @@ #define FIR_COEF_A(x) SET_BITS(19, 0, x) #define FIR_COEF_B(x) SET_BITS(19, 0, x) +/* 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)) + #define dmic_irq(dmic) dmic->plat_data.irq #define dmic_irq_name(dmic) dmic->plat_data.irq_name +/* Common data for all DMIC DAI instances */ struct dmic_global_shared { struct sof_ipc_dai_dmic_params prm[DMIC_HW_FIFOS]; /* Configuration requests */ uint32_t active_fifos_mask; /* Bits (dai->index) are set to indicate active FIFO */ @@ -335,6 +381,40 @@ struct dmic_pdata { }; +struct 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 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_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; +}; + +int dmic_set_config_computed(struct dai *dai); +int dmic_get_hw_params_computed(struct dai *dai, struct sof_ipc_stream_params *params, int dir); + extern const struct dai_driver dmic_driver; #endif /* DMIC_HW_VERSION */ diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index e85b2c5ae..d1b953007 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -154,6 +154,7 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V15) zephyr_library_sources_ifdef(CONFIG_INTEL_DMIC ${SOF_DRIVERS_PATH}/intel/dmic/dmic.c + ${SOF_DRIVERS_PATH}/intel/dmic/dmic_computed.c ) # Platform sources @@ -207,6 +208,7 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V18) zephyr_library_sources_ifdef(CONFIG_INTEL_DMIC ${SOF_DRIVERS_PATH}/intel/dmic/dmic.c + ${SOF_DRIVERS_PATH}/intel/dmic/dmic_computed.c ) # Platform sources @@ -262,6 +264,7 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V20) zephyr_library_sources_ifdef(CONFIG_INTEL_DMIC ${SOF_DRIVERS_PATH}/intel/dmic/dmic.c + ${SOF_DRIVERS_PATH}/intel/dmic/dmic_computed.c ) # Platform sources @@ -319,6 +322,7 @@ if (CONFIG_SOC_SERIES_INTEL_CAVS_V25) zephyr_library_sources_ifdef(CONFIG_INTEL_DMIC ${SOF_DRIVERS_PATH}/intel/dmic/dmic.c + ${SOF_DRIVERS_PATH}/intel/dmic/dmic_computed.c ) # Platform sources