[Sound-open-firmware] [PATCH 1/4] [RFC]DMIC: Add PDM microphones (DMIC) support to DAI
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.
Tested in APL UP squared board without connected microphones SOF git master 3ad69eb715a09de9a0b91c56c9cca8a79ead00a9
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- configure.ac | 6 + src/audio/dai.c | 5 + src/drivers/Makefile.am | 6 +- src/drivers/dmic.c | 1331 +++++++++++++++++++++++++++++++++++++++++++++++ src/include/sof/dmic.h | 318 +++++++++++ src/include/sof/io.h | 8 + src/include/sof/trace.h | 3 +- src/include/uapi/ipc.h | 61 ++- 8 files changed, 1734 insertions(+), 4 deletions(-) create mode 100644 src/drivers/dmic.c create mode 100644 src/include/sof/dmic.h
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]), diff --git a/src/audio/dai.c b/src/audio/dai.c index 7905665..1eeecd2 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -639,6 +639,11 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) break; } break; + case SOF_DAI_INTEL_DMIC: + /* TODO: No hardwired values here. */ + dd->config.burst_elems = 2; + dev->frame_bytes = 8; + break; default: /* other types of DAIs not handled for now */ trace_dai_error("de2"); 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 = \ diff --git a/src/drivers/dmic.c b/src/drivers/dmic.c new file mode 100644 index 0000000..d6b623b --- /dev/null +++ b/src/drivers/dmic.c @@ -0,0 +1,1331 @@ +/* + * 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 45 + +/* 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 { + int clkdiv[DMIC_MAX_MODES]; + int mcic[DMIC_MAX_MODES]; + int mfir[DMIC_MAX_MODES]; + int num_of_modes; +}; + +struct matched_modes { + int clkdiv[DMIC_MAX_MODES]; + int mcic[DMIC_MAX_MODES]; + int mfir_a[DMIC_MAX_MODES]; + int 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 if 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) + +/* 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 c2; + 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++) { + c1 = clkdiv >> 1; + c2 = clkdiv - c1; + du_min = 100 * c1 / clkdiv; + du_max = 100 * c2 / clkdiv; + 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; + } + } + } +} + +/* 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) +{ + int 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; + } + 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; + } + 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(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 new_scale; + int32_t fir_gain; + int shift; + const int32_t coef_max_val = Q_CONVERT_FLOAT(0.9999, 20); /* Q1.20 */ + + /* Multiply gain passed from CIC with output full scale, result Q4.20 */ + fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q23, 20, 23, 20); + + /* Find the largest FIR coefficient value */ + amax = find_max_abs_int32((int32_t *)coef, coef_length); + + /* Scale with FIR gain */ + new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, 20, 20); + if (new_amax <= 0) + return -EINVAL; + + /* Needed scale for FIR taps, target is 0.9999 max */ + new_scale = (int32_t)((((int64_t)coef_max_val << 20)) / new_amax); + + /* Find shift value */ + if (new_scale == 0) + return -EINVAL; + + shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++; + + /* Add to shift in storate Q31 format and store to configuration */ + *fir_shift = shift + add_shift; + + /* Compensate shift value to scale and store to configuration + * as Q1.31. Also apply raw 32 bit to actual HW FIR coef + * precision. + */ + *fir_scale = (fir_gain >> shift); + return 0; +} + +/* This function selects with a simple criteria one mode to set up the + * decimator. For the settings chosen for FIFOs A and B output a lookup + * is done for FIR coefficients from the included coefficients tables. + * For some decimation factors there may be several length coefficient sets. + * It is due to possible restruction of decimation engine cycles per given + * sample rate. If the coefficients length is exceeded the lookup continues. + * Therefore the list of coefficient set must present the filters for a + * decimation factor in decreasing length order. + * + * Note: If there is no filter available an error is returned. The parameters + * should be reviewed for such case. If still a filter is missing it should be + * added into the included set. FIR decimation with a high factor usually + * needs compromizes into specifications and is not desirable. + */ +static int select_mode(struct dmic_configuration *cfg, + struct matched_modes *modes) +{ + int32_t g_cic; + int32_t g_tmp; + int32_t fir_in_max; + int32_t cic_out_max; + int32_t gain_to_fir; + int idx[DMIC_MAX_MODES]; + int n = 1; + int mmin; + int count; + int *mfir; + 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; + } + + if (modes->mfir_a[0] > 0) + mfir = modes->mfir_a; + else + mfir = modes->mfir_b; + + mmin = find_min(mfir, modes->num_of_modes); + count = find_equal(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 foctors 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. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic; + g_tmp = g_cic; + bits_cic = 1; + if (g_tmp < 0) { + /* Erroneus decimation factor and CIC gain */ + trace_dmic_error("gci"); + return -EINVAL; + } + while (g_tmp > 0) { + g_tmp = g_tmp >> 1; + bits_cic++; + } + cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT; + + /* Calculate remaining gain to FIR as Q4.20. */ + 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) << 20) / 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 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++) { + if (dmic->pdm[i].enable_mic_a > 0 || + dmic->pdm[i].enable_mic_b > 0) + pdm[i] = 1; + else + pdm[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 indicates active pdm controllers. */ + *ipm = 0; + + if (pdm[0] == 0 && pdm[1] > 0) + *ipm = 1; + + if (pdm[0] > 0 && pdm[1] > 0) + *ipm = 2; +} +#endif + +#if 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 + + /* 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 ^ 1; + + 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, 20, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_A(ci); + dmic_write(dai, coef_base_a[i] + + ((length - j - 1) << 2), cu); + } + + /* 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, 20, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_B(ci); + dmic_write(dai, coef_base_b[i] + + ((length - j - 1) << 2), cu); + } + } + + /* 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 = 768000; /* Min 768 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 = 0; + 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, int direction) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int i; + int mic_a; + int mic_b; + int fir_a; + int fir_b; + + if (direction != DAI_DIR_CAPTURE) + return; + + /* 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); + + /* stop Rx if we are not capturing */ + if (dmic->state != COMP_STATE_ACTIVE) { + /* Set every PDM controller to soft reset */ + trace_dmic("sre"); + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + dmic_update_bits(dai, base[i] + CIC_CONTROL, + CIC_CONTROL_SOFT_RESET_BIT, 0); + } + + /* 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); + + dmic->state = COMP_STATE_PREPARE; + } + + /* 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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + 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; +} + +/* TODO: No idea what should be done here. Currently only trace the + * status register that contains a few 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(dmic_read(dai, OUTSTAT0)); + + /* 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 diff --git a/src/include/sof/dmic.h b/src/include/sof/dmic.h new file mode 100644 index 0000000..d86c0a7 --- /dev/null +++ b/src/include/sof/dmic.h @@ -0,0 +1,318 @@ +/* + * 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 + */ + +#ifndef __INCLUDE_DMIC__ +#define __INCLUDE_DMIC__ + +#include <sof/dai.h> +#include <sof/io.h> + +#if defined CONFIG_DMIC + +#if defined CONFIG_APOLLOLAKE +#define DMIC_HW_VERSION 1 +#define DMIC_HW_CONTROLLERS 2 +#define DMIC_HW_IOCLK 19200000 +#endif + +#if defined CONFIG_CANNONLAKE +#define DMIC_HW_VERSION 1 +#define DMIC_HW_CONTROLLERS 2 +#define DMIC_HW_IOCLK 24000000 +#endif + +#endif + +#if defined DMIC_HW_VERSION + +/* Parameters used in modes computation */ +#define DMIC_HW_BITS_CIC 26 +#define DMIC_HW_BITS_FIR_COEF 20 +#define DMIC_HW_BITS_FIR_GAIN 20 +#define DMIC_HW_BITS_FIR_INPUT 22 +#define DMIC_HW_BITS_FIR_OUTPUT 24 +#define DMIC_HW_BITS_FIR_INTERNAL 26 +#define DMIC_HW_BITS_GAIN_OUTPUT 22 +#define DMIC_HW_FIR_LENGTH_MAX 250 +#define DMIC_HW_CIC_SHIFT_MIN -8 +#define DMIC_HW_CIC_SHIFT_MAX 4 +#define DMIC_HW_FIR_SHIFT_MIN 0 +#define DMIC_HW_FIR_SHIFT_MAX 8 +#define DMIC_HW_CIC_DECIM_MIN 5 +#define DMIC_HW_CIC_DECIM_MAX 31 /* Note: Limited by BITS_CIC */ +#define DMIC_HW_FIR_DECIM_MIN 2 +#define DMIC_HW_FIR_DECIM_MAX 20 /* Note: Practical upper limit */ +#define DMIC_HW_SENS_Q23 Q_CONVERT_FLOAT(0.95, 23) /* Q1.23 */ +#define DMIC_HW_PDM_CLK_MIN 100000 /* Note: Practical min value */ +#define DMIC_HW_DUTY_MIN 20 /* Note: Practical min value */ +#define DMIC_HW_DUTY_MAX 80 /* Note: Practical max value */ + +/* DMIC register offsets */ + +/* Global registers */ +#define OUTCONTROL0 0x0000 +#define OUTSTAT0 0x0004 +#define OUTDATA0 0x0008 +#define OUTCONTROL1 0x0100 +#define OUTSTAT1 0x0104 +#define OUTDATA1 0x0108 +#define PDM0 0x1000 +#define PDM0_COEFFICIENT_A 0x1400 +#define PDM0_COEFFICIENT_B 0x1800 +#define PDM1 0x2000 +#define PDM1_COEFFICIENT_A 0x2400 +#define PDM1_COEFFICIENT_B 0x2800 +#define PDM2 0x3000 +#define PDM2_COEFFICIENT_A 0x3400 +#define PDM2_COEFFICIENT_B 0x3800 +#define PDM3 0x4000 +#define PDM3_COEFFICIENT_A 0x4400 +#define PDM3_COEFFICIENT_B 0x4800 +#define PDM_COEF_RAM_A_LENGTH 0x0400 +#define PDM_COEF_RAM_B_LENGTH 0x0400 + +/* Local registers in each PDMx */ +#define CIC_CONTROL 0x000 +#define CIC_CONFIG 0x004 +#define MIC_CONTROL 0x00c +#define FIR_CONTROL_A 0x020 +#define FIR_CONFIG_A 0x024 +#define DC_OFFSET_LEFT_A 0x028 +#define DC_OFFSET_RIGHT_A 0x02c +#define OUT_GAIN_LEFT_A 0x030 +#define OUT_GAIN_RIGHT_A 0x034 +#define FIR_CONTROL_B 0x040 +#define FIR_CONFIG_B 0x044 +#define DC_OFFSET_LEFT_B 0x048 +#define DC_OFFSET_RIGHT_B 0x04c +#define OUT_GAIN_LEFT_B 0x050 +#define OUT_GAIN_RIGHT_B 0x054 + +/* Register bits */ + +#if DMIC_HW_VERSION == 1 +/* OUTCONTROL0 bits */ +#define OUTCONTROL0_TIE_BIT BIT(27) +#define OUTCONTROL0_SIP_BIT BIT(26) +#define OUTCONTROL0_FINIT_BIT BIT(25) +#define OUTCONTROL0_FCI_BIT BIT(24) +#define OUTCONTROL0_TIE(x) SET_BIT(27, x) +#define OUTCONTROL0_SIP(x) SET_BIT(26, x) +#define OUTCONTROL0_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL0_FCI(x) SET_BIT(24, x) +#define OUTCONTROL0_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL0_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL0_IPM(x) SET_BITS(17, 16, x) +#define OUTCONTROL0_TH(x) SET_BITS(5, 0, x) + +/* OUTCONTROL1 bits */ +#define OUTCONTROL1_TIE_BIT BIT(27) +#define OUTCONTROL1_SIP_BIT BIT(26) +#define OUTCONTROL1_FINIT_BIT BIT(25) +#define OUTCONTROL1_FCI_BIT BIT(24) +#define OUTCONTROL1_TIE(x) SET_BIT(27, x) +#define OUTCONTROL1_SIP(x) SET_BIT(26, x) +#define OUTCONTROL1_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL1_FCI(x) SET_BIT(24, x) +#define OUTCONTROL1_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL1_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL1_IPM(x) SET_BITS(17, 16, x) +#define OUTCONTROL1_TH(x) SET_BITS(5, 0, x) +#endif + +#if DMIC_HW_VERSION == 2 +/* OUTCONTROL0 bits */ +#define OUTCONTROL0_TIE_BIT BIT(27) +#define OUTCONTROL0_SIP_BIT BIT(26) +#define OUTCONTROL0_FINIT_BIT BIT(25) +#define OUTCONTROL0_FCI_BIT BIT(24) +#define OUTCONTROL0_TIE(x) SET_BIT(27, x) +#define OUTCONTROL0_SIP(x) SET_BIT(26, x) +#define OUTCONTROL0_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL0_FCI(x) SET_BIT(24, x) +#define OUTCONTROL0_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL0_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL0_NUMBER_OF_DECIMATORS(x) SET_BITS(17, 15, x) +#define OUTCONTROL0_IPM_SOURCE_1(x) SET_BITS(14, 13, x) +#define OUTCONTROL0_IPM_SOURCE_2(x) SET_BITS(12, 11, x) +#define OUTCONTROL0_IPM_SOURCE_3(x) SET_BITS(10, 9, x) +#define OUTCONTROL0_IPM_SOURCE_4(x) SET_BITS(8, 7, x) +#define OUTCONTROL0_TH(x) SET_BITS(5, 0, x) + +/* OUTCONTROL1 bits */ +#define OUTCONTROL1_TIE_BIT BIT(27) +#define OUTCONTROL1_SIP_BIT BIT(26) +#define OUTCONTROL1_FINIT_BIT BIT(25) +#define OUTCONTROL1_FCI_BIT BIT(24) +#define OUTCONTROL1_TIE(x) SET_BIT(27, x) +#define OUTCONTROL1_SIP(x) SET_BIT(26, x) +#define OUTCONTROL1_FINIT(x) SET_BIT(25, x) +#define OUTCONTROL1_FCI(x) SET_BIT(24, x) +#define OUTCONTROL1_BFTH(x) SET_BITS(23, 20, x) +#define OUTCONTROL1_OF(x) SET_BITS(19, 18, x) +#define OUTCONTROL1_NUMBER_OF_DECIMATORS(x) SET_BITS(17, 15, x) +#define OUTCONTROL1_IPM_SOURCE_1(x) SET_BITS(14, 13, x) +#define OUTCONTROL1_IPM_SOURCE_2(x) SET_BITS(12, 11, x) +#define OUTCONTROL1_IPM_SOURCE_3(x) SET_BITS(10, 9, x) +#define OUTCONTROL1_IPM_SOURCE_4(x) SET_BITS(8, 7, x) +#define OUTCONTROL1_TH(x) SET_BITS(5, 0, x) +#endif + +/* OUTSTAT0 bits */ +#define OUTSTAT0_AFE_BIT BIT(31) +#define OUTSTAT0_ASNE_BIT BIT(29) +#define OUTSTAT0_RFS_BIT BIT(28) +#define OUTSTAT0_ROR_BIT BIT(27) +#define OUTSTAT0_FL_MASK MASK(6, 0) + +/* OUTSTAT1 bits */ +#define OUTSTAT1_AFE_BIT BIT(31) +#define OUTSTAT1_ASNE_BIT BIT(29) +#define OUTSTAT1_RFS_BIT BIT(28) +#define OUTSTAT1_ROR_BIT BIT(27) +#define OUTSTAT1_FL_MASK MASK(6, 0) + +/* CIC_CONTROL bits */ +#define CIC_CONTROL_SOFT_RESET_BIT BIT(16) +#define CIC_CONTROL_CIC_START_B_BIT BIT(15) +#define CIC_CONTROL_CIC_START_A_BIT BIT(14) +#define CIC_CONTROL_MIC_B_POLARITY_BIT BIT(3) +#define CIC_CONTROL_MIC_A_POLARITY_BIT BIT(2) +#define CIC_CONTROL_MIC_MUTE_BIT BIT(1) +#define CIC_CONTROL_STEREO_MODE_BIT BIT(0) + +#define CIC_CONTROL_SOFT_RESET(x) SET_BIT(16, x) +#define CIC_CONTROL_CIC_START_B(x) SET_BIT(15, x) +#define CIC_CONTROL_CIC_START_A(x) SET_BIT(14, x) +#define CIC_CONTROL_MIC_B_POLARITY(x) SET_BIT(3, x) +#define CIC_CONTROL_MIC_A_POLARITY(x) SET_BIT(2, x) +#define CIC_CONTROL_MIC_MUTE(x) SET_BIT(1, x) +#define CIC_CONTROL_STEREO_MODE(x) SET_BIT(0, x) + +/* CIC_CONFIG bits */ +#define CIC_CONFIG_CIC_SHIFT(x) SET_BITS(27, 24, x) +#define CIC_CONFIG_COMB_COUNT(x) SET_BITS(15, 8, x) + +/* MIC_CONTROL bits */ +#define MIC_CONTROL_PDM_EN_B_BIT BIT(1) +#define MIC_CONTROL_PDM_EN_A_BIT BIT(0) +#define MIC_CONTROL_PDM_CLKDIV(x) SET_BITS(15, 8, x) +#define MIC_CONTROL_PDM_SKEW(x) SET_BITS(7, 4, x) +#define MIC_CONTROL_CLK_EDGE(x) SET_BIT(3, x) +#define MIC_CONTROL_PDM_EN_B(x) SET_BIT(1, x) +#define MIC_CONTROL_PDM_EN_A(x) SET_BIT(0, x) + +/* FIR_CONTROL_A bits */ +#define FIR_CONTROL_A_START_BIT BIT(7) +#define FIR_CONTROL_A_ARRAY_START_EN_BIT BIT(6) +#define FIR_CONTROL_A_MUTE_BIT BIT(1) +#define FIR_CONTROL_A_START(x) SET_BIT(7, x) +#define FIR_CONTROL_A_ARRAY_START_EN(x) SET_BIT(6, x) +#define FIR_CONTROL_A_DCCOMP(x) SET_BIT(4, x) +#define FIR_CONTROL_A_MUTE(x) SET_BIT(1, x) +#define FIR_CONTROL_A_STEREO(x) SET_BIT(0, x) + +/* FIR_CONFIG_A bits */ +#define FIR_CONFIG_A_FIR_DECIMATION(x) SET_BITS(20, 16, x) +#define FIR_CONFIG_A_FIR_SHIFT(x) SET_BITS(11, 8, x) +#define FIR_CONFIG_A_FIR_LENGTH(x) SET_BITS(7, 0, x) + +/* DC offset compensation time constants */ +#define DCCOMP_TC0 0 +#define DCCOMP_TC1 1 +#define DCCOMP_TC2 2 +#define DCCOMP_TC3 3 +#define DCCOMP_TC4 4 +#define DCCOMP_TC5 5 +#define DCCOMP_TC6 6 +#define DCCOMP_TC7 7 + +/* DC_OFFSET_LEFT_A bits */ +#define DC_OFFSET_LEFT_A_DC_OFFS(x) SET_BITS(21, 0, x) + +/* DC_OFFSET_RIGHT_A bits */ +#define DC_OFFSET_RIGHT_A_DC_OFFS(x) SET_BITS(21, 0, x) + +/* OUT_GAIN_LEFT_A bits */ +#define OUT_GAIN_LEFT_A_GAIN(x) SET_BITS(19, 0, x) + +/* OUT_GAIN_RIGHT_A bits */ +#define OUT_GAIN_RIGHT_A_GAIN(x) SET_BITS(19, 0, x) + +/* FIR_CONTROL_B bits */ +#define FIR_CONTROL_B_START_BIT BIT(7) +#define FIR_CONTROL_B_ARRAY_START_EN_BIT BIT(6) +#define FIR_CONTROL_B_MUTE_BIT BIT(1) +#define FIR_CONTROL_B_START(x) SET_BIT(7, x) +#define FIR_CONTROL_B_ARRAY_START_EN(x) SET_BIT(6, x) +#define FIR_CONTROL_B_DCCOMP(x) SET_BIT(4, x) +#define FIR_CONTROL_B_MUTE(x) SET_BIT(1, x) +#define FIR_CONTROL_B_STEREO(x) SET_BIT(0, x) + +/* FIR_CONFIG_B bits */ +#define FIR_CONFIG_B_FIR_DECIMATION(x) SET_BITS(20, 16, x) +#define FIR_CONFIG_B_FIR_SHIFT(x) SET_BITS(11, 8, x) +#define FIR_CONFIG_B_FIR_LENGTH(x) SET_BITS(7, 0, x) + +/* DC_OFFSET_LEFT_B bits */ +#define DC_OFFSET_LEFT_B_DC_OFFS(x) SET_BITS(21, 0, x) + +/* DC_OFFSET_RIGHT_B bits */ +#define DC_OFFSET_RIGHT_B_DC_OFFS(x) SET_BITS(21, 0, x) + +/* OUT_GAIN_LEFT_B bits */ +#define OUT_GAIN_LEFT_B_GAIN(x) SET_BITS(19, 0, x) + +/* OUT_GAIN_RIGHT_B bits */ +#define OUT_GAIN_RIGHT_B_GAIN(x) SET_BITS(19, 0, x) + +/* FIR coefficients */ +#define FIR_COEF_A(x) SET_BITS(19, 0, x) +#define FIR_COEF_B(x) SET_BITS(19, 0, x) + +#define dmic_irq(dmic) \ + dmic->plat_data.irq + +/* DMIC private data */ +struct dmic_pdata { + spinlock_t lock; /* Spinlock that's used in registers IO */ + uint16_t fifo_a; + uint16_t fifo_b; + uint16_t enable[DMIC_HW_CONTROLLERS]; + uint32_t state; + completion_t drain_complete; + struct sof_ipc_dai_config config; + struct sof_ipc_dai_dmic_params params; +}; + +extern const struct dai_ops dmic_ops; + +#endif /* DMIC_HW_VERSION */ +#endif /* __INCLUDE_DMIC__ */ diff --git a/src/include/sof/io.h b/src/include/sof/io.h index 3181d41..11f870a 100644 --- a/src/include/sof/io.h +++ b/src/include/sof/io.h @@ -33,6 +33,14 @@
#include <stdint.h>
+/* Macros for register bits access */ +#define BIT(b) (1 << (b)) +#define MASK(b_hi, b_lo) ((1 << ((b_hi)-(b_lo)+1)) - 1) +#define SET_BIT(b, x) (((x) & 1) << (b)) +#define SET_BITS(b_hi, b_lo, x) \ + (((x) & ((1 << ((b_hi)-(b_lo)+1)) - 1)) << (b_lo)) + + static inline uint32_t io_reg_read(uint32_t reg) { return *((volatile uint32_t*)reg); diff --git a/src/include/sof/trace.h b/src/include/sof/trace.h index 5409a1f..2fd77fe 100644 --- a/src/include/sof/trace.h +++ b/src/include/sof/trace.h @@ -71,7 +71,7 @@ #define TRACE_BOOT_PLATFORM_IPC (TRACE_BOOT_PLATFORM + 0x170) #define TRACE_BOOT_PLATFORM_DMA (TRACE_BOOT_PLATFORM + 0x180) #define TRACE_BOOT_PLATFORM_SSP (TRACE_BOOT_PLATFORM + 0x190) - +#define TRACE_BOOT_PLATFORM_DMIC (TRACE_BOOT_PLATFORM + 0x1a0)
/* trace event classes - high 8 bits*/ #define TRACE_CLASS_IRQ (1 << 24) @@ -95,6 +95,7 @@ #define TRACE_CLASS_EQ_FIR (19 << 24) #define TRACE_CLASS_EQ_IIR (20 << 24) #define TRACE_CLASS_SA (21 << 24) +#define TRACE_CLASS_DMIC (22 << 24)
/* move to config.h */ #define TRACE 1 diff --git a/src/include/uapi/ipc.h b/src/include/uapi/ipc.h index 7d0a9a4..b32376d 100644 --- a/src/include/uapi/ipc.h +++ b/src/include/uapi/ipc.h @@ -273,9 +273,68 @@ struct sof_ipc_dai_hda_params { } __attribute__((packed));
/* DMIC Configuration Request - SOF_IPC_DAI_DMIC_CONFIG */ + +/* This struct is defined per 2ch PDM controller available in the platform. + * Normally it is sufficient to set the used microphone specific enables to 1 + * and keep other parameters as zero. The customizations are: + * + * 1. If a device mixes different microphones types with different polarity + * and/or the absolute polarity matters the PCM signal from a microphone + * can be inverted with the controls. + * + * 2. If the microphones in a stereo pair do not appear in captured stream + * in desired order due to board schematics choises they can be swapped with + * the clk_edge parameter. + * + * 3. If PDM bit errors are seen in capture (poor quality) the skew parameter + * that delays the sampling time of data by half cycles of DMIC source clock + * can be tried for improvement. However there is no guarantee for this to fix + * data integrity problems. + */ +struct sof_ipc_dai_dmic_pdm_ctrl { + uint16_t enable_mic_a; /* Use A (left) channel mic (0 or 1)*/ + uint16_t enable_mic_b; /* Use B (right) channel mic (0 or 1)*/ + uint16_t polarity_mic_a; /* Optionally invert mic A signal (0 or 1) */ + uint16_t polarity_mic_b; /* Optionally invert mic B signal (0 or 1) */ + uint16_t clk_edge; /* Optionally swap data clock edge (0 or 1) */ + uint16_t skew; /* Adjust PDM data sampling vs. clock (0..15) */ +} __attribute__((packed)); + +/* This struct contains the global settings for all 2ch PDM controllers. The + * version number used in configuration data is checked vs. version used by + * device driver src/drivers/dmic.c need to match. It is incremented from + * initial value 1 if updates done for the to driver would alter the operation + * of the microhone. + * + * Note: The microphone clock (pdmclk_min, pdmclk_max, duty_min, duty_max) + * parameters need to be set as defined in microphone data sheet. E.g. clock + * range 1.0 - 3.2 MHz is usually supported microphones. Some microphones are + * multi-mode capable and there may be denied mic clock frequencies between + * the modes. In such case set the clock range limits of the desired mode to + * avoid the driver to set clock to an illegal rate. + * + * The duty cycle could be set to 48-52% if not known. Generally these + * parameters can be altered within data sheet specified limits to match + * required audio application performance power. + * + * The microphone clock needs to be usually about 50-80 times the used audio + * sample rate. With highest sample rates above 48 kHz this can relaxed + * somewhat. + */ struct sof_ipc_dai_dmic_params { struct sof_ipc_hdr hdr; - /* TODO */ + uint32_t driver_ipc_version; /* Version (1..N) */ + uint32_t pdmclk_min; /* Minimum microphone clock in Hz (100000..N) */ + uint32_t pdmclk_max; /* Maximum microphone clock in Hz (min...N) */ + uint32_t fifo_fs_a; /* FIFO A sample rate in Hz (8000..96000) */ + uint32_t fifo_fs_b; /* FIFO B sample rate in Hz (8000..96000) */ + /* TODO: FIFO word lengths can be retrieved from SOF_DAI_FMT */ + uint16_t fifo_bits_a; /* FIFO A word length (16 or 24) */ + uint16_t fifo_bits_b; /* FIFO B word length (16 or 24) */ + uint16_t duty_min; /* Min. mic clock duty cycle in % (20..80) */ + uint16_t duty_max; /* Max. mic clock duty cycle in % (min..80) */ + uint32_t number_of_pdm_controllers; /* Number 2ch controllers (2) */ + struct sof_ipc_dai_dmic_pdm_ctrl pdm[]; } __attribute__((packed));
/* general purpose DAI configuration */
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/arch/xtensa/Makefile.am | 2 +- src/audio/src.c | 1 + src/audio/src.h | 3 -- src/include/sof/math/numbers.h | 18 +++++++++++ src/include/sof/ssp.h | 2 -- src/math/numbers.c | 68 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 6 deletions(-)
diff --git a/src/arch/xtensa/Makefile.am b/src/arch/xtensa/Makefile.am index 9f53e88..082b3c2 100644 --- a/src/arch/xtensa/Makefile.am +++ b/src/arch/xtensa/Makefile.am @@ -59,8 +59,8 @@ sof_LDADD = \ ../../platform/$(PLATFORM)/libplatform.a \ ../../ipc/libsof_ipc.a \ ../../audio/libaudio.a \ - ../../math/libsof_math.a \ ../../drivers/libdrivers.a \ + ../../math/libsof_math.a \ libreset.a \ xtos/libxtos.a \ xtos/libxlevel2.a \ diff --git a/src/audio/src.c b/src/audio/src.c index cded8b8..c19828a 100644 --- a/src/audio/src.c +++ b/src/audio/src.c @@ -42,6 +42,7 @@ #include <sof/clock.h> #include <sof/audio/component.h> #include <sof/audio/pipeline.h> +#include <sof/math/numbers.h> #include <uapi/ipc.h>
#include "src_config.h" diff --git a/src/audio/src.h b/src/audio/src.h index 3208693..5268a66 100644 --- a/src/audio/src.h +++ b/src/audio/src.h @@ -32,9 +32,6 @@ #ifndef SRC_H #define SRC_H
-#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) - struct src_param { int fir_s1; int fir_s2; diff --git a/src/include/sof/math/numbers.h b/src/include/sof/math/numbers.h index 927d39c..cadbcf9 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -33,6 +33,24 @@ #ifndef NUMBERS_H #define NUMBERS_H
+#include <stdint.h> + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) + 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); + +/* Find indices of equal values in a vector of integer values */ +int find_equal(int idx[], int vec[], int n, int vec_length, int max_results); + +/* Return the smallest value found in a vector */ +int find_min(int vec[], int vec_length); + +/* Return the largest absolute value found in a vector */ +int32_t find_max_abs_int32(int32_t vec[], int vec_length); + + #endif /* NUMBERS_H */ diff --git a/src/include/sof/ssp.h b/src/include/sof/ssp.h index c66f67b..5a57d58 100644 --- a/src/include/sof/ssp.h +++ b/src/include/sof/ssp.h @@ -38,8 +38,6 @@ #include <sof/trace.h> #include <sof/wait.h>
-#define BIT(x) (1 << (x)) - #define SSP_CLK_AUDIO 0 #define SSP_CLK_NET_PLL 1 #define SSP_CLK_EXT 2 diff --git a/src/math/numbers.c b/src/math/numbers.c index d7a1717..f3ee08d 100644 --- a/src/math/numbers.c +++ b/src/math/numbers.c @@ -36,6 +36,7 @@ */
#include <sof/math/numbers.h> +#include <sof/audio/format.h>
int gcd(int a, int b) { @@ -47,3 +48,70 @@ 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 + * 0 (or negative) or vec_length get all the matches. The max_result can be set + * to 1 to receive only the first match in ascending order. It avoids need + * for an array for idx. + */ +int find_equal(int idx[], int vec[], int n, int vec_length, + int max_results) +{ + int nresults = 0; + int i; + + for (i = 0; i < vec_length; i++) { + if (vec[i] == n) { + idx[nresults++] = i; + if (nresults == max_results) + break; + } + } + + return nresults; +} + +/* Return the smallest value found in the vector */ +int find_min(int vec[], int vec_length) +{ + int i; + int min = vec[0]; + + for (i = 1; i < vec_length; i++) + min = (vec[i] < min) ? vec[i] : min; + + return min; +} + +/* Return the largest absolute value found in the vector. Note that + * smallest negative value need to be saturated to preset as int32_t. + */ +int32_t find_max_abs_int32(int32_t vec[], int vec_length) +{ + int i; + int64_t amax = (vec[0] > 0) ? vec[0] : -vec[0]; + + for (i = 1; i < vec_length; i++) { + amax = (vec[i] > amax) ? vec[i] : amax; + amax = (-vec[i] > amax) ? -vec[i] : amax; + } + + return SATP_INT32(amax); /* Amax is always a positive value */ +}
On 4/30/18 11:04 AM, Seppo Ingalsuo wrote:
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/arch/xtensa/Makefile.am | 2 +- src/audio/src.c | 1 + src/audio/src.h | 3 -- src/include/sof/math/numbers.h | 18 +++++++++++ src/include/sof/ssp.h | 2 -- src/math/numbers.c | 68 ++++++++++++++++++++++++++++++++++++++++++
looks ok, I would add something to find the first non-zero bit (something you use quite a bit to scale factors)
6 files changed, 88 insertions(+), 6 deletions(-)
diff --git a/src/arch/xtensa/Makefile.am b/src/arch/xtensa/Makefile.am index 9f53e88..082b3c2 100644 --- a/src/arch/xtensa/Makefile.am +++ b/src/arch/xtensa/Makefile.am @@ -59,8 +59,8 @@ sof_LDADD = \ ../../platform/$(PLATFORM)/libplatform.a \ ../../ipc/libsof_ipc.a \ ../../audio/libaudio.a \
- ../../math/libsof_math.a \ ../../drivers/libdrivers.a \
- ../../math/libsof_math.a \ libreset.a \ xtos/libxtos.a \ xtos/libxlevel2.a \
diff --git a/src/audio/src.c b/src/audio/src.c index cded8b8..c19828a 100644 --- a/src/audio/src.c +++ b/src/audio/src.c @@ -42,6 +42,7 @@ #include <sof/clock.h> #include <sof/audio/component.h> #include <sof/audio/pipeline.h> +#include <sof/math/numbers.h> #include <uapi/ipc.h>
#include "src_config.h" diff --git a/src/audio/src.h b/src/audio/src.h index 3208693..5268a66 100644 --- a/src/audio/src.h +++ b/src/audio/src.h @@ -32,9 +32,6 @@ #ifndef SRC_H #define SRC_H
-#define MAX(a, b) (((a) > (b)) ? (a) : (b)) -#define MIN(a, b) (((a) < (b)) ? (a) : (b))
- struct src_param { int fir_s1; int fir_s2;
diff --git a/src/include/sof/math/numbers.h b/src/include/sof/math/numbers.h index 927d39c..cadbcf9 100644 --- a/src/include/sof/math/numbers.h +++ b/src/include/sof/math/numbers.h @@ -33,6 +33,24 @@ #ifndef NUMBERS_H #define NUMBERS_H
+#include <stdint.h>
+#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b))
- 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);
+/* Find indices of equal values in a vector of integer values */ +int find_equal(int idx[], int vec[], int n, int vec_length, int max_results);
+/* Return the smallest value found in a vector */ +int find_min(int vec[], int vec_length);
+/* Return the largest absolute value found in a vector */ +int32_t find_max_abs_int32(int32_t vec[], int vec_length);
- #endif /* NUMBERS_H */
diff --git a/src/include/sof/ssp.h b/src/include/sof/ssp.h index c66f67b..5a57d58 100644 --- a/src/include/sof/ssp.h +++ b/src/include/sof/ssp.h @@ -38,8 +38,6 @@ #include <sof/trace.h> #include <sof/wait.h>
-#define BIT(x) (1 << (x))
- #define SSP_CLK_AUDIO 0 #define SSP_CLK_NET_PLL 1 #define SSP_CLK_EXT 2
diff --git a/src/math/numbers.c b/src/math/numbers.c index d7a1717..f3ee08d 100644 --- a/src/math/numbers.c +++ b/src/math/numbers.c @@ -36,6 +36,7 @@ */
#include <sof/math/numbers.h> +#include <sof/audio/format.h>
int gcd(int a, int b) { @@ -47,3 +48,70 @@ 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
- 0 (or negative) or vec_length get all the matches. The max_result can be set
- to 1 to receive only the first match in ascending order. It avoids need
- for an array for idx.
- */
+int find_equal(int idx[], int vec[], int n, int vec_length,
- int max_results)
+{
- int nresults = 0;
- int i;
- for (i = 0; i < vec_length; i++) {
if (vec[i] == n) {
idx[nresults++] = i;
if (nresults == max_results)
break;
}
- }
- return nresults;
+}
+/* Return the smallest value found in the vector */ +int find_min(int vec[], int vec_length) +{
- int i;
- int min = vec[0];
- for (i = 1; i < vec_length; i++)
min = (vec[i] < min) ? vec[i] : min;
- return min;
+}
+/* Return the largest absolute value found in the vector. Note that
- smallest negative value need to be saturated to preset as int32_t.
- */
+int32_t find_max_abs_int32(int32_t vec[], int vec_length) +{
- int i;
- int64_t amax = (vec[0] > 0) ? vec[0] : -vec[0];
- for (i = 1; i < vec_length; i++) {
amax = (vec[i] > amax) ? vec[i] : amax;
amax = (-vec[i] > amax) ? -vec[i] : amax;
- }
- return SATP_INT32(amax); /* Amax is always a positive value */
+}
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- src/platform/apollolake/dai.c | 67 ++++++++++++++++++++++- src/platform/apollolake/include/platform/memory.h | 5 ++ src/platform/apollolake/include/platform/shim.h | 2 +- src/platform/apollolake/platform.c | 12 ++++ src/platform/cannonlake/dai.c | 65 +++++++++++++++++++++- src/platform/cannonlake/include/platform/memory.h | 8 ++- src/platform/cannonlake/platform.c | 11 ++++ 7 files changed, 161 insertions(+), 9 deletions(-)
diff --git a/src/platform/apollolake/dai.c b/src/platform/apollolake/dai.c index 7e5da44..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,14 +147,74 @@ 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;
- for (i = 0; i < ARRAY_SIZE(ssp); i++) { - if (ssp[i].type == type && ssp[i].index == index) - return &ssp[i]; + if (type == SOF_DAI_INTEL_SSP) { + for (i = 0; i < ARRAY_SIZE(ssp); i++) { + if (ssp[i].type == type && ssp[i].index == index) + return &ssp[i]; + } + } + +#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 c71d8c6..8a78892 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 0x18000 +#endif
/* bss data */ #define SOF_BSS_DATA_SIZE 0x2800 diff --git a/src/platform/apollolake/include/platform/shim.h b/src/platform/apollolake/include/platform/shim.h index c7fcc8f..daff826 100644 --- a/src/platform/apollolake/include/platform/shim.h +++ b/src/platform/apollolake/include/platform/shim.h @@ -123,7 +123,7 @@
/* LP GPDMA Force Dynamic Clock Gating bits, 0--enable */ #define SHIM_CLKCTL_LPGPDMAFDCGB(x) (0x1 << (26 + x)) -#define SHIM_CLKCTL_DMICFDCGB(x) (0x1 << 24) +#define SHIM_CLKCTL_DMICFDCGB (0x1 << 24) #define SHIM_CLKCTL_I2SFDCGB(x) (0x1 << (20 + x)) #define SHIM_CLKCTL_I2SEFDCGB(x) (0x1 << (18 + x)) #define SHIM_CLKCTL_TCPLCG(x) (0x1 << (16 + x)) diff --git a/src/platform/apollolake/platform.c b/src/platform/apollolake/platform.c index d24a440..25c880d 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 == NULL) + return -ENODEV; + + dai_probe(dmic0); + /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
diff --git a/src/platform/cannonlake/dai.c b/src/platform/cannonlake/dai.c index 6b2db36..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,14 +96,72 @@ 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;
- for (i = 0; i < ARRAY_SIZE(ssp); i++) { - if (ssp[i].type == type && ssp[i].index == index) - return &ssp[i]; + if (type == SOF_DAI_INTEL_SSP) { + for (i = 0; i < ARRAY_SIZE(ssp); i++) { + if (ssp[i].type == type && ssp[i].index == index) + return &ssp[i]; + } + } + +#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 c7f4750..08cbf28 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 0x1a000 +#else #define SOF_DATA_SIZE 0x18000 +#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..d801a0e 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 == NULL) + return -ENODEV; + + dai_probe(dmic0); + /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
Moi Seppo, maybe split this patch in two? see below.
On 4/30/18 11:04 AM, Seppo Ingalsuo wrote:
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
src/platform/apollolake/dai.c | 67 ++++++++++++++++++++++- src/platform/apollolake/include/platform/memory.h | 5 ++ src/platform/apollolake/include/platform/shim.h | 2 +- src/platform/apollolake/platform.c | 12 ++++ src/platform/cannonlake/dai.c | 65 +++++++++++++++++++++- src/platform/cannonlake/include/platform/memory.h | 8 ++- src/platform/cannonlake/platform.c | 11 ++++ 7 files changed, 161 insertions(+), 9 deletions(-)
diff --git a/src/platform/apollolake/dai.c b/src/platform/apollolake/dai.c index 7e5da44..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,14 +147,74 @@ 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;
- for (i = 0; i < ARRAY_SIZE(ssp); i++) {
if (ssp[i].type == type && ssp[i].index == index)
return &ssp[i];
- if (type == SOF_DAI_INTEL_SSP) {
for (i = 0; i < ARRAY_SIZE(ssp); i++) {
if (ssp[i].type == type && ssp[i].index == index)
return &ssp[i];
}
- }
this is unrelated to DMICs and would be good as a separate patch added first.
+#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 c71d8c6..8a78892 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 0x18000 +#endif
/* bss data */ #define SOF_BSS_DATA_SIZE 0x2800 diff --git a/src/platform/apollolake/include/platform/shim.h b/src/platform/apollolake/include/platform/shim.h index c7fcc8f..daff826 100644 --- a/src/platform/apollolake/include/platform/shim.h +++ b/src/platform/apollolake/include/platform/shim.h @@ -123,7 +123,7 @@
/* LP GPDMA Force Dynamic Clock Gating bits, 0--enable */ #define SHIM_CLKCTL_LPGPDMAFDCGB(x) (0x1 << (26 + x)) -#define SHIM_CLKCTL_DMICFDCGB(x) (0x1 << 24) +#define SHIM_CLKCTL_DMICFDCGB (0x1 << 24)
same here, this is not DMIC-specific
#define SHIM_CLKCTL_I2SFDCGB(x) (0x1 << (20 + x)) #define SHIM_CLKCTL_I2SEFDCGB(x) (0x1 << (18 + x)) #define SHIM_CLKCTL_TCPLCG(x) (0x1 << (16 + x)) diff --git a/src/platform/apollolake/platform.c b/src/platform/apollolake/platform.c index d24a440..25c880d 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 |
same here
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 == NULL)
return -ENODEV;
- dai_probe(dmic0);
- /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
diff --git a/src/platform/cannonlake/dai.c b/src/platform/cannonlake/dai.c index 6b2db36..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,14 +96,72 @@ 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;
- for (i = 0; i < ARRAY_SIZE(ssp); i++) {
if (ssp[i].type == type && ssp[i].index == index)
return &ssp[i];
- if (type == SOF_DAI_INTEL_SSP) {
for (i = 0; i < ARRAY_SIZE(ssp); i++) {
if (ssp[i].type == type && ssp[i].index == index)
return &ssp[i];
}
- }
and here
+#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 c7f4750..08cbf28 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 0x1a000 +#else #define SOF_DATA_SIZE 0x18000 +#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..d801a0e 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 == NULL)
return -ENODEV;
- dai_probe(dmic0);
- /* Initialize DMA for Trace*/ dma_trace_init_complete(sof->dmat);
On 30.04.2018 22:06, Pierre-Louis Bossart wrote:
Moi Seppo, maybe split this patch in two? see below.
OK, I'll send the 1st patch soon that addresses the generic (not directly DMIC related) modifications for dai.c and shim.h first.
Thanks, Seppo
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com --- .../audio/coefficients/pdm_decim/pdm_decim_fir.h | 43 ++++ .../pdm_decim_int32_02_4288_5100_010_095.h | 98 ++++++++ .../pdm_decim_int32_02_4375_5100_010_095.h | 108 +++++++++ .../pdm_decim_int32_03_3850_5100_010_095.h | 100 ++++++++ .../pdm_decim_int32_03_4375_5100_010_095.h | 164 +++++++++++++ .../pdm_decim_int32_04_4375_5100_010_095.h | 218 +++++++++++++++++ .../pdm_decim_int32_05_4331_5100_010_095.h | 258 +++++++++++++++++++++ .../pdm_decim_int32_06_4156_5100_010_095.h | 256 ++++++++++++++++++++ .../pdm_decim_int32_08_4156_5380_010_090.h | 254 ++++++++++++++++++++ .../audio/coefficients/pdm_decim/pdm_decim_table.h | 60 +++++ 10 files changed, 1559 insertions(+) create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_fir.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4288_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4375_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_3850_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_4375_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_04_4375_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_05_4331_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_06_4156_5100_010_095.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_08_4156_5380_010_090.h create mode 100644 src/include/sof/audio/coefficients/pdm_decim/pdm_decim_table.h
diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_fir.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_fir.h new file mode 100644 index 0000000..788bf8c --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_fir.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018, 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 + * + */ + +/* Format for generated coefficients tables */ + +struct pdm_decim { + int decim_factor; + int length; + int shift; + int relative_passband; + int relative_stopband; + int passband_ripple; + int stopband_ripple; + const int32_t *coef; +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4288_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4288_5100_010_095.h new file mode 100644 index 0000000..389a0a8 --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4288_5100_010_095.h @@ -0,0 +1,98 @@ +const int32_t fir_int32_02_4288_5100_010_095[91] = { + -193886, + 104552, + 2140521, + 5355562, + 5945318, + 1216991, + -4511367, + -3527243, + 3415937, + 5526880, + -1935806, + -7515849, + -386524, + 9252379, + 3862172, + -10093681, + -8251552, + 9738620, + 13536988, + -7464432, + -19100785, + 2935187, + 24375729, + 4314479, + -28271418, + -14141458, + 29999307, + 26636531, + -28180625, + -41208987, + 21649022, + 57264356, + -8757879, + -73520802, + -12043366, + 88868414, + 43784598, + -101162825, + -91853580, + 106519993, + 169110273, + -94206010, + -317139630, + 5894310, + 739661049, + 1132200744, + 739661049, + 5894310, + -317139630, + -94206010, + 169110273, + 106519993, + -91853580, + -101162825, + 43784598, + 88868414, + -12043366, + -73520802, + -8757879, + 57264356, + 21649022, + -41208987, + -28180625, + 26636531, + 29999307, + -14141458, + -28271418, + 4314479, + 24375729, + 2935187, + -19100785, + -7464432, + 13536988, + 9738620, + -8251552, + -10093681, + 3862172, + 9252379, + -386524, + -7515849, + -1935806, + 5526880, + 3415937, + -3527243, + -4511367, + 1216991, + 5945318, + 5355562, + 2140521, + 104552, + -193886 + +}; + +struct pdm_decim pdm_decim_int32_02_4288_5100_010_095 = { + 2, 91, 0, 4288, 5100, 10, 95, fir_int32_02_4288_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4375_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4375_5100_010_095.h new file mode 100644 index 0000000..3825593 --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_02_4375_5100_010_095.h @@ -0,0 +1,108 @@ +const int32_t fir_int32_02_4375_5100_010_095[101] = { + -587830, + -2653881, + -5154608, + -4845367, + -226473, + 4220832, + 2571159, + -3184701, + -4043579, + 2206821, + 5554546, + -750496, + -6923900, + -1268584, + 8073360, + 4085183, + -8546477, + -7505364, + 8176185, + 11533751, + -6471060, + -15704256, + 3359707, + 19852409, + 1635593, + -23144509, + -8252640, + 25285013, + 16574479, + -25723227, + -26663304, + 23549736, + 38139664, + -17943366, + -50446983, + 8141043, + 63090267, + 7051351, + -75166959, + -29039894, + 85772627, + 60568979, + -93167356, + -106799776, + 94198973, + 180962815, + -78385592, + -324820245, + -12243140, + 742491441, + 1151461281, + 742491441, + -12243140, + -324820245, + -78385592, + 180962815, + 94198973, + -106799776, + -93167356, + 60568979, + 85772627, + -29039894, + -75166959, + 7051351, + 63090267, + 8141043, + -50446983, + -17943366, + 38139664, + 23549736, + -26663304, + -25723227, + 16574479, + 25285013, + -8252640, + -23144509, + 1635593, + 19852409, + 3359707, + -15704256, + -6471060, + 11533751, + 8176185, + -7505364, + -8546477, + 4085183, + 8073360, + -1268584, + -6923900, + -750496, + 5554546, + 2206821, + -4043579, + -3184701, + 2571159, + 4220832, + -226473, + -4845367, + -5154608, + -2653881, + -587830 + +}; + +struct pdm_decim pdm_decim_int32_02_4375_5100_010_095 = { + 2, 101, 0, 4375, 5100, 10, 95, fir_int32_02_4375_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_3850_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_3850_5100_010_095.h new file mode 100644 index 0000000..9b98d7e --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_3850_5100_010_095.h @@ -0,0 +1,100 @@ +const int32_t fir_int32_03_3850_5100_010_095[93] = { + 44212, + -302176, + -1360920, + -3291119, + -5495374, + -6588668, + -5102927, + -753403, + 4644861, + 7677973, + 5490212, + -1618066, + -9302333, + -11445730, + -4732005, + 7709057, + 17063964, + 14699335, + -457541, + -18980508, + -26202607, + -13319132, + 13695670, + 35545996, + 32740745, + 1994202, + -37267233, + -54079734, + -29355959, + 25448678, + 71128406, + 67328423, + 5964667, + -74773313, + -111583346, + -62643893, + 52737606, + 154899783, + 153311689, + 16759099, + -184962926, + -304661987, + -201939017, + 160731847, + 673585388, + 1122541471, + 1300561761, + 1122541471, + 673585388, + 160731847, + -201939017, + -304661987, + -184962926, + 16759099, + 153311689, + 154899783, + 52737606, + -62643893, + -111583346, + -74773313, + 5964667, + 67328423, + 71128406, + 25448678, + -29355959, + -54079734, + -37267233, + 1994202, + 32740745, + 35545996, + 13695670, + -13319132, + -26202607, + -18980508, + -457541, + 14699335, + 17063964, + 7709057, + -4732005, + -11445730, + -9302333, + -1618066, + 5490212, + 7677973, + 4644861, + -753403, + -5102927, + -6588668, + -5495374, + -3291119, + -1360920, + -302176, + 44212 + +}; + +struct pdm_decim pdm_decim_int32_03_3850_5100_010_095 = { + 3, 93, 1, 3850, 5100, 10, 95, fir_int32_03_3850_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_4375_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_4375_5100_010_095.h new file mode 100644 index 0000000..d8f2f27 --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_03_4375_5100_010_095.h @@ -0,0 +1,164 @@ +const int32_t fir_int32_03_4375_5100_010_095[157] = { + 350904, + 1127891, + 2233546, + 3059556, + 2752177, + 818057, + -2252661, + -4944515, + -5550704, + -3466227, + 53089, + 2496755, + 1904111, + -1421730, + -4818556, + -5204443, + -1721082, + 3155305, + 5311444, + 2454515, + -3518616, + -7589471, + -5713308, + 1327821, + 7901341, + 7958087, + 527903, + -8633996, + -11354795, + -4214518, + 7627116, + 13970242, + 8263364, + -5861946, + -16549651, + -13529964, + 2213919, + 17870757, + 19056207, + 2854066, + -18029728, + -24979102, + -9860164, + 16175117, + 30545984, + 18605951, + -11894271, + -35172531, + -28918160, + 4746528, + 38201092, + 40590666, + 5825429, + -38712951, + -53159148, + -20283384, + 35723256, + 66120531, + 39265868, + -27910967, + -78795928, + -63663750, + 13458962, + 90416047, + 95193346, + 10755142, + -99897049, + -137497231, + -51075841, + 105457455, + 200047804, + 124561003, + -101612190, + -313384381, + -297343743, + 53701878, + 639681661, + 1187800564, + 1411050887, + 1187800564, + 639681661, + 53701878, + -297343743, + -313384381, + -101612190, + 124561003, + 200047804, + 105457455, + -51075841, + -137497231, + -99897049, + 10755142, + 95193346, + 90416047, + 13458962, + -63663750, + -78795928, + -27910967, + 39265868, + 66120531, + 35723256, + -20283384, + -53159148, + -38712951, + 5825429, + 40590666, + 38201092, + 4746528, + -28918160, + -35172531, + -11894271, + 18605951, + 30545984, + 16175117, + -9860164, + -24979102, + -18029728, + 2854066, + 19056207, + 17870757, + 2213919, + -13529964, + -16549651, + -5861946, + 8263364, + 13970242, + 7627116, + -4214518, + -11354795, + -8633996, + 527903, + 7958087, + 7901341, + 1327821, + -5713308, + -7589471, + -3518616, + 2454515, + 5311444, + 3155305, + -1721082, + -5204443, + -4818556, + -1421730, + 1904111, + 2496755, + 53089, + -3466227, + -5550704, + -4944515, + -2252661, + 818057, + 2752177, + 3059556, + 2233546, + 1127891, + 350904 + +}; + +struct pdm_decim pdm_decim_int32_03_4375_5100_010_095 = { + 3, 157, 1, 4375, 5100, 10, 95, fir_int32_03_4375_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_04_4375_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_04_4375_5100_010_095.h new file mode 100644 index 0000000..a2a64fa --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_04_4375_5100_010_095.h @@ -0,0 +1,218 @@ +const int32_t fir_int32_04_4375_5100_010_095[211] = { + 126017, + 745791, + 1735783, + 3250059, + 4963537, + 6400069, + 6949502, + 6142229, + 3902336, + 705842, + -2485787, + -4540607, + -4626085, + -2636100, + 646448, + 3784383, + 5275090, + 4259502, + 1015665, + -3045153, + -5950963, + -6099091, + -3115593, + 1807814, + 6342508, + 8093924, + 5838000, + 296533, + -6011752, + -9879686, + -9013045, + -3339610, + 4700101, + 11152288, + 12461593, + 7387989, + -2085667, + -11448775, + -15769545, + -12252662, + -1940150, + 10437445, + 18576639, + 17768944, + 7550319, + -7626200, + -20251921, + -23428157, + -14572076, + 2797464, + 20329234, + 28790530, + 22864155, + 4330980, + -18201801, + -33211863, + -32081436, + -13923851, + 13233081, + 35870337, + 41620943, + 25914222, + -4896316, + -35895437, + -50712381, + -40042952, + -7212823, + 32428935, + 58525317, + 56015029, + 23582638, + -24374765, + -63897455, + -73234460, + -44550852, + 10604843, + 65540282, + 91054416, + 70624752, + 10408242, + -61706258, + -108584856, + -102490195, + -40744453, + 50090298, + 124919090, + 141743373, + 84173003, + -26760833, + -138851415, + -191966143, + -148464813, + -16173880, + 148695193, + 262835505, + 254088691, + 100293640, + -149816104, + -385869984, + -474827254, + -315628113, + 112831849, + 731720961, + 1381072959, + 1872325415, + 2055132469, + 1872325415, + 1381072959, + 731720961, + 112831849, + -315628113, + -474827254, + -385869984, + -149816104, + 100293640, + 254088691, + 262835505, + 148695193, + -16173880, + -148464813, + -191966143, + -138851415, + -26760833, + 84173003, + 141743373, + 124919090, + 50090298, + -40744453, + -102490195, + -108584856, + -61706258, + 10408242, + 70624752, + 91054416, + 65540282, + 10604843, + -44550852, + -73234460, + -63897455, + -24374765, + 23582638, + 56015029, + 58525317, + 32428935, + -7212823, + -40042952, + -50712381, + -35895437, + -4896316, + 25914222, + 41620943, + 35870337, + 13233081, + -13923851, + -32081436, + -33211863, + -18201801, + 4330980, + 22864155, + 28790530, + 20329234, + 2797464, + -14572076, + -23428157, + -20251921, + -7626200, + 7550319, + 17768944, + 18576639, + 10437445, + -1940150, + -12252662, + -15769545, + -11448775, + -2085667, + 7387989, + 12461593, + 11152288, + 4700101, + -3339610, + -9013045, + -9879686, + -6011752, + 296533, + 5838000, + 8093924, + 6342508, + 1807814, + -3115593, + -6099091, + -5950963, + -3045153, + 1015665, + 4259502, + 5275090, + 3784383, + 646448, + -2636100, + -4626085, + -4540607, + -2485787, + 705842, + 3902336, + 6142229, + 6949502, + 6400069, + 4963537, + 3250059, + 1735783, + 745791, + 126017 + +}; + +struct pdm_decim pdm_decim_int32_04_4375_5100_010_095 = { + 4, 211, 2, 4375, 5100, 10, 95, fir_int32_04_4375_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_05_4331_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_05_4331_5100_010_095.h new file mode 100644 index 0000000..25bb9eb --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_05_4331_5100_010_095.h @@ -0,0 +1,258 @@ +const int32_t fir_int32_05_4331_5100_010_095[251] = { + -250963, + -530472, + -956449, + -1440505, + -1861966, + -2058350, + -1862594, + -1155766, + 78930, + 1719645, + 3501958, + 5061143, + 6013975, + 6066039, + 5113131, + 3303104, + 1032343, + -1135681, + -2614668, + -2968117, + -2057986, + -118896, + 2276521, + 4363476, + 5409644, + 4953617, + 2982225, + -20415, + -3181416, + -5487913, + -6107986, + -4685208, + -1505051, + 2535666, + 6167372, + 8134966, + 7619351, + 4554012, + -280001, + -5435156, + -9220394, + -10246686, + -7922490, + -2726786, + 3853867, + 9734139, + 12878468, + 11991508, + 7020539, + -717311, + -8859727, + -14705610, + -16097433, + -12195790, + -3874308, + 6431337, + 15415517, + 19946809, + 18139792, + 10093658, + -1980159, + -14334496, + -22829097, + -24298020, + -17696570, + -4621840, + 11019276, + 24149376, + 30144976, + 26434112, + 13524367, + -4930822, + -23124320, + -34892082, + -35768599, + -24626114, + -4311380, + 19011680, + 37690383, + 45054463, + 37744852, + 17102968, + -10919933, + -37416293, + -53300866, + -52364722, + -33596881, + -1935268, + 32928131, + 59438198, + 67925581, + 54022129, + 20569914, + -22737901, + -62048140, + -83661289, + -78659667, + -46330665, + 4859442, + 59276671, + 98707953, + 108233225, + 81552245, + 23955872, + -48257755, + -112070264, + -144691658, + -131171943, + -70276392, + 23448172, + 122479978, + 193637242, + 208100323, + 151336952, + 29895897, + -127299559, + -274102498, + -357874627, + -333256339, + -175377009, + 111180608, + 490397234, + 900786668, + 1268179136, + 1522305381, + 1613059887, + 1522305381, + 1268179136, + 900786668, + 490397234, + 111180608, + -175377009, + -333256339, + -357874627, + -274102498, + -127299559, + 29895897, + 151336952, + 208100323, + 193637242, + 122479978, + 23448172, + -70276392, + -131171943, + -144691658, + -112070264, + -48257755, + 23955872, + 81552245, + 108233225, + 98707953, + 59276671, + 4859442, + -46330665, + -78659667, + -83661289, + -62048140, + -22737901, + 20569914, + 54022129, + 67925581, + 59438198, + 32928131, + -1935268, + -33596881, + -52364722, + -53300866, + -37416293, + -10919933, + 17102968, + 37744852, + 45054463, + 37690383, + 19011680, + -4311380, + -24626114, + -35768599, + -34892082, + -23124320, + -4930822, + 13524367, + 26434112, + 30144976, + 24149376, + 11019276, + -4621840, + -17696570, + -24298020, + -22829097, + -14334496, + -1980159, + 10093658, + 18139792, + 19946809, + 15415517, + 6431337, + -3874308, + -12195790, + -16097433, + -14705610, + -8859727, + -717311, + 7020539, + 11991508, + 12878468, + 9734139, + 3853867, + -2726786, + -7922490, + -10246686, + -9220394, + -5435156, + -280001, + 4554012, + 7619351, + 8134966, + 6167372, + 2535666, + -1505051, + -4685208, + -6107986, + -5487913, + -3181416, + -20415, + 2982225, + 4953617, + 5409644, + 4363476, + 2276521, + -118896, + -2057986, + -2968117, + -2614668, + -1135681, + 1032343, + 3303104, + 5113131, + 6066039, + 6013975, + 5061143, + 3501958, + 1719645, + 78930, + -1155766, + -1862594, + -2058350, + -1861966, + -1440505, + -956449, + -530472, + -250963 + +}; + +struct pdm_decim pdm_decim_int32_05_4331_5100_010_095 = { + 5, 251, 2, 4331, 5100, 10, 95, fir_int32_05_4331_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_06_4156_5100_010_095.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_06_4156_5100_010_095.h new file mode 100644 index 0000000..01d149b --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_06_4156_5100_010_095.h @@ -0,0 +1,256 @@ +const int32_t fir_int32_06_4156_5100_010_095[249] = { + -145670, + -159762, + -183049, + -132077, + 40047, + 375196, + 897258, + 1599108, + 2433074, + 3308273, + 4097062, + 4651618, + 4829700, + 4525669, + 3700263, + 2402258, + 776038, + -950161, + -2496541, + -3579851, + -3970442, + -3547641, + -2337956, + -529617, + 1547925, + 3476297, + 4827277, + 5253398, + 4572039, + 2823110, + 284249, + -2564887, + -5127174, + -6808166, + -7148614, + -5940791, + -3300873, + 324045, + 4222816, + 7553233, + 9518313, + 9550081, + 7459490, + 3515883, + -1570147, + -6764980, + -10915019, + -12995804, + -12355318, + -8895306, + -3143626, + 3808819, + 10504528, + 15416643, + 17288185, + 15439779, + 9973227, + 1815303, + -7421871, + -15747713, + -21211963, + -22349384, + -18551421, + -10274491, + 976318, + 12902423, + 22867792, + 28468157, + 28093650, + 21356476, + 9275878, + -5840836, + -20818261, + -32262581, + -37311768, + -34324420, + -23346677, + -6234152, + 13625405, + 31957173, + 44495096, + 47945809, + 40808741, + 23856504, + 140921, + -25513494, + -47455167, + -60412143, + -60710132, + -47229950, + -21869611, + 10631696, + 43519737, + 69370312, + 81696614, + 76484113, + 53307373, + 15747228, + -29042608, + -71597234, + -102026296, + -112167649, + -97565632, + -58831316, + -2036750, + 62029440, + 119693403, + 157031723, + 162840122, + 131357475, + 64171528, + -29147598, + -131921532, + -222701994, + -278742412, + -280032642, + -213184166, + -74440208, + 128790505, + 378234661, + 646928572, + 902966510, + 1114231037, + 1253321369, + 1301843153, + 1253321369, + 1114231037, + 902966510, + 646928572, + 378234661, + 128790505, + -74440208, + -213184166, + -280032642, + -278742412, + -222701994, + -131921532, + -29147598, + 64171528, + 131357475, + 162840122, + 157031723, + 119693403, + 62029440, + -2036750, + -58831316, + -97565632, + -112167649, + -102026296, + -71597234, + -29042608, + 15747228, + 53307373, + 76484113, + 81696614, + 69370312, + 43519737, + 10631696, + -21869611, + -47229950, + -60710132, + -60412143, + -47455167, + -25513494, + 140921, + 23856504, + 40808741, + 47945809, + 44495096, + 31957173, + 13625405, + -6234152, + -23346677, + -34324420, + -37311768, + -32262581, + -20818261, + -5840836, + 9275878, + 21356476, + 28093650, + 28468157, + 22867792, + 12902423, + 976318, + -10274491, + -18551421, + -22349384, + -21211963, + -15747713, + -7421871, + 1815303, + 9973227, + 15439779, + 17288185, + 15416643, + 10504528, + 3808819, + -3143626, + -8895306, + -12355318, + -12995804, + -10915019, + -6764980, + -1570147, + 3515883, + 7459490, + 9550081, + 9518313, + 7553233, + 4222816, + 324045, + -3300873, + -5940791, + -7148614, + -6808166, + -5127174, + -2564887, + 284249, + 2823110, + 4572039, + 5253398, + 4827277, + 3476297, + 1547925, + -529617, + -2337956, + -3547641, + -3970442, + -3579851, + -2496541, + -950161, + 776038, + 2402258, + 3700263, + 4525669, + 4829700, + 4651618, + 4097062, + 3308273, + 2433074, + 1599108, + 897258, + 375196, + 40047, + -132077, + -183049, + -159762, + -145670 + +}; + +struct pdm_decim pdm_decim_int32_06_4156_5100_010_095 = { + 6, 249, 2, 4156, 5100, 10, 95, fir_int32_06_4156_5100_010_095 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_08_4156_5380_010_090.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_08_4156_5380_010_090.h new file mode 100644 index 0000000..b18df3d --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_int32_08_4156_5380_010_090.h @@ -0,0 +1,254 @@ +const int32_t fir_int32_08_4156_5380_010_090[247] = { + -337052, + -90075, + 37780, + 310298, + 762602, + 1418014, + 2279975, + 3324413, + 4495298, + 5703536, + 6831280, + 7740936, + 8289504, + 8346168, + 7812523, + 6641741, + 4854841, + 2549644, + -98570, + -2847418, + -5407472, + -7472951, + -8758755, + -9040926, + -8192669, + -6213693, + -3246278, + 424677, + 4395972, + 8180309, + 11265189, + 13176475, + 13545386, + 12170147, + 9059463, + 4454447, + -1180491, + -7196755, + -12830960, + -17296055, + -19885729, + -20077500, + -17621714, + -12601266, + -5452197, + 3062539, + 11925735, + 19980775, + 26075722, + 29220338, + 28734576, + 24368837, + 16375791, + 5519930, + -6982346, + -19587960, + -30609997, + -38433685, + -41741838, + -39718804, + -32202058, + -19758650, + -3667506, + 14195787, + 31565234, + 46060011, + 55497992, + 58207057, + 53292128, + 40817642, + 21874421, + -1487369, + -26460171, + -49799428, + -68227825, + -78875217, + -79697543, + -69816864, + -49730377, + -21350328, + 12143051, + 46630130, + 77532435, + 100390923, + 111473843, + 108334402, + 90240198, + 58408793, + 16001822, + -32138446, + -79996329, + -121085165, + -149268412, + -159601418, + -149086287, + -117235239, + -66355092, + -1494553, + 69965597, + 139055884, + 196228005, + 232526458, + 240790250, + 216740138, + 159811182, + 73611998, + -34070591, + -151754583, + -265210746, + -358796902, + -417052464, + -426388521, + -376691518, + -262659116, + -84707915, + 150664267, + 431125422, + 739363099, + 1054476937, + 1353781691, + 1614844650, + 1817552687, + 1945997296, + 1989982406, + 1945997296, + 1817552687, + 1614844650, + 1353781691, + 1054476937, + 739363099, + 431125422, + 150664267, + -84707915, + -262659116, + -376691518, + -426388521, + -417052464, + -358796902, + -265210746, + -151754583, + -34070591, + 73611998, + 159811182, + 216740138, + 240790250, + 232526458, + 196228005, + 139055884, + 69965597, + -1494553, + -66355092, + -117235239, + -149086287, + -159601418, + -149268412, + -121085165, + -79996329, + -32138446, + 16001822, + 58408793, + 90240198, + 108334402, + 111473843, + 100390923, + 77532435, + 46630130, + 12143051, + -21350328, + -49730377, + -69816864, + -79697543, + -78875217, + -68227825, + -49799428, + -26460171, + -1487369, + 21874421, + 40817642, + 53292128, + 58207057, + 55497992, + 46060011, + 31565234, + 14195787, + -3667506, + -19758650, + -32202058, + -39718804, + -41741838, + -38433685, + -30609997, + -19587960, + -6982346, + 5519930, + 16375791, + 24368837, + 28734576, + 29220338, + 26075722, + 19980775, + 11925735, + 3062539, + -5452197, + -12601266, + -17621714, + -20077500, + -19885729, + -17296055, + -12830960, + -7196755, + -1180491, + 4454447, + 9059463, + 12170147, + 13545386, + 13176475, + 11265189, + 8180309, + 4395972, + 424677, + -3246278, + -6213693, + -8192669, + -9040926, + -8758755, + -7472951, + -5407472, + -2847418, + -98570, + 2549644, + 4854841, + 6641741, + 7812523, + 8346168, + 8289504, + 7740936, + 6831280, + 5703536, + 4495298, + 3324413, + 2279975, + 1418014, + 762602, + 310298, + 37780, + -90075, + -337052 + +}; + +struct pdm_decim pdm_decim_int32_08_4156_5380_010_090 = { + 8, 247, 3, 4156, 5380, 10, 90, fir_int32_08_4156_5380_010_090 +}; diff --git a/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_table.h b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_table.h new file mode 100644 index 0000000..8237252 --- /dev/null +++ b/src/include/sof/audio/coefficients/pdm_decim/pdm_decim_table.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018, 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 + * + */ + +/* PDM decimation FIR filters */ + +#include "pdm_decim_fir.h" +#include "pdm_decim_int32_02_4288_5100_010_095.h" +#include "pdm_decim_int32_02_4375_5100_010_095.h" +#include "pdm_decim_int32_03_3850_5100_010_095.h" +#include "pdm_decim_int32_03_4375_5100_010_095.h" +#include "pdm_decim_int32_04_4375_5100_010_095.h" +#include "pdm_decim_int32_05_4331_5100_010_095.h" +#include "pdm_decim_int32_06_4156_5100_010_095.h" +#include "pdm_decim_int32_08_4156_5380_010_090.h" + +#define DMIC_FIR_LIST_LENGTH 8 + +/* Note: Higher spec filter must be before lower spec filter + * if there are multiple filters for a decimation factor. The naming + * scheme of coefficients set is: + * <type>_<decim factor>_<rel passband>_<rel stopband>_<ripple>_<attenuation> + */ +struct pdm_decim *fir_list[DMIC_FIR_LIST_LENGTH] = { + &pdm_decim_int32_02_4375_5100_010_095, + &pdm_decim_int32_02_4288_5100_010_095, + &pdm_decim_int32_03_4375_5100_010_095, + &pdm_decim_int32_03_3850_5100_010_095, + &pdm_decim_int32_04_4375_5100_010_095, + &pdm_decim_int32_05_4331_5100_010_095, + &pdm_decim_int32_06_4156_5100_010_095, + &pdm_decim_int32_08_4156_5380_010_090, +};
On 4/30/18 11:04 AM, 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.
Tested in APL UP squared board without connected microphones SOF git master 3ad69eb715a09de9a0b91c56c9cca8a79ead00a9
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
configure.ac | 6 + src/audio/dai.c | 5 + src/drivers/Makefile.am | 6 +- src/drivers/dmic.c | 1331 +++++++++++++++++++++++++++++++++++++++++++++++
this is a big one...
src/include/sof/dmic.h | 318 +++++++++++ src/include/sof/io.h | 8 + src/include/sof/trace.h | 3 +- src/include/uapi/ipc.h | 61 ++-
and all those files could really be in separate patches since they prepare the work for dmic enablement.
8 files changed, 1734 insertions(+), 4 deletions(-) create mode 100644 src/drivers/dmic.c create mode 100644 src/include/sof/dmic.h
[...]
+/* 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 c2;
- 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++) {
c1 = clkdiv >> 1;
c2 = clkdiv - c1;
du_min = 100 * c1 / clkdiv;
du_min = 100 * (clkdiv/2)/clkdiv -> 50?
du_max = 100 * c2 / clkdiv;
c2 = clkdiv - c1 = clkdiv/2, so du_min == du_max ? what am I missing?
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;
}
}
- }
+}
+/* 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)
+{
- int 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;
is this initialization to zero needed?
}
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;
same here, is this initialization needed?
}
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(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 new_scale;
- int32_t fir_gain;
- int shift;
- const int32_t coef_max_val = Q_CONVERT_FLOAT(0.9999, 20); /* Q1.20 */
- /* Multiply gain passed from CIC with output full scale, result Q4.20 */
- fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q23, 20, 23, 20);
- /* Find the largest FIR coefficient value */
- amax = find_max_abs_int32((int32_t *)coef, coef_length);
- /* Scale with FIR gain */
- new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, 20, 20);
- if (new_amax <= 0)
return -EINVAL;
- /* Needed scale for FIR taps, target is 0.9999 max */
- new_scale = (int32_t)((((int64_t)coef_max_val << 20)) / new_amax);
- /* Find shift value */
- if (new_scale == 0)
return -EINVAL;
- shift = 0;
- while ((new_scale << shift) < (1 << 20))
shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
- /* Add to shift in storate Q31 format and store to configuration */
- *fir_shift = shift + add_shift;
- /* Compensate shift value to scale and store to configuration
* as Q1.31. Also apply raw 32 bit to actual HW FIR coef
* precision.
*/
- *fir_scale = (fir_gain >> shift);
- return 0;
+}
+/* This function selects with a simple criteria one mode to set up the
- decimator. For the settings chosen for FIFOs A and B output a lookup
- is done for FIR coefficients from the included coefficients tables.
- For some decimation factors there may be several length coefficient sets.
- It is due to possible restruction of decimation engine cycles per given
- sample rate. If the coefficients length is exceeded the lookup continues.
- Therefore the list of coefficient set must present the filters for a
- decimation factor in decreasing length order.
- Note: If there is no filter available an error is returned. The parameters
- should be reviewed for such case. If still a filter is missing it should be
- added into the included set. FIR decimation with a high factor usually
- needs compromizes into specifications and is not desirable.
- */
+static int select_mode(struct dmic_configuration *cfg,
- struct matched_modes *modes)
+{
- int32_t g_cic;
- int32_t g_tmp;
- int32_t fir_in_max;
- int32_t cic_out_max;
- int32_t gain_to_fir;
- int idx[DMIC_MAX_MODES];
- int n = 1;
- int mmin;
- int count;
- int *mfir;
- 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;
- }
- if (modes->mfir_a[0] > 0)
mfir = modes->mfir_a;
- else
mfir = modes->mfir_b;
does look like the initialization done above has a side effect here... worth a comment maybe?
- mmin = find_min(mfir, modes->num_of_modes);
- count = find_equal(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 foctors of FIR
factors
* 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.
*/
- mcic = cfg->mcic;
- g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
- g_tmp = g_cic;
- bits_cic = 1;
- if (g_tmp < 0) {
/* Erroneus decimation factor and CIC gain */
erroneous?
trace_dmic_error("gci");
return -EINVAL;
- }
- while (g_tmp > 0) {
g_tmp = g_tmp >> 1;
bits_cic++;
- }
this again looks like finding the first non-zero bit
- cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT;
- /* Calculate remaining gain to FIR as Q4.20. */
- 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) << 20) / 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 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++) {
if (dmic->pdm[i].enable_mic_a > 0 ||
dmic->pdm[i].enable_mic_b > 0)
pdm[i] = 1;
else
pdm[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;
}
the logic is weird here.
if (a || b) ... else ...
if (a && b) ... else // this could be a || b or !a && !b??
- }
- /* 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;
+} +#endif
+#if 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
- /* 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 ^ 1;
! 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, 20, DMIC_HW_FIR_COEF_Q);
cu = FIR_COEF_A(ci);
dmic_write(dai, coef_base_a[i]
+ ((length - j - 1) << 2), cu);
}
/* 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, 20, DMIC_HW_FIR_COEF_Q);
cu = FIR_COEF_B(ci);
dmic_write(dai, coef_base_b[i]
+ ((length - j - 1) << 2), cu);
}
- }
- /* 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 = 768000; /* Min 768 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 = 0;
- 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, int direction) +{
- struct dmic_pdata *dmic = dai_get_drvdata(dai);
- int i;
- int mic_a;
- int mic_b;
- int fir_a;
- int fir_b;
- if (direction != DAI_DIR_CAPTURE)
return;
- /* 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);
- /* stop Rx if we are not capturing */
- if (dmic->state != COMP_STATE_ACTIVE) {
/* Set every PDM controller to soft reset */
trace_dmic("sre");
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
dmic_update_bits(dai, base[i] + CIC_CONTROL,
CIC_CONTROL_SOFT_RESET_BIT, 0);
}
/* 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);
dmic->state = COMP_STATE_PREPARE;
- }
- /* 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_START:
if (dmic->state == COMP_STATE_PREPARE ||
dmic->state == COMP_STATE_PAUSED) {
dmic_start(dai, direction);
} else {
trace_dmic_error("cst");
trace_value(dmic->state);
}
break;
- case COMP_TRIGGER_RELEASE:
if (dmic->state == COMP_STATE_PREPARE) {
dmic_start(dai, direction);
} else {
trace_dmic_error("crl");
trace_value(dmic->state);
}
break;
- case COMP_TRIGGER_STOP:
- case COMP_TRIGGER_PAUSE:
dmic->state = COMP_STATE_PAUSED;
dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
break;
- case COMP_TRIGGER_RESUME:
dmic_context_restore(dai);
break;
- case COMP_TRIGGER_SUSPEND:
dmic_context_store(dai);
break;
- default:
break;
- }
- return 0;
+}
+/* TODO: No idea what should be done here. Currently only trace the
- status register that contains a few 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(dmic_read(dai, OUTSTAT0));
- /* 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,
+};
[cut here]
Thanks for reading this! I have questions below:
On 30.04.2018 23:22, Pierre-Louis Bossart wrote:
On 4/30/18 11:04 AM, 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.
Tested in APL UP squared board without connected microphones SOF git master 3ad69eb715a09de9a0b91c56c9cca8a79ead00a9
Signed-off-by: Seppo Ingalsuo seppo.ingalsuo@linux.intel.com
configure.ac | 6 + src/audio/dai.c | 5 + src/drivers/Makefile.am | 6 +- src/drivers/dmic.c | 1331 +++++++++++++++++++++++++++++++++++++++++++++++
this is a big one...
src/include/sof/dmic.h | 318 +++++++++++ src/include/sof/io.h | 8 + src/include/sof/trace.h | 3 +- src/include/uapi/ipc.h | 61 ++-
and all those files could really be in separate patches since they prepare the work for dmic enablement.
OK, will do!
8 files changed, 1734 insertions(+), 4 deletions(-) create mode 100644 src/drivers/dmic.c create mode 100644 src/include/sof/dmic.h
[...]
+/* 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 c2; + 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++) { + c1 = clkdiv >> 1; + c2 = clkdiv - c1; + du_min = 100 * c1 / clkdiv;
du_min = 100 * (clkdiv/2)/clkdiv -> 50?
+ du_max = 100 * c2 / clkdiv;
c2 = clkdiv - c1 = clkdiv/2, so du_min == du_max ? what am I missing?
The duty cycle becomes non-50% when the divider is odd, e.g. clkdiv = 5 case:
c1 = clkdiv >> 1 = 2 c2 = clkdiv - c1 = 5 - 2 = 3 du_min = 100*c1/clkdiv = 100*2/5 = 200/5 = 40 du_max = 100*c2/clkdif = 100*3/5 = 300/5 = 60
The latter equation can be though simplified to "c2 = 100 - c1;" that I didn't realize earlier.
+ 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; + } + } + } +}
+/* 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) +{ + int 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;
is this initialization to zero needed?
Yes, It indicates the FIFO B path won't be 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;
same here, is this initialization needed?
And vice versa no FIFO A path 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(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 new_scale; + int32_t fir_gain; + int shift; + const int32_t coef_max_val = Q_CONVERT_FLOAT(0.9999, 20); /* Q1.20 */
+ /* Multiply gain passed from CIC with output full scale, result Q4.20 */ + fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q23, 20, 23, 20);
+ /* Find the largest FIR coefficient value */ + amax = find_max_abs_int32((int32_t *)coef, coef_length);
+ /* Scale with FIR gain */ + new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, 20, 20); + if (new_amax <= 0) + return -EINVAL;
+ /* Needed scale for FIR taps, target is 0.9999 max */ + new_scale = (int32_t)((((int64_t)coef_max_val << 20)) / new_amax);
+ /* Find shift value */ + if (new_scale == 0) + return -EINVAL;
+ shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
Not yet I think but such would be useful. I'll add it to math/numbers. Since DSPs have an instruction for that it would be then easier to optimize into single location.
+ /* Add to shift in storate Q31 format and store to configuration */ + *fir_shift = shift + add_shift;
+ /* Compensate shift value to scale and store to configuration + * as Q1.31. Also apply raw 32 bit to actual HW FIR coef + * precision. + */ + *fir_scale = (fir_gain >> shift); + return 0; +}
+/* This function selects with a simple criteria one mode to set up the
- decimator. For the settings chosen for FIFOs A and B output a lookup
- is done for FIR coefficients from the included coefficients tables.
- For some decimation factors there may be several length
coefficient sets.
- It is due to possible restruction of decimation engine cycles per
given
- sample rate. If the coefficients length is exceeded the lookup
continues.
- Therefore the list of coefficient set must present the filters for a
- decimation factor in decreasing length order.
- Note: If there is no filter available an error is returned. The
parameters
- should be reviewed for such case. If still a filter is missing it
should be
- added into the included set. FIR decimation with a high factor
usually
- needs compromizes into specifications and is not desirable.
- */
+static int select_mode(struct dmic_configuration *cfg, + struct matched_modes *modes) +{ + int32_t g_cic; + int32_t g_tmp; + int32_t fir_in_max; + int32_t cic_out_max; + int32_t gain_to_fir; + int idx[DMIC_MAX_MODES]; + int n = 1; + int mmin; + int count; + int *mfir; + 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; + }
+ if (modes->mfir_a[0] > 0) + mfir = modes->mfir_a; + else + mfir = modes->mfir_b;
does look like the initialization done above has a side effect here... worth a comment maybe?
Yes, it's checked for here. Will add.
+ mmin = find_min(mfir, modes->num_of_modes); + count = find_equal(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 foctors of FIR
factors
+ * 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. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
+ g_tmp = g_cic; + bits_cic = 1; + if (g_tmp < 0) { + /* Erroneus decimation factor and CIC gain */
erroneous?
+ trace_dmic_error("gci"); + return -EINVAL; + } + while (g_tmp > 0) { + g_tmp = g_tmp >> 1; + bits_cic++; + }
this again looks like finding the first non-zero bit
yep, to be replaced with function call
+ cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT;
+ /* Calculate remaining gain to FIR as Q4.20. */ + 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) << 20) / 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 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++) { + if (dmic->pdm[i].enable_mic_a > 0 || + dmic->pdm[i].enable_mic_b > 0) + pdm[i] = 1; + else + pdm[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; + }
the logic is weird here.
if (a || b) ... else ...
if (a && b) ... else // this could be a || b or !a && !b??
I'm not sure this if spaghetti can be simplified. If the PDM bits processing is not stereo the mono-left mic (a) HW mode is used. If the the right mic (b) needs to be used as mono input then need to swap the PDM bit inputs to process it as mono-left.
The FIFO packer packer has a mono mode so the use or left channel processing for right microphone is only a HW internal thing.
+ }
+ /* 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; +} +#endif
+#if 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
+ /* 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 ^ 1;
! edge?
If C standard has it defined that !0 gives 1 and !1 gives 0 it's OK to change. I used bitwise XOR to make it sure.
+ 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, 20, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_A(ci); + dmic_write(dai, coef_base_a[i] + + ((length - j - 1) << 2), cu); + }
+ /* 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, 20, DMIC_HW_FIR_COEF_Q); + cu = FIR_COEF_B(ci); + dmic_write(dai, coef_base_b[i] + + ((length - j - 1) << 2), cu); + } + }
+ /* 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 = 768000; /* Min 768 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 = 0; + 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, int direction) +{ + struct dmic_pdata *dmic = dai_get_drvdata(dai); + int i; + int mic_a; + int mic_b; + int fir_a; + int fir_b;
+ if (direction != DAI_DIR_CAPTURE) + return;
+ /* 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);
+ /* stop Rx if we are not capturing */ + if (dmic->state != COMP_STATE_ACTIVE) { + /* Set every PDM controller to soft reset */ + trace_dmic("sre"); + for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { + dmic_update_bits(dai, base[i] + CIC_CONTROL, + CIC_CONTROL_SOFT_RESET_BIT, 0); + }
+ /* 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);
+ dmic->state = COMP_STATE_PREPARE; + }
+ /* 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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
I followed in this part the ssp driver. I have no plans to implement anything like RAM buffer for recording pause so I don't know what should be done with it. Could COMP_TRIGGER_PAUSE be removed and return error if attempted?
+ break; + case COMP_TRIGGER_RESUME: + dmic_context_restore(dai); + break; + case COMP_TRIGGER_SUSPEND: + dmic_context_store(dai); + break; + default: + break; + }
+ return 0; +}
+/* TODO: No idea what should be done here. Currently only trace the
- status register that contains a few 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(dmic_read(dai, OUTSTAT0));
+ /* 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, +};
[cut here] _______________________________________________ Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
+ /* 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++) { + c1 = clkdiv >> 1; + c2 = clkdiv - c1; + du_min = 100 * c1 / clkdiv;
du_min = 100 * (clkdiv/2)/clkdiv -> 50?
+ du_max = 100 * c2 / clkdiv;
c2 = clkdiv - c1 = clkdiv/2, so du_min == du_max ? what am I missing?
The duty cycle becomes non-50% when the divider is odd, e.g. clkdiv = 5 case:
c1 = clkdiv >> 1 = 2 c2 = clkdiv - c1 = 5 - 2 = 3 du_min = 100*c1/clkdiv = 100*2/5 = 200/5 = 40 du_max = 100*c2/clkdif = 100*3/5 = 300/5 = 60
The latter equation can be though simplified to "c2 = 100 - c1;" that I didn't realize earlier.
And you may want to add a comment that the ratio is not 50% for odd dividers, I missed the non-linear result.
+ /* 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;
is this initialization to zero needed?
Yes, It indicates the FIFO B path won't be used.
yes, but you are only using the first element as an indication. you want to add a comment here to help the reader follow the flow.
+ } + 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;
same here, is this initialization needed?
And vice versa no FIFO A path used.
same here.
+ shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
Not yet I think but such would be useful. I'll add it to math/numbers. Since DSPs have an instruction for that it would be then easier to optimize into single location.
ok
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
+ 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; + else + pdm[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; + }
the logic is weird here.
if (a || b) ... else ...
if (a && b) ... else // this could be a || b or !a && !b??
I'm not sure this if spaghetti can be simplified. If the PDM bits processing is not stereo the mono-left mic (a) HW mode is used. If the the right mic (b) needs to be used as mono input then need to swap the PDM bit inputs to process it as mono-left.
The FIFO packer packer has a mono mode so the use or left channel processing for right microphone is only a HW internal thing.
may something like:
cnt=0; for (i = 0; i < dmic->number_of_pdm_controllers; i++) { if (dmic->pdm[i].enable_mic_a > 0) cnt++; if (dmic->pdm[i].enable_mic_b > 0) cnt++;
pdm[i] = !!cnt; cnt >>= 1; stereo[i] = cnt; swap[i] = dmic->pdm[i].enable_mic_b & !cnt; }
+ /* 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 ^ 1;
! edge?
If C standard has it defined that !0 gives 1 and !1 gives 0 it's OK to change. I used bitwise XOR to make it sure.
yes, that's ok. The xor is not usually not used for this.
+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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
I followed in this part the ssp driver. I have no plans to implement anything like RAM buffer for recording pause so I don't know what should be done with it. Could COMP_TRIGGER_PAUSE be removed and return error if attempted?
Liam or Keyon, can you look at this? The spinlock usage is odd and I can't remember why we would use PAUSE even for SSP.
On 02.05.2018 16:43, Pierre-Louis Bossart wrote:
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
I see now -- There's a 5th order integrator-comb filter as 1st decimation filter stage for the PDM bits. Wikipedia has nowadays everything:
https://en.wikipedia.org/wiki/Cascaded_integrator%E2%80%93comb_filter
The high DC gain is caused by the integration part. The 1st equation with the Sigma statement shows the transfer function. By setting R=mcic, N=5, omega = 0 the same DC gain formula that I used is achieved.
Cheers, Seppo
On 5/2/18 10:27 AM, Seppo Ingalsuo wrote:
On 02.05.2018 16:43, Pierre-Louis Bossart wrote:
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
I see now -- There's a 5th order integrator-comb filter as 1st decimation filter stage for the PDM bits. Wikipedia has nowadays everything:
https://en.wikipedia.org/wiki/Cascaded_integrator%E2%80%93comb_filter
The high DC gain is caused by the integration part. The 1st equation with the Sigma statement shows the transfer function. By setting R=mcic, N=5, omega = 0 the same DC gain formula that I used is achieved.
Do I get this right that this 5 figure is hardware-related and used to figure out the compensation that needs to happens in additional FIR-based processing?
On 02.05.2018 19:54, Pierre-Louis Bossart wrote:
On 5/2/18 10:27 AM, Seppo Ingalsuo wrote:
On 02.05.2018 16:43, Pierre-Louis Bossart wrote:
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
I see now -- There's a 5th order integrator-comb filter as 1st decimation filter stage for the PDM bits. Wikipedia has nowadays everything:
https://en.wikipedia.org/wiki/Cascaded_integrator%E2%80%93comb_filter
The high DC gain is caused by the integration part. The 1st equation with the Sigma statement shows the transfer function. By setting R=mcic, N=5, omega = 0 the same DC gain formula that I used is achieved.
Do I get this right that this 5 figure is hardware-related and used to figure out the compensation that needs to happens in additional FIR-based processing?
Yes, it's due to 5th order filter in hardware. With CIC based PDM->PCM conversion the CIC order needs to be at least equal to typical microphones side noise shaper order that's usually fourth order. The 5th order CIC ensures the noise shaping created by the microphones above audio band will alias only a very little amount.
Since the driver supports several sample rates in range 8 - 96 kHz (*) the HW shifter setting need to be computed this way due to varying CIC decimation factor. Though the shifter setting could be also a lookup table for min..max CIC decimation factor. But since there may be changes in hardware revisions maybe the code to calculate the shift is simplest to maintain.
Thanks, Seppo
(*) If suitable integer factors exist for microphone clock divider, CIC decimation factor, FIR decimation factor from the platform IO clock that's usually 19.2 or 24 MHz
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
On 02.05.2018 16:43, Pierre-Louis Bossart wrote:
+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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
I followed in this part the ssp driver. I have no plans to implement anything like RAM buffer for recording pause so I don't know what should be done with it. Could COMP_TRIGGER_PAUSE be removed and return error if attempted?
Liam or Keyon, can you look at this? The spinlock usage is odd and I can't remember why we would use PAUSE even for SSP.
Your comments would be useful. This is an open that I didn't yet address in RFC v2 patch.
Thanks, Seppo
On 2018年05月02日 21:43, Pierre-Louis Bossart wrote:
+ /* 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++) { + c1 = clkdiv >> 1; + c2 = clkdiv - c1; + du_min = 100 * c1 / clkdiv;
du_min = 100 * (clkdiv/2)/clkdiv -> 50?
+ du_max = 100 * c2 / clkdiv;
c2 = clkdiv - c1 = clkdiv/2, so du_min == du_max ? what am I missing?
The duty cycle becomes non-50% when the divider is odd, e.g. clkdiv = 5 case:
c1 = clkdiv >> 1 = 2 c2 = clkdiv - c1 = 5 - 2 = 3 du_min = 100*c1/clkdiv = 100*2/5 = 200/5 = 40 du_max = 100*c2/clkdif = 100*3/5 = 300/5 = 60
The latter equation can be though simplified to "c2 = 100 - c1;" that I didn't realize earlier.
And you may want to add a comment that the ratio is not 50% for odd dividers, I missed the non-linear result.
+ /* 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;
is this initialization to zero needed?
Yes, It indicates the FIFO B path won't be used.
yes, but you are only using the first element as an indication. you want to add a comment here to help the reader follow the flow.
+ } + 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;
same here, is this initialization needed?
And vice versa no FIFO A path used.
same here.
+ shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
Not yet I think but such would be useful. I'll add it to math/numbers. Since DSPs have an instruction for that it would be then easier to optimize into single location.
ok
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
+ 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; + else + pdm[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; + }
the logic is weird here.
if (a || b) ... else ...
if (a && b) ... else // this could be a || b or !a && !b??
I'm not sure this if spaghetti can be simplified. If the PDM bits processing is not stereo the mono-left mic (a) HW mode is used. If the the right mic (b) needs to be used as mono input then need to swap the PDM bit inputs to process it as mono-left.
The FIFO packer packer has a mono mode so the use or left channel processing for right microphone is only a HW internal thing.
may something like:
cnt=0; for (i = 0; i < dmic->number_of_pdm_controllers; i++) { if (dmic->pdm[i].enable_mic_a > 0) cnt++; if (dmic->pdm[i].enable_mic_b > 0) cnt++;
pdm[i] = !!cnt; cnt >>= 1; stereo[i] = cnt; swap[i] = dmic->pdm[i].enable_mic_b & !cnt; }
+ /* 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 ^ 1;
! edge?
If C standard has it defined that !0 gives 1 and !1 gives 0 it's OK to change. I used bitwise XOR to make it sure.
yes, that's ok. The xor is not usually not used for this.
+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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
I followed in this part the ssp driver. I have no plans to implement anything like RAM buffer for recording pause so I don't know what should be done with it. Could COMP_TRIGGER_PAUSE be removed and return error if attempted?
Liam or Keyon, can you look at this? The spinlock usage is odd and I can't remember why we would use PAUSE even for SSP.
I think the intension for spin_lock here was to protect and avoid reentering during status transition of the SSP port.
Thanks, ~Keyon
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
On 2018年05月07日 17:39, Keyon Jie wrote:
On 2018年05月02日 21:43, Pierre-Louis Bossart wrote:
+ /* 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++) { + c1 = clkdiv >> 1; + c2 = clkdiv - c1; + du_min = 100 * c1 / clkdiv;
du_min = 100 * (clkdiv/2)/clkdiv -> 50?
+ du_max = 100 * c2 / clkdiv;
c2 = clkdiv - c1 = clkdiv/2, so du_min == du_max ? what am I missing?
The duty cycle becomes non-50% when the divider is odd, e.g. clkdiv = 5 case:
c1 = clkdiv >> 1 = 2 c2 = clkdiv - c1 = 5 - 2 = 3 du_min = 100*c1/clkdiv = 100*2/5 = 200/5 = 40 du_max = 100*c2/clkdif = 100*3/5 = 300/5 = 60
The latter equation can be though simplified to "c2 = 100 - c1;" that I didn't realize earlier.
And you may want to add a comment that the ratio is not 50% for odd dividers, I missed the non-linear result.
+ /* 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;
is this initialization to zero needed?
Yes, It indicates the FIFO B path won't be used.
yes, but you are only using the first element as an indication. you want to add a comment here to help the reader follow the flow.
+ } + 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;
same here, is this initialization needed?
And vice versa no FIFO A path used.
same here.
+ shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
Not yet I think but such would be useful. I'll add it to math/numbers. Since DSPs have an instruction for that it would be then easier to optimize into single location.
ok
+ /* Calculate CIC shift from the decimation factor specific gain. + */ + mcic = cfg->mcic; + g_cic = mcic * mcic * mcic * mcic * mcic;
yikes, what is this mcic^5 ?
Yep, it's the gain for +1 / -1 PDM bits in CIC decimator part. It's needed to set the scaling HW shifter. It also gives some idea of oversampling ratios to use for a microphone to get some # of bits of resolution as PCM code.
I meant: where does this ^5 come from? First time i see this sort of formula in filter design.
+ 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; + else + pdm[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; + }
the logic is weird here.
if (a || b) ... else ...
if (a && b) ... else // this could be a || b or !a && !b??
I'm not sure this if spaghetti can be simplified. If the PDM bits processing is not stereo the mono-left mic (a) HW mode is used. If the the right mic (b) needs to be used as mono input then need to swap the PDM bit inputs to process it as mono-left.
The FIFO packer packer has a mono mode so the use or left channel processing for right microphone is only a HW internal thing.
may something like:
cnt=0; for (i = 0; i < dmic->number_of_pdm_controllers; i++) { if (dmic->pdm[i].enable_mic_a > 0) cnt++; if (dmic->pdm[i].enable_mic_b > 0) cnt++;
pdm[i] = !!cnt; cnt >>= 1; stereo[i] = cnt; swap[i] = dmic->pdm[i].enable_mic_b & !cnt; }
+ /* 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 ^ 1;
! edge?
If C standard has it defined that !0 gives 1 and !1 gives 0 it's OK to change. I used bitwise XOR to make it sure.
yes, that's ok. The xor is not usually not used for this.
+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_START: + if (dmic->state == COMP_STATE_PREPARE || + dmic->state == COMP_STATE_PAUSED) { + dmic_start(dai, direction); + } else { + trace_dmic_error("cst"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_RELEASE: + if (dmic->state == COMP_STATE_PREPARE) { + dmic_start(dai, direction); + } else { + trace_dmic_error("crl"); + trace_value(dmic->state); + } + break; + case COMP_TRIGGER_STOP: + case COMP_TRIGGER_PAUSE: + dmic->state = COMP_STATE_PAUSED; + dmic_stop(dai);
shouldn't the state assignment be protected by a spinlock (as done for the start case)? Also is this really PAUSED?
I followed in this part the ssp driver. I have no plans to implement anything like RAM buffer for recording pause so I don't know what should be done with it. Could COMP_TRIGGER_PAUSE be removed and return error if attempted?
Liam or Keyon, can you look at this? The spinlock usage is odd and I can't remember why we would use PAUSE even for SSP.
I think the intension for spin_lock here was to protect and avoid reentering during status transition of the SSP port.
Just got the point after Seppo's explanation, let me do a patch to fix it.
Thanks, ~Keyon
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
Sound-open-firmware mailing list Sound-open-firmware@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/sound-open-firmware
On 30.04.2018 23:22, Pierre-Louis Bossart wrote:
+/* 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 new_scale; + int32_t fir_gain; + int shift; + const int32_t coef_max_val = Q_CONVERT_FLOAT(0.9999, 20); /* Q1.20 */
+ /* Multiply gain passed from CIC with output full scale, result Q4.20 */ + fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q23, 20, 23, 20);
+ /* Find the largest FIR coefficient value */ + amax = find_max_abs_int32((int32_t *)coef, coef_length);
+ /* Scale with FIR gain */ + new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31, 20, 20); + if (new_amax <= 0) + return -EINVAL;
+ /* Needed scale for FIR taps, target is 0.9999 max */ + new_scale = (int32_t)((((int64_t)coef_max_val << 20)) / new_amax);
+ /* Find shift value */ + if (new_scale == 0) + return -EINVAL;
+ shift = 0; + while ((new_scale << shift) < (1 << 20)) + shift++;
don't we have some sort of macro for this (find number of leading zeroes)?
I rewrote this function partially since I noticed I can do the same without the division. The shift value can be calculated from the newly made norm_int32(new_amax) function output.
The other larger change is that I updated the modes search struct to use int16_t arrays to save RAM.
It took me some time to test these but I'll email shortly the new RFC patch set!
Thanks, Seppo
participants (3)
-
Keyon Jie
-
Pierre-Louis Bossart
-
Seppo Ingalsuo