1450 lines
40 KiB
C
1450 lines
40 KiB
C
/*
|
|
* Copyright (c) 2018, Intel Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Author: Seppo Ingalsuo <seppo.ingalsuo@linux.intel.com>
|
|
* Liam Girdwood <liam.r.girdwood@linux.intel.com>
|
|
* Keyon Jie <yang.jie@linux.intel.com>
|
|
* Sathish Kuttan <sathish.k.kuttan@intel.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <zephyr.h>
|
|
#include <device.h>
|
|
#include <soc.h>
|
|
#include <drivers/dma.h>
|
|
|
|
#include <audio/dmic.h>
|
|
#include "intel_dmic.h"
|
|
#include "decimation/pdm_decim_fir.h"
|
|
|
|
#define LOG_LEVEL CONFIG_AUDIO_DMIC_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(audio_dmic);
|
|
|
|
/*
|
|
* Maximum number of PDM controller instances supported by this driver
|
|
* configuration data types are selected based on this max.
|
|
* For example, uint32_t is selected when a config parameter is 4bits wide
|
|
* and 8 instances fit within a 32 bit type
|
|
*/
|
|
#define MAX_PDM_CONTROLLERS_SUPPORTED 8
|
|
/* Actual number of hardware controllers */
|
|
#define DMIC_HW_CONTROLLERS 4
|
|
|
|
#define DMIC_MAX_MODES 50
|
|
|
|
/* HW FIR pipeline needs 5 additional cycles per channel for internal
|
|
* operations. This is used in MAX filter length check.
|
|
*/
|
|
#define DMIC_FIR_PIPELINE_OVERHEAD 5
|
|
|
|
struct decim_modes {
|
|
int16_t clkdiv[DMIC_MAX_MODES];
|
|
int16_t mcic[DMIC_MAX_MODES];
|
|
int16_t mfir[DMIC_MAX_MODES];
|
|
int num_of_modes;
|
|
};
|
|
|
|
struct matched_modes {
|
|
int16_t clkdiv[DMIC_MAX_MODES];
|
|
int16_t mcic[DMIC_MAX_MODES];
|
|
int16_t mfir_a[DMIC_MAX_MODES];
|
|
int16_t mfir_b[DMIC_MAX_MODES];
|
|
int num_of_modes;
|
|
};
|
|
|
|
struct dmic_configuration {
|
|
struct pdm_decim *fir_a;
|
|
struct pdm_decim *fir_b;
|
|
int clkdiv;
|
|
int mcic;
|
|
int mfir_a;
|
|
int mfir_b;
|
|
int cic_shift;
|
|
int fir_a_shift;
|
|
int fir_b_shift;
|
|
int fir_a_length;
|
|
int fir_b_length;
|
|
int32_t fir_a_scale;
|
|
int32_t fir_b_scale;
|
|
};
|
|
|
|
/* 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
|
|
|
|
/* Fractional multiplication with shift and round
|
|
* Note that the parameters px and py must be cast to (int64_t) if other type.
|
|
*/
|
|
#define Q_MULTSR_32X32(px, py, qx, qy, qp) \
|
|
((((px) * (py) >> ((qx)+(qy)-(qp)-1)) + 1) >> 1)
|
|
|
|
/* Saturation */
|
|
#define SATP_INT32(x) (((x) > INT32_MAX) ? INT32_MAX : (x))
|
|
#define SATM_INT32(x) (((x) < INT32_MIN) ? INT32_MIN : (x))
|
|
|
|
/* Macros to set bit(s) */
|
|
#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))
|
|
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
|
|
/* queue size to hold buffers in process */
|
|
#define DMIC_BUF_Q_LEN 2
|
|
|
|
#define DMIC_REG_RD(reg) (*((volatile uint32_t *)(PDM_BASE + (reg))))
|
|
#define DMIC_REG_WR(reg, val) \
|
|
(*((volatile uint32_t *)(PDM_BASE + (reg))) = (val))
|
|
#define DMIC_REG_UPD(reg, mask, val) \
|
|
DMIC_REG_WR((reg), (DMIC_REG_RD((reg)) & ~(mask)) | ((val) & (mask)))
|
|
|
|
struct _stream_data {
|
|
struct k_msgq in_queue;
|
|
struct k_msgq out_queue;
|
|
void *in_msgs[DMIC_BUF_Q_LEN];
|
|
void *out_msgs[DMIC_BUF_Q_LEN];
|
|
struct k_mem_slab *mem_slab;
|
|
size_t block_size;
|
|
};
|
|
|
|
/* DMIC private data */
|
|
static struct _dmic_pdata {
|
|
enum dmic_state state;
|
|
uint16_t fifo_a;
|
|
uint16_t fifo_b;
|
|
uint16_t mic_en_mask;
|
|
uint8_t num_streams;
|
|
uint8_t reserved;
|
|
struct _stream_data streams[DMIC_MAX_STREAMS];
|
|
const struct device *dma_dev;
|
|
} dmic_private;
|
|
|
|
static inline void dmic_parse_channel_map(uint32_t channel_map_lo,
|
|
uint32_t channel_map_hi, uint8_t channel, uint8_t *pdm, enum pdm_lr *lr);
|
|
static inline uint8_t dmic_parse_clk_skew_map(uint32_t skew_map, uint8_t pdm);
|
|
static void dmic_stop(void);
|
|
|
|
/* 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_int16(int16_t idx[], int16_t vec[], int n, int vec_length,
|
|
int max_results)
|
|
{
|
|
int nresults = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < vec_length; i++) {
|
|
if (vec[i] == n) {
|
|
idx[nresults++] = i;
|
|
if (nresults == max_results) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nresults;
|
|
}
|
|
|
|
/* Return the smallest value found in the vector */
|
|
int16_t find_min_int16(int16_t 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 */
|
|
}
|
|
|
|
/* Count the left shift amount to normalize a 32 bit signed integer value
|
|
* without causing overflow. Input value 0 will result to 31.
|
|
*/
|
|
int norm_int32(int32_t val)
|
|
{
|
|
if (val == 0) {
|
|
return 31;
|
|
}
|
|
|
|
if (val < 0) {
|
|
val = -val;
|
|
}
|
|
|
|
return __builtin_clz(val) - 1;
|
|
}
|
|
|
|
/* 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 dmic_cfg *config, 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 == 0U) {
|
|
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 ((config->io.max_pdm_clk_freq < DMIC_HW_PDM_CLK_MIN) ||
|
|
(config->io.max_pdm_clk_freq > (DMIC_HW_IOCLK / 2))) {
|
|
LOG_ERR("max_pdm_clk_freq %u invalid",
|
|
config->io.max_pdm_clk_freq);
|
|
return;
|
|
}
|
|
|
|
if ((config->io.min_pdm_clk_freq < DMIC_HW_PDM_CLK_MIN) ||
|
|
(config->io.min_pdm_clk_freq > config->io.max_pdm_clk_freq)) {
|
|
LOG_ERR("min_pdm_clk_freq %u invalid",
|
|
config->io.min_pdm_clk_freq);
|
|
return;
|
|
}
|
|
|
|
/* Check for sane duty cycle */
|
|
if (config->io.min_pdm_clk_dc > config->io.max_pdm_clk_dc) {
|
|
LOG_ERR("min_pdm_clk_dc %u max_pdm_clk_dc %u invalid",
|
|
config->io.min_pdm_clk_dc,
|
|
config->io.max_pdm_clk_dc);
|
|
return;
|
|
}
|
|
|
|
if ((config->io.min_pdm_clk_dc < DMIC_HW_DUTY_MIN) ||
|
|
(config->io.min_pdm_clk_dc > DMIC_HW_DUTY_MAX)) {
|
|
LOG_ERR("min_pdm_clk_dc %u invalid",
|
|
config->io.min_pdm_clk_dc);
|
|
return;
|
|
}
|
|
|
|
if ((config->io.max_pdm_clk_dc < DMIC_HW_DUTY_MIN) ||
|
|
(config->io.max_pdm_clk_dc > DMIC_HW_DUTY_MAX)) {
|
|
LOG_ERR("max_pdm_clk_dc %u invalid", config->io.max_pdm_clk_dc);
|
|
return;
|
|
}
|
|
|
|
/* Min and max clock dividers */
|
|
clkdiv_min = (DMIC_HW_IOCLK + config->io.max_pdm_clk_freq - 1) /
|
|
config->io.max_pdm_clk_freq;
|
|
clkdiv_min = (clkdiv_min > DMIC_HW_CIC_DECIM_MIN) ?
|
|
clkdiv_min : DMIC_HW_CIC_DECIM_MIN;
|
|
clkdiv_max = DMIC_HW_IOCLK / config->io.min_pdm_clk_freq;
|
|
|
|
/* 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 < config->io.min_pdm_clk_dc) ||
|
|
(du_max > config->io.max_pdm_clk_dc)) {
|
|
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)
|
|
{
|
|
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;
|
|
struct pdm_decim **fir_list;
|
|
|
|
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 = (DMIC_HW_IOCLK / fs / 2) - DMIC_FIR_PIPELINE_OVERHEAD;
|
|
fir_max_length = MIN(fir_max_length, DMIC_HW_FIR_LENGTH_MAX);
|
|
|
|
fir_list = pdm_decim_get_fir_list();
|
|
|
|
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);
|
|
}
|
|
|
|
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 restriction 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) {
|
|
LOG_ERR("num_of_modes is 0");
|
|
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) {
|
|
LOG_ERR("FIR filter not found for mfir_a %d",
|
|
cfg->mfir_a);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (cfg->mfir_b > 0) {
|
|
cfg->fir_b = get_fir(cfg, cfg->mfir_b);
|
|
if (!cfg->fir_b) {
|
|
LOG_ERR("FIR filter not found for mfir_b %d",
|
|
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 */
|
|
LOG_ERR("Invalid CIC gain %d", g_cic);
|
|
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. */
|
|
LOG_ERR("Invalid coefficient A");
|
|
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. */
|
|
LOG_ERR("Invalid coefficient B");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
cfg->fir_b_scale = 0;
|
|
cfg->fir_b_shift = 0;
|
|
cfg->fir_b_length = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int source_ipm_helper(struct pdm_chan_cfg *config, uint32_t *source_mask,
|
|
uint8_t *controller_mask, uint8_t *stereo_mask, uint8_t *swap_mask)
|
|
{
|
|
uint8_t pdm_ix;
|
|
uint8_t chan_ix;
|
|
enum pdm_lr lr;
|
|
uint16_t pdm_lr_mask = 0U;
|
|
int ipm = 0;
|
|
|
|
/* clear outputs */
|
|
*source_mask = 0U;
|
|
*stereo_mask = 0U;
|
|
*swap_mask = 0U;
|
|
*controller_mask = 0U;
|
|
|
|
/* 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 (chan_ix = 0U; chan_ix < config->req_num_chan; chan_ix++) {
|
|
|
|
dmic_parse_channel_map(config->req_chan_map_lo,
|
|
config->req_chan_map_hi,
|
|
chan_ix, &pdm_ix, &lr);
|
|
|
|
if (pdm_ix >= DMIC_HW_CONTROLLERS) {
|
|
LOG_ERR("Invalid PDM controller %u in channel %u",
|
|
pdm_ix, chan_ix);
|
|
continue;
|
|
}
|
|
|
|
if ((*controller_mask & BIT(pdm_ix)) == 0U) {
|
|
*controller_mask |= BIT(pdm_ix);
|
|
*source_mask |= pdm_ix << (ipm << 2);
|
|
ipm++;
|
|
}
|
|
pdm_lr_mask |= BIT(lr) << (pdm_ix << 1);
|
|
/*
|
|
* if both L and R are requested,
|
|
* set the controller to be stereo
|
|
*/
|
|
if ((pdm_lr_mask >> (pdm_ix << 1)) &
|
|
(BIT(PDM_CHAN_LEFT) | BIT(PDM_CHAN_RIGHT))) {
|
|
*stereo_mask |= BIT(pdm_ix);
|
|
}
|
|
|
|
/*
|
|
* if R channel mic was requested first
|
|
* set the controller to swap the channels
|
|
*/
|
|
if ((pdm_lr_mask & BIT(PDM_CHAN_LEFT + (pdm_ix << 1))) == 0U) {
|
|
*swap_mask |= BIT(pdm_ix);
|
|
}
|
|
}
|
|
|
|
/* IPM bit field is set to count of active pdm controllers. */
|
|
LOG_DBG("%u decimators has to be configured", ipm);
|
|
return ipm;
|
|
}
|
|
|
|
static int configure_registers(const struct device *dev,
|
|
struct dmic_configuration *hw_cfg,
|
|
struct dmic_cfg *config)
|
|
{
|
|
uint8_t skew;
|
|
uint8_t swap_mask;
|
|
uint8_t edge_mask;
|
|
uint8_t stereo_mask;
|
|
uint8_t controller_mask;
|
|
uint32_t val;
|
|
int32_t ci;
|
|
uint32_t cu;
|
|
uint32_t coeff_ix;
|
|
int ipm;
|
|
int of0;
|
|
int of1;
|
|
int fir_decim;
|
|
int fir_length;
|
|
int length;
|
|
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;
|
|
|
|
int array_a = 0;
|
|
int array_b = 0;
|
|
int cic_mute = 0;
|
|
int fir_mute = 0;
|
|
|
|
/* Normal start sequence */
|
|
dccomp = 1;
|
|
soft_reset = 1;
|
|
cic_start_a = 0;
|
|
cic_start_b = 0;
|
|
fir_start_a = 0;
|
|
fir_start_b = 0;
|
|
|
|
uint32_t source_mask;
|
|
|
|
/* OUTCONTROL0 and OUTCONTROL1 */
|
|
of0 = (config->streams[0].pcm_width == 32U) ? 2 : 0;
|
|
if (config->channel.req_num_streams > 1) {
|
|
of1 = (config->streams[1].pcm_width == 32U) ? 2 : 0;
|
|
} else {
|
|
of1 = 0;
|
|
}
|
|
|
|
ipm = source_ipm_helper(&config->channel, &source_mask,
|
|
&controller_mask, &stereo_mask, &swap_mask);
|
|
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_mask) |
|
|
OUTCONTROL0_IPM_SOURCE_2(source_mask >> 4) |
|
|
OUTCONTROL0_IPM_SOURCE_3(source_mask >> 8) |
|
|
OUTCONTROL0_IPM_SOURCE_4(source_mask >> 12) |
|
|
OUTCONTROL0_TH(3);
|
|
DMIC_REG_WR(OUTCONTROL0, val);
|
|
LOG_DBG("WR: OUTCONTROL0: 0x%08X", 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_mask) |
|
|
OUTCONTROL1_IPM_SOURCE_2(source_mask >> 4) |
|
|
OUTCONTROL1_IPM_SOURCE_3(source_mask >> 8) |
|
|
OUTCONTROL1_IPM_SOURCE_4(source_mask >> 12) |
|
|
OUTCONTROL1_TH(3);
|
|
DMIC_REG_WR(OUTCONTROL1, val);
|
|
LOG_DBG("WR: OUTCONTROL1: 0x%08X", val);
|
|
|
|
/* 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++) {
|
|
if ((controller_mask & BIT(i)) == 0U) {
|
|
/* controller is not enabled */
|
|
continue;
|
|
}
|
|
|
|
if (stereo_mask & BIT(i)) {
|
|
dmic_private.mic_en_mask |= (BIT(PDM_CHAN_LEFT) |
|
|
BIT(PDM_CHAN_RIGHT)) << (i << 1);
|
|
} else {
|
|
dmic_private.mic_en_mask |=
|
|
((swap_mask & BIT(i)) == 0U) ?
|
|
BIT(PDM_CHAN_LEFT) << (i << 1) :
|
|
BIT(PDM_CHAN_RIGHT) << (i << 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Mono right channel mic usage requires swap of PDM channels
|
|
* since the mono decimation is done with only left channel
|
|
* processing active.
|
|
*/
|
|
edge_mask = config->io.pdm_clk_pol ^ swap_mask;
|
|
|
|
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(config->io.pdm_data_pol >> i) |
|
|
CIC_CONTROL_MIC_A_POLARITY(config->io.pdm_data_pol >> i) |
|
|
CIC_CONTROL_MIC_MUTE(cic_mute) |
|
|
CIC_CONTROL_STEREO_MODE(stereo_mask >> i);
|
|
DMIC_REG_WR(CIC_CONTROL(i), val);
|
|
LOG_DBG("WR: CIC_CONTROL[%u]: 0x%08X", i, val);
|
|
|
|
val = CIC_CONFIG_CIC_SHIFT(hw_cfg->cic_shift + 8) |
|
|
CIC_CONFIG_COMB_COUNT(hw_cfg->mcic - 1);
|
|
DMIC_REG_WR(CIC_CONFIG(i), val);
|
|
LOG_DBG("WR: CIC_CONFIG[%u]: 0x%08X", i, val);
|
|
|
|
skew = dmic_parse_clk_skew_map(config->io.pdm_clk_skew, i);
|
|
val = MIC_CONTROL_PDM_CLKDIV(hw_cfg->clkdiv - 2) |
|
|
MIC_CONTROL_PDM_SKEW(skew) |
|
|
MIC_CONTROL_CLK_EDGE(edge_mask >> i) |
|
|
MIC_CONTROL_PDM_EN_B(cic_start_b) |
|
|
MIC_CONTROL_PDM_EN_A(cic_start_a);
|
|
DMIC_REG_WR(MIC_CONTROL(i), val);
|
|
LOG_DBG("WR: MIC_CONTROL[%u]: 0x%08X", i, val);
|
|
|
|
/* FIR A */
|
|
fir_decim = MAX(hw_cfg->mfir_a - 1, 0);
|
|
fir_length = MAX(hw_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_mask >> i);
|
|
DMIC_REG_WR(FIR_CONTROL_A(i), val);
|
|
LOG_DBG("WR: FIR_CONTROL_A[%u]: 0x%08X", i, val);
|
|
|
|
val = FIR_CONFIG_A_FIR_DECIMATION(fir_decim) |
|
|
FIR_CONFIG_A_FIR_SHIFT(hw_cfg->fir_a_shift) |
|
|
FIR_CONFIG_A_FIR_LENGTH(fir_length);
|
|
DMIC_REG_WR(FIR_CONFIG_A(i), val);
|
|
LOG_DBG("WR: FIR_CONFIG_A[%u]: 0x%08X", i, val);
|
|
|
|
val = DC_OFFSET_LEFT_A_DC_OFFS(DCCOMP_TC0);
|
|
DMIC_REG_WR(DC_OFFSET_LEFT_A(i), val);
|
|
LOG_DBG("WR: DC_OFFSET_LEFT_A[%u]: 0x%08X", i, val);
|
|
|
|
val = DC_OFFSET_RIGHT_A_DC_OFFS(DCCOMP_TC0);
|
|
DMIC_REG_WR(DC_OFFSET_RIGHT_A(i), val);
|
|
LOG_DBG("WR: DC_OFFSET_RIGHT_A[%u]: 0x%08X", i, val);
|
|
|
|
val = OUT_GAIN_LEFT_A_GAIN(0);
|
|
DMIC_REG_WR(OUT_GAIN_LEFT_A(i), val);
|
|
LOG_DBG("WR: OUT_GAIN_LEFT_A[%u]: 0x%08X", i, val);
|
|
|
|
val = OUT_GAIN_RIGHT_A_GAIN(0);
|
|
DMIC_REG_WR(OUT_GAIN_RIGHT_A(i), val);
|
|
LOG_DBG("WR: OUT_GAIN_RIGHT_A[%u]: 0x%08X", i, val);
|
|
|
|
/* FIR B */
|
|
fir_decim = MAX(hw_cfg->mfir_b - 1, 0);
|
|
fir_length = MAX(hw_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_mask >> i);
|
|
DMIC_REG_WR(FIR_CONTROL_B(i), val);
|
|
LOG_DBG("WR: FIR_CONTROL_B[%u]: 0x%08X", i, val);
|
|
|
|
val = FIR_CONFIG_B_FIR_DECIMATION(fir_decim) |
|
|
FIR_CONFIG_B_FIR_SHIFT(hw_cfg->fir_b_shift) |
|
|
FIR_CONFIG_B_FIR_LENGTH(fir_length);
|
|
DMIC_REG_WR(FIR_CONFIG_B(i), val);
|
|
LOG_DBG("WR: FIR_CONFIG_B[%u]: 0x%08X", i, val);
|
|
|
|
val = DC_OFFSET_LEFT_B_DC_OFFS(DCCOMP_TC0);
|
|
DMIC_REG_WR(DC_OFFSET_LEFT_B(i), val);
|
|
LOG_DBG("WR: DC_OFFSET_LEFT_B[%u]: 0x%08X", i, val);
|
|
|
|
val = DC_OFFSET_RIGHT_B_DC_OFFS(DCCOMP_TC0);
|
|
DMIC_REG_WR(DC_OFFSET_RIGHT_B(i), val);
|
|
LOG_DBG("WR: DC_OFFSET_RIGHT_B[%u]: 0x%08X", i, val);
|
|
|
|
val = OUT_GAIN_LEFT_B_GAIN(0);
|
|
DMIC_REG_WR(OUT_GAIN_LEFT_B(i), val);
|
|
LOG_DBG("WR: OUT_GAIN_LEFT_B[%u]: 0x%08X", i, val);
|
|
|
|
val = OUT_GAIN_RIGHT_B_GAIN(0);
|
|
DMIC_REG_WR(OUT_GAIN_RIGHT_B(i), val);
|
|
LOG_DBG("WR: OUT_GAIN_RIGHT_B[%u]: 0x%08X", i, val);
|
|
}
|
|
|
|
/* Write coef RAM A with scaled coefficient in reverse order */
|
|
length = hw_cfg->fir_a_length;
|
|
for (j = 0; j < length; j++) {
|
|
ci = (int32_t)Q_MULTSR_32X32((int64_t)hw_cfg->fir_a->coef[j],
|
|
hw_cfg->fir_a_scale, 31, DMIC_FIR_SCALE_Q,
|
|
DMIC_HW_FIR_COEF_Q);
|
|
cu = FIR_COEF_A(ci);
|
|
coeff_ix = (length - j - 1) << 2;
|
|
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
|
|
DMIC_REG_WR(PDM_COEFF_A(i) + coeff_ix, cu);
|
|
}
|
|
}
|
|
|
|
/* Write coef RAM B with scaled coefficient in reverse order */
|
|
length = hw_cfg->fir_b_length;
|
|
for (j = 0; j < length; j++) {
|
|
ci = (int32_t)Q_MULTSR_32X32((int64_t)hw_cfg->fir_b->coef[j],
|
|
hw_cfg->fir_b_scale, 31, DMIC_FIR_SCALE_Q,
|
|
DMIC_HW_FIR_COEF_Q);
|
|
cu = FIR_COEF_B(ci);
|
|
coeff_ix = (length - j - 1) << 2;
|
|
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
|
|
DMIC_REG_WR(PDM_COEFF_B(i) + coeff_ix, cu);
|
|
}
|
|
}
|
|
|
|
/* Function dmic_start() uses these to start the used FIFOs */
|
|
dmic_private.fifo_a = (hw_cfg->mfir_a > 0) ? 1 : 0;
|
|
dmic_private.fifo_b = (hw_cfg->mfir_b > 0) ? 1 : 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dmic_dma_callback(const struct device *dev, void *arg,
|
|
uint32_t chan, int err_code)
|
|
{
|
|
void *buffer;
|
|
size_t size;
|
|
int stream;
|
|
struct _stream_data *stream_data;
|
|
int ret;
|
|
|
|
stream = (chan == DMA_CHANNEL_DMIC_RXA) ? 0 : 1;
|
|
stream_data = &dmic_private.streams[stream];
|
|
|
|
/* retrieve buffer from input queue */
|
|
ret = k_msgq_get(&stream_data->in_queue, &buffer, K_NO_WAIT);
|
|
|
|
if (ret) {
|
|
LOG_ERR("stream %u in_queue is empty", stream);
|
|
}
|
|
|
|
if (dmic_private.state == DMIC_STATE_ACTIVE) {
|
|
size = stream_data->block_size;
|
|
/* put buffer in output queue */
|
|
ret = k_msgq_put(&stream_data->out_queue, &buffer,
|
|
K_NO_WAIT);
|
|
if (ret) {
|
|
LOG_ERR("stream%u out_queue is full", stream);
|
|
}
|
|
|
|
/* allocate new buffer for next audio frame */
|
|
ret = k_mem_slab_alloc(stream_data->mem_slab, &buffer,
|
|
K_NO_WAIT);
|
|
if (ret) {
|
|
LOG_ERR("buffer alloc from slab %p err %d",
|
|
stream_data->mem_slab, ret);
|
|
} else {
|
|
/* put buffer in input queue */
|
|
ret = k_msgq_put(&stream_data->in_queue, &buffer,
|
|
K_NO_WAIT);
|
|
if (ret) {
|
|
LOG_ERR("buffer %p -> in_queue %p err %d",
|
|
buffer, &stream_data->in_queue,
|
|
ret);
|
|
}
|
|
|
|
/* reload the DMA */
|
|
dmic_reload_dma(chan, buffer, stream_data->block_size);
|
|
dmic_start_dma(chan);
|
|
}
|
|
} else {
|
|
/* stop activity, free buffers */
|
|
dmic_stop();
|
|
dmic_stop_dma(chan);
|
|
k_mem_slab_free(stream_data->mem_slab, &buffer);
|
|
}
|
|
}
|
|
|
|
static int dmic_set_config(const struct device *dev, struct dmic_cfg *config)
|
|
{
|
|
struct decim_modes modes_a;
|
|
struct decim_modes modes_b;
|
|
struct matched_modes modes_ab;
|
|
struct dmic_configuration hw_cfg;
|
|
int ret;
|
|
int stream;
|
|
|
|
LOG_DBG("min_pdm_clk_freq %u max_pdm_clk_freq %u",
|
|
config->io.min_pdm_clk_freq,
|
|
config->io.max_pdm_clk_freq);
|
|
LOG_DBG("min_pdm_clk_dc %u max_pdm_clk_dc %u",
|
|
config->io.min_pdm_clk_dc,
|
|
config->io.max_pdm_clk_dc);
|
|
LOG_DBG("num_chan %u", config->channel.req_num_chan);
|
|
LOG_DBG("req_num_streams %u", config->channel.req_num_streams);
|
|
|
|
if (config->channel.req_num_streams == 0U) {
|
|
LOG_ERR("req_num_streams is 0");
|
|
return -EINVAL;
|
|
}
|
|
|
|
config->channel.act_num_streams = MIN(config->channel.req_num_streams,
|
|
DMIC_MAX_STREAMS);
|
|
|
|
LOG_DBG("req_num_streams %u act_num_streams %u",
|
|
config->channel.req_num_streams,
|
|
config->channel.act_num_streams);
|
|
dmic_private.num_streams = config->channel.act_num_streams;
|
|
|
|
for (stream = 0; stream < dmic_private.num_streams; stream++) {
|
|
LOG_DBG("stream %u pcm_rate %u pcm_width %u", stream,
|
|
config->streams[stream].pcm_rate,
|
|
config->streams[stream].pcm_width);
|
|
|
|
if ((config->streams[stream].pcm_width) &&
|
|
(config->streams[stream].mem_slab == NULL)) {
|
|
LOG_ERR("Invalid mem_slab for stream %u", stream);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dmic_private.streams[stream].mem_slab =
|
|
config->streams[stream].mem_slab;
|
|
dmic_private.streams[stream].block_size =
|
|
config->streams[stream].block_size;
|
|
}
|
|
|
|
/* 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, config, config->streams[0].pcm_rate);
|
|
if ((modes_a.num_of_modes == 0) && (config->streams[0].pcm_rate > 0)) {
|
|
LOG_ERR("stream A num_of_modes is 0 and pcm_rate is %u",
|
|
config->streams[0].pcm_rate);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dmic_private.num_streams > 1) {
|
|
find_modes(&modes_b, config, config->streams[1].pcm_rate);
|
|
if ((modes_b.num_of_modes == 0) &&
|
|
(config->streams[1].pcm_rate > 0)) {
|
|
LOG_ERR("stream B num_of_modes = 0 & pcm_rate = %u",
|
|
config->streams[1].pcm_rate);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
modes_b.num_of_modes = 0;
|
|
}
|
|
|
|
match_modes(&modes_ab, &modes_a, &modes_b);
|
|
ret = select_mode(&hw_cfg, &modes_ab);
|
|
if (ret < 0) {
|
|
LOG_ERR("select_mode failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
LOG_DBG("clkdiv %u mcic %u", hw_cfg.clkdiv, hw_cfg.mcic);
|
|
LOG_DBG("mfir_a %d mfir_b %d", hw_cfg.mfir_a, hw_cfg.mfir_b);
|
|
LOG_DBG("fir_a_length %d fir_b_length %d", hw_cfg.fir_a_length,
|
|
hw_cfg.fir_b_length);
|
|
LOG_DBG("cic_shift %d fir_a_shift %d fir_b_shift %d", hw_cfg.cic_shift,
|
|
hw_cfg.fir_a_shift, hw_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(dev, &hw_cfg, config);
|
|
if (ret < 0) {
|
|
LOG_ERR("configure_registers failed RC: %d", ret);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dmic_private.state = DMIC_STATE_CONFIGURED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* start the DMIC for capture */
|
|
static void dmic_start(const struct device *dev)
|
|
{
|
|
struct _stream_data *stream;
|
|
unsigned int key;
|
|
int i;
|
|
int mic_a;
|
|
int mic_b;
|
|
int fir_a;
|
|
int fir_b;
|
|
void *buffer;
|
|
int ret;
|
|
|
|
for (i = 0; i < dmic_private.num_streams; i++) {
|
|
stream = &dmic_private.streams[i];
|
|
/* allocate buffer for stream A */
|
|
ret = k_mem_slab_alloc(stream->mem_slab, &buffer, K_NO_WAIT);
|
|
if (ret) {
|
|
LOG_ERR("alloc from mem_slab_a %p failed",
|
|
stream->mem_slab);
|
|
return;
|
|
}
|
|
/* load buffer to DMA */
|
|
dmic_reload_dma((i == 0) ? DMA_CHANNEL_DMIC_RXA :
|
|
DMA_CHANNEL_DMIC_RXB,
|
|
buffer, stream->block_size);
|
|
ret = k_msgq_put(&stream->in_queue, &buffer, K_NO_WAIT);
|
|
if (ret) {
|
|
LOG_ERR("stream %u in_queue full", i);
|
|
k_mem_slab_free(stream->mem_slab, &buffer);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* enable port */
|
|
key = irq_lock();
|
|
|
|
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
|
|
mic_a = dmic_private.mic_en_mask >> (PDM_CHAN_LEFT + (i << 1));
|
|
mic_a &= BIT(0);
|
|
|
|
mic_b = dmic_private.mic_en_mask >> (PDM_CHAN_RIGHT + (i << 1));
|
|
mic_b &= BIT(0);
|
|
|
|
if ((dmic_private.mic_en_mask >> (i << 1)) &
|
|
(BIT(PDM_CHAN_LEFT) | BIT(PDM_CHAN_RIGHT))) {
|
|
fir_a = (dmic_private.fifo_a) ? 1 : 0;
|
|
fir_b = (dmic_private.fifo_b) ? 1 : 0;
|
|
} else {
|
|
fir_a = fir_b = 0;
|
|
}
|
|
|
|
LOG_DBG("mic_a %d mic_b %d", mic_a, mic_b);
|
|
LOG_DBG("fir_a %d fir_b %d", fir_a, fir_b);
|
|
|
|
DMIC_REG_UPD(CIC_CONTROL(i),
|
|
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_REG_UPD(MIC_CONTROL(i),
|
|
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_REG_UPD(FIR_CONTROL_A(i),
|
|
FIR_CONTROL_A_START_BIT, FIR_CONTROL_A_START(fir_a));
|
|
DMIC_REG_UPD(FIR_CONTROL_B(i),
|
|
FIR_CONTROL_B_START_BIT, FIR_CONTROL_B_START(fir_b));
|
|
LOG_DBG("CIC_CONTROL[%u]: %08X", i,
|
|
DMIC_REG_RD(CIC_CONTROL(i)));
|
|
LOG_DBG("MIC_CONTROL[%u]: %08X", i,
|
|
DMIC_REG_RD(MIC_CONTROL(i)));
|
|
LOG_DBG("FIR_CONTROL_A[%u]: %08X", i,
|
|
DMIC_REG_RD(FIR_CONTROL_A(i)));
|
|
LOG_DBG("FIR_CONTROL_B[%u]: %08X", i,
|
|
DMIC_REG_RD(FIR_CONTROL_B(i)));
|
|
}
|
|
|
|
/* start the DMA channel(s) */
|
|
if (dmic_private.fifo_a) {
|
|
dmic_start_dma(DMA_CHANNEL_DMIC_RXA);
|
|
}
|
|
|
|
if (dmic_private.fifo_b) {
|
|
dmic_start_dma(DMA_CHANNEL_DMIC_RXB);
|
|
}
|
|
|
|
if (dmic_private.fifo_a) {
|
|
/* Clear FIFO A initialize, Enable interrupts to DSP,
|
|
* Start FIFO A packer.
|
|
*/
|
|
DMIC_REG_UPD(OUTCONTROL0,
|
|
OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT,
|
|
OUTCONTROL0_SIP_BIT);
|
|
}
|
|
if (dmic_private.fifo_b) {
|
|
/* Clear FIFO B initialize, Enable interrupts to DSP,
|
|
* Start FIFO B packer.
|
|
*/
|
|
DMIC_REG_UPD(OUTCONTROL1,
|
|
OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT,
|
|
OUTCONTROL1_SIP_BIT);
|
|
}
|
|
|
|
LOG_DBG("OUTCONTROL0: %08X", DMIC_REG_RD(OUTCONTROL0));
|
|
LOG_DBG("OUTCONTROL1: %08X", DMIC_REG_RD(OUTCONTROL1));
|
|
|
|
/* Clear soft reset for all/used PDM controllers. This should
|
|
* start capture in sync.
|
|
*/
|
|
LOG_DBG("Releasing soft reset for all PDM controllers");
|
|
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
|
|
DMIC_REG_UPD(CIC_CONTROL(i), CIC_CONTROL_SOFT_RESET_BIT, 0);
|
|
}
|
|
|
|
dmic_private.state = DMIC_STATE_ACTIVE;
|
|
irq_unlock(key);
|
|
|
|
LOG_DBG("State changed to DMIC_STATE_ACTIVE");
|
|
|
|
/* 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.
|
|
*/
|
|
}
|
|
|
|
/* stop the DMIC for capture */
|
|
static void dmic_stop(void)
|
|
{
|
|
int i;
|
|
|
|
/* Stop FIFO packers and set FIFO initialize bits */
|
|
DMIC_REG_UPD(OUTCONTROL0,
|
|
OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT,
|
|
OUTCONTROL0_FINIT_BIT);
|
|
DMIC_REG_UPD(OUTCONTROL1,
|
|
OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT,
|
|
OUTCONTROL1_FINIT_BIT);
|
|
|
|
/* Set soft reset for all PDM controllers.
|
|
*/
|
|
LOG_DBG("Soft reset all PDM controllers");
|
|
for (i = 0; i < DMIC_HW_CONTROLLERS; i++) {
|
|
DMIC_REG_UPD(CIC_CONTROL(i),
|
|
CIC_CONTROL_SOFT_RESET_BIT, CIC_CONTROL_SOFT_RESET_BIT);
|
|
}
|
|
}
|
|
|
|
static int dmic_trigger_device(const struct device *dev,
|
|
enum dmic_trigger cmd)
|
|
{
|
|
unsigned int key;
|
|
|
|
LOG_DBG("cmd: %d", cmd);
|
|
|
|
switch (cmd) {
|
|
case DMIC_TRIGGER_RELEASE:
|
|
case DMIC_TRIGGER_START:
|
|
if ((dmic_private.state == DMIC_STATE_CONFIGURED) ||
|
|
(dmic_private.state == DMIC_STATE_PAUSED)) {
|
|
dmic_start(dev);
|
|
} else {
|
|
LOG_ERR("Invalid state %d for cmd %d",
|
|
dmic_private.state, cmd);
|
|
}
|
|
break;
|
|
case DMIC_TRIGGER_STOP:
|
|
case DMIC_TRIGGER_PAUSE:
|
|
key = irq_lock();
|
|
dmic_private.state = DMIC_STATE_CONFIGURED;
|
|
irq_unlock(key);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline uint8_t dmic_parse_clk_skew_map(uint32_t skew_map, uint8_t pdm)
|
|
{
|
|
return (uint8_t)((skew_map >> ((pdm & BIT_MASK(3)) * 4U)) & BIT_MASK(4));
|
|
}
|
|
|
|
static int dmic_initialize_device(const struct device *dev)
|
|
{
|
|
int stream;
|
|
struct _stream_data *stream_data;
|
|
/* Initialize the buffer queues */
|
|
for (stream = 0; stream < DMIC_MAX_STREAMS; stream++) {
|
|
stream_data = &dmic_private.streams[stream];
|
|
k_msgq_init(&stream_data->in_queue,
|
|
(char *)stream_data->in_msgs,
|
|
sizeof(void *), DMIC_BUF_Q_LEN);
|
|
k_msgq_init(&stream_data->out_queue,
|
|
(char *)stream_data->out_msgs,
|
|
sizeof(void *), DMIC_BUF_Q_LEN);
|
|
}
|
|
|
|
/* Set state, note there is no playback direction support */
|
|
dmic_private.state = DMIC_STATE_INITIALIZED;
|
|
|
|
LOG_DBG("Device %s Initialized", dev->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dmic_configure_device(const struct device *dev,
|
|
struct dmic_cfg *config)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = dmic_set_config(dev, config);
|
|
if (ret) {
|
|
LOG_ERR("dmic_set_config failed with code %d", ret);
|
|
}
|
|
|
|
ret = dmic_configure_dma(config->streams, dmic_private.num_streams);
|
|
if (ret) {
|
|
LOG_ERR("dmic_configure_dma failed with code %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int dmic_read_device(const struct device *dev, uint8_t stream,
|
|
void **buffer, size_t *size, int32_t timeout)
|
|
{
|
|
int ret;
|
|
|
|
if (stream >= dmic_private.num_streams) {
|
|
LOG_ERR("stream %u invalid. must be < %u", stream,
|
|
dmic_private.num_streams);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* retrieve buffer from out queue */
|
|
ret = k_msgq_get(&dmic_private.streams[stream].out_queue,
|
|
buffer, K_MSEC(timeout));
|
|
if (ret) {
|
|
LOG_ERR("No buffers in stream %u out_queue", stream);
|
|
} else {
|
|
*size = dmic_private.streams[stream].block_size;
|
|
SOC_DCACHE_INVALIDATE(*buffer, *size);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dmic_configure_dma(struct pcm_stream_cfg *config, uint8_t num_streams)
|
|
{
|
|
int ret = 0;
|
|
int stream;
|
|
uint32_t channel;
|
|
struct dma_block_config dma_block;
|
|
struct dma_config dma_cfg = {
|
|
.dma_slot = DMA_HANDSHAKE_DMIC_RXA,
|
|
.channel_direction = PERIPHERAL_TO_MEMORY,
|
|
.complete_callback_en = 1,
|
|
.error_callback_en = 0,
|
|
.source_handshake = 0,
|
|
.dest_handshake = 0,
|
|
.channel_priority = 0,
|
|
.source_chaining_en = 0,
|
|
.dest_chaining_en = 0,
|
|
.source_data_size = 4,
|
|
.dest_data_size = 4,
|
|
.source_burst_length = 8,
|
|
.dest_burst_length = 8,
|
|
.block_count = 1,
|
|
.head_block = &dma_block,
|
|
.dma_callback = dmic_dma_callback,
|
|
};
|
|
|
|
dmic_private.dma_dev = device_get_binding(DMIC_DMA_DEV_NAME);
|
|
if (!dmic_private.dma_dev) {
|
|
LOG_ERR("Failed to bind to device: %s", DMIC_DMA_DEV_NAME);
|
|
return -ENODEV;
|
|
}
|
|
|
|
for (stream = 0; stream < num_streams; stream++) {
|
|
channel = (stream == 0) ? DMA_CHANNEL_DMIC_RXA :
|
|
DMA_CHANNEL_DMIC_RXB;
|
|
dma_cfg.dma_slot = (stream == 0) ? DMA_HANDSHAKE_DMIC_RXA :
|
|
DMA_HANDSHAKE_DMIC_RXB;
|
|
|
|
LOG_DBG("Configuring stream %u DMA ch%u handshake %u", stream,
|
|
channel, dma_cfg.dma_slot);
|
|
|
|
dma_block.source_address = (uint32_t)NULL;
|
|
dma_block.dest_address = (uint32_t)NULL;
|
|
dma_block.block_size = 0U;
|
|
dma_block.next_block = NULL;
|
|
|
|
ret = dma_config(dmic_private.dma_dev, channel, &dma_cfg);
|
|
if (ret) {
|
|
LOG_ERR("dma_config channel %u failed (%d)", channel,
|
|
ret);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int dmic_reload_dma(uint32_t channel, void *buffer, size_t size)
|
|
{
|
|
uint32_t source;
|
|
|
|
source = (channel == DMA_CHANNEL_DMIC_RXA) ? OUTDATA0 : OUTDATA1;
|
|
|
|
LOG_DBG("Loading buffer %p size %u to channel %u", buffer, size,
|
|
channel);
|
|
return dma_reload(dmic_private.dma_dev, channel,
|
|
PDM_BASE + source, (uint32_t)buffer, size);
|
|
}
|
|
|
|
int dmic_start_dma(uint32_t channel)
|
|
{
|
|
LOG_DBG("Starting DMA channel %u", channel);
|
|
return dma_start(dmic_private.dma_dev, channel);
|
|
}
|
|
|
|
int dmic_stop_dma(uint32_t channel)
|
|
{
|
|
LOG_DBG("Stopping DMA channel %u", channel);
|
|
return dma_stop(dmic_private.dma_dev, channel);
|
|
}
|
|
|
|
static struct _dmic_ops dmic_ops = {
|
|
.trigger = dmic_trigger_device,
|
|
.configure = dmic_configure_device,
|
|
.read = dmic_read_device,
|
|
};
|
|
|
|
DEVICE_AND_API_INIT(dmic, "PDM", &dmic_initialize_device, NULL, NULL,
|
|
POST_KERNEL, CONFIG_AUDIO_DMIC_INIT_PRIORITY, &dmic_ops);
|