[Sound-open-firmware] [PATCH 0/7] [v3]DMIC: Patch series for direct attach microphones
This v3 patch set completes the missing changes from previous v2 patch set.
Only stereo S32_LE 48 kHz capture is supported due to hard wiring of DMIC and DMA parameters. Configurable format will be introduced later.
The used SOF kernel driver and topology were test versions. Patches will be upstreamed separately later.
This patch version was tested with the UP2 board on top of SOF git master 1e231b9dd297bc88c324502a561be67007604b16. Since there was no DMIC exHAT board connected the capture only produced the DC compensation ramp shape when the HW tries to compensate the max. full scale DC signal caused by the non-toggling PDM data line. Ramp shape looked correct and there were no discontinuities in PCM samples.
The CNL version has not been tested on a device.
Patch was also tested on a BYT device without DMIC capability where DAI/SSP feature functioned OK.
Seppo Ingalsuo (7): [v3]DMIC: Add PDM microphones (DMIC) support to DAI [v3]DMIC: Add configure command switch to disable DMIC [v3]DMIC: DMIC: Add dmic.c to drivers Makefile.am [v3]DMIC: Changes to APL platform [v3]DMIC: Changes to CNL platform [v3]DMIC: Add for format.h conversion from fractional to float [v3]DMIC: Move ceil_divide from numbers.c to numbers.h as static inline
configure.ac | 6 + src/drivers/Makefile.am | 6 +- src/drivers/dmic.c | 1361 +++++++++++++++++++++ src/include/sof/audio/format.h | 3 + src/include/sof/math/numbers.h | 15 +- src/math/numbers.c | 14 - src/platform/apollolake/dai.c | 59 + src/platform/apollolake/include/platform/memory.h | 5 + src/platform/apollolake/platform.c | 12 + src/platform/cannonlake/dai.c | 57 + src/platform/cannonlake/include/platform/memory.h | 8 +- src/platform/cannonlake/platform.c | 11 + 12 files changed, 1537 insertions(+), 20 deletions(-) create mode 100644 src/drivers/dmic.c
This patch adds the DMIC audio capture driver for SOF DAI component use. The DMIC feature allows to directly attach one to (typically) four PDM digital microphones into Intel SoC without a separate codec IC. This is supported by APL and most successor platforms.
Corresponding patches are needed for kernel driver and topology to enable this feature.
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/drivers/dmic.c | 1361 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1361 insertions(+) create mode 100644 src/drivers/dmic.c
diff --git a/src/drivers/dmic.c b/src/drivers/dmic.c new file mode 100644 index 0000000..33783f8 --- /dev/null +++ b/src/drivers/dmic.c @@ -0,0 +1,1361 @@ +/* + * Copyright (c) 2017, Intel Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the Intel Corporation nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Author: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com + */ + +#include <sof/stream.h> +#include <sof/dmic.h> +#include <sof/interrupt.h> +#include <sof/math/numbers.h> +#include <sof/audio/format.h> + +#if defined DMIC_HW_VERSION + +#include <sof/audio/coefficients/pdm_decim/pdm_decim_table.h> + +#if defined MODULE_TEST +#include <stdio.h> +#endif + +#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 + +/* Force a 48 kHz config while kernel driver IPC is under construction. Remove + * or undefine when it's available. + */ +#define DMIC_FORCE_CONFIG + +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; +}; + +struct pdm_controllers_configuration { + uint32_t cic_control; + uint32_t cic_config; + uint32_t mic_control; + uint32_t fir_control_a; + uint32_t fir_config_a; + uint32_t dc_offset_left_a; + uint32_t dc_offset_right_a; + uint32_t out_gain_left_a; + uint32_t out_gain_right_a; + uint32_t fir_control_b; + uint32_t fir_config_b; + uint32_t dc_offset_left_b; + uint32_t dc_offset_right_b; + uint32_t out_gain_left_b; + uint32_t out_gain_right_b; +}; + +/* 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 coeffcients 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 + +/* tracing */ +#define trace_dmic(__e) trace_event(TRACE_CLASS_DMIC, __e) +#define trace_dmic_error(__e) trace_error(TRACE_CLASS_DMIC, __e) +#define tracev_dmic(__e) tracev_event(TRACE_CLASS_DMIC, __e) + +/* 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}; + +#if defined MODULE_TEST +#define IO_BYTES_GLOBAL (PDM0 - OUTCONTROL0) +#define IO_BYTES_MIDDLE (PDM1 - PDM0) +#define IO_BYTES_LAST (PDM0_COEFFICIENT_B + PDM_COEF_RAM_B_LENGTH - PDM0) +#define IO_BYTES (((DMIC_HW_CONTROLLERS) - 1) * (IO_BYTES_MIDDLE) \ + + (IO_BYTES_LAST) + (IO_BYTES_GLOBAL)) +#define IO_LENGTH ((IO_BYTES) >> 2) + +static uint32_t dmic_io[IO_LENGTH]; + +static void dmic_write(struct dai *dai, uint32_t reg, uint32_t value) +{ + printf("W %04x %08x\n", reg, value); + dmic_io[reg >> 2] = value; +} + +static uint32_t dmic_read(struct dai *dai, uint32_t reg) +{ + uint32_t value = dmic_io[reg >> 2]; + + printf("R %04x %08x\n", reg, value); + return value; +} + +static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t mask, + uint32_t value) +{ + uint32_t new_value; + uint32_t old_value = dmic_io[reg >> 2]; + + new_value = (old_value & (~mask)) | value; + dmic_io[reg >> 2] = new_value; + printf("W %04x %08x\n", reg, new_value); +} +#else + +static void dmic_write(struct dai *dai, uint32_t reg, uint32_t value) +{ + io_reg_write(dai_base(dai) + reg, value); +} + +static uint32_t dmic_read(struct dai *dai, uint32_t reg) +{ + return io_reg_read(dai_base(dai) + reg); +} + +static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t mask, + uint32_t value) +{ + io_reg_update_bits(dai_base(dai) + reg, mask, value); +} +#endif + +/* 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 decim_modes *modes, + struct sof_ipc_dai_dmic_params *prm, uint32_t 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 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 (prm->pdmclk_max < DMIC_HW_PDM_CLK_MIN || + prm->pdmclk_max > DMIC_HW_IOCLK / 2) { + trace_dmic_error("pmx"); + return; + } + if (prm->pdmclk_min < DMIC_HW_PDM_CLK_MIN || + prm->pdmclk_min > prm->pdmclk_max) { + trace_dmic_error("pmn"); + return; + } + + /* Check for sane duty cycle */ + if (prm->duty_min > prm->duty_max) { + trace_dmic_error("pdu"); + return; + } + if (prm->duty_min < DMIC_HW_DUTY_MIN || + prm->duty_min > DMIC_HW_DUTY_MAX) { + trace_dmic_error("pdn"); + return; + } + if (prm->duty_max < DMIC_HW_DUTY_MIN || + prm->duty_max > DMIC_HW_DUTY_MAX) { + trace_dmic_error("pdx"); + return; + } + + /* Min and max clock dividers */ + clkdiv_min = ceil_divide(DMIC_HW_IOCLK, prm->pdmclk_max); + clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN); + clkdiv_max = DMIC_HW_IOCLK / prm->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 < prm->duty_min || + du_max > prm->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 (mfir = DMIC_HW_FIR_DECIM_MIN; + mfir <= DMIC_HW_FIR_DECIM_MAX; mfir++) { + 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; + } + } + } +#if defined MODULE_TEST + printf("# Found %d modes\n", i); +#endif +} + +/* 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; + } + + /* 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 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); + + for (i = 0; i < DMIC_FIR_LIST_LENGTH; i++) { + if (fir_list[i]->decim_factor == mfir && + 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; + } + } + + 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); + +#if defined MODULE_TEST + printf("# FIR gain need Q28 = %d (%f)\n", fir_gain, + Q_CONVERT_QTOF(fir_gain, 28)); + printf("# FIR max coef no gain Q31 = %d (%f)\n", amax, + Q_CONVERT_QTOF(amax, 31)); + printf("# FIR max coef with gain Q28 = %d (%f)\n", new_amax, + Q_CONVERT_QTOF(new_amax, 28)); + printf("# FIR coef norm rshift = %d\n", shift); + printf("# FIR coef old rshift = %d\n", add_shift); + printf("# FIR coef new rshift = %d\n", *fir_shift); + printf("# FIR coef scaler Q28 = %d (%f)\n", *fir_scale, + Q_CONVERT_QTOF(*fir_scale, 28)); +#endif + + 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 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 n = 1; + int mmin; + int count; + int mcic; + int bits_cic; + int ret; + + /* If there are more than one possibilities select a mode with lowest + * 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) { + trace_dmic_error("nom"); + 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; + + mmin = find_min_int16(mfir, modes->num_of_modes); + count = find_equal_int16(idx, mfir, mmin, modes->num_of_modes, 0); + n = idx[count - 1]; + + /* 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(cfg, cfg->mfir_a); + if (!cfg->fir_a) { + trace_dmic_error("fam"); + trace_value(cfg->mfir_a); + return -EINVAL; + } + } + + if (cfg->mfir_b > 0) { + cfg->fir_b = get_fir(cfg, cfg->mfir_b); + if (!cfg->fir_b) { + trace_dmic_error("fbm"); + trace_value(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 */ + trace_dmic_error("gci"); + 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 = (1 << (DMIC_HW_BITS_FIR_INPUT - 1)); + 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. */ + trace_dmic_error("ina"); + 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. */ + trace_dmic_error("inb"); + 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. + */ +#if DMIC_HW_VERSION == 1 + +static inline void ipm_helper(int *ipm, int stereo[], int swap[], + struct sof_ipc_dai_dmic_params *dmic) +{ + int pdm[DMIC_HW_CONTROLLERS]; + int cnt; + 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. Also it + * is checked whether 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. + */ + for (i = 0; i < dmic->number_of_pdm_controllers; i++) { + cnt = 0; + if (dmic->pdm[i].enable_mic_a > 0) + cnt++; + + if (dmic->pdm[i].enable_mic_b > 0) + cnt++; + + /* A PDM controller is used if at least one mic was enabled. */ + pdm[i] = !!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->pdm[i].enable_mic_b & !cnt; + } + + /* IPM indicates active pdm controllers. */ + *ipm = 0; + + if (pdm[0] == 0 && pdm[1] > 0) + *ipm = 1; + + if (pdm[0] > 0 && pdm[1] > 0) + *ipm = 2; +} + +#elif DMIC_HW_VERSION == 2 + +static inline void source_ipm_helper(int source[], int *ipm, int stereo[], + int swap[], struct sof_ipc_dai_dmic_params *dmic) +{ + int pdm[DMIC_HW_CONTROLLERS]; + int i; + int n = 0; + + /* Loop number of PDM controllers in the configuration. If mic A + * or B is enabled then a pdm controller is marked as active. Also it + * is checked whether 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. The function returns also in array source[] the + * indice of enabled pdm controllers to be used for IPM configuration. + */ + for (i = 0; i < dmic->number_of_pdm_controllers; i++) { + if (dmic->pdm[i].enable_mic_a > 0 || + dmic->pdm[i].enable_mic_b > 0) { + pdm[i] = 1; + source[n] = i; + n++; + } else { + pdm[i] = 0; + swap[i] = 0; + } + + if (dmic->pdm[i].enable_mic_a > 0 && + dmic->pdm[i].enable_mic_b > 0) { + stereo[i] = 1; + swap[i] = 0; + } else { + stereo[i] = 0; + if (dmic->pdm[i].enable_mic_a == 0) + swap[i] = 1; + else + swap[i] = 0; + } + } + + /* IPM bit field is set to count of active pdm controllers. */ + *ipm = pdm[0]; + for (i = 0; i < dmic->number_of_pdm_controllers; i++) + *ipm += pdm[i]; +} +#endif + +static int configure_registers(struct dai *dai, struct dmic_configuration *cfg, + struct sof_ipc_dai_dmic_params *dmic) +{ + 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 dccomp; + int cic_start_a; + int cic_start_b; + int fir_start_a; + int fir_start_b; + int soft_reset; + int i; + int j; + + struct dmic_pdata *pdata = dai_get_drvdata(dai); + int array_a = 0; + int array_b = 0; + int cic_mute = 0; + int fir_mute = 0; + int bfth = 1; /* Should be 3 for 8 entries, 1 is 2 entries */ + int th = 0; /* Used with TIE=1 */ + + /* Normal start sequence */ + dccomp = 1; + soft_reset = 1; + cic_start_a = 0; + cic_start_b = 0; + fir_start_a = 0; + fir_start_b = 0; + +#if DMIC_HW_VERSION == 2 + int source[4] = {0, 0, 0, 0}; +#endif + +#if defined MODULE_TEST + int32_t fir_a_max = 0; + int32_t fir_a_min = 0; + int32_t fir_b_max = 0; + int32_t fir_b_min = 0; +#endif + + /* pdata is set by dmic_probe(), error if it has not been set */ + if (!pdata) { + trace_dmic_error("cfr"); + return -EINVAL; + } + + /* Sanity checks */ + if (dmic->number_of_pdm_controllers > DMIC_HW_CONTROLLERS) { + trace_dmic_error("num"); + return -EINVAL; + } + + /* OUTCONTROL0 and OUTCONTROL1 */ + trace_dmic("reg"); + of0 = (dmic->fifo_bits_a == 32) ? 2 : 0; + of1 = (dmic->fifo_bits_b == 32) ? 2 : 0; + +#if DMIC_HW_VERSION == 1 + ipm_helper(&ipm, stereo, swap, dmic); + 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); + dmic_write(dai, OUTCONTROL0, val); + trace_value(val); + + 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); + dmic_write(dai, OUTCONTROL1, val); + trace_value(val); +#endif + +#if DMIC_HW_VERSION == 2 + source_ipm_helper(source, &ipm, stereo, swap, dmic); + val = OUTCONTROL0_TIE(0) | + OUTCONTROL0_SIP(0) | + OUTCONTROL0_FINIT(1) | + OUTCONTROL0_FCI(0) | + OUTCONTROL0_BFTH(3) | + OUTCONTROL0_OF(of0) | + OUTCONTROL0_NUMBER_OF_DECIMATORS(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(3); + dmic_write(dai, OUTCONTROL0, val); + trace_value(val); + + val = OUTCONTROL1_TIE(0) | + OUTCONTROL1_SIP(0) | + OUTCONTROL1_FINIT(1) | + OUTCONTROL1_FCI(0) | + OUTCONTROL1_BFTH(3) | + OUTCONTROL1_OF(of1) | + OUTCONTROL1_NUMBER_OF_DECIMATORS(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(3); + dmic_write(dai, OUTCONTROL1, val); + trace_value(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++) { + pdata->enable[i] = (dmic->pdm[i].enable_mic_b << 1) | + dmic->pdm[i].enable_mic_a; + } + + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + /* CIC */ + val = CIC_CONTROL_SOFT_RESET(soft_reset) | + CIC_CONTROL_CIC_START_B(cic_start_b) | + CIC_CONTROL_CIC_START_A(cic_start_a) | + CIC_CONTROL_MIC_B_POLARITY(dmic->pdm[i].polarity_mic_a) | + CIC_CONTROL_MIC_A_POLARITY(dmic->pdm[i].polarity_mic_b) | + CIC_CONTROL_MIC_MUTE(cic_mute) | + CIC_CONTROL_STEREO_MODE(stereo[i]); + dmic_write(dai, base[i] + CIC_CONTROL, val); + trace_value(val); + + val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) | + CIC_CONFIG_COMB_COUNT(cfg->mcic - 1); + dmic_write(dai, base[i] + CIC_CONFIG, val); + trace_value(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->pdm[i].clk_edge; + if (swap[i]) + edge = !edge; + + val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) | + MIC_CONTROL_PDM_SKEW(dmic->pdm[i].skew) | + MIC_CONTROL_CLK_EDGE(edge) | + MIC_CONTROL_PDM_EN_B(cic_start_b) | + MIC_CONTROL_PDM_EN_A(cic_start_a); + dmic_write(dai, base[i] + MIC_CONTROL, val); + trace_value(val); + + /* FIR A */ + fir_decim = MAX(cfg->mfir_a - 1, 0); + fir_length = MAX(cfg->fir_a_length - 1, 0); + val = FIR_CONTROL_A_START(fir_start_a) | + 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]); + dmic_write(dai, base[i] + FIR_CONTROL_A, val); + trace_value(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); + dmic_write(dai, base[i] + FIR_CONFIG_A, val); + trace_value(val); + + val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0); + dmic_write(dai, base[i] + DC_OFFSET_LEFT_A, val); + trace_value(val); + + val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0); + dmic_write(dai, base[i] + DC_OFFSET_RIGHT_A, val); + trace_value(val); + + val = OUT_GAIN_LEFT_A_GAIN(0); + dmic_write(dai, base[i] + OUT_GAIN_LEFT_A, val); + trace_value(val); + + val = OUT_GAIN_RIGHT_A_GAIN(0); + dmic_write(dai, base[i] + OUT_GAIN_RIGHT_A, val); + trace_value(val); + + /* FIR B */ + fir_decim = MAX(cfg->mfir_b - 1, 0); + fir_length = MAX(cfg->fir_b_length - 1, 0); + val = FIR_CONTROL_B_START(fir_start_b) | + 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]); + dmic_write(dai, base[i] + FIR_CONTROL_B, val); + trace_value(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); + dmic_write(dai, base[i] + FIR_CONFIG_B, val); + trace_value(val); + + val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0); + dmic_write(dai, base[i] + DC_OFFSET_LEFT_B, val); + trace_value(val); + trace_value(val); + + val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0); + dmic_write(dai, base[i] + DC_OFFSET_RIGHT_B, val); + trace_value(val); + + val = OUT_GAIN_LEFT_B_GAIN(0); + dmic_write(dai, base[i] + OUT_GAIN_LEFT_B, val); + trace_value(val); + + val = OUT_GAIN_RIGHT_B_GAIN(0); + dmic_write(dai, base[i] + OUT_GAIN_RIGHT_B, val); + trace_value(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); + dmic_write(dai, coef_base_a[i] + + ((length - j - 1) << 2), cu); +#if defined MODULE_TEST + fir_a_max = MAX(fir_a_max, ci); + fir_a_min = MIN(fir_a_min, ci); +#endif + } + + /* 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); + dmic_write(dai, coef_base_b[i] + + ((length - j - 1) << 2), cu); +#if defined MODULE_TEST + fir_b_max = MAX(fir_b_max, ci); + fir_b_min = MIN(fir_b_min, ci); +#endif + } + } + +#if defined MODULE_TEST + printf("# FIR A max Q19 = %d (%f)\n", fir_a_max, + Q_CONVERT_QTOF(fir_a_max, 19)); + printf("# FIR A min Q19 = %d (%f)\n", fir_a_min, + Q_CONVERT_QTOF(fir_a_min, 19)); + printf("# FIR B max Q19 = %d (%f)\n", fir_b_max, + Q_CONVERT_QTOF(fir_b_max, 19)); + printf("# FIR B min Q19 = %d (%f)\n", fir_b_min, + Q_CONVERT_QTOF(fir_b_min, 19)); +#endif + + /* Function dmic_start() uses these to start the used FIFOs */ + if (cfg->mfir_a > 0) + pdata->fifo_a = 1; + else + pdata->fifo_a = 0; + + if (cfg->mfir_b > 0) + pdata->fifo_b = 1; + else + pdata->fifo_b = 0; + + return 0; +} + +static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) +{ + struct decim_modes modes_a; + struct decim_modes modes_b; + struct matched_modes modes_ab; + struct dmic_configuration cfg; + int ret; + struct sof_ipc_dai_dmic_params *prm = &config->dmic; + struct dmic_pdata *dmic = dai_get_drvdata(dai); + + trace_dmic("dsc"); + +#if defined DMIC_FORCE_CONFIG + /* This is a temporary workaound to set parameters while + * there is no driver and topology scripts support to + * set these. Also the PCM sample format(s) and sample rate(s) + * setting would be better to be common with other DAI types. + */ + prm->driver_ipc_version = 1; + prm->pdmclk_min = 500000; /* Min 500 kHz */ + prm->pdmclk_max = 4800000; /* Max 4.80 MHz */ + prm->fifo_fs_a = 48000; + prm->fifo_fs_b = 0; + prm->fifo_bits_a = 32; + prm->fifo_bits_b = 32; + prm->duty_min = 40; /* Min. 40% */ + prm->duty_max = 60; /* Max. 60% */ + prm->number_of_pdm_controllers = 2; + prm->pdm[0].clk_edge = 0; + prm->pdm[0].enable_mic_a = 1; /* Left */ + prm->pdm[0].enable_mic_b = 1; /* Right */ + prm->pdm[0].polarity_mic_a = 0; + prm->pdm[0].polarity_mic_b = 0; + prm->pdm[0].skew = 0; + prm->pdm[1].clk_edge = 0; + prm->pdm[1].enable_mic_a = 0; /* Left */ + prm->pdm[1].enable_mic_b = 0; /* Right */ + prm->pdm[1].polarity_mic_a = 0; + prm->pdm[1].polarity_mic_b = 0; + prm->pdm[1].skew = 0; +#endif + + trace_value(prm->driver_ipc_version); + trace_value(prm->pdmclk_min); + trace_value(prm->pdmclk_max); + trace_value(prm->fifo_fs_a); + trace_value(prm->fifo_fs_b); + trace_value(prm->fifo_bits_a); + trace_value(prm->fifo_bits_b); + trace_value(prm->duty_min); + trace_value(prm->duty_max); + trace_value(prm->number_of_pdm_controllers); + + if (prm->driver_ipc_version != DMIC_IPC_VERSION) { + trace_dmic_error("ver"); + 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(&modes_a, prm, prm->fifo_fs_a); + if (modes_a.num_of_modes == 0 && prm->fifo_fs_a > 0) { + trace_dmic_error("amo"); + return -EINVAL; + } + + find_modes(&modes_b, prm, prm->fifo_fs_b); + if (modes_b.num_of_modes == 0 && prm->fifo_fs_b > 0) { + trace_dmic_error("bmo"); + return -EINVAL; + } + + match_modes(&modes_ab, &modes_a, &modes_b); + ret = select_mode(&cfg, &modes_ab); + if (ret < 0) { + trace_dmic_error("smo"); + return -EINVAL; + } + + trace_dmic("cfg"); + trace_value(cfg.clkdiv); + trace_value(cfg.mcic); + trace_value(cfg.mfir_a); + trace_value(cfg.mfir_b); + trace_value(cfg.fir_a_length); + trace_value(cfg.fir_b_length); + trace_value(cfg.cic_shift); + trace_value(cfg.fir_a_shift); + trace_value(cfg.fir_b_shift); + + /* 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, prm); + if (ret < 0) { + trace_dmic_error("cor"); + return -EINVAL; + } + + dmic->state = COMP_STATE_PREPARE; + + return 0; +} + +/* start the DMIC for capture */ +static void dmic_start(struct dai *dai) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int i; + int mic_a; + int mic_b; + int fir_a; + int fir_b; + + /* enable port */ + spin_lock(&dmic->lock); + trace_dmic("sta"); + dmic->state = COMP_STATE_ACTIVE; + + if (dmic->fifo_a) { + trace_dmic("ffa"); + /* Clear FIFO A initialize, Enable interrupts to DSP, + * Start FIFO A packer. + */ + dmic_update_bits(dai, OUTCONTROL0, + OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT, + OUTCONTROL0_SIP_BIT); + } + if (dmic->fifo_b) { + trace_dmic("ffb"); + /* Clear FIFO B initialize, Enable interrupts to DSP, + * Start FIFO B packer. + */ + dmic_update_bits(dai, OUTCONTROL1, + OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT, + OUTCONTROL1_SIP_BIT); + } + + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + mic_a = dmic->enable[i] & 1; + mic_b = (dmic->enable[i] & 2) >> 1; + if (dmic->fifo_a) + fir_a = (dmic->enable[i] > 0) ? 1 : 0; + else + fir_a = 0; + if (dmic->fifo_b) + fir_b = (dmic->enable[i] > 0) ? 1 : 0; + else + fir_b = 0; + + trace_dmic("mfn"); + trace_value(mic_a); + trace_value(mic_b); + trace_value(fir_a); + trace_value(fir_b); + + dmic_update_bits(dai, base[i] + CIC_CONTROL, + CIC_CONTROL_CIC_START_A_BIT | + CIC_CONTROL_CIC_START_B_BIT, + CIC_CONTROL_CIC_START_A(mic_a) | + CIC_CONTROL_CIC_START_B(mic_b)); + dmic_update_bits(dai, base[i] + MIC_CONTROL, + MIC_CONTROL_PDM_EN_A_BIT | + MIC_CONTROL_PDM_EN_B_BIT, + MIC_CONTROL_PDM_EN_A(mic_a) | + MIC_CONTROL_PDM_EN_B(mic_b)); + + dmic_update_bits(dai, base[i] + FIR_CONTROL_A, + FIR_CONTROL_A_START_BIT, FIR_CONTROL_A_START(fir_a)); + dmic_update_bits(dai, base[i] + FIR_CONTROL_B, + FIR_CONTROL_B_START_BIT, FIR_CONTROL_B_START(fir_b)); + } + + /* Clear soft reset for all/used PDM controllers. This should + * start capture in sync. + */ + trace_dmic("unr"); + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + dmic_update_bits(dai, base[i] + CIC_CONTROL, + CIC_CONTROL_SOFT_RESET_BIT, 0); + } + + spin_unlock(&dmic->lock); + + /* Currently there's no DMIC HW internal mutings and wait times + * applied into this start sequence. It can be implemented here if + * start of audio capture would contain clicks and/or noise and it + * is not suppressed by gain ramp somewhere in the capture pipe. + */ + trace_dmic("run"); +} +/* stop the DMIC for capture */ +static void dmic_stop(struct dai *dai) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int i; + + trace_dmic("sto") + spin_lock(&dmic->lock); + dmic->state = COMP_STATE_PREPARE; + + /* Stop FIFO packers and set FIFO initialize bits */ + dmic_update_bits(dai, OUTCONTROL0, + OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT, + OUTCONTROL0_FINIT_BIT); + dmic_update_bits(dai, OUTCONTROL1, + OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT, + OUTCONTROL1_FINIT_BIT); + + /* Set soft reset for all PDM controllers. + */ + trace_dmic("sre"); + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + dmic_update_bits(dai, base[i] + CIC_CONTROL, + CIC_CONTROL_SOFT_RESET_BIT, CIC_CONTROL_SOFT_RESET_BIT); + } + + spin_unlock(&dmic->lock); +} + +/* save DMIC context prior to entering D3 */ +static int dmic_context_store(struct dai *dai) +{ + /* TODO: Nothing stored at the moment. Could read the registers into + * persisten memory if needed but the large coef RAM is not desirable + * to copy. It would be better to store selected mode parametesr from + * previous configuration request and re-program registers from + * scratch. + */ + return 0; +} + +/* restore DMIC context after leaving D3 */ +static int dmic_context_restore(struct dai *dai) +{ + /* Nothing restored at the moment. */ + return 0; +} + +static int dmic_trigger(struct dai *dai, int cmd, int direction) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + + trace_dmic("tri"); + + /* dai private is set in dmic_probe(), error if not set */ + if (!dmic) { + trace_dmic_error("trn"); + return -EINVAL; + } + + if (direction != DAI_DIR_CAPTURE) { + trace_dmic_error("cap"); + return -EINVAL; + } + + switch (cmd) { + case COMP_TRIGGER_RELEASE: + case COMP_TRIGGER_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic_stop(dai); + break; + case COMP_TRIGGER_RESUME: + dmic_context_restore(dai); + break; + case COMP_TRIGGER_SUSPEND: + dmic_context_store(dai); + break; + default: + break; + } + + return 0; +} + +/* On DMIC IRQ event trace the status register that contains the status and + * error bit fields. + */ +static void dmic_irq_handler(void *data) +{ + struct dai *dai = data; + uint32_t val; + + /* Trace OUTSTAT0 register */ + val = dmic_read(dai, OUTSTAT0); + trace_dmic("irq"); + + if (val & OUTSTAT0_ROR_BIT) + trace_dmic_error("eor"); /* Full fifo or PDM overrrun */ + + trace_value(val); + + /* clear IRQ */ + platform_interrupt_clear(dmic_irq(dai), 1); +} + +static int dmic_probe(struct dai *dai) +{ + struct dmic_pdata *dmic; + + trace_dmic("pro"); + + /* allocate private data */ + dmic = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*dmic)); + dai_set_drvdata(dai, dmic); + + spinlock_init(&dmic->lock); + + /* Set state, note there is no playback direction support */ + dmic->state = COMP_STATE_READY; + + /* register our IRQ handler */ + interrupt_register(dmic_irq(dai), dmic_irq_handler, dai); + + platform_interrupt_unmask(dmic_irq(dai), 1); + interrupt_enable(dmic_irq(dai)); + + return 0; +} + +/* DMIC has no loopback support */ +static inline int dmic_set_loopback_mode(struct dai *dai, uint32_t lbm) +{ + return -EINVAL; +} + +const struct dai_ops dmic_ops = { + .trigger = dmic_trigger, + .set_config = dmic_set_config, + .pm_context_store = dmic_context_store, + .pm_context_restore = dmic_context_restore, + .probe = dmic_probe, + .set_loopback_mode = dmic_set_loopback_mode, +}; + +#endif
On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
This patch adds the DMIC audio capture driver for SOF DAI component use. The DMIC feature allows to directly attach one to (typically) four PDM digital microphones into Intel SoC without a separate codec IC. This is supported by APL and most successor platforms.
Corresponding patches are needed for kernel driver and topology to enable this feature.
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/drivers/dmic.c | 1361 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1361 insertions(+) create mode 100644 src/drivers/dmic.c
diff --git a/src/drivers/dmic.c b/src/drivers/dmic.c new file mode 100644 index 0000000..33783f8 --- /dev/null +++ b/src/drivers/dmic.c @@ -0,0 +1,1361 @@ +/*
- Copyright (c) 2017, Intel Corporation
- All rights reserved.
- Redistribution and use in source and binary forms, with or
without
- modification, are permitted provided that the following
conditions are met:
- Redistributions of source code must retain the above
copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above
copyright
notice, this list of conditions and the following disclaimer
in the
documentation and/or other materials provided with the
distribution.
- Neither the name of the Intel Corporation nor the
names of its contributors may be used to endorse or promote
products
derived from this software without specific prior written
permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- Author: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
- */
+#include <sof/stream.h> +#include <sof/dmic.h> +#include <sof/interrupt.h> +#include <sof/math/numbers.h> +#include <sof/audio/format.h>
+#if defined DMIC_HW_VERSION
+#include <sof/audio/coefficients/pdm_decim/pdm_decim_table.h>
+#if defined MODULE_TEST +#include <stdio.h> +#endif
Are the MODULE_TEST bits in the code for ci tests?
+#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
+/* Force a 48 kHz config while kernel driver IPC is under construction. Remove
- or undefine when it's available.
- */
+#define DMIC_FORCE_CONFIG
+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;
+};
+struct pdm_controllers_configuration {
- uint32_t cic_control;
- uint32_t cic_config;
- uint32_t mic_control;
- uint32_t fir_control_a;
- uint32_t fir_config_a;
- uint32_t dc_offset_left_a;
- uint32_t dc_offset_right_a;
- uint32_t out_gain_left_a;
- uint32_t out_gain_right_a;
- uint32_t fir_control_b;
- uint32_t fir_config_b;
- uint32_t dc_offset_left_b;
- uint32_t dc_offset_right_b;
- uint32_t out_gain_left_b;
- uint32_t out_gain_right_b;
+};
+/* 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 coeffcients for HW */ +#define DMIC_HW_FIR_COEF_MAX ((1 << (DMIC_HW_BITS_FIR_COEF - 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
+/* tracing */ +#define trace_dmic(__e) trace_event(TRACE_CLASS_DMIC, __e) +#define trace_dmic_error(__e) trace_error(TRACE_CLASS_DMIC, __e) +#define tracev_dmic(__e) tracev_event(TRACE_CLASS_DMIC, __e)
+/* 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};
+#if defined MODULE_TEST +#define IO_BYTES_GLOBAL (PDM0 - OUTCONTROL0) +#define IO_BYTES_MIDDLE (PDM1 - PDM0) +#define IO_BYTES_LAST (PDM0_COEFFICIENT_B + PDM_COEF_RAM_B_LENGTH
- PDM0)
+#define IO_BYTES (((DMIC_HW_CONTROLLERS) - 1) * (IO_BYTES_MIDDLE) \
+ (IO_BYTES_LAST) +
(IO_BYTES_GLOBAL)) +#define IO_LENGTH ((IO_BYTES) >> 2)
+static uint32_t dmic_io[IO_LENGTH];
+static void dmic_write(struct dai *dai, uint32_t reg, uint32_t value) +{
- printf("W %04x %08x\n", reg, value);
- dmic_io[reg >> 2] = value;
+}
+static uint32_t dmic_read(struct dai *dai, uint32_t reg) +{
- uint32_t value = dmic_io[reg >> 2];
- printf("R %04x %08x\n", reg, value);
- return value;
+}
+static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t mask,
- uint32_t value)
+{
- uint32_t new_value;
- uint32_t old_value = dmic_io[reg >> 2];
- new_value = (old_value & (~mask)) | value;
- dmic_io[reg >> 2] = new_value;
- printf("W %04x %08x\n", reg, new_value);
+} +#else
+static void dmic_write(struct dai *dai, uint32_t reg, uint32_t value) +{
- io_reg_write(dai_base(dai) + reg, value);
+}
+static uint32_t dmic_read(struct dai *dai, uint32_t reg) +{
- return io_reg_read(dai_base(dai) + reg);
+}
+static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t mask,
- uint32_t value)
+{
- io_reg_update_bits(dai_base(dai) + reg, mask, value);
+} +#endif
+/* 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 decim_modes *modes,
- struct sof_ipc_dai_dmic_params *prm, uint32_t 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 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 (prm->pdmclk_max < DMIC_HW_PDM_CLK_MIN ||
prm->pdmclk_max > DMIC_HW_IOCLK / 2) {
trace_dmic_error("pmx");
return;
- }
- if (prm->pdmclk_min < DMIC_HW_PDM_CLK_MIN ||
prm->pdmclk_min > prm->pdmclk_max) {
trace_dmic_error("pmn");
return;
- }
- /* Check for sane duty cycle */
- if (prm->duty_min > prm->duty_max) {
trace_dmic_error("pdu");
return;
- }
- if (prm->duty_min < DMIC_HW_DUTY_MIN ||
prm->duty_min > DMIC_HW_DUTY_MAX) {
trace_dmic_error("pdn");
return;
- }
- if (prm->duty_max < DMIC_HW_DUTY_MIN ||
prm->duty_max > DMIC_HW_DUTY_MAX) {
trace_dmic_error("pdx");
return;
- }
- /* Min and max clock dividers */
- clkdiv_min = ceil_divide(DMIC_HW_IOCLK, prm->pdmclk_max);
- clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN);
- clkdiv_max = DMIC_HW_IOCLK / prm->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 < prm->duty_min ||
du_max > prm->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 (mfir = DMIC_HW_FIR_DECIM_MIN;
mfir <= DMIC_HW_FIR_DECIM_MAX; mfir++) {
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;
}
}
- }
+#if defined MODULE_TEST
- printf("# Found %d modes\n", i);
+#endif +}
+/* 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;
- }
- /* 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 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);
- for (i = 0; i < DMIC_FIR_LIST_LENGTH; i++) {
if (fir_list[i]->decim_factor == mfir &&
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;
}
- }
- 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);
+#if defined MODULE_TEST
- printf("# FIR gain need Q28 = %d (%f)\n", fir_gain,
Q_CONVERT_QTOF(fir_gain, 28));
- printf("# FIR max coef no gain Q31 = %d (%f)\n", amax,
Q_CONVERT_QTOF(amax, 31));
- printf("# FIR max coef with gain Q28 = %d (%f)\n", new_amax,
Q_CONVERT_QTOF(new_amax, 28));
- printf("# FIR coef norm rshift = %d\n", shift);
- printf("# FIR coef old rshift = %d\n", add_shift);
- printf("# FIR coef new rshift = %d\n", *fir_shift);
- printf("# FIR coef scaler Q28 = %d (%f)\n", *fir_scale,
Q_CONVERT_QTOF(*fir_scale, 28));
+#endif
- 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 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 n = 1;
- int mmin;
- int count;
- int mcic;
- int bits_cic;
- int ret;
- /* If there are more than one possibilities select a mode
with lowest
* 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) {
trace_dmic_error("nom");
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;
- mmin = find_min_int16(mfir, modes->num_of_modes);
- count = find_equal_int16(idx, mfir, mmin, modes-
num_of_modes, 0);
- n = idx[count - 1];
- /* 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(cfg, cfg->mfir_a);
if (!cfg->fir_a) {
trace_dmic_error("fam");
trace_value(cfg->mfir_a);
return -EINVAL;
}
- }
- if (cfg->mfir_b > 0) {
cfg->fir_b = get_fir(cfg, cfg->mfir_b);
if (!cfg->fir_b) {
trace_dmic_error("fbm");
trace_value(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
*/
- mcic = cfg->mcic;
- g_cic = mcic * mcic * mcic * mcic * mcic;
- if (g_cic < 0) {
/* Erroneous decimation factor and CIC gain */
trace_dmic_error("gci");
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 = (1 << (DMIC_HW_BITS_FIR_INPUT - 1));
- 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. */
trace_dmic_error("ina");
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. */
trace_dmic_error("inb");
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.
- */
+#if DMIC_HW_VERSION == 1
+static inline void ipm_helper(int *ipm, int stereo[], int swap[],
- struct sof_ipc_dai_dmic_params *dmic)
+{
- int pdm[DMIC_HW_CONTROLLERS];
- int cnt;
- 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. Also it
* is checked whether 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.
*/
- for (i = 0; i < dmic->number_of_pdm_controllers; i++) {
cnt = 0;
if (dmic->pdm[i].enable_mic_a > 0)
cnt++;
if (dmic->pdm[i].enable_mic_b > 0)
cnt++;
/* A PDM controller is used if at least one mic was
enabled. */
pdm[i] = !!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->pdm[i].enable_mic_b & !cnt;
- }
- /* IPM indicates active pdm controllers. */
- *ipm = 0;
- if (pdm[0] == 0 && pdm[1] > 0)
*ipm = 1;
- if (pdm[0] > 0 && pdm[1] > 0)
*ipm = 2;
+}
+#elif DMIC_HW_VERSION == 2
+static inline void source_ipm_helper(int source[], int *ipm, int stereo[],
- int swap[], struct sof_ipc_dai_dmic_params *dmic)
+{
- int pdm[DMIC_HW_CONTROLLERS];
- int i;
- int n = 0;
- /* Loop number of PDM controllers in the configuration. If
mic A
* or B is enabled then a pdm controller is marked as
active. Also it
* is checked whether 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. The function returns also in array
source[] the
* indice of enabled pdm controllers to be used for IPM
configuration.
*/
- for (i = 0; i < dmic->number_of_pdm_controllers; i++) {
if (dmic->pdm[i].enable_mic_a > 0 ||
dmic->pdm[i].enable_mic_b > 0) {
pdm[i] = 1;
source[n] = i;
n++;
} else {
pdm[i] = 0;
swap[i] = 0;
}
if (dmic->pdm[i].enable_mic_a > 0 &&
dmic->pdm[i].enable_mic_b > 0) {
stereo[i] = 1;
swap[i] = 0;
} else {
stereo[i] = 0;
if (dmic->pdm[i].enable_mic_a == 0)
swap[i] = 1;
else
swap[i] = 0;
}
- }
- /* IPM bit field is set to count of active pdm controllers.
*/
- *ipm = pdm[0];
- for (i = 0; i < dmic->number_of_pdm_controllers; i++)
*ipm += pdm[i];
+} +#endif
+static int configure_registers(struct dai *dai, struct dmic_configuration *cfg,
- struct sof_ipc_dai_dmic_params *dmic)
+{
- 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 dccomp;
- int cic_start_a;
- int cic_start_b;
- int fir_start_a;
- int fir_start_b;
- int soft_reset;
- int i;
- int j;
- struct dmic_pdata *pdata = dai_get_drvdata(dai);
- int array_a = 0;
- int array_b = 0;
- int cic_mute = 0;
- int fir_mute = 0;
- int bfth = 1; /* Should be 3 for 8 entries, 1 is 2 entries
*/
- int th = 0; /* Used with TIE=1 */
- /* Normal start sequence */
- dccomp = 1;
- soft_reset = 1;
- cic_start_a = 0;
- cic_start_b = 0;
- fir_start_a = 0;
- fir_start_b = 0;
+#if DMIC_HW_VERSION == 2
- int source[4] = {0, 0, 0, 0};
+#endif
+#if defined MODULE_TEST
int32_t fir_a_max = 0;
int32_t fir_a_min = 0;
int32_t fir_b_max = 0;
int32_t fir_b_min = 0;
+#endif
- /* pdata is set by dmic_probe(), error if it has not been
set */
- if (!pdata) {
trace_dmic_error("cfr");
return -EINVAL;
- }
- /* Sanity checks */
- if (dmic->number_of_pdm_controllers > DMIC_HW_CONTROLLERS) {
trace_dmic_error("num");
return -EINVAL;
- }
- /* OUTCONTROL0 and OUTCONTROL1 */
- trace_dmic("reg");
- of0 = (dmic->fifo_bits_a == 32) ? 2 : 0;
- of1 = (dmic->fifo_bits_b == 32) ? 2 : 0;
+#if DMIC_HW_VERSION == 1
- ipm_helper(&ipm, stereo, swap, dmic);
- 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);
- dmic_write(dai, OUTCONTROL0, val);
- trace_value(val);
- 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);
- dmic_write(dai, OUTCONTROL1, val);
- trace_value(val);
+#endif
+#if DMIC_HW_VERSION == 2
- source_ipm_helper(source, &ipm, stereo, swap, dmic);
- val = OUTCONTROL0_TIE(0) |
OUTCONTROL0_SIP(0) |
OUTCONTROL0_FINIT(1) |
OUTCONTROL0_FCI(0) |
OUTCONTROL0_BFTH(3) |
OUTCONTROL0_OF(of0) |
OUTCONTROL0_NUMBER_OF_DECIMATORS(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(3);
- dmic_write(dai, OUTCONTROL0, val);
- trace_value(val);
- val = OUTCONTROL1_TIE(0) |
OUTCONTROL1_SIP(0) |
OUTCONTROL1_FINIT(1) |
OUTCONTROL1_FCI(0) |
OUTCONTROL1_BFTH(3) |
OUTCONTROL1_OF(of1) |
OUTCONTROL1_NUMBER_OF_DECIMATORS(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(3);
- dmic_write(dai, OUTCONTROL1, val);
- trace_value(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++) {
pdata->enable[i] = (dmic->pdm[i].enable_mic_b << 1)
|
dmic->pdm[i].enable_mic_a;
- }
- for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
/* CIC */
val = CIC_CONTROL_SOFT_RESET(soft_reset) |
CIC_CONTROL_CIC_START_B(cic_start_b) |
CIC_CONTROL_CIC_START_A(cic_start_a) |
CIC_CONTROL_MIC_B_POLARITY(dmic-
pdm[i].polarity_mic_a) |
CIC_CONTROL_MIC_A_POLARITY(dmic-
pdm[i].polarity_mic_b) |
CIC_CONTROL_MIC_MUTE(cic_mute) |
CIC_CONTROL_STEREO_MODE(stereo[i]);
dmic_write(dai, base[i] + CIC_CONTROL, val);
trace_value(val);
val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) |
CIC_CONFIG_COMB_COUNT(cfg->mcic - 1);
dmic_write(dai, base[i] + CIC_CONFIG, val);
trace_value(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->pdm[i].clk_edge;
if (swap[i])
edge = !edge;
val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) |
MIC_CONTROL_PDM_SKEW(dmic->pdm[i].skew) |
MIC_CONTROL_CLK_EDGE(edge) |
MIC_CONTROL_PDM_EN_B(cic_start_b) |
MIC_CONTROL_PDM_EN_A(cic_start_a);
dmic_write(dai, base[i] + MIC_CONTROL, val);
trace_value(val);
/* FIR A */
fir_decim = MAX(cfg->mfir_a - 1, 0);
fir_length = MAX(cfg->fir_a_length - 1, 0);
val = FIR_CONTROL_A_START(fir_start_a) |
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]);
dmic_write(dai, base[i] + FIR_CONTROL_A, val);
trace_value(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);
dmic_write(dai, base[i] + FIR_CONFIG_A, val);
trace_value(val);
val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0);
dmic_write(dai, base[i] + DC_OFFSET_LEFT_A, val);
trace_value(val);
val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0);
dmic_write(dai, base[i] + DC_OFFSET_RIGHT_A, val);
trace_value(val);
val = OUT_GAIN_LEFT_A_GAIN(0);
dmic_write(dai, base[i] + OUT_GAIN_LEFT_A, val);
trace_value(val);
val = OUT_GAIN_RIGHT_A_GAIN(0);
dmic_write(dai, base[i] + OUT_GAIN_RIGHT_A, val);
trace_value(val);
/* FIR B */
fir_decim = MAX(cfg->mfir_b - 1, 0);
fir_length = MAX(cfg->fir_b_length - 1, 0);
val = FIR_CONTROL_B_START(fir_start_b) |
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]);
dmic_write(dai, base[i] + FIR_CONTROL_B, val);
trace_value(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);
dmic_write(dai, base[i] + FIR_CONFIG_B, val);
trace_value(val);
val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0);
dmic_write(dai, base[i] + DC_OFFSET_LEFT_B, val);
trace_value(val);
trace_value(val);
val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0);
dmic_write(dai, base[i] + DC_OFFSET_RIGHT_B, val);
trace_value(val);
val = OUT_GAIN_LEFT_B_GAIN(0);
dmic_write(dai, base[i] + OUT_GAIN_LEFT_B, val);
trace_value(val);
val = OUT_GAIN_RIGHT_B_GAIN(0);
dmic_write(dai, base[i] + OUT_GAIN_RIGHT_B, val);
trace_value(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);
dmic_write(dai, coef_base_a[i]
+ ((length - j - 1) << 2), cu);
+#if defined MODULE_TEST
fir_a_max = MAX(fir_a_max, ci);
fir_a_min = MIN(fir_a_min, ci);
+#endif
}
/* 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);
dmic_write(dai, coef_base_b[i]
+ ((length - j - 1) << 2), cu);
+#if defined MODULE_TEST
fir_b_max = MAX(fir_b_max, ci);
fir_b_min = MIN(fir_b_min, ci);
+#endif
}
- }
+#if defined MODULE_TEST
- printf("# FIR A max Q19 = %d (%f)\n", fir_a_max,
Q_CONVERT_QTOF(fir_a_max, 19));
- printf("# FIR A min Q19 = %d (%f)\n", fir_a_min,
Q_CONVERT_QTOF(fir_a_min, 19));
- printf("# FIR B max Q19 = %d (%f)\n", fir_b_max,
Q_CONVERT_QTOF(fir_b_max, 19));
- printf("# FIR B min Q19 = %d (%f)\n", fir_b_min,
Q_CONVERT_QTOF(fir_b_min, 19));
+#endif
- /* Function dmic_start() uses these to start the used FIFOs
*/
- if (cfg->mfir_a > 0)
pdata->fifo_a = 1;
- else
pdata->fifo_a = 0;
- if (cfg->mfir_b > 0)
pdata->fifo_b = 1;
- else
pdata->fifo_b = 0;
- return 0;
+}
+static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) +{
- struct decim_modes modes_a;
- struct decim_modes modes_b;
- struct matched_modes modes_ab;
- struct dmic_configuration cfg;
- int ret;
- struct sof_ipc_dai_dmic_params *prm = &config->dmic;
- struct dmic_pdata *dmic = dai_get_drvdata(dai);
- trace_dmic("dsc");
+#if defined DMIC_FORCE_CONFIG
- /* This is a temporary workaound to set parameters while
* there is no driver and topology scripts support to
* set these. Also the PCM sample format(s) and sample
rate(s)
* setting would be better to be common with other DAI
types.
*/
- prm->driver_ipc_version = 1;
- prm->pdmclk_min = 500000; /* Min 500 kHz */
- prm->pdmclk_max = 4800000; /* Max 4.80 MHz */
- prm->fifo_fs_a = 48000;
- prm->fifo_fs_b = 0;
- prm->fifo_bits_a = 32;
- prm->fifo_bits_b = 32;
- prm->duty_min = 40; /* Min. 40% */
- prm->duty_max = 60; /* Max. 60% */
- prm->number_of_pdm_controllers = 2;
- prm->pdm[0].clk_edge = 0;
- prm->pdm[0].enable_mic_a = 1; /* Left */
- prm->pdm[0].enable_mic_b = 1; /* Right */
- prm->pdm[0].polarity_mic_a = 0;
- prm->pdm[0].polarity_mic_b = 0;
- prm->pdm[0].skew = 0;
- prm->pdm[1].clk_edge = 0;
- prm->pdm[1].enable_mic_a = 0; /* Left */
- prm->pdm[1].enable_mic_b = 0; /* Right */
- prm->pdm[1].polarity_mic_a = 0;
- prm->pdm[1].polarity_mic_b = 0;
- prm->pdm[1].skew = 0;
+#endif
- trace_value(prm->driver_ipc_version);
- trace_value(prm->pdmclk_min);
- trace_value(prm->pdmclk_max);
- trace_value(prm->fifo_fs_a);
- trace_value(prm->fifo_fs_b);
- trace_value(prm->fifo_bits_a);
- trace_value(prm->fifo_bits_b);
- trace_value(prm->duty_min);
- trace_value(prm->duty_max);
- trace_value(prm->number_of_pdm_controllers);
- if (prm->driver_ipc_version != DMIC_IPC_VERSION) {
trace_dmic_error("ver");
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(&modes_a, prm, prm->fifo_fs_a);
- if (modes_a.num_of_modes == 0 && prm->fifo_fs_a > 0) {
trace_dmic_error("amo");
return -EINVAL;
- }
- find_modes(&modes_b, prm, prm->fifo_fs_b);
- if (modes_b.num_of_modes == 0 && prm->fifo_fs_b > 0) {
trace_dmic_error("bmo");
return -EINVAL;
- }
- match_modes(&modes_ab, &modes_a, &modes_b);
- ret = select_mode(&cfg, &modes_ab);
- if (ret < 0) {
trace_dmic_error("smo");
return -EINVAL;
- }
- trace_dmic("cfg");
- trace_value(cfg.clkdiv);
- trace_value(cfg.mcic);
- trace_value(cfg.mfir_a);
- trace_value(cfg.mfir_b);
- trace_value(cfg.fir_a_length);
- trace_value(cfg.fir_b_length);
- trace_value(cfg.cic_shift);
- trace_value(cfg.fir_a_shift);
- trace_value(cfg.fir_b_shift);
- /* 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, prm);
- if (ret < 0) {
trace_dmic_error("cor");
return -EINVAL;
- }
- dmic->state = COMP_STATE_PREPARE;
- return 0;
+}
+/* start the DMIC for capture */ +static void dmic_start(struct dai *dai) +{
- struct dmic_pdata *dmic = dai_get_drvdata(dai);
- int i;
- int mic_a;
- int mic_b;
- int fir_a;
- int fir_b;
- /* enable port */
- spin_lock(&dmic->lock);
- trace_dmic("sta");
- dmic->state = COMP_STATE_ACTIVE;
- if (dmic->fifo_a) {
trace_dmic("ffa");
/* Clear FIFO A initialize, Enable interrupts to
DSP,
* Start FIFO A packer.
*/
dmic_update_bits(dai, OUTCONTROL0,
OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT,
OUTCONTROL0_SIP_BIT);
- }
- if (dmic->fifo_b) {
trace_dmic("ffb");
/* Clear FIFO B initialize, Enable interrupts to
DSP,
* Start FIFO B packer.
*/
dmic_update_bits(dai, OUTCONTROL1,
OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT,
OUTCONTROL1_SIP_BIT);
- }
- for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
mic_a = dmic->enable[i] & 1;
mic_b = (dmic->enable[i] & 2) >> 1;
if (dmic->fifo_a)
fir_a = (dmic->enable[i] > 0) ? 1 : 0;
else
fir_a = 0;
if (dmic->fifo_b)
fir_b = (dmic->enable[i] > 0) ? 1 : 0;
else
fir_b = 0;
trace_dmic("mfn");
trace_value(mic_a);
trace_value(mic_b);
trace_value(fir_a);
trace_value(fir_b);
dmic_update_bits(dai, base[i] + CIC_CONTROL,
CIC_CONTROL_CIC_START_A_BIT |
CIC_CONTROL_CIC_START_B_BIT,
CIC_CONTROL_CIC_START_A(mic_a) |
CIC_CONTROL_CIC_START_B(mic_b));
dmic_update_bits(dai, base[i] + MIC_CONTROL,
MIC_CONTROL_PDM_EN_A_BIT |
MIC_CONTROL_PDM_EN_B_BIT,
MIC_CONTROL_PDM_EN_A(mic_a) |
MIC_CONTROL_PDM_EN_B(mic_b));
dmic_update_bits(dai, base[i] + FIR_CONTROL_A,
FIR_CONTROL_A_START_BIT,
FIR_CONTROL_A_START(fir_a));
dmic_update_bits(dai, base[i] + FIR_CONTROL_B,
FIR_CONTROL_B_START_BIT,
FIR_CONTROL_B_START(fir_b));
- }
- /* Clear soft reset for all/used PDM controllers. This
should
* start capture in sync.
*/
- trace_dmic("unr");
- for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
dmic_update_bits(dai, base[i] + CIC_CONTROL,
CIC_CONTROL_SOFT_RESET_BIT, 0);
- }
- spin_unlock(&dmic->lock);
- /* Currently there's no DMIC HW internal mutings and wait
times
* applied into this start sequence. It can be implemented
here if
* start of audio capture would contain clicks and/or noise
and it
* is not suppressed by gain ramp somewhere in the capture
pipe.
*/
- trace_dmic("run");
+} +/* stop the DMIC for capture */ +static void dmic_stop(struct dai *dai) +{
- struct dmic_pdata *dmic = dai_get_drvdata(dai);
- int i;
- trace_dmic("sto")
- spin_lock(&dmic->lock);
- dmic->state = COMP_STATE_PREPARE;
- /* Stop FIFO packers and set FIFO initialize bits */
- dmic_update_bits(dai, OUTCONTROL0,
OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT,
OUTCONTROL0_FINIT_BIT);
- dmic_update_bits(dai, OUTCONTROL1,
OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT,
OUTCONTROL1_FINIT_BIT);
- /* Set soft reset for all PDM controllers.
*/
- trace_dmic("sre");
- for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
dmic_update_bits(dai, base[i] + CIC_CONTROL,
CIC_CONTROL_SOFT_RESET_BIT,
CIC_CONTROL_SOFT_RESET_BIT);
- }
- spin_unlock(&dmic->lock);
+}
+/* save DMIC context prior to entering D3 */ +static int dmic_context_store(struct dai *dai) +{
- /* TODO: Nothing stored at the moment. Could read the
registers into
* persisten memory if needed but the large coef RAM is not
desirable
* to copy. It would be better to store selected mode
parametesr from
* previous configuration request and re-program registers
from
* scratch.
*/
- return 0;
+}
+/* restore DMIC context after leaving D3 */ +static int dmic_context_restore(struct dai *dai) +{
- /* Nothing restored at the moment. */
- return 0;
+}
+static int dmic_trigger(struct dai *dai, int cmd, int direction) +{
- struct dmic_pdata *dmic = dai_get_drvdata(dai);
- trace_dmic("tri");
- /* dai private is set in dmic_probe(), error if not set */
- if (!dmic) {
trace_dmic_error("trn");
return -EINVAL;
- }
- if (direction != DAI_DIR_CAPTURE) {
trace_dmic_error("cap");
return -EINVAL;
- }
- switch (cmd) {
- case COMP_TRIGGER_RELEASE:
- case COMP_TRIGGER_START:
if (dmic->state == COMP_STATE_PREPARE ||
dmic->state == COMP_STATE_PAUSED) {
dmic_start(dai);
} else {
trace_dmic_error("cst");
trace_value(dmic->state);
}
break;
- case COMP_TRIGGER_STOP:
- case COMP_TRIGGER_PAUSE:
dmic_stop(dai);
break;
- case COMP_TRIGGER_RESUME:
dmic_context_restore(dai);
break;
- case COMP_TRIGGER_SUSPEND:
dmic_context_store(dai);
break;
- default:
break;
- }
- return 0;
+}
+/* On DMIC IRQ event trace the status register that contains the status and
- error bit fields.
- */
+static void dmic_irq_handler(void *data) +{
- struct dai *dai = data;
- uint32_t val;
- /* Trace OUTSTAT0 register */
- val = dmic_read(dai, OUTSTAT0);
- trace_dmic("irq");
- if (val & OUTSTAT0_ROR_BIT)
trace_dmic_error("eor"); /* Full fifo or PDM
overrrun */
- trace_value(val);
- /* clear IRQ */
- platform_interrupt_clear(dmic_irq(dai), 1);
+}
+static int dmic_probe(struct dai *dai) +{
- struct dmic_pdata *dmic;
- trace_dmic("pro");
- /* allocate private data */
- dmic = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*dmic));
- dai_set_drvdata(dai, dmic);
- spinlock_init(&dmic->lock);
- /* Set state, note there is no playback direction support */
- dmic->state = COMP_STATE_READY;
- /* register our IRQ handler */
- interrupt_register(dmic_irq(dai), dmic_irq_handler, dai);
- platform_interrupt_unmask(dmic_irq(dai), 1);
- interrupt_enable(dmic_irq(dai));
- return 0;
+}
+/* DMIC has no loopback support */ +static inline int dmic_set_loopback_mode(struct dai *dai, uint32_t lbm) +{
- return -EINVAL;
+}
+const struct dai_ops dmic_ops = {
- .trigger = dmic_trigger,
- .set_config = dmic_set_config,
- .pm_context_store = dmic_context_store,
- .pm_context_restore = dmic_context_restore,
- .probe = dmic_probe,
- .set_loopback_mode = dmic_set_loopback_mode,
+};
+#endif
On 12.05.2018 08:01, Ranjani Sridharan wrote:
On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
+#if defined MODULE_TEST +#include <stdio.h> +#endif
Are the MODULE_TEST bits in the code for ci tests?
Yes for myself, generally maybe. I used the output that's generated with MODULE_TEST to compare the HW registers programming to the setup that the closed Intel firmware version does. I use a hacked test bench version to run the DAI component up to capture start event.
Today or in near future it's not possible to have this in CI since the DAI component gets replaced by the file I/O component in the test pipelines. Though the test bench could run this driver (or SSP) up to capture start and then switch into file I/O based capture/playback simulation. But there's more work that likely couldn't make it to the next SOF release so better to not target it yet.
I could keep the test code or delete it for now as you prefer. Something like that would be useful if we in future would like to module test / unit test partially drivers code this way.
Thanks, Seppo
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/configure.ac b/configure.ac index d67c154..49e58a5 100644 --- a/configure.ac +++ b/configure.ac @@ -44,6 +44,12 @@ if test "$have_rimage" = "yes"; then fi AM_CONDITIONAL(BUILD_RIMAGE, test "$have_rimage" = "yes")
+# Disable DMIC driver if requested, by default build for supported platforms +AC_ARG_ENABLE([dmic], AS_HELP_STRING([--disable-dmic], [Disable DMIC driver])) +AS_IF([test "x$enable_dmic" != "xno"], [ + AC_DEFINE([CONFIG_DMIC], [1], [Configure to build DMIC driver]) +]) + # Architecture support AC_ARG_WITH([arch], AS_HELP_STRING([--with-arch], [Specify DSP architecture]),
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 99232a3..5d0c5b3 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -13,12 +13,14 @@ endif
if BUILD_APOLLOLAKE libdrivers_a_SOURCES += \ - hda-dma.c + hda-dma.c \ + dmic.c endif
if BUILD_CANNONLAKE libdrivers_a_SOURCES += \ - hda-dma.c + hda-dma.c \ + dmic.c endif
libdrivers_a_CFLAGS = \
On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 99232a3..5d0c5b3 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -13,12 +13,14 @@ endif
Applying: DMIC: DMIC: Add dmic.c to drivers Makefile.am error: patch failed: src/drivers/Makefile.am:13 error: src/drivers/Makefile.am: patch does not apply Patch failed at 0003 DMIC: DMIC: Add dmic.c to drivers Makefile.am Use 'git am --show-current-patch' to see the failed patch When you have resolved this problem, run "git am --continue".
Can you resend.
Liam
On Thu, 2018-05-17 at 14:37 +0100, Liam Girdwood wrote:
On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/drivers/Makefile.am | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am index 99232a3..5d0c5b3 100644 --- a/src/drivers/Makefile.am +++ b/src/drivers/Makefile.am @@ -13,12 +13,14 @@ endif
Applying: DMIC: DMIC: Add dmic.c to drivers Makefile.am error: patch failed: src/drivers/Makefile.am:13 error: src/drivers/Makefile.am: patch does not apply Patch failed at 0003 DMIC: DMIC: Add dmic.c to drivers Makefile.am Use 'git am --show-current-patch' to see the failed patch When you have resolved this problem, run "git am --continue".
Fixed and applied all.
Thanks
Liam
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/platform/apollolake/dai.c | 59 +++++++++++++++++++++++ src/platform/apollolake/include/platform/memory.h | 5 ++ src/platform/apollolake/platform.c | 12 +++++ 3 files changed, 76 insertions(+)
diff --git a/src/platform/apollolake/dai.c b/src/platform/apollolake/dai.c index 4463103..fea9faf 100644 --- a/src/platform/apollolake/dai.c +++ b/src/platform/apollolake/dai.c @@ -32,6 +32,7 @@ #include <sof/sof.h> #include <sof/dai.h> #include <sof/ssp.h> +#include <sof/dmic.h> #include <sof/stream.h> #include <sof/audio/component.h> #include <platform/platform.h> @@ -146,6 +147,55 @@ static struct dai ssp[PLATFORM_NUM_SSP] = { .ops = &ssp_ops, },};
+#if defined CONFIG_DMIC + +static struct dai dmic[2] = { + /* Testing idea if DMIC FIFOs A and B to access the same microphones + * with two different sample rate and PCM format could be presented + * similarly as SSP0..N. The difference however is that the DMIC + * programming is global and not per FIFO. + */ + + /* Primary FIFO A */ + { + .type = SOF_DAI_INTEL_DMIC, + .index = 0, + .plat_data = { + .base = DMIC_BASE, + .irq = IRQ_EXT_DMIC_LVL5(0), + .fifo[SOF_IPC_STREAM_PLAYBACK] = { + .offset = 0, /* No playback */ + .handshake = 0, + }, + .fifo[SOF_IPC_STREAM_CAPTURE] = { + .offset = DMIC_BASE + OUTDATA0, + .handshake = DMA_HANDSHAKE_DMIC_CH0, + } + }, + .ops = &dmic_ops, + }, + /* Secondary FIFO B */ + { + .type = SOF_DAI_INTEL_DMIC, + .index = 1, + .plat_data = { + .base = DMIC_BASE, + .irq = IRQ_EXT_DMIC_LVL5(0), + .fifo[SOF_IPC_STREAM_PLAYBACK] = { + .offset = 0, /* No playback */ + .handshake = 0, + }, + .fifo[SOF_IPC_STREAM_CAPTURE] = { + .offset = DMIC_BASE + OUTDATA1, + .handshake = DMA_HANDSHAKE_DMIC_CH1, + } + }, + .ops = &dmic_ops, + } +}; + +#endif + struct dai *dai_get(uint32_t type, uint32_t index) { int i; @@ -157,5 +207,14 @@ struct dai *dai_get(uint32_t type, uint32_t index) } }
+#if defined CONFIG_DMIC + if (type == SOF_DAI_INTEL_DMIC) { + for (i = 0; i < ARRAY_SIZE(dmic); i++) { + if (dmic[i].type == type && dmic[i].index == index) + return &dmic[i]; + } + } +#endif + return NULL; } diff --git a/src/platform/apollolake/include/platform/memory.h b/src/platform/apollolake/include/platform/memory.h index 6b3f21f..e7f3ee3 100644 --- a/src/platform/apollolake/include/platform/memory.h +++ b/src/platform/apollolake/include/platform/memory.h @@ -1,3 +1,4 @@ + /* * Copyright (c) 2016, Intel Corporation * All rights reserved. @@ -171,7 +172,11 @@ #define SOF_TEXT_SIZE 0x19000
/* initialized data */ +#if defined CONFIG_DMIC +#define SOF_DATA_SIZE 0x1a000 +#else #define SOF_DATA_SIZE 0x19000 +#endif
/* bss data */ #define SOF_BSS_DATA_SIZE 0x2800 diff --git a/src/platform/apollolake/platform.c b/src/platform/apollolake/platform.c index d24a440..0b26ae5 100644 --- a/src/platform/apollolake/platform.c +++ b/src/platform/apollolake/platform.c @@ -188,6 +188,7 @@ int platform_init(struct sof *sof) { struct dma *dmac; struct dai *ssp; + struct dai *dmic0; int i;
platform_interrupt_init(); @@ -230,6 +231,7 @@ int platform_init(struct sof *sof) SHIM_CLKCTL_I2SFDCGB(2) | SHIM_CLKCTL_I2SFDCGB(1) | SHIM_CLKCTL_I2SFDCGB(0) | + SHIM_CLKCTL_DMICFDCGB | SHIM_CLKCTL_I2SEFDCGB(1) | SHIM_CLKCTL_I2SEFDCGB(0) | SHIM_CLKCTL_TCPAPLLS | @@ -271,6 +273,16 @@ int platform_init(struct sof *sof) dai_probe(ssp); }
+ /* Init DMIC. Note that the two PDM controllers and four microphones + * supported max. those are available in platform are handled by dmic0. + */ + trace_point(TRACE_BOOT_PLATFORM_DMIC); + dmic0 = dai_get(SOF_DAI_INTEL_DMIC, 0); + if (!dmic0) + return -ENODEV; + + dai_probe(dmic0); + /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/platform/cannonlake/dai.c | 57 +++++++++++++++++++++++ src/platform/cannonlake/include/platform/memory.h | 8 +++- src/platform/cannonlake/platform.c | 11 +++++ 3 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/src/platform/cannonlake/dai.c b/src/platform/cannonlake/dai.c index 6949add..f195eca 100644 --- a/src/platform/cannonlake/dai.c +++ b/src/platform/cannonlake/dai.c @@ -33,6 +33,7 @@ #include <sof/sof.h> #include <sof/dai.h> #include <sof/ssp.h> +#include <sof/dmic.h> #include <sof/stream.h> #include <sof/audio/component.h> #include <platform/memory.h> @@ -95,6 +96,53 @@ static struct dai ssp[] = { .ops = &ssp_ops, },};
+#if defined CONFIG_DMIC +static struct dai dmic[2] = { + /* Testing idea if DMIC FIFOs A and B to access the same microphones + * with two different sample rate and PCM format could be presented + * similarly as SSP0..N. The difference however is that the DMIC + * programming is global and not per FIFO. + */ + + /* Primary FIFO A */ + { + .type = SOF_DAI_INTEL_DMIC, + .index = 0, + .plat_data = { + .base = DMIC_BASE, + .irq = IRQ_EXT_DMIC_LVL5(0), + .fifo[SOF_IPC_STREAM_PLAYBACK] = { + .offset = 0, /* No playback */ + .handshake = 0, + }, + .fifo[SOF_IPC_STREAM_CAPTURE] = { + .offset = DMIC_BASE + OUTDATA0, + .handshake = DMA_HANDSHAKE_DMIC_CH0, + } + }, + .ops = &dmic_ops, + }, + /* Secondary FIFO B */ + { + .type = SOF_DAI_INTEL_DMIC, + .index = 1, + .plat_data = { + .base = DMIC_BASE, + .irq = IRQ_EXT_DMIC_LVL5(0), + .fifo[SOF_IPC_STREAM_PLAYBACK] = { + .offset = 0, /* No playback */ + .handshake = 0, + }, + .fifo[SOF_IPC_STREAM_CAPTURE] = { + .offset = DMIC_BASE + OUTDATA1, + .handshake = DMA_HANDSHAKE_DMIC_CH1, + } + }, + .ops = &dmic_ops, + } +}; +#endif + struct dai *dai_get(uint32_t type, uint32_t index) { int i; @@ -106,5 +154,14 @@ struct dai *dai_get(uint32_t type, uint32_t index) } }
+#if defined CONFIG_DMIC + if (type == SOF_DAI_INTEL_DMIC) { + for (i = 0; i < ARRAY_SIZE(dmic); i++) { + if (dmic[i].type == type && dmic[i].index == index) + return &dmic[i]; + } + } +#endif + return NULL; } diff --git a/src/platform/cannonlake/include/platform/memory.h b/src/platform/cannonlake/include/platform/memory.h index 67f574a..244dd25 100644 --- a/src/platform/cannonlake/include/platform/memory.h +++ b/src/platform/cannonlake/include/platform/memory.h @@ -108,8 +108,8 @@ #define L2_HP_SRAM_TLB_BASE 0x00003000
/* DMICs */ -#define DMIC_BASE 0x00004000 -#define DMIC_SIZE 0x00004000 +#define DMIC_BASE 0x00010000 +#define DMIC_SIZE 0x00008000
/* SSP */ #define SSP_BASE(x) (0x00077000 + x * SSP_SIZE) @@ -240,7 +240,11 @@ #define SOF_TEXT_SIZE 0x18000
/* initialized data */ +#if defined CONFIG_DMIC +#define SOF_DATA_SIZE 0x1b000 +#else #define SOF_DATA_SIZE 0x19000 +#endif
/* bss data */ #define SOF_BSS_DATA_SIZE 0x8000 diff --git a/src/platform/cannonlake/platform.c b/src/platform/cannonlake/platform.c index 9c56047..2e85088 100644 --- a/src/platform/cannonlake/platform.c +++ b/src/platform/cannonlake/platform.c @@ -210,6 +210,7 @@ int platform_init(struct sof *sof) { struct dma *dmac; struct dai *ssp; + struct dai *dmic0; int i;
trace_point(TRACE_BOOT_PLATFORM_ENTRY); @@ -289,6 +290,16 @@ int platform_init(struct sof *sof) dai_probe(ssp); }
+ /* Init DMIC. Note that the two PDM controllers and four microphones + * supported max. those are available in platform are handled by dmic0. + */ + trace_point(TRACE_BOOT_PLATFORM_DMIC); + dmic0 = dai_get(SOF_DAI_INTEL_DMIC, 0); + if (!dmic0) + return -ENODEV; + + dai_probe(dmic0); + /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/platform/cannonlake/dai.c | 57 +++++++++++++++++++++++ src/platform/cannonlake/include/platform/memory.h | 8 +++- src/platform/cannonlake/platform.c | 11 +++++ 3 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/src/platform/cannonlake/dai.c b/src/platform/cannonlake/dai.c index 6949add..f195eca 100644 --- a/src/platform/cannonlake/dai.c +++ b/src/platform/cannonlake/dai.c @@ -33,6 +33,7 @@ #include <sof/sof.h> #include <sof/dai.h> #include <sof/ssp.h> +#include <sof/dmic.h> #include <sof/stream.h> #include <sof/audio/component.h> #include <platform/memory.h> @@ -95,6 +96,53 @@ static struct dai ssp[] = { .ops = &ssp_ops, },};
+#if defined CONFIG_DMIC +static struct dai dmic[2] = {
- /* Testing idea if DMIC FIFOs A and B to access the same
microphones
* with two different sample rate and PCM format could be
presented
* similarly as SSP0..N. The difference however is that the
DMIC
* programming is global and not per FIFO.
*/
- /* Primary FIFO A */
- {
.type = SOF_DAI_INTEL_DMIC,
.index = 0,
.plat_data = {
.base = DMIC_BASE,
.irq = IRQ_EXT_DMIC_LVL5(0),
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = 0, /* No playback */
.handshake = 0,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = DMIC_BASE + OUTDATA0,
.handshake = DMA_HANDSHAKE_DMIC_CH0,
}
},
.ops = &dmic_ops,
- },
- /* Secondary FIFO B */
- {
.type = SOF_DAI_INTEL_DMIC,
.index = 1,
.plat_data = {
.base = DMIC_BASE,
.irq = IRQ_EXT_DMIC_LVL5(0),
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = 0, /* No playback */
.handshake = 0,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = DMIC_BASE + OUTDATA1,
.handshake = DMA_HANDSHAKE_DMIC_CH1,
}
},
.ops = &dmic_ops,
- }
+}; +#endif
struct dai *dai_get(uint32_t type, uint32_t index) { int i; @@ -106,5 +154,14 @@ struct dai *dai_get(uint32_t type, uint32_t index) } }
+#if defined CONFIG_DMIC
- if (type == SOF_DAI_INTEL_DMIC) {
for (i = 0; i < ARRAY_SIZE(dmic); i++) {
if (dmic[i].type == type && dmic[i].index ==
index)
return &dmic[i];
}
- }
+#endif
- return NULL;
} diff --git a/src/platform/cannonlake/include/platform/memory.h b/src/platform/cannonlake/include/platform/memory.h index 67f574a..244dd25 100644 --- a/src/platform/cannonlake/include/platform/memory.h +++ b/src/platform/cannonlake/include/platform/memory.h @@ -108,8 +108,8 @@ #define L2_HP_SRAM_TLB_BASE 0x00003000
/* DMICs */ -#define DMIC_BASE 0x00004000 -#define DMIC_SIZE 0x00004000 +#define DMIC_BASE 0x00010000 +#define DMIC_SIZE 0x00008000
/* SSP */ #define SSP_BASE(x) (0x00077000 + x * SSP_SIZE) @@ -240,7 +240,11 @@ #define SOF_TEXT_SIZE 0x18000
/* initialized data */ +#if defined CONFIG_DMIC +#define SOF_DATA_SIZE 0x1b000
Seppo, just curious, why is this different for APL and CNL?
+#else #define SOF_DATA_SIZE 0x19000 +#endif
/* bss data */ #define SOF_BSS_DATA_SIZE 0x8000 diff --git a/src/platform/cannonlake/platform.c b/src/platform/cannonlake/platform.c index 9c56047..2e85088 100644 --- a/src/platform/cannonlake/platform.c +++ b/src/platform/cannonlake/platform.c @@ -210,6 +210,7 @@ int platform_init(struct sof *sof) { struct dma *dmac; struct dai *ssp;
struct dai *dmic0; int i;
trace_point(TRACE_BOOT_PLATFORM_ENTRY);
@@ -289,6 +290,16 @@ int platform_init(struct sof *sof) dai_probe(ssp); }
- /* Init DMIC. Note that the two PDM controllers and four
microphones
* supported max. those are available in platform are
handled by dmic0.
*/
- trace_point(TRACE_BOOT_PLATFORM_DMIC);
- dmic0 = dai_get(SOF_DAI_INTEL_DMIC, 0);
- if (!dmic0)
return -ENODEV;
- dai_probe(dmic0);
- /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
On 12.05.2018 07:54, Ranjani Sridharan wrote:
/* initialized data */ +#if defined CONFIG_DMIC +#define SOF_DATA_SIZE 0x1b000
Seppo, just curious, why is this different for APL and CNL?
Yep, they are different (0x1a000 vs. 0x1b000) and I wonder it too. If I don't increase in CNL the DMIC enabled data size the FW build fails. There's something in CNL prior to DMIC that requires larger data size after the DMIC stuff is added.
Also I noticed that SOF_DATA_SIZE need to be incremented by 0x1000 multiples. Otherwise rimage phase fails.
Thanks, Seppo
Note: Use with care since this generic macro is currently used only in host test bench for debug print commands so there are no speed or precision optimized versions yet.The cast to int64_t is not necessary for all integer types. Also float restricts precision of a 64 bit int.
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/include/sof/audio/format.h | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/src/include/sof/audio/format.h b/src/include/sof/audio/format.h index c9c23ec..f00b6f9 100644 --- a/src/include/sof/audio/format.h +++ b/src/include/sof/audio/format.h @@ -70,6 +70,9 @@ */ #define Q_CONVERT_FLOAT(f, qy) ((int)((f) * (1 << qy) + 0.5)) /* f is float */
+/* 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)
The DMIC driver needs this function. This inline version is a bit faster and the change does not increase code size.
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/include/sof/math/numbers.h | 15 +++++++++++++-- src/math/numbers.c | 14 -------------- 2 files changed, 13 insertions(+), 16 deletions(-)
diff --git a/src/include/sof/math/numbers.h b/src/include/sof/math/numbers.h index 7b57d13..5aacd8b 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -40,8 +40,19 @@
int gcd(int a, int b); /* Calculate greatest common divisor for a and b */
-/* Divide function that returns ceil() of quotient */ -int ceil_divide(int a, int b); +/* 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 inline int ceil_divide(int a, int b) +{ + int c; + + c = a / b; + if (c * b < a) + c++; + + return c; +}
/* Find indices of equal values in a vector of integer values */ int find_equal_int16(int16_t idx[], int16_t vec[], int n, int vec_length, diff --git a/src/math/numbers.c b/src/math/numbers.c index 7e39ac8..e26e4cd 100644 --- a/src/math/numbers.c +++ b/src/math/numbers.c @@ -49,20 +49,6 @@ int gcd(int a, int b) return a; }
-/* 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. - */ -int ceil_divide(int a, int b) -{ - int c; - - c = a / b; - if (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
participants (3)
-
Liam Girdwood
-
Ranjani Sridharan
-
Seppo Ingalsuo