zephyr/drivers/audio/intel_dmic.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);