[Sound-open-firmware] [PATCH 1/7] [v3]DMIC: Add PDM microphones (DMIC) support to DAI

Ranjani Sridharan ranjani.sridharan at linux.intel.com
Sat May 12 07:01:01 CEST 2018


On Fri, 2018-05-11 at 17:03 +0300, Seppo Ingalsuo wrote:
> This patch adds the DMIC audio capture driver for SOF DAI component
> use.
> The DMIC feature allows to directly attach one to (typically) four
> PDM
> digital microphones into Intel SoC without a separate codec IC. This
> is
> supported by APL and most successor platforms.
> 
> Corresponding patches are needed for kernel driver and topology to
> enable
> this feature.
> 
> Signed-off-by: Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
> ---
>  src/drivers/dmic.c | 1361
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 1361 insertions(+)
>  create mode 100644 src/drivers/dmic.c
> 
> diff --git a/src/drivers/dmic.c b/src/drivers/dmic.c
> new file mode 100644
> index 0000000..33783f8
> --- /dev/null
> +++ b/src/drivers/dmic.c
> @@ -0,0 +1,1361 @@
> +/*
> + * Copyright (c) 2017, Intel Corporation
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or
> without
> + * modification, are permitted provided that the following
> conditions are met:
> + *   * Redistributions of source code must retain the above
> copyright
> + *     notice, this list of conditions and the following disclaimer.
> + *   * Redistributions in binary form must reproduce the above
> copyright
> + *     notice, this list of conditions and the following disclaimer
> in the
> + *     documentation and/or other materials provided with the
> distribution.
> + *   * Neither the name of the Intel Corporation nor the
> + *     names of its contributors may be used to endorse or promote
> products
> + *     derived from this software without specific prior written
> permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
> CONTRIBUTORS "AS IS"
> + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
> TO, THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
> PARTICULAR PURPOSE
> + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
> CONTRIBUTORS BE
> + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
> OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
> OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
> BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
> WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
> OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
> ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + *
> + * Author:	Seppo Ingalsuo <seppo.ingalsuo at linux.intel.com>
> + */
> +
> +#include <sof/stream.h>
> +#include <sof/dmic.h>
> +#include <sof/interrupt.h>
> +#include <sof/math/numbers.h>
> +#include <sof/audio/format.h>
> +
> +#if defined DMIC_HW_VERSION
> +
> +#include <sof/audio/coefficients/pdm_decim/pdm_decim_table.h>
> +
> +#if defined MODULE_TEST
> +#include <stdio.h>
> +#endif

Are the MODULE_TEST bits in the code for ci tests? 
> +
> +#define DMIC_MAX_MODES 50
> +
> +/* HW FIR pipeline needs 5 additional cycles per channel for
> internal
> + * operations. This is used in MAX filter length check.
> + */
> +#define DMIC_FIR_PIPELINE_OVERHEAD 5
> +
> +/* Force a 48 kHz config while kernel driver IPC is under
> construction. Remove
> + * or undefine when it's available.
> + */
> +#define DMIC_FORCE_CONFIG
> +
> +struct decim_modes {
> +	int16_t clkdiv[DMIC_MAX_MODES];
> +	int16_t mcic[DMIC_MAX_MODES];
> +	int16_t mfir[DMIC_MAX_MODES];
> +	int num_of_modes;
> +};
> +
> +struct matched_modes {
> +	int16_t clkdiv[DMIC_MAX_MODES];
> +	int16_t mcic[DMIC_MAX_MODES];
> +	int16_t mfir_a[DMIC_MAX_MODES];
> +	int16_t mfir_b[DMIC_MAX_MODES];
> +	int num_of_modes;
> +};
> +
> +struct dmic_configuration {
> +	struct pdm_decim *fir_a;
> +	struct pdm_decim *fir_b;
> +	int clkdiv;
> +	int mcic;
> +	int mfir_a;
> +	int mfir_b;
> +	int cic_shift;
> +	int fir_a_shift;
> +	int fir_b_shift;
> +	int fir_a_length;
> +	int fir_b_length;
> +	int32_t fir_a_scale;
> +	int32_t fir_b_scale;
> +};
> +
> +struct pdm_controllers_configuration {
> +	uint32_t cic_control;
> +	uint32_t cic_config;
> +	uint32_t mic_control;
> +	uint32_t fir_control_a;
> +	uint32_t fir_config_a;
> +	uint32_t dc_offset_left_a;
> +	uint32_t dc_offset_right_a;
> +	uint32_t out_gain_left_a;
> +	uint32_t out_gain_right_a;
> +	uint32_t fir_control_b;
> +	uint32_t fir_config_b;
> +	uint32_t dc_offset_left_b;
> +	uint32_t dc_offset_right_b;
> +	uint32_t out_gain_left_b;
> +	uint32_t out_gain_right_b;
> +};
> +
> +/* Configuration ABI version, increment if not compatible with
> previous
> + * version.
> + */
> +#define DMIC_IPC_VERSION 1
> +
> +/* Minimum OSR is always applied for 48 kHz and less sample rates */
> +#define DMIC_MIN_OSR  50
> +
> +/* These are used as guideline for configuring > 48 kHz sample
> rates. The
> + * minimum OSR can be relaxed down to 40 (use 3.84 MHz clock for 96
> kHz).
> + */
> +#define DMIC_HIGH_RATE_MIN_FS	64000
> +#define DMIC_HIGH_RATE_OSR_MIN	40
> +
> +/* Used for scaling FIR coeffcients for HW */
> +#define DMIC_HW_FIR_COEF_MAX ((1 << (DMIC_HW_BITS_FIR_COEF - 1)) -
> 1)
> +#define DMIC_HW_FIR_COEF_Q (DMIC_HW_BITS_FIR_COEF - 1)
> +
> +/* Internal precision in gains computation, e.g. Q4.28 in int32_t */
> +#define DMIC_FIR_SCALE_Q 28
> +
> +/* tracing */
> +#define trace_dmic(__e) trace_event(TRACE_CLASS_DMIC, __e)
> +#define trace_dmic_error(__e) trace_error(TRACE_CLASS_DMIC, __e)
> +#define tracev_dmic(__e) tracev_event(TRACE_CLASS_DMIC, __e)
> +
> +/* Base addresses (in PDM scope) of 2ch PDM controllers and
> coefficient RAM. */
> +static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3};
> +static const uint32_t coef_base_a[4] = {PDM0_COEFFICIENT_A,
> PDM1_COEFFICIENT_A,
> +	PDM2_COEFFICIENT_A, PDM3_COEFFICIENT_A};
> +static const uint32_t coef_base_b[4] = {PDM0_COEFFICIENT_B,
> PDM1_COEFFICIENT_B,
> +	PDM2_COEFFICIENT_B, PDM3_COEFFICIENT_B};
> +
> +#if defined MODULE_TEST
> +#define IO_BYTES_GLOBAL  (PDM0 - OUTCONTROL0)
> +#define IO_BYTES_MIDDLE  (PDM1 - PDM0)
> +#define IO_BYTES_LAST    (PDM0_COEFFICIENT_B + PDM_COEF_RAM_B_LENGTH
> - PDM0)
> +#define IO_BYTES  (((DMIC_HW_CONTROLLERS) - 1) * (IO_BYTES_MIDDLE) \
> +				+ (IO_BYTES_LAST) +
> (IO_BYTES_GLOBAL))
> +#define IO_LENGTH ((IO_BYTES) >> 2)
> +
> +static uint32_t dmic_io[IO_LENGTH];
> +
> +static void dmic_write(struct dai *dai, uint32_t reg, uint32_t
> value)
> +{
> +	printf("W %04x %08x\n", reg, value);
> +	dmic_io[reg >> 2] = value;
> +}
> +
> +static uint32_t dmic_read(struct dai *dai, uint32_t reg)
> +{
> +	uint32_t value = dmic_io[reg >> 2];
> +
> +	printf("R %04x %08x\n", reg, value);
> +	return value;
> +}
> +
> +static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t
> mask,
> +	uint32_t value)
> +{
> +	uint32_t new_value;
> +	uint32_t old_value = dmic_io[reg >> 2];
> +
> +	new_value = (old_value & (~mask)) | value;
> +	dmic_io[reg >> 2] = new_value;
> +	printf("W %04x %08x\n", reg, new_value);
> +}
> +#else
> +
> +static void dmic_write(struct dai *dai, uint32_t reg, uint32_t
> value)
> +{
> +	io_reg_write(dai_base(dai) + reg, value);
> +}
> +
> +static uint32_t dmic_read(struct dai *dai, uint32_t reg)
> +{
> +	return io_reg_read(dai_base(dai) + reg);
> +}
> +
> +static void dmic_update_bits(struct dai *dai, uint32_t reg, uint32_t
> mask,
> +	uint32_t value)
> +{
> +	io_reg_update_bits(dai_base(dai) + reg, mask, value);
> +}
> +#endif
> +
> +/* This function returns a raw list of potential microphone clock
> and decimation
> + * modes for achieving requested sample rates. The search is
> constrained by
> + * decimation HW capabililies and setup parameters. The parameters
> such as
> + * microphone clock min/max and duty cycle requirements need be
> checked from
> + * used microphone component datasheet.
> + */
> +static void find_modes(struct decim_modes *modes,
> +	struct sof_ipc_dai_dmic_params *prm, uint32_t fs)
> +{
> +	int clkdiv_min;
> +	int clkdiv_max;
> +	int clkdiv;
> +	int c1;
> +	int du_min;
> +	int du_max;
> +	int pdmclk;
> +	int osr;
> +	int mfir;
> +	int mcic;
> +	int ioclk_test;
> +	int osr_min = DMIC_MIN_OSR;
> +	int i = 0;
> +
> +	/* Defaults, empty result */
> +	modes->num_of_modes = 0;
> +
> +	/* The FIFO is not requested if sample rate is set to zero.
> Just
> +	 * return in such case with num_of_modes as zero.
> +	 */
> +	if (fs == 0)
> +		return;
> +
> +	/* Override DMIC_MIN_OSR for very high sample rates, use as
> minimum
> +	 * the nominal clock for the high rates.
> +	 */
> +	if (fs >= DMIC_HIGH_RATE_MIN_FS)
> +		osr_min = DMIC_HIGH_RATE_OSR_MIN;
> +
> +	/* Check for sane pdm clock, min 100 kHz, max ioclk/2 */
> +	if (prm->pdmclk_max < DMIC_HW_PDM_CLK_MIN ||
> +		prm->pdmclk_max > DMIC_HW_IOCLK / 2) {
> +		trace_dmic_error("pmx");
> +		return;
> +	}
> +	if (prm->pdmclk_min < DMIC_HW_PDM_CLK_MIN ||
> +		prm->pdmclk_min > prm->pdmclk_max) {
> +		trace_dmic_error("pmn");
> +		return;
> +	}
> +
> +	/* Check for sane duty cycle */
> +	if (prm->duty_min > prm->duty_max) {
> +		trace_dmic_error("pdu");
> +		return;
> +	}
> +	if (prm->duty_min < DMIC_HW_DUTY_MIN ||
> +		prm->duty_min > DMIC_HW_DUTY_MAX) {
> +		trace_dmic_error("pdn");
> +		return;
> +	}
> +	if (prm->duty_max < DMIC_HW_DUTY_MIN ||
> +		prm->duty_max > DMIC_HW_DUTY_MAX) {
> +		trace_dmic_error("pdx");
> +		return;
> +	}
> +
> +	/* Min and max clock dividers */
> +	clkdiv_min = ceil_divide(DMIC_HW_IOCLK, prm->pdmclk_max);
> +	clkdiv_min = MAX(clkdiv_min, DMIC_HW_CIC_DECIM_MIN);
> +	clkdiv_max = DMIC_HW_IOCLK / prm->pdmclk_min;
> +
> +	/* Loop possible clock dividers and check based on resulting
> +	 * oversampling ratio that CIC and FIR decimation ratios are
> +	 * feasible. The ratios need to be integers. Also the mic
> clock
> +	 * duty cycle need to be within limits.
> +	 */
> +	for (clkdiv = clkdiv_min; clkdiv <= clkdiv_max; clkdiv++) {
> +		/* Calculate duty cycle for this clock divider. Note
> that
> +		 * odd dividers cause non-50% duty cycle.
> +		 */
> +		c1 = clkdiv >> 1;
> +		du_min = 100 * c1 / clkdiv;
> +		du_max = 100 - du_min;
> +
> +		/* Calculate PDM clock rate and oversampling ratio.
> */
> +		pdmclk = DMIC_HW_IOCLK / clkdiv;
> +		osr = pdmclk / fs;
> +
> +		/* Check that OSR constraints is met and clock duty
> cycle does
> +		 * not exceed microphone specification. If exceed
> proceed to
> +		 * next clkdiv.
> +		 */
> +		if (osr < osr_min || du_min < prm->duty_min ||
> +			du_max > prm->duty_max)
> +			continue;
> +
> +		/* Loop FIR decimation factors candidates. If the
> +		 * integer divided decimation factors and clock
> dividers
> +		 * as multiplied with sample rate match the IO clock
> +		 * rate the division was exact and such decimation
> mode
> +		 * is possible. Then check that CIC decimation
> constraints
> +		 * are met. The passed decimation modes are added to
> array.
> +		 */
> +		for (mfir = DMIC_HW_FIR_DECIM_MIN;
> +			mfir <= DMIC_HW_FIR_DECIM_MAX; mfir++) {
> +			mcic = osr / mfir;
> +			ioclk_test = fs * mfir * mcic * clkdiv;
> +
> +			if (ioclk_test == DMIC_HW_IOCLK &&
> +				mcic >= DMIC_HW_CIC_DECIM_MIN &&
> +				mcic <= DMIC_HW_CIC_DECIM_MAX &&
> +				i < DMIC_MAX_MODES) {
> +				modes->clkdiv[i] = clkdiv;
> +				modes->mcic[i] = mcic;
> +				modes->mfir[i] = mfir;
> +				i++;
> +				modes->num_of_modes = i;
> +			}
> +		}
> +	}
> +#if defined MODULE_TEST
> +	printf("# Found %d modes\n", i);
> +#endif
> +}
> +
> +/* The previous raw modes list contains sane configuration
> possibilities. When
> + * there is request for both FIFOs A and B operation this function
> returns
> + * list of compatible settings.
> + */
> +static void match_modes(struct matched_modes *c, struct decim_modes
> *a,
> +	struct decim_modes *b)
> +{
> +	int16_t idx[DMIC_MAX_MODES];
> +	int idx_length;
> +	int i;
> +	int n;
> +	int m;
> +
> +	/* Check if previous search got results. */
> +	c->num_of_modes = 0;
> +	if (a->num_of_modes == 0 && b->num_of_modes == 0) {
> +		/* Nothing to do */
> +		return;
> +	}
> +
> +	/* Check for request only for FIFO A or B. In such case pass
> list for
> +	 * A or B as such.
> +	 */
> +	if (b->num_of_modes == 0) {
> +		c->num_of_modes = a->num_of_modes;
> +		for (i = 0; i < a->num_of_modes; i++) {
> +			c->clkdiv[i] = a->clkdiv[i];
> +			c->mcic[i] = a->mcic[i];
> +			c->mfir_a[i] = a->mfir[i];
> +			c->mfir_b[i] = 0; /* Mark FIR B as non-used
> */
> +		}
> +		return;
> +	}
> +
> +	if (a->num_of_modes == 0) {
> +		c->num_of_modes = b->num_of_modes;
> +		for (i = 0; i < b->num_of_modes; i++) {
> +			c->clkdiv[i] = b->clkdiv[i];
> +			c->mcic[i] = b->mcic[i];
> +			c->mfir_b[i] = b->mfir[i];
> +			c->mfir_a[i] = 0; /* Mark FIR A as non-used
> */
> +		}
> +		return;
> +	}
> +
> +	/* Merge a list of compatible modes */
> +	i = 0;
> +	for (n = 0; n < a->num_of_modes; n++) {
> +		/* Find all indices of values a->clkdiv[n] in b-
> >clkdiv[] */
> +		idx_length = find_equal_int16(idx, b->clkdiv, a-
> >clkdiv[n],
> +			b->num_of_modes, 0);
> +		for (m = 0; m < idx_length; m++) {
> +			if (b->mcic[idx[m]] == a->mcic[n]) {
> +				c->clkdiv[i] = a->clkdiv[n];
> +				c->mcic[i] = a->mcic[n];
> +				c->mfir_a[i] = a->mfir[n];
> +				c->mfir_b[i] = b->mfir[idx[m]];
> +				i++;
> +			}
> +		}
> +		c->num_of_modes = i;
> +	}
> +}
> +
> +/* Finds a suitable FIR decimation filter from the included set */
> +static struct pdm_decim *get_fir(struct dmic_configuration *cfg, int
> mfir)
> +{
> +	int i;
> +	int fs;
> +	int cic_fs;
> +	int fir_max_length;
> +	struct pdm_decim *fir = NULL;
> +
> +	if (mfir <= 0)
> +		return fir;
> +
> +	cic_fs = DMIC_HW_IOCLK / cfg->clkdiv / cfg->mcic;
> +	fs = cic_fs / mfir;
> +	/* FIR max. length depends on available cycles and coef RAM
> +	 * length. Exceeding this length sets HW overrun status and
> +	 * overwrite of other register.
> +	 */
> +	fir_max_length = MIN(DMIC_HW_FIR_LENGTH_MAX,
> +		DMIC_HW_IOCLK / fs / 2 -
> DMIC_FIR_PIPELINE_OVERHEAD);
> +
> +	for (i = 0; i < DMIC_FIR_LIST_LENGTH; i++) {
> +		if (fir_list[i]->decim_factor == mfir &&
> +			fir_list[i]->length <= fir_max_length) {
> +			/* Store pointer, break from loop to avoid a
> +			 * Possible other mode with lower FIR
> length.
> +			 */
> +			fir = fir_list[i];
> +			break;
> +		}
> +	}
> +
> +	return fir;
> +}
> +
> +/* Calculate scale and shift to use for FIR coefficients. Scale is
> applied
> + * before write to HW coef RAM. Shift will be programmed to HW
> register.
> + */
> +static int fir_coef_scale(int32_t *fir_scale, int *fir_shift, int
> add_shift,
> +	const int32_t coef[], int coef_length, int32_t gain)
> +{
> +	int32_t amax;
> +	int32_t new_amax;
> +	int32_t fir_gain;
> +	int shift;
> +
> +	/* Multiply gain passed from CIC with output full scale. */
> +	fir_gain = Q_MULTSR_32X32((int64_t)gain, DMIC_HW_SENS_Q28,
> +		DMIC_FIR_SCALE_Q, 28, DMIC_FIR_SCALE_Q);
> +
> +	/* Find the largest FIR coefficient value. */
> +	amax = find_max_abs_int32((int32_t *)coef, coef_length);
> +
> +	/* Scale max. tap value with FIR gain. */
> +	new_amax = Q_MULTSR_32X32((int64_t)amax, fir_gain, 31,
> +		DMIC_FIR_SCALE_Q, DMIC_FIR_SCALE_Q);
> +	if (new_amax <= 0)
> +		return -EINVAL;
> +
> +	/* Get left shifts count to normalize the fractional value
> as 32 bit.
> +	 * We need right shifts count for scaling so need to invert.
> The
> +	 * difference of Q31 vs. used Q format is added to get the
> correct
> +	 * normalization right shift value.
> +	 */
> +	shift = 31 - DMIC_FIR_SCALE_Q - norm_int32(new_amax);
> +
> +	/* Add to shift for coef raw Q31 format shift and store to
> +	 * configuration. Ensure range (fail should not happen with
> OK
> +	 * coefficient set).
> +	 */
> +	*fir_shift = -shift + add_shift;
> +	if (*fir_shift < DMIC_HW_FIR_SHIFT_MIN ||
> +		*fir_shift > DMIC_HW_FIR_SHIFT_MAX)
> +		return -EINVAL;
> +
> +	/* Compensate shift into FIR coef scaler and store as Q4.20.
> */
> +	if (shift < 0)
> +		*fir_scale = (fir_gain << -shift);
> +	else
> +		*fir_scale = (fir_gain >> shift);
> +
> +#if defined MODULE_TEST
> +	printf("# FIR gain need Q28 = %d (%f)\n", fir_gain,
> +		Q_CONVERT_QTOF(fir_gain, 28));
> +	printf("# FIR max coef no gain Q31 = %d (%f)\n", amax,
> +		Q_CONVERT_QTOF(amax, 31));
> +	printf("# FIR max coef with gain Q28 = %d (%f)\n", new_amax,
> +		Q_CONVERT_QTOF(new_amax, 28));
> +	printf("# FIR coef norm rshift = %d\n", shift);
> +	printf("# FIR coef old rshift = %d\n", add_shift);
> +	printf("# FIR coef new rshift = %d\n", *fir_shift);
> +	printf("# FIR coef scaler Q28 = %d (%f)\n", *fir_scale,
> +		Q_CONVERT_QTOF(*fir_scale, 28));
> +#endif
> +
> +	return 0;
> +}
> +
> +/* This function selects with a simple criteria one mode to set up
> the
> + * decimator. For the settings chosen for FIFOs A and B output a
> lookup
> + * is done for FIR coefficients from the included coefficients
> tables.
> + * For some decimation factors there may be several length
> coefficient sets.
> + * It is due to possible restruction of decimation engine cycles per
> given
> + * sample rate. If the coefficients length is exceeded the lookup
> continues.
> + * Therefore the list of coefficient set must present the filters
> for a
> + * decimation factor in decreasing length order.
> + *
> + * Note: If there is no filter available an error is returned. The
> parameters
> + * should be reviewed for such case. If still a filter is missing it
> should be
> + * added into the included set. FIR decimation with a high factor
> usually
> + * needs compromizes into specifications and is not desirable.
> + */
> +static int select_mode(struct dmic_configuration *cfg,
> +	struct matched_modes *modes)
> +{
> +	int32_t g_cic;
> +	int32_t fir_in_max;
> +	int32_t cic_out_max;
> +	int32_t gain_to_fir;
> +	int16_t idx[DMIC_MAX_MODES];
> +	int16_t *mfir;
> +	int n = 1;
> +	int mmin;
> +	int count;
> +	int mcic;
> +	int bits_cic;
> +	int ret;
> +
> +	/* If there are more than one possibilities select a mode
> with lowest
> +	 * FIR decimation factor. If there are several select mode
> with highest
> +	 * ioclk divider to minimize microphone power consumption.
> The highest
> +	 * clock divisors are in the end of list so select the last
> of list.
> +	 * The minimum OSR criteria used in previous ensures that
> quality in
> +	 * the candidates should be sufficient.
> +	 */
> +	if (modes->num_of_modes == 0) {
> +		trace_dmic_error("nom");
> +		return -EINVAL;
> +	}
> +
> +	/* Valid modes presence is indicated with non-zero
> decimation
> +	 * factor in 1st element. If FIR A is not used get
> decimation factors
> +	 * from FIR B instead.
> +	 */
> +	if (modes->mfir_a[0] > 0)
> +		mfir = modes->mfir_a;
> +	else
> +		mfir = modes->mfir_b;
> +
> +	mmin = find_min_int16(mfir, modes->num_of_modes);
> +	count = find_equal_int16(idx, mfir, mmin, modes-
> >num_of_modes, 0);
> +	n = idx[count - 1];
> +
> +	/* Get microphone clock and decimation parameters for used
> mode from
> +	 * the list.
> +	 */
> +	cfg->clkdiv = modes->clkdiv[n];
> +	cfg->mfir_a = modes->mfir_a[n];
> +	cfg->mfir_b = modes->mfir_b[n];
> +	cfg->mcic = modes->mcic[n];
> +	cfg->fir_a = NULL;
> +	cfg->fir_b = NULL;
> +
> +	/* Find raw FIR coefficients to match the decimation factors
> of FIR
> +	 * A and B.
> +	 */
> +	if (cfg->mfir_a > 0) {
> +		cfg->fir_a = get_fir(cfg, cfg->mfir_a);
> +		if (!cfg->fir_a) {
> +			trace_dmic_error("fam");
> +			trace_value(cfg->mfir_a);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (cfg->mfir_b > 0) {
> +		cfg->fir_b = get_fir(cfg, cfg->mfir_b);
> +		if (!cfg->fir_b) {
> +			trace_dmic_error("fbm");
> +			trace_value(cfg->mfir_b);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Calculate CIC shift from the decimation factor specific
> gain. The
> +	 * gain of HW decimator equals decimation factor to power of
> 5.
> +	 */
> +	mcic = cfg->mcic;
> +	g_cic = mcic * mcic * mcic * mcic * mcic;
> +	if (g_cic < 0) {
> +		/* Erroneous decimation factor and CIC gain */
> +		trace_dmic_error("gci");
> +		return -EINVAL;
> +	}
> +
> +	bits_cic = 32 - norm_int32(g_cic);
> +	cfg->cic_shift = bits_cic - DMIC_HW_BITS_FIR_INPUT;
> +
> +	/* Calculate remaining gain to FIR in Q format used for gain
> +	 * values.
> +	 */
> +	fir_in_max = (1 << (DMIC_HW_BITS_FIR_INPUT - 1));
> +	if (cfg->cic_shift >= 0)
> +		cic_out_max = g_cic >> cfg->cic_shift;
> +	else
> +		cic_out_max = g_cic << -cfg->cic_shift;
> +
> +	gain_to_fir = (int32_t)((((int64_t)fir_in_max) <<
> DMIC_FIR_SCALE_Q) /
> +		cic_out_max);
> +
> +	/* Calculate FIR scale and shift */
> +	if (cfg->mfir_a > 0) {
> +		cfg->fir_a_length = cfg->fir_a->length;
> +		ret = fir_coef_scale(&cfg->fir_a_scale, &cfg-
> >fir_a_shift,
> +			cfg->fir_a->shift, cfg->fir_a->coef, cfg-
> >fir_a->length,
> +			gain_to_fir);
> +		if (ret < 0) {
> +			/* Invalid coefficient set found, should not
> happen. */
> +			trace_dmic_error("ina");
> +			return -EINVAL;
> +		}
> +	} else {
> +		cfg->fir_a_scale = 0;
> +		cfg->fir_a_shift = 0;
> +		cfg->fir_a_length = 0;
> +	}
> +
> +	if (cfg->mfir_b > 0) {
> +		cfg->fir_b_length = cfg->fir_b->length;
> +		ret = fir_coef_scale(&cfg->fir_b_scale, &cfg-
> >fir_b_shift,
> +			cfg->fir_b->shift, cfg->fir_b->coef, cfg-
> >fir_b->length,
> +			gain_to_fir);
> +		if (ret < 0) {
> +			/* Invalid coefficient set found, should not
> happen. */
> +			trace_dmic_error("inb");
> +			return -EINVAL;
> +		}
> +	} else {
> +		cfg->fir_b_scale = 0;
> +		cfg->fir_b_shift = 0;
> +		cfg->fir_b_length = 0;
> +	}
> +
> +	return 0;
> +}
> +
> +/* The FIFO input packer mode (IPM) settings are somewhat different
> in
> + * HW versions. This helper function returns a suitable IPM bit
> field
> + * value to use.
> + */
> +#if DMIC_HW_VERSION == 1
> +
> +static inline void ipm_helper(int *ipm, int stereo[], int swap[],
> +	struct sof_ipc_dai_dmic_params *dmic)
> +{
> +	int pdm[DMIC_HW_CONTROLLERS];
> +	int cnt;
> +	int i;
> +
> +	/* Loop number of PDM controllers in the configuration. If
> mic A
> +	 * or B is enabled then a pdm controller is marked as
> active. Also it
> +	 * is checked whether the controller should operate as
> stereo or mono
> +	 * left (A) or mono right (B) mode. Mono right mode is setup
> as channel
> +	 * swapped mono left.
> +	 */
> +	for (i = 0; i < dmic->number_of_pdm_controllers; i++) {
> +		cnt = 0;
> +		if (dmic->pdm[i].enable_mic_a > 0)
> +			cnt++;
> +
> +		if (dmic->pdm[i].enable_mic_b > 0)
> +			cnt++;
> +
> +		/* A PDM controller is used if at least one mic was
> enabled. */
> +		pdm[i] = !!cnt;
> +
> +		/* Set stereo mode if both mic A anc B are enabled.
> */
> +		cnt >>= 1;
> +		stereo[i] = cnt;
> +
> +		/* Swap channels if only mic B is used for mono
> processing. */
> +		swap[i] = dmic->pdm[i].enable_mic_b & !cnt;
> +	}
> +
> +	/* IPM indicates active pdm controllers. */
> +	*ipm = 0;
> +
> +	if (pdm[0] == 0 && pdm[1] > 0)
> +		*ipm = 1;
> +
> +	if (pdm[0] > 0 && pdm[1] > 0)
> +		*ipm = 2;
> +}
> +
> +#elif DMIC_HW_VERSION == 2
> +
> +static inline void source_ipm_helper(int source[], int *ipm, int
> stereo[],
> +	int swap[], struct sof_ipc_dai_dmic_params *dmic)
> +{
> +	int pdm[DMIC_HW_CONTROLLERS];
> +	int i;
> +	int n = 0;
> +
> +	/* Loop number of PDM controllers in the configuration. If
> mic A
> +	 * or B is enabled then a pdm controller is marked as
> active. Also it
> +	 * is checked whether the controller should operate as
> stereo or mono
> +	 * left (A) or mono right (B) mode. Mono right mode is setup
> as channel
> +	 * swapped mono left. The function returns also in array
> source[] the
> +	 * indice of enabled pdm controllers to be used for IPM
> configuration.
> +	 */
> +	for (i = 0; i < dmic->number_of_pdm_controllers; i++) {
> +		if (dmic->pdm[i].enable_mic_a > 0 ||
> +			dmic->pdm[i].enable_mic_b > 0) {
> +			pdm[i] = 1;
> +			source[n] = i;
> +			n++;
> +		} else {
> +			pdm[i] = 0;
> +			swap[i] = 0;
> +		}
> +
> +		if (dmic->pdm[i].enable_mic_a > 0 &&
> +			dmic->pdm[i].enable_mic_b > 0) {
> +			stereo[i] = 1;
> +			swap[i] = 0;
> +		} else {
> +			stereo[i] = 0;
> +			if (dmic->pdm[i].enable_mic_a == 0)
> +				swap[i] = 1;
> +			else
> +				swap[i] = 0;
> +		}
> +	}
> +
> +	/* IPM bit field is set to count of active pdm controllers.
> */
> +	*ipm = pdm[0];
> +	for (i = 0; i < dmic->number_of_pdm_controllers; i++)
> +		*ipm += pdm[i];
> +}
> +#endif
> +
> +static int configure_registers(struct dai *dai, struct
> dmic_configuration *cfg,
> +	struct sof_ipc_dai_dmic_params *dmic)
> +{
> +	int stereo[DMIC_HW_CONTROLLERS];
> +	int swap[DMIC_HW_CONTROLLERS];
> +	uint32_t val;
> +	int32_t ci;
> +	uint32_t cu;
> +	int ipm;
> +	int of0;
> +	int of1;
> +	int fir_decim;
> +	int fir_length;
> +	int length;
> +	int edge;
> +	int dccomp;
> +	int cic_start_a;
> +	int cic_start_b;
> +	int fir_start_a;
> +	int fir_start_b;
> +	int soft_reset;
> +	int i;
> +	int j;
> +
> +	struct dmic_pdata *pdata = dai_get_drvdata(dai);
> +	int array_a = 0;
> +	int array_b = 0;
> +	int cic_mute = 0;
> +	int fir_mute = 0;
> +	int bfth = 1; /* Should be 3 for 8 entries, 1 is 2 entries
> */
> +	int th = 0; /* Used with TIE=1 */
> +
> +	/* Normal start sequence */
> +	dccomp = 1;
> +	soft_reset = 1;
> +	cic_start_a = 0;
> +	cic_start_b = 0;
> +	fir_start_a = 0;
> +	fir_start_b = 0;
> +
> +#if DMIC_HW_VERSION == 2
> +	int source[4] = {0, 0, 0, 0};
> +#endif
> +
> +#if defined MODULE_TEST
> +		int32_t fir_a_max = 0;
> +		int32_t fir_a_min = 0;
> +		int32_t fir_b_max = 0;
> +		int32_t fir_b_min = 0;
> +#endif
> +
> +	/* pdata is set by dmic_probe(), error if it has not been
> set */
> +	if (!pdata) {
> +		trace_dmic_error("cfr");
> +		return -EINVAL;
> +	}
> +
> +	/* Sanity checks */
> +	if (dmic->number_of_pdm_controllers > DMIC_HW_CONTROLLERS) {
> +		trace_dmic_error("num");
> +		return -EINVAL;
> +	}
> +
> +	/* OUTCONTROL0 and OUTCONTROL1 */
> +	trace_dmic("reg");
> +	of0 = (dmic->fifo_bits_a == 32) ? 2 : 0;
> +	of1 = (dmic->fifo_bits_b == 32) ? 2 : 0;
> +
> +#if DMIC_HW_VERSION == 1
> +	ipm_helper(&ipm, stereo, swap, dmic);
> +	val = OUTCONTROL0_TIE(0) |
> +		OUTCONTROL0_SIP(0) |
> +		OUTCONTROL0_FINIT(1) |
> +		OUTCONTROL0_FCI(0) |
> +		OUTCONTROL0_BFTH(bfth) |
> +		OUTCONTROL0_OF(of0) |
> +		OUTCONTROL0_IPM(ipm) |
> +		OUTCONTROL0_TH(th);
> +	dmic_write(dai, OUTCONTROL0, val);
> +	trace_value(val);
> +
> +	val = OUTCONTROL1_TIE(0) |
> +		OUTCONTROL1_SIP(0) |
> +		OUTCONTROL1_FINIT(1) |
> +		OUTCONTROL1_FCI(0) |
> +		OUTCONTROL1_BFTH(bfth) |
> +		OUTCONTROL1_OF(of1) |
> +		OUTCONTROL1_IPM(ipm) |
> +		OUTCONTROL1_TH(th);
> +	dmic_write(dai, OUTCONTROL1, val);
> +	trace_value(val);
> +#endif
> +
> +#if DMIC_HW_VERSION == 2
> +	source_ipm_helper(source, &ipm, stereo, swap, dmic);
> +	val = OUTCONTROL0_TIE(0) |
> +		OUTCONTROL0_SIP(0) |
> +		OUTCONTROL0_FINIT(1) |
> +		OUTCONTROL0_FCI(0) |
> +		OUTCONTROL0_BFTH(3) |
> +		OUTCONTROL0_OF(of0) |
> +		OUTCONTROL0_NUMBER_OF_DECIMATORS(ipm) |
> +		OUTCONTROL0_IPM_SOURCE_1(source[0]) |
> +		OUTCONTROL0_IPM_SOURCE_2(source[1]) |
> +		OUTCONTROL0_IPM_SOURCE_3(source[2]) |
> +		OUTCONTROL0_IPM_SOURCE_4(source[3]) |
> +		OUTCONTROL0_TH(3);
> +	dmic_write(dai, OUTCONTROL0, val);
> +	trace_value(val);
> +
> +	val = OUTCONTROL1_TIE(0) |
> +		OUTCONTROL1_SIP(0) |
> +		OUTCONTROL1_FINIT(1) |
> +		OUTCONTROL1_FCI(0) |
> +		OUTCONTROL1_BFTH(3) |
> +		OUTCONTROL1_OF(of1) |
> +		OUTCONTROL1_NUMBER_OF_DECIMATORS(ipm) |
> +		OUTCONTROL1_IPM_SOURCE_1(source[0]) |
> +		OUTCONTROL1_IPM_SOURCE_2(source[1]) |
> +		OUTCONTROL1_IPM_SOURCE_3(source[2]) |
> +		OUTCONTROL1_IPM_SOURCE_4(source[3]) |
> +		OUTCONTROL1_TH(3);
> +	dmic_write(dai, OUTCONTROL1, val);
> +	trace_value(val);
> +#endif
> +
> +	/* Mark enabled microphones into private data to be later
> used
> +	 * for starting correct parts of the HW.
> +	 */
> +	for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
> +		pdata->enable[i] = (dmic->pdm[i].enable_mic_b << 1)
> |
> +			dmic->pdm[i].enable_mic_a;
> +	}
> +
> +	for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
> +		/* CIC */
> +		val = CIC_CONTROL_SOFT_RESET(soft_reset) |
> +			CIC_CONTROL_CIC_START_B(cic_start_b) |
> +			CIC_CONTROL_CIC_START_A(cic_start_a) |
> +		CIC_CONTROL_MIC_B_POLARITY(dmic-
> >pdm[i].polarity_mic_a) |
> +		CIC_CONTROL_MIC_A_POLARITY(dmic-
> >pdm[i].polarity_mic_b) |
> +			CIC_CONTROL_MIC_MUTE(cic_mute) |
> +			CIC_CONTROL_STEREO_MODE(stereo[i]);
> +		dmic_write(dai, base[i] + CIC_CONTROL, val);
> +		trace_value(val);
> +
> +		val = CIC_CONFIG_CIC_SHIFT(cfg->cic_shift + 8) |
> +			CIC_CONFIG_COMB_COUNT(cfg->mcic - 1);
> +		dmic_write(dai, base[i] + CIC_CONFIG, val);
> +		trace_value(val);
> +
> +		/* Mono right channel mic usage requires swap of PDM
> channels
> +		 * since the mono decimation is done with only left
> channel
> +		 * processing active.
> +		 */
> +		edge = dmic->pdm[i].clk_edge;
> +		if (swap[i])
> +			edge = !edge;
> +
> +		val = MIC_CONTROL_PDM_CLKDIV(cfg->clkdiv - 2) |
> +			MIC_CONTROL_PDM_SKEW(dmic->pdm[i].skew) |
> +			MIC_CONTROL_CLK_EDGE(edge) |
> +			MIC_CONTROL_PDM_EN_B(cic_start_b) |
> +			MIC_CONTROL_PDM_EN_A(cic_start_a);
> +		dmic_write(dai, base[i] + MIC_CONTROL, val);
> +		trace_value(val);
> +
> +		/* FIR A */
> +		fir_decim = MAX(cfg->mfir_a - 1, 0);
> +		fir_length = MAX(cfg->fir_a_length - 1, 0);
> +		val = FIR_CONTROL_A_START(fir_start_a) |
> +			FIR_CONTROL_A_ARRAY_START_EN(array_a) |
> +			FIR_CONTROL_A_DCCOMP(dccomp) |
> +			FIR_CONTROL_A_MUTE(fir_mute) |
> +			FIR_CONTROL_A_STEREO(stereo[i]);
> +		dmic_write(dai, base[i] + FIR_CONTROL_A, val);
> +		trace_value(val);
> +
> +		val = FIR_CONFIG_A_FIR_DECIMATION(fir_decim) |
> +			FIR_CONFIG_A_FIR_SHIFT(cfg->fir_a_shift) |
> +			FIR_CONFIG_A_FIR_LENGTH(fir_length);
> +		dmic_write(dai, base[i] + FIR_CONFIG_A, val);
> +		trace_value(val);
> +
> +		val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0);
> +		dmic_write(dai, base[i] + DC_OFFSET_LEFT_A, val);
> +		trace_value(val);
> +
> +		val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0);
> +		dmic_write(dai, base[i] + DC_OFFSET_RIGHT_A, val);
> +		trace_value(val);
> +
> +		val = OUT_GAIN_LEFT_A_GAIN(0);
> +		dmic_write(dai, base[i] + OUT_GAIN_LEFT_A, val);
> +		trace_value(val);
> +
> +		val = OUT_GAIN_RIGHT_A_GAIN(0);
> +		dmic_write(dai, base[i] + OUT_GAIN_RIGHT_A, val);
> +		trace_value(val);
> +
> +		/* FIR B */
> +		fir_decim = MAX(cfg->mfir_b - 1, 0);
> +		fir_length = MAX(cfg->fir_b_length - 1, 0);
> +		val = FIR_CONTROL_B_START(fir_start_b) |
> +			FIR_CONTROL_B_ARRAY_START_EN(array_b) |
> +			FIR_CONTROL_B_DCCOMP(dccomp) |
> +			FIR_CONTROL_B_MUTE(fir_mute) |
> +			FIR_CONTROL_B_STEREO(stereo[i]);
> +		dmic_write(dai, base[i] + FIR_CONTROL_B, val);
> +		trace_value(val);
> +
> +		val = FIR_CONFIG_B_FIR_DECIMATION(fir_decim) |
> +			FIR_CONFIG_B_FIR_SHIFT(cfg->fir_b_shift) |
> +			FIR_CONFIG_B_FIR_LENGTH(fir_length);
> +		dmic_write(dai, base[i] + FIR_CONFIG_B, val);
> +		trace_value(val);
> +
> +		val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0);
> +		dmic_write(dai, base[i] + DC_OFFSET_LEFT_B, val);
> +		trace_value(val);
> +		trace_value(val);
> +
> +		val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0);
> +		dmic_write(dai, base[i] + DC_OFFSET_RIGHT_B, val);
> +		trace_value(val);
> +
> +		val = OUT_GAIN_LEFT_B_GAIN(0);
> +		dmic_write(dai, base[i] + OUT_GAIN_LEFT_B, val);
> +		trace_value(val);
> +
> +		val = OUT_GAIN_RIGHT_B_GAIN(0);
> +		dmic_write(dai, base[i] + OUT_GAIN_RIGHT_B, val);
> +		trace_value(val);
> +
> +		/* Write coef RAM A with scaled coefficient in
> reverse order */
> +		length = cfg->fir_a_length;
> +		for (j = 0; j < length; j++) {
> +			ci = (int32_t)Q_MULTSR_32X32(
> +				(int64_t)cfg->fir_a->coef[j], cfg-
> >fir_a_scale,
> +				31, DMIC_FIR_SCALE_Q,
> DMIC_HW_FIR_COEF_Q);
> +			cu = FIR_COEF_A(ci);
> +			dmic_write(dai, coef_base_a[i]
> +				+ ((length - j - 1) << 2), cu);
> +#if defined MODULE_TEST
> +			fir_a_max = MAX(fir_a_max, ci);
> +			fir_a_min = MIN(fir_a_min, ci);
> +#endif
> +		}
> +
> +		/* Write coef RAM B with scaled coefficient in
> reverse order */
> +		length = cfg->fir_b_length;
> +		for (j = 0; j < length; j++) {
> +			ci = (int32_t)Q_MULTSR_32X32(
> +				(int64_t)cfg->fir_b->coef[j], cfg-
> >fir_b_scale,
> +				31, DMIC_FIR_SCALE_Q,
> DMIC_HW_FIR_COEF_Q);
> +			cu = FIR_COEF_B(ci);
> +			dmic_write(dai, coef_base_b[i]
> +				+ ((length - j - 1) << 2), cu);
> +#if defined MODULE_TEST
> +			fir_b_max = MAX(fir_b_max, ci);
> +			fir_b_min = MIN(fir_b_min, ci);
> +#endif
> +		}
> +	}
> +
> +#if defined MODULE_TEST
> +	printf("# FIR A max Q19 = %d (%f)\n", fir_a_max,
> +		Q_CONVERT_QTOF(fir_a_max, 19));
> +	printf("# FIR A min Q19 = %d (%f)\n", fir_a_min,
> +		Q_CONVERT_QTOF(fir_a_min, 19));
> +	printf("# FIR B max Q19 = %d (%f)\n", fir_b_max,
> +		Q_CONVERT_QTOF(fir_b_max, 19));
> +	printf("# FIR B min Q19 = %d (%f)\n", fir_b_min,
> +		Q_CONVERT_QTOF(fir_b_min, 19));
> +#endif
> +
> +	/* Function dmic_start() uses these to start the used FIFOs
> */
> +	if (cfg->mfir_a > 0)
> +		pdata->fifo_a = 1;
> +	else
> +		pdata->fifo_a = 0;
> +
> +	if (cfg->mfir_b > 0)
> +		pdata->fifo_b = 1;
> +	else
> +		pdata->fifo_b = 0;
> +
> +	return 0;
> +}
> +
> +static int dmic_set_config(struct dai *dai, struct
> sof_ipc_dai_config *config)
> +{
> +	struct decim_modes modes_a;
> +	struct decim_modes modes_b;
> +	struct matched_modes modes_ab;
> +	struct dmic_configuration cfg;
> +	int ret;
> +	struct sof_ipc_dai_dmic_params *prm = &config->dmic;
> +	struct dmic_pdata *dmic = dai_get_drvdata(dai);
> +
> +	trace_dmic("dsc");
> +
> +#if defined DMIC_FORCE_CONFIG
> +	/* This is a temporary workaound to set parameters while
> +	 * there is no driver and topology scripts support to
> +	 * set these. Also the PCM sample format(s) and sample
> rate(s)
> +	 * setting would be better to be common with other DAI
> types.
> +	 */
> +	prm->driver_ipc_version = 1;
> +	prm->pdmclk_min =  500000; /* Min 500 kHz */
> +	prm->pdmclk_max = 4800000; /* Max 4.80 MHz */
> +	prm->fifo_fs_a = 48000;
> +	prm->fifo_fs_b = 0;
> +	prm->fifo_bits_a = 32;
> +	prm->fifo_bits_b = 32;
> +	prm->duty_min = 40; /* Min. 40% */
> +	prm->duty_max = 60; /* Max. 60% */
> +	prm->number_of_pdm_controllers = 2;
> +	prm->pdm[0].clk_edge = 0;
> +	prm->pdm[0].enable_mic_a = 1; /* Left */
> +	prm->pdm[0].enable_mic_b = 1; /* Right */
> +	prm->pdm[0].polarity_mic_a = 0;
> +	prm->pdm[0].polarity_mic_b = 0;
> +	prm->pdm[0].skew = 0;
> +	prm->pdm[1].clk_edge = 0;
> +	prm->pdm[1].enable_mic_a = 0; /* Left */
> +	prm->pdm[1].enable_mic_b = 0; /* Right */
> +	prm->pdm[1].polarity_mic_a = 0;
> +	prm->pdm[1].polarity_mic_b = 0;
> +	prm->pdm[1].skew = 0;
> +#endif
> +
> +	trace_value(prm->driver_ipc_version);
> +	trace_value(prm->pdmclk_min);
> +	trace_value(prm->pdmclk_max);
> +	trace_value(prm->fifo_fs_a);
> +	trace_value(prm->fifo_fs_b);
> +	trace_value(prm->fifo_bits_a);
> +	trace_value(prm->fifo_bits_b);
> +	trace_value(prm->duty_min);
> +	trace_value(prm->duty_max);
> +	trace_value(prm->number_of_pdm_controllers);
> +
> +	if (prm->driver_ipc_version != DMIC_IPC_VERSION) {
> +		trace_dmic_error("ver");
> +		return -EINVAL;
> +	}
> +
> +	/* Match and select optimal decimators configuration for
> FIFOs A and B
> +	 * paths. This setup phase is still abstract. Successful
> completion
> +	 * points struct cfg to FIR coefficients and contains the
> scale value
> +	 * to use for FIR coefficient RAM write as well as the CIC
> and FIR
> +	 * shift values.
> +	 */
> +	find_modes(&modes_a, prm, prm->fifo_fs_a);
> +	if (modes_a.num_of_modes == 0 && prm->fifo_fs_a > 0) {
> +		trace_dmic_error("amo");
> +		return -EINVAL;
> +	}
> +
> +	find_modes(&modes_b, prm, prm->fifo_fs_b);
> +	if (modes_b.num_of_modes == 0 && prm->fifo_fs_b > 0) {
> +		trace_dmic_error("bmo");
> +		return -EINVAL;
> +	}
> +
> +	match_modes(&modes_ab, &modes_a, &modes_b);
> +	ret = select_mode(&cfg, &modes_ab);
> +	if (ret < 0) {
> +		trace_dmic_error("smo");
> +		return -EINVAL;
> +	}
> +
> +	trace_dmic("cfg");
> +	trace_value(cfg.clkdiv);
> +	trace_value(cfg.mcic);
> +	trace_value(cfg.mfir_a);
> +	trace_value(cfg.mfir_b);
> +	trace_value(cfg.fir_a_length);
> +	trace_value(cfg.fir_b_length);
> +	trace_value(cfg.cic_shift);
> +	trace_value(cfg.fir_a_shift);
> +	trace_value(cfg.fir_b_shift);
> +
> +	/* Struct reg contains a mirror of actual HW registers.
> Determine
> +	 * register bits configuration from decimator configuration
> and the
> +	 * requested parameters.
> +	 */
> +	ret = configure_registers(dai, &cfg, prm);
> +	if (ret < 0) {
> +		trace_dmic_error("cor");
> +		return -EINVAL;
> +	}
> +
> +	dmic->state = COMP_STATE_PREPARE;
> +
> +	return 0;
> +}
> +
> +/* start the DMIC for capture */
> +static void dmic_start(struct dai *dai)
> +{
> +	struct dmic_pdata *dmic = dai_get_drvdata(dai);
> +	int i;
> +	int mic_a;
> +	int mic_b;
> +	int fir_a;
> +	int fir_b;
> +
> +	/* enable port */
> +	spin_lock(&dmic->lock);
> +	trace_dmic("sta");
> +	dmic->state = COMP_STATE_ACTIVE;
> +
> +	if (dmic->fifo_a) {
> +		trace_dmic("ffa");
> +		/*  Clear FIFO A initialize, Enable interrupts to
> DSP,
> +		 *  Start FIFO A packer.
> +		 */
> +		dmic_update_bits(dai, OUTCONTROL0,
> +			OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT,
> +			OUTCONTROL0_SIP_BIT);
> +	}
> +	if (dmic->fifo_b) {
> +		trace_dmic("ffb");
> +		/*  Clear FIFO B initialize, Enable interrupts to
> DSP,
> +		 *  Start FIFO B packer.
> +		 */
> +		dmic_update_bits(dai, OUTCONTROL1,
> +			OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT,
> +			OUTCONTROL1_SIP_BIT);
> +	}
> +
> +	for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
> +		mic_a = dmic->enable[i] & 1;
> +		mic_b = (dmic->enable[i] & 2) >> 1;
> +		if (dmic->fifo_a)
> +			fir_a = (dmic->enable[i] > 0) ? 1 : 0;
> +		else
> +			fir_a = 0;
> +		if (dmic->fifo_b)
> +			fir_b = (dmic->enable[i] > 0) ? 1 : 0;
> +		else
> +			fir_b = 0;
> +
> +		trace_dmic("mfn");
> +		trace_value(mic_a);
> +		trace_value(mic_b);
> +		trace_value(fir_a);
> +		trace_value(fir_b);
> +
> +		dmic_update_bits(dai, base[i] + CIC_CONTROL,
> +			CIC_CONTROL_CIC_START_A_BIT |
> +			CIC_CONTROL_CIC_START_B_BIT,
> +			CIC_CONTROL_CIC_START_A(mic_a) |
> +			CIC_CONTROL_CIC_START_B(mic_b));
> +		dmic_update_bits(dai, base[i] + MIC_CONTROL,
> +			MIC_CONTROL_PDM_EN_A_BIT |
> +			MIC_CONTROL_PDM_EN_B_BIT,
> +			MIC_CONTROL_PDM_EN_A(mic_a) |
> +			MIC_CONTROL_PDM_EN_B(mic_b));
> +
> +		dmic_update_bits(dai, base[i] + FIR_CONTROL_A,
> +			FIR_CONTROL_A_START_BIT,
> FIR_CONTROL_A_START(fir_a));
> +		dmic_update_bits(dai, base[i] + FIR_CONTROL_B,
> +			FIR_CONTROL_B_START_BIT,
> FIR_CONTROL_B_START(fir_b));
> +	}
> +
> +	/* Clear soft reset for all/used PDM controllers. This
> should
> +	 * start capture in sync.
> +	 */
> +	trace_dmic("unr");
> +	for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
> +		dmic_update_bits(dai, base[i] + CIC_CONTROL,
> +			CIC_CONTROL_SOFT_RESET_BIT, 0);
> +	}
> +
> +	spin_unlock(&dmic->lock);
> +
> +	/* Currently there's no DMIC HW internal mutings and wait
> times
> +	 * applied into this start sequence. It can be implemented
> here if
> +	 * start of audio capture would contain clicks and/or noise
> and it
> +	 * is not suppressed by gain ramp somewhere in the capture
> pipe.
> +	 */
> +	trace_dmic("run");
> +}
> +/* stop the DMIC for capture */
> +static void dmic_stop(struct dai *dai)
> +{
> +	struct dmic_pdata *dmic = dai_get_drvdata(dai);
> +	int i;
> +
> +	trace_dmic("sto")
> +	spin_lock(&dmic->lock);
> +	dmic->state = COMP_STATE_PREPARE;
> +
> +	/* Stop FIFO packers and set FIFO initialize bits */
> +	dmic_update_bits(dai, OUTCONTROL0,
> +		OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT,
> +		OUTCONTROL0_FINIT_BIT);
> +	dmic_update_bits(dai, OUTCONTROL1,
> +		OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT,
> +		OUTCONTROL1_FINIT_BIT);
> +
> +	/* Set soft reset for all PDM controllers.
> +	 */
> +	trace_dmic("sre");
> +	for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
> +		dmic_update_bits(dai, base[i] + CIC_CONTROL,
> +			CIC_CONTROL_SOFT_RESET_BIT,
> CIC_CONTROL_SOFT_RESET_BIT);
> +	}
> +
> +	spin_unlock(&dmic->lock);
> +}
> +
> +/* save DMIC context prior to entering D3 */
> +static int dmic_context_store(struct dai *dai)
> +{
> +	/* TODO: Nothing stored at the moment. Could read the
> registers into
> +	 * persisten memory if needed but the large coef RAM is not
> desirable
> +	 * to copy. It would be better to store selected mode
> parametesr from
> +	 * previous configuration request and re-program registers
> from
> +	 * scratch.
> +	 */
> +	return 0;
> +}
> +
> +/* restore DMIC context after leaving D3 */
> +static int dmic_context_restore(struct dai *dai)
> +{
> +	/* Nothing restored at the moment. */
> +	return 0;
> +}
> +
> +static int dmic_trigger(struct dai *dai, int cmd, int direction)
> +{
> +	struct dmic_pdata *dmic = dai_get_drvdata(dai);
> +
> +	trace_dmic("tri");
> +
> +	/* dai private is set in dmic_probe(), error if not set */
> +	if (!dmic) {
> +		trace_dmic_error("trn");
> +		return -EINVAL;
> +	}
> +
> +	if (direction != DAI_DIR_CAPTURE) {
> +		trace_dmic_error("cap");
> +		return -EINVAL;
> +	}
> +
> +	switch (cmd) {
> +	case COMP_TRIGGER_RELEASE:
> +	case COMP_TRIGGER_START:
> +		if (dmic->state == COMP_STATE_PREPARE ||
> +			dmic->state == COMP_STATE_PAUSED) {
> +			dmic_start(dai);
> +		} else {
> +			trace_dmic_error("cst");
> +			trace_value(dmic->state);
> +		}
> +		break;
> +	case COMP_TRIGGER_STOP:
> +	case COMP_TRIGGER_PAUSE:
> +		dmic_stop(dai);
> +		break;
> +	case COMP_TRIGGER_RESUME:
> +		dmic_context_restore(dai);
> +		break;
> +	case COMP_TRIGGER_SUSPEND:
> +		dmic_context_store(dai);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +/* On DMIC IRQ event trace the status register that contains the
> status and
> + * error bit fields.
> + */
> +static void dmic_irq_handler(void *data)
> +{
> +	struct dai *dai = data;
> +	uint32_t val;
> +
> +	/* Trace OUTSTAT0 register */
> +	val = dmic_read(dai, OUTSTAT0);
> +	trace_dmic("irq");
> +
> +	if (val & OUTSTAT0_ROR_BIT)
> +		trace_dmic_error("eor"); /* Full fifo or PDM
> overrrun */
> +
> +	trace_value(val);
> +
> +	/* clear IRQ */
> +	platform_interrupt_clear(dmic_irq(dai), 1);
> +}
> +
> +static int dmic_probe(struct dai *dai)
> +{
> +	struct dmic_pdata *dmic;
> +
> +	trace_dmic("pro");
> +
> +	/* allocate private data */
> +	dmic = rzalloc(RZONE_SYS, SOF_MEM_CAPS_RAM, sizeof(*dmic));
> +	dai_set_drvdata(dai, dmic);
> +
> +	spinlock_init(&dmic->lock);
> +
> +	/* Set state, note there is no playback direction support */
> +	dmic->state = COMP_STATE_READY;
> +
> +	/* register our IRQ handler */
> +	interrupt_register(dmic_irq(dai), dmic_irq_handler, dai);
> +
> +	platform_interrupt_unmask(dmic_irq(dai), 1);
> +	interrupt_enable(dmic_irq(dai));
> +
> +	return 0;
> +}
> +
> +/* DMIC has no loopback support */
> +static inline int dmic_set_loopback_mode(struct dai *dai, uint32_t
> lbm)
> +{
> +	return -EINVAL;
> +}
> +
> +const struct dai_ops dmic_ops = {
> +	.trigger = dmic_trigger,
> +	.set_config = dmic_set_config,
> +	.pm_context_store = dmic_context_store,
> +	.pm_context_restore = dmic_context_restore,
> +	.probe = dmic_probe,
> +	.set_loopback_mode = dmic_set_loopback_mode,
> +};
> +
> +#endif


More information about the Sound-open-firmware mailing list