Drivers: dmic-dai: Driver for Intel DMIC

This is a driver for Intel Digital Microphone

TODO:
 - volume rampup
 - TPLG config

Signed-off-by: Marcin Szkudlinski <marcin.szkudlinski@intel.com>
This commit is contained in:
Marcin Szkudlinski 2022-05-11 16:42:40 +02:00 committed by Anas Nashif
commit ae8b17d2bb
8 changed files with 1773 additions and 0 deletions

View file

@ -2,3 +2,4 @@
add_subdirectory_ifdef(CONFIG_DAI_INTEL_SSP intel/ssp) add_subdirectory_ifdef(CONFIG_DAI_INTEL_SSP intel/ssp)
add_subdirectory_ifdef(CONFIG_DAI_INTEL_ALH intel/alh) add_subdirectory_ifdef(CONFIG_DAI_INTEL_ALH intel/alh)
add_subdirectory_ifdef(CONFIG_DAI_INTEL_DMIC intel/dmic)

View file

@ -27,5 +27,6 @@ comment "Device Drivers"
source "drivers/dai/intel/ssp/Kconfig.ssp" source "drivers/dai/intel/ssp/Kconfig.ssp"
source "drivers/dai/intel/alh/Kconfig.alh" source "drivers/dai/intel/alh/Kconfig.alh"
source "drivers/dai/intel/dmic/Kconfig.dmic"
endif # DAI endif # DAI

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_DAI_INTEL_DMIC dmic.c)
zephyr_library_sources_ifdef(CONFIG_DAI_INTEL_DMIC_NHLT dmic_nhlt.c)

View file

@ -0,0 +1,76 @@
# SOF DMIC configuration options
# Copyright (c) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
config DAI_INTEL_DMIC
bool "Intel digital PDM microphone driver support for DAI interface"
help
Enable Intel digital PDM microphone driver for DAI interface
if DAI_INTEL_DMIC
choice
prompt "Driver operation mode"
default DAI_INTEL_DMIC_NHLT
help
The driver can support two operation modes.
1. A HW registers dump blob that is passed via IPC
2. DAI tokens those describe the use case PCM format
and PDM bus and microphone parameters
config DAI_INTEL_DMIC_NHLT
bool "Use NHLT DMIC blob"
help
All registers configuration is retrieved from blob. The
number of channels, sample rate, and PCM format are
defined in the blob and there are no runtime made
configuration choices.
config DAI_INTEL_DMIC_TPLG_PARAMS
bool "Use parameters from topology - WIP"
help
All registers configuration is computed on the fly
based on use case and microphone datasheet parameters
and topology defined PCM format. The parameters are
easy to to customize in the topology.
WORK IN PROGRESS, not enabled in the driver yet
endchoice
config DAI_DMIC_HAS_OWNERSHIP
bool "Use DMIC ownership claim/release"
default n
help
a feature introduced in ACE1.5 hardware
dmic ownership must be claimed before use of dmic
config DAI_DMIC_HAS_MULTIPLE_LINE_SYNC
bool "Use DMIC sync for multiple lines"
default n
help
a feature introduced in ACE1.5 hardware
dmic sync registers must be set before use of dmic
config DAI_DMIC_HW_CONTROLLERS
int "Number of hardware controllers in the system"
default 2
config DAI_DMIC_HW_FIFOS
int "Number of stream FIFOs in DMIC controller"
default 2
config DAI_DMIC_HW_IOCLK
int "IO Clock value for DMIC"
default 19200000
config DAI_DMIC_PLATFORM_SYNC_PERIOD
int "Sync period per platform"
default 4000
help
DMIC sync period used for:
CONFIG_DAI_DMIC_HW_IOCLK / CONFIG_DAI_DMIC_PLATFORM_SYNC_PERIOD
From spec: E.g. for 19.2 MHz XTAL oscillator clock, 4 KHz sync period,
the value to be programmed is 4799 (12BFh)
endif

View file

@ -0,0 +1,682 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT intel_dai_dmic
#define LOG_DOMAIN dai_intel_dmic
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_DOMAIN);
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <zephyr/spinlock.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/dai.h>
#include "dmic.h"
/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */
static const uint32_t base[4] = {PDM0, PDM1, PDM2, PDM3};
/* global data shared between all dmic instances */
struct dai_dmic_global_shared dai_dmic_global;
/* Helper macro to read 64-bit data using two 32-bit data read */
#define sys_read64(addr) (((uint64_t)(sys_read32(addr + 4)) << 32) | \
sys_read32(addr))
int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *spec_config);
static void dai_dmic_update_bits(const struct dai_intel_dmic *dmic,
uint32_t reg, uint32_t mask, uint32_t val)
{
uint32_t dest = dmic->reg_base + reg;
LOG_INF("%s base %x, reg %x, mask %x, value %x", __func__,
dmic->reg_base, reg, mask, val);
sys_write32((sys_read32(dest) & (~mask)) | (val & mask), dest);
}
static inline void dai_dmic_write(const struct dai_intel_dmic *dmic,
uint32_t reg, uint32_t val)
{
sys_write32(val, dmic->reg_base + reg);
}
static inline uint32_t dai_dmic_read(const struct dai_intel_dmic *dmic,
uint32_t reg)
{
return sys_read32(dmic->reg_base + reg);
}
#if CONFIG_DAI_DMIC_HAS_OWNERSHIP
static inline void dai_dmic_claim_ownership(const struct dai_intel_dmic *dmic)
{
/* DMIC Owner Select to DSP */
sys_write32(sys_read32(dmic->shim_base + DMICLCTL_OFFSET) |
DMICLCTL_OSEL(0x3), dmic->shim_base + DMICLCTL_OFFSET);
}
static inline void dai_dmic_release_ownership(const struct dai_intel_dmic *dmic)
{
/* DMIC Owner Select back to Host CPU + DSP */
sys_write32(sys_read32(dmic->shim_base + DMICLCTL_OFFSET) &
~DMICLCTL_OSEL(0x0), dmic->shim_base + DMICLCTL_OFFSET);
}
#else /* CONFIG_DAI_DMIC_HAS_OWNERSHIP */
static inline void dai_dmic_claim_ownership(const struct dai_intel_dmic *dmic) {}
static inline void dai_dmic_release_ownership(const struct dai_intel_dmic *dmic) {}
#endif /* CONFIG_DAI_DMIC_HAS_OWNERSHIP */
#if CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC
static inline void dai_dmic_set_sync_period(uint32_t period, const struct dai_intel_dmic *dmic)
{
uint32_t val = CONFIG_DAI_DMIC_HW_IOCLK / period - 1;
/* DMIC Change sync period */
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) | DMICSYNC_SYNCPRD(val),
dmic->shim_base + DMICSYNC_OFFSET);
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) | DMICSYNC_CMDSYNC,
dmic->shim_base + DMICSYNC_OFFSET);
}
static inline void dai_dmic_clear_sync_period(const struct dai_intel_dmic *dmic)
{
/* DMIC Clean sync period */
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) & ~DMICSYNC_SYNCPRD(0x0000),
dmic->shim_base + DMICSYNC_OFFSET);
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) & ~DMICSYNC_CMDSYNC,
dmic->shim_base + DMICSYNC_OFFSET);
}
/* Preparing for command synchronization on multiple link segments */
static inline void dai_dmic_sync_prepare(const struct dai_intel_dmic *dmic)
{
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) | DMICSYNC_CMDSYNC,
dmic->shim_base + DMICSYNC_OFFSET);
}
/* Trigering synchronization of command execution */
static void dmic_sync_trigger(const struct dai_intel_dmic *dmic)
{
__ASSERT_NO_MSG((sys_read32(dmic->shim_base + DMICSYNC_OFFSET) & DMICSYNC_CMDSYNC) != 0);
sys_write32(sys_read32(dmic->shim_base + DMICSYNC_OFFSET) |
DMICSYNC_SYNCGO, dmic->shim_base + DMICSYNC_OFFSET);
/* waiting for CMDSYNC bit clearing */
while (sys_read32(dmic->shim_base + DMICSYNC_OFFSET) & DMICSYNC_CMDSYNC) {
k_sleep(K_USEC(100));
}
}
#else /* CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC */
static inline void dai_dmic_set_sync_period(uint32_t period, const struct dai_intel_dmic *dmic) {}
static inline void dai_dmic_clear_sync_period(const struct dai_intel_dmic *dmic) {}
static inline void dai_dmic_sync_prepare(const struct dai_intel_dmic *dmic) {}
static void dmic_sync_trigger(const struct dai_intel_dmic *dmic) {}
#endif /* CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC */
static void dai_dmic_stop_fifo_packers(struct dai_intel_dmic *dmic,
int fifo_index)
{
/* Stop FIFO packers and set FIFO initialize bits */
switch (fifo_index) {
case 0:
dai_dmic_update_bits(dmic, OUTCONTROL0,
OUTCONTROL0_SIP_BIT | OUTCONTROL0_FINIT_BIT,
OUTCONTROL0_FINIT_BIT);
break;
case 1:
dai_dmic_update_bits(dmic, OUTCONTROL1,
OUTCONTROL1_SIP_BIT | OUTCONTROL1_FINIT_BIT,
OUTCONTROL1_FINIT_BIT);
break;
}
}
/* On DMIC IRQ event trace the status register that contains the status and
* error bit fields.
*/
static void dai_dmic_irq_handler(const void *data)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *) data;
uint32_t val0;
uint32_t val1;
/* Trace OUTSTAT0 register */
val0 = dai_dmic_read(dmic, OUTSTAT0);
val1 = dai_dmic_read(dmic, OUTSTAT1);
LOG_INF("dmic_irq_handler(), OUTSTAT0 = 0x%x, OUTSTAT1 = 0x%x", val0, val1);
if (val0 & OUTSTAT0_ROR_BIT) {
LOG_ERR("dmic_irq_handler(): full fifo A or PDM overrun");
dai_dmic_write(dmic, OUTSTAT0, val0);
dai_dmic_stop_fifo_packers(dmic, 0);
}
if (val1 & OUTSTAT1_ROR_BIT) {
LOG_ERR("dmic_irq_handler(): full fifo B or PDM overrun");
dai_dmic_write(dmic, OUTSTAT1, val1);
dai_dmic_stop_fifo_packers(dmic, 1);
}
}
static inline void dai_dmic_dis_clk_gating(const struct dai_intel_dmic *dmic)
{
/* Disable DMIC clock gating */
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) | DMIC_DCGD),
dmic->shim_base + DMICLCTL_OFFSET);
}
static inline void dai_dmic_en_clk_gating(const struct dai_intel_dmic *dmic)
{
/* Enable DMIC clock gating */
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) & ~DMIC_DCGD),
dmic->shim_base + DMICLCTL_OFFSET);
}
static inline void dai_dmic_en_power(const struct dai_intel_dmic *dmic)
{
/* Enable DMIC power */
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) | DMICLCTL_SPA),
dmic->shim_base + DMICLCTL_OFFSET);
}
static inline void dai_dmic_dis_power(const struct dai_intel_dmic *dmic)
{
/* Disable DMIC power */
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) & (~DMICLCTL_SPA)),
dmic->shim_base + DMICLCTL_OFFSET);
}
static int dai_dmic_probe(struct dai_intel_dmic *dmic)
{
LOG_INF("dmic_probe()");
/* Set state, note there is no playback direction support */
dmic->state = DAI_STATE_NOT_READY;
/* Enable DMIC power */
dai_dmic_en_power(dmic);
/* Disable dynamic clock gating for dmic before touching any reg */
dai_dmic_dis_clk_gating(dmic);
/* DMIC Change sync period */
dai_dmic_set_sync_period(CONFIG_DAI_DMIC_PLATFORM_SYNC_PERIOD, dmic);
/* DMIC Owner Select to DSP */
dai_dmic_claim_ownership(dmic);
irq_enable(dmic->irq);
return 0;
}
static int dai_dmic_remove(struct dai_intel_dmic *dmic)
{
uint32_t active_fifos_mask = dai_dmic_global.active_fifos_mask;
uint32_t pause_mask = dai_dmic_global.pause_mask;
LOG_INF("dmic_remove()");
irq_disable(dmic->irq);
LOG_INF("dmic_remove(), dmic_active_fifos_mask = 0x%x, dmic_pause_mask = 0x%x",
active_fifos_mask, pause_mask);
/* The next end tasks must be passed if another DAI FIFO still runs.
* Note: dai_put() function that calls remove() applies the spinlock
* so it is not needed here to protect access to mask bits.
*/
if (active_fifos_mask || pause_mask)
return 0;
/* Disable DMIC clock and power */
dai_dmic_en_clk_gating(dmic);
dai_dmic_dis_power(dmic);
/* DMIC Clean sync period */
dai_dmic_clear_sync_period(dmic);
/* DMIC Owner Select back to Host CPU + DSP */
dai_dmic_release_ownership(dmic);
return 0;
}
static int dai_dmic_timestamp_config(const struct device *dev, struct dai_ts_cfg *cfg)
{
cfg->walclk_rate = CONFIG_DAI_DMIC_HW_IOCLK;
return 0;
}
static int dai_timestamp_dmic_start(const struct device *dev, struct dai_ts_cfg *cfg)
{
uint32_t addr = TS_DMIC_LOCAL_TSCTRL;
uint32_t cdmas;
/* Set DMIC timestamp registers */
/* First point CDMAS to GPDMA channel that is used by DMIC
* also clear NTK to be sure there is no old timestamp.
*/
cdmas = TS_LOCAL_TSCTRL_CDMAS(cfg->dma_chan_index +
cfg->dma_chan_count * cfg->dma_id);
sys_write32(TS_LOCAL_TSCTRL_NTK_BIT | cdmas, addr);
/* Request on demand timestamp */
sys_write32(TS_LOCAL_TSCTRL_ODTS_BIT | cdmas, addr);
return 0;
}
static int dai_timestamp_dmic_stop(const struct device *dev, struct dai_ts_cfg *cfg)
{
/* Clear NTK and write zero to CDMAS */
sys_write32(TS_LOCAL_TSCTRL_NTK_BIT,
TS_DMIC_LOCAL_TSCTRL);
return 0;
}
static int dai_timestamp_dmic_get(const struct device *dev, struct dai_ts_cfg *cfg,
struct dai_ts_data *tsd)
{
/* Read DMIC timestamp registers */
uint32_t tsctrl = TS_DMIC_LOCAL_TSCTRL;
uint32_t ntk;
/* Read SSP timestamp registers */
ntk = sys_read32(tsctrl) & TS_LOCAL_TSCTRL_NTK_BIT;
if (!ntk)
goto out;
/* NTK was set, get wall clock */
tsd->walclk = sys_read64(TS_DMIC_LOCAL_WALCLK);
/* Sample */
tsd->sample = sys_read64(TS_DMIC_LOCAL_SAMPLE);
/* Clear NTK to enable successive timestamps */
sys_write32(TS_LOCAL_TSCTRL_NTK_BIT, tsctrl);
out:
tsd->walclk_rate = cfg->walclk_rate;
if (!ntk)
return -ENODATA;
return 0;
}
static void dai_dmic_start(struct dai_intel_dmic *dmic)
{
k_spinlock_key_t key;
int i;
int mic_a;
int mic_b;
int fir_a;
int fir_b;
/* enable port */
key = k_spin_lock(&dmic->lock);
LOG_DBG("dmic_start()");
dai_dmic_sync_prepare(dmic);
switch (dmic->dai_config_params.dai_index) {
case 0:
LOG_INF("dmic_start(), dmic->fifo_a");
/* Clear FIFO A initialize, Enable interrupts to DSP,
* Start FIFO A packer.
*/
dai_dmic_update_bits(
dmic,
OUTCONTROL0,
OUTCONTROL0_FINIT_BIT | OUTCONTROL0_SIP_BIT,
OUTCONTROL0_SIP_BIT);
break;
case 1:
LOG_INF("dmic_start(), dmic->fifo_b");
/* Clear FIFO B initialize, Enable interrupts to DSP,
* Start FIFO B packer.
*/
dai_dmic_update_bits(dmic, OUTCONTROL1,
OUTCONTROL1_FINIT_BIT | OUTCONTROL1_SIP_BIT,
OUTCONTROL1_SIP_BIT);
}
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
mic_a = dmic->enable[i] & 1;
mic_b = (dmic->enable[i] & 2) >> 1;
fir_a = (dmic->enable[i] > 0) ? 1 : 0;
fir_b = (dmic->enable[i] > 0) ? 1 : 0;
LOG_INF("dmic_start(), pdm%d mic_a = %u, mic_b = %u", i, mic_a, mic_b);
/* If both microphones are needed start them simultaneously
* to start them in sync. The reset may be cleared for another
* FIFO already. If only one mic, start them independently.
* This makes sure we do not clear start/en for another DAI.
*/
if (mic_a && mic_b) {
dai_dmic_update_bits(dmic, base[i] + CIC_CONTROL,
CIC_CONTROL_CIC_START_A_BIT |
CIC_CONTROL_CIC_START_B_BIT,
CIC_CONTROL_CIC_START_A(1) |
CIC_CONTROL_CIC_START_B(1));
dai_dmic_update_bits(dmic, base[i] + MIC_CONTROL,
MIC_CONTROL_PDM_EN_A_BIT |
MIC_CONTROL_PDM_EN_B_BIT,
MIC_CONTROL_PDM_EN_A(1) |
MIC_CONTROL_PDM_EN_B(1));
} else if (mic_a) {
dai_dmic_update_bits(dmic, base[i] + CIC_CONTROL,
CIC_CONTROL_CIC_START_A_BIT,
CIC_CONTROL_CIC_START_A(1));
dai_dmic_update_bits(dmic, base[i] + MIC_CONTROL,
MIC_CONTROL_PDM_EN_A_BIT,
MIC_CONTROL_PDM_EN_A(1));
} else if (mic_b) {
dai_dmic_update_bits(dmic, base[i] + CIC_CONTROL,
CIC_CONTROL_CIC_START_B_BIT,
CIC_CONTROL_CIC_START_B(1));
dai_dmic_update_bits(dmic, base[i] + MIC_CONTROL,
MIC_CONTROL_PDM_EN_B_BIT,
MIC_CONTROL_PDM_EN_B(1));
}
switch (dmic->dai_config_params.dai_index) {
case 0:
dai_dmic_update_bits(dmic, base[i] + FIR_CONTROL_A,
FIR_CONTROL_A_START_BIT,
FIR_CONTROL_A_START(fir_a));
break;
case 1:
dai_dmic_update_bits(dmic, base[i] + FIR_CONTROL_B,
FIR_CONTROL_B_START_BIT,
FIR_CONTROL_B_START(fir_b));
break;
}
}
/* Clear soft reset for all/used PDM controllers. This should
* start capture in sync.
*/
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
dai_dmic_update_bits(dmic, base[i] + CIC_CONTROL,
CIC_CONTROL_SOFT_RESET_BIT, 0);
}
/* Set bit dai->index */
dai_dmic_global.active_fifos_mask |= BIT(dmic->dai_config_params.dai_index);
dai_dmic_global.pause_mask &= ~BIT(dmic->dai_config_params.dai_index);
dmic->state = DAI_STATE_RUNNING;
k_spin_unlock(&dmic->lock, key);
dmic_sync_trigger(dmic);
LOG_INF("dmic_start(), dmic_active_fifos_mask = 0x%x",
dai_dmic_global.active_fifos_mask);
}
static void dai_dmic_stop(struct dai_intel_dmic *dmic, bool stop_is_pause)
{
k_spinlock_key_t key;
int i;
LOG_DBG("dmic_stop()");
key = k_spin_lock(&dmic->lock);
dai_dmic_stop_fifo_packers(dmic, dmic->dai_config_params.dai_index);
/* Set soft reset and mute on for all PDM controllers. */
LOG_INF("dmic_stop(), dmic_active_fifos_mask = 0x%x",
dai_dmic_global.active_fifos_mask);
/* Clear bit dmic->dai_config_params.dai_index for active FIFO.
* If stop for pause, set pause mask bit.
* If stop is not for pausing, it is safe to clear the pause bit.
*/
dai_dmic_global.active_fifos_mask &= ~BIT(dmic->dai_config_params.dai_index);
if (stop_is_pause)
dai_dmic_global.pause_mask |= BIT(dmic->dai_config_params.dai_index);
else
dai_dmic_global.pause_mask &= ~BIT(dmic->dai_config_params.dai_index);
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
/* Don't stop CIC yet if one FIFO remains active */
if (dai_dmic_global.active_fifos_mask == 0) {
dai_dmic_update_bits(dmic, base[i] + CIC_CONTROL,
CIC_CONTROL_SOFT_RESET_BIT |
CIC_CONTROL_MIC_MUTE_BIT,
CIC_CONTROL_SOFT_RESET_BIT |
CIC_CONTROL_MIC_MUTE_BIT);
}
switch (dmic->dai_config_params.dai_index) {
case 0:
dai_dmic_update_bits(dmic, base[i] + FIR_CONTROL_A,
FIR_CONTROL_A_MUTE_BIT,
FIR_CONTROL_A_MUTE_BIT);
break;
case 1:
dai_dmic_update_bits(dmic, base[i] + FIR_CONTROL_B,
FIR_CONTROL_B_MUTE_BIT,
FIR_CONTROL_B_MUTE_BIT);
break;
}
}
k_spin_unlock(&dmic->lock, key);
}
const struct dai_properties *dai_dmic_get_properties(const struct device *dev,
enum dai_dir dir,
int stream_id)
{
const struct dai_intel_dmic *dmic = (const struct dai_intel_dmic *)dev->data;
struct dai_properties *prop = (struct dai_properties *)dev->config;
prop->fifo_address = dmic->fifo.offset;
prop->dma_hs_id = dmic->fifo.handshake;
prop->reg_init_delay = 0;
return prop;
}
static int dai_dmic_trigger(const struct device *dev, enum dai_dir dir,
enum dai_trigger_cmd cmd)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
LOG_DBG("dmic_trigger()");
if (dir != DAI_DIR_RX) {
LOG_ERR("dmic_trigger(): direction != DAI_DIR_RX");
return -EINVAL;
}
switch (cmd) {
case DAI_TRIGGER_START:
if (dmic->state == DAI_STATE_PAUSED ||
dmic->state == DAI_STATE_PRE_RUNNING) {
dai_dmic_start(dmic);
dmic->state = DAI_STATE_RUNNING;
} else {
LOG_ERR("dmic_trigger(): state is not prepare or paused, dmic->state = %u",
dmic->state);
}
break;
case DAI_TRIGGER_STOP:
dai_dmic_stop(dmic, false);
dmic->state = DAI_STATE_PRE_RUNNING;
break;
case DAI_TRIGGER_PAUSE:
dai_dmic_stop(dmic, true);
dmic->state = DAI_STATE_PAUSED;
break;
default:
break;
}
return 0;
}
static const struct dai_config *dai_dmic_get_config(const struct device *dev, enum dai_dir dir)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
__ASSERT_NO_MSG(dir == DAI_DIR_CAPTURE);
return &dmic->dai_config_params;
}
static int dai_dmic_set_config(const struct device *dev,
const struct dai_config *cfg, const void *bespoke_cfg)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
int ret = 0;
int di = dmic->dai_config_params.dai_index;
k_spinlock_key_t key;
LOG_INF("dmic_set_config()");
if (di >= CONFIG_DAI_DMIC_HW_FIFOS) {
LOG_ERR("dmic_set_config(): DAI index exceeds number of FIFOs");
return -EINVAL;
}
if (!bespoke_cfg) {
LOG_ERR("dmic_set_config(): NULL config");
return -EINVAL;
}
__ASSERT_NO_MSG(dmic->created);
key = k_spin_lock(&dmic->lock);
#if CONFIG_DAI_INTEL_DMIC_TPLG_PARAMS
#error DMIC TPLG is not yet implemented
#elif CONFIG_DAI_INTEL_DMIC_NHLT
ret = dai_dmic_set_config_nhlt(dmic, bespoke_cfg);
#else
#error No DMIC config selected
#endif
if (ret < 0) {
LOG_ERR("dmic_set_config(): Failed to set the requested configuration.");
goto out;
}
dmic->state = DAI_STATE_PRE_RUNNING;
out:
k_spin_unlock(&dmic->lock, key);
return ret;
}
static int dai_dmic_probe_wrapper(const struct device *dev)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
k_spinlock_key_t key;
int ret = 0;
key = k_spin_lock(&dmic->lock);
if (dmic->sref == 0) {
ret = dai_dmic_probe(dmic);
}
if (!ret) {
dmic->sref++;
}
k_spin_unlock(&dmic->lock, key);
return ret;
}
static int dai_dmic_remove_wrapper(const struct device *dev)
{
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
k_spinlock_key_t key;
int ret = 0;
key = k_spin_lock(&dmic->lock);
if (--dmic->sref == 0) {
ret = dai_dmic_remove(dmic);
}
k_spin_unlock(&dmic->lock, key);
return ret;
}
const struct dai_driver_api dai_dmic_ops = {
.probe = dai_dmic_probe_wrapper,
.remove = dai_dmic_remove_wrapper,
.config_set = dai_dmic_set_config,
.config_get = dai_dmic_get_config,
.get_properties = dai_dmic_get_properties,
.trigger = dai_dmic_trigger,
.ts_config = dai_dmic_timestamp_config,
.ts_start = dai_timestamp_dmic_start,
.ts_stop = dai_timestamp_dmic_stop,
.ts_get = dai_timestamp_dmic_get
};
static int dai_dmic_initialize_device(const struct device *dev)
{
IRQ_CONNECT(
DT_INST_IRQN(0),
IRQ_DEFAULT_PRIORITY,
dai_dmic_irq_handler,
DEVICE_DT_INST_GET(0),
0);
return 0;
};
#define DAI_INTEL_DMIC_DEVICE_INIT(n) \
static struct dai_properties dai_intel_dmic_properties_##n; \
\
static struct dai_intel_dmic dai_intel_dmic_data_##n = \
{ .dai_config_params = \
{ \
.type = DAI_INTEL_DMIC, \
.dai_index = n \
}, \
.reg_base = DT_INST_REG_ADDR_BY_IDX(n, 0), \
.shim_base = DT_INST_PROP_BY_IDX(n, shim, 0), \
.irq = DT_INST_IRQN(n), \
.fifo = \
{ \
.offset = DT_INST_REG_ADDR_BY_IDX(n, 0) \
+ OUTDATA##n, \
.handshake = DMA_HANDSHAKE_DMIC_CH##n \
}, \
}; \
\
DEVICE_DT_INST_DEFINE(n, \
dai_dmic_initialize_device, \
NULL, \
&dai_intel_dmic_data_##n, \
&dai_intel_dmic_properties_##n, \
POST_KERNEL, \
CONFIG_DAI_INIT_PRIORITY, \
&dai_dmic_ops);
DT_INST_FOREACH_STATUS_OKAY(DAI_INTEL_DMIC_DEVICE_INIT);

View file

@ -0,0 +1,486 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __INTEL_DAI_DRIVER_DMIC_H__
#define __INTEL_DAI_DRIVER_DMIC_H__
#include <zephyr/sys/util_macro.h>
/* bit operations macros */
#define MASK(b_hi, b_lo) \
(((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL) << (b_lo))
#define SET_BIT(b, x) (((x) & 1) << (b))
#define SET_BITS(b_hi, b_lo, x) \
(((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo))
#define GET_BIT(b, x) \
(((x) & (1ULL << (b))) >> (b))
#define GET_BITS(b_hi, b_lo, x) \
(((x) & MASK(b_hi, b_lo)) >> (b_lo))
/* DMIC timestamping registers */
#define TS_DMIC_LOCAL_TSCTRL_OFFSET 0x000
#define TS_DMIC_LOCAL_OFFS_OFFSET 0x004
#define TS_DMIC_LOCAL_SAMPLE_OFFSET 0x008
#define TS_DMIC_LOCAL_WALCLK_OFFSET 0x010
#define TS_DMIC_TSCC_OFFSET 0x018
/* Timestamping */
#define TIMESTAMP_BASE 0x00071800
#define TS_DMIC_LOCAL_TSCTRL (TIMESTAMP_BASE + TS_DMIC_LOCAL_TSCTRL_OFFSET)
#define TS_DMIC_LOCAL_OFFS (TIMESTAMP_BASE + TS_DMIC_LOCAL_OFFS_OFFSET)
#define TS_DMIC_LOCAL_SAMPLE (TIMESTAMP_BASE + TS_DMIC_LOCAL_SAMPLE_OFFSET)
#define TS_DMIC_LOCAL_WALCLK (TIMESTAMP_BASE + TS_DMIC_LOCAL_WALCLK_OFFSET)
#define TS_DMIC_TSCC (TIMESTAMP_BASE + TS_DMIC_TSCC_OFFSET)
#define TS_LOCAL_TSCTRL_NTK_BIT BIT(31)
#define TS_LOCAL_TSCTRL_IONTE_BIT BIT(30)
#define TS_LOCAL_TSCTRL_SIP_BIT BIT(8)
#define TS_LOCAL_TSCTRL_HHTSE_BIT BIT(7)
#define TS_LOCAL_TSCTRL_ODTS_BIT BIT(5)
#define TS_LOCAL_TSCTRL_CDMAS(x) SET_BITS(4, 0, x)
#define TS_LOCAL_OFFS_FRM GET_BITS(15, 12)
#define TS_LOCAL_OFFS_CLK GET_BITS(11, 0)
/* Digital Mic Shim Registers */
#define DMICLCTL_OFFSET 0x04
#define DMICIPPTR_OFFSET 0x08
#define DMICSYNC_OFFSET 0x0C
/* DMIC power ON bit */
#define DMICLCTL_SPA BIT(0)
/* DMIC Owner Select */
#define DMICLCTL_OSEL(x) SET_BITS(25, 24, x)
/* DMIC disable clock gating */
#define DMIC_DCGD BIT(30)
/* DMIC Command Sync */
#define DMICSYNC_CMDSYNC BIT(16)
/* DMIC Sync Go */
#define DMICSYNC_SYNCGO BIT(24)
/* DMIC Sync Period */
#define DMICSYNC_SYNCPRD(x) SET_BITS(14, 0, x)
/* Parameters used in modes computation */
#define DMIC_HW_BITS_CIC 26
#define DMIC_HW_BITS_FIR_COEF 20
#define DMIC_HW_BITS_FIR_GAIN 20
#define DMIC_HW_BITS_FIR_INPUT 22
#define DMIC_HW_BITS_FIR_OUTPUT 24
#define DMIC_HW_BITS_FIR_INTERNAL 26
#define DMIC_HW_BITS_GAIN_OUTPUT 22
#define DMIC_HW_FIR_LENGTH_MAX 250
#define DMIC_HW_CIC_SHIFT_MIN -8
#define DMIC_HW_CIC_SHIFT_MAX 4
#define DMIC_HW_FIR_SHIFT_MIN 0
#define DMIC_HW_FIR_SHIFT_MAX 8
#define DMIC_HW_CIC_DECIM_MIN 5
#define DMIC_HW_CIC_DECIM_MAX 31 /* Note: Limited by BITS_CIC */
#define DMIC_HW_FIR_DECIM_MIN 2
#define DMIC_HW_FIR_DECIM_MAX 20 /* Note: Practical upper limit */
#define DMIC_HW_SENS_Q28 Q_CONVERT_FLOAT(1.0, 28) /* Q1.28 */
#define DMIC_HW_PDM_CLK_MIN 100000 /* Note: Practical min value */
#define DMIC_HW_DUTY_MIN 20 /* Note: Practical min value */
#define DMIC_HW_DUTY_MAX 80 /* Note: Practical max value */
/* DMIC register offsets */
/* Global registers */
#define OUTCONTROL0 0x0000
#define OUTSTAT0 0x0004
#define OUTDATA0 0x0008
#define OUTCONTROL1 0x0100
#define OUTSTAT1 0x0104
#define OUTDATA1 0x0108
#define PDM0 0x1000
#define PDM0_COEFFICIENT_A 0x1400
#define PDM0_COEFFICIENT_B 0x1800
#define PDM1 0x2000
#define PDM1_COEFFICIENT_A 0x2400
#define PDM1_COEFFICIENT_B 0x2800
#define PDM2 0x3000
#define PDM2_COEFFICIENT_A 0x3400
#define PDM2_COEFFICIENT_B 0x3800
#define PDM3 0x4000
#define PDM3_COEFFICIENT_A 0x4400
#define PDM3_COEFFICIENT_B 0x4800
#define PDM_COEF_RAM_A_LENGTH 0x0400
#define PDM_COEF_RAM_B_LENGTH 0x0400
/* Local registers in each PDMx */
#define CIC_CONTROL 0x000
#define CIC_CONFIG 0x004
#define MIC_CONTROL 0x00c
#define FIR_CONTROL_A 0x020
#define FIR_CONFIG_A 0x024
#define DC_OFFSET_LEFT_A 0x028
#define DC_OFFSET_RIGHT_A 0x02c
#define OUT_GAIN_LEFT_A 0x030
#define OUT_GAIN_RIGHT_A 0x034
#define FIR_CONTROL_B 0x040
#define FIR_CONFIG_B 0x044
#define DC_OFFSET_LEFT_B 0x048
#define DC_OFFSET_RIGHT_B 0x04c
#define OUT_GAIN_LEFT_B 0x050
#define OUT_GAIN_RIGHT_B 0x054
/* Register bits */
/* OUTCONTROLx IPM bit fields style */
#define OUTCONTROL0_BFTH_MAX 4 /* Max depth 16 */
/* OUTCONTROL0 bits */
#define OUTCONTROL0_TIE_BIT BIT(27)
#define OUTCONTROL0_SIP_BIT BIT(26)
#define OUTCONTROL0_FINIT_BIT BIT(25)
#define OUTCONTROL0_FCI_BIT BIT(24)
#define OUTCONTROL0_TIE(x) SET_BIT(27, x)
#define OUTCONTROL0_SIP(x) SET_BIT(26, x)
#define OUTCONTROL0_FINIT(x) SET_BIT(25, x)
#define OUTCONTROL0_FCI(x) SET_BIT(24, x)
#define OUTCONTROL0_BFTH(x) SET_BITS(23, 20, x)
#define OUTCONTROL0_OF(x) SET_BITS(19, 18, x)
#define OUTCONTROL0_IPM(x) SET_BITS(17, 15, x)
#define OUTCONTROL0_IPM_SOURCE_1(x) SET_BITS(14, 13, x)
#define OUTCONTROL0_IPM_SOURCE_2(x) SET_BITS(12, 11, x)
#define OUTCONTROL0_IPM_SOURCE_3(x) SET_BITS(10, 9, x)
#define OUTCONTROL0_IPM_SOURCE_4(x) SET_BITS(8, 7, x)
#define OUTCONTROL0_IPM_SOURCE_MODE(x) SET_BIT(6, x)
#define OUTCONTROL0_TH(x) SET_BITS(5, 0, x)
#define OUTCONTROL0_TIE_GET(x) GET_BIT(27, x)
#define OUTCONTROL0_SIP_GET(x) GET_BIT(26, x)
#define OUTCONTROL0_FINIT_GET(x) GET_BIT(25, x)
#define OUTCONTROL0_FCI_GET(x) GET_BIT(24, x)
#define OUTCONTROL0_BFTH_GET(x) GET_BITS(23, 20, x)
#define OUTCONTROL0_OF_GET(x) GET_BITS(19, 18, x)
#define OUTCONTROL0_IPM_GET(x) GET_BITS(17, 15, x)
#define OUTCONTROL0_IPM_SOURCE_1_GET(x) GET_BITS(14, 13, x)
#define OUTCONTROL0_IPM_SOURCE_2_GET(x) GET_BITS(12, 11, x)
#define OUTCONTROL0_IPM_SOURCE_3_GET(x) GET_BITS(10, 9, x)
#define OUTCONTROL0_IPM_SOURCE_4_GET(x) GET_BITS(8, 7, x)
#define OUTCONTROL0_IPM_SOURCE_MODE_GET(x) GET_BIT(6, x)
#define OUTCONTROL0_TH_GET(x) GET_BITS(5, 0, x)
/* OUTCONTROL1 bits */
#define OUTCONTROL1_TIE_BIT BIT(27)
#define OUTCONTROL1_SIP_BIT BIT(26)
#define OUTCONTROL1_FINIT_BIT BIT(25)
#define OUTCONTROL1_FCI_BIT BIT(24)
#define OUTCONTROL1_TIE(x) SET_BIT(27, x)
#define OUTCONTROL1_SIP(x) SET_BIT(26, x)
#define OUTCONTROL1_FINIT(x) SET_BIT(25, x)
#define OUTCONTROL1_FCI(x) SET_BIT(24, x)
#define OUTCONTROL1_BFTH(x) SET_BITS(23, 20, x)
#define OUTCONTROL1_OF(x) SET_BITS(19, 18, x)
#define OUTCONTROL1_IPM(x) SET_BITS(17, 15, x)
#define OUTCONTROL1_IPM_SOURCE_1(x) SET_BITS(14, 13, x)
#define OUTCONTROL1_IPM_SOURCE_2(x) SET_BITS(12, 11, x)
#define OUTCONTROL1_IPM_SOURCE_3(x) SET_BITS(10, 9, x)
#define OUTCONTROL1_IPM_SOURCE_4(x) SET_BITS(8, 7, x)
#define OUTCONTROL1_IPM_SOURCE_MODE(x) SET_BIT(6, x)
#define OUTCONTROL1_TH(x) SET_BITS(5, 0, x)
#define OUTCONTROL1_TIE_GET(x) GET_BIT(27, x)
#define OUTCONTROL1_SIP_GET(x) GET_BIT(26, x)
#define OUTCONTROL1_FINIT_GET(x) GET_BIT(25, x)
#define OUTCONTROL1_FCI_GET(x) GET_BIT(24, x)
#define OUTCONTROL1_BFTH_GET(x) GET_BITS(23, 20, x)
#define OUTCONTROL1_OF_GET(x) GET_BITS(19, 18, x)
#define OUTCONTROL1_IPM_GET(x) GET_BITS(17, 15, x)
#define OUTCONTROL1_IPM_SOURCE_1_GET(x) GET_BITS(14, 13, x)
#define OUTCONTROL1_IPM_SOURCE_2_GET(x) GET_BITS(12, 11, x)
#define OUTCONTROL1_IPM_SOURCE_3_GET(x) GET_BITS(10, 9, x)
#define OUTCONTROL1_IPM_SOURCE_4_GET(x) GET_BITS(8, 7, x)
#define OUTCONTROL1_IPM_SOURCE_MODE_GET(x) GET_BIT(6, x)
#define OUTCONTROL1_TH_GET(x) GET_BITS(5, 0, x)
#define OUTCONTROLX_IPM_NUMSOURCES 4
/* OUTSTAT0 bits */
#define OUTSTAT0_AFE_BIT BIT(31)
#define OUTSTAT0_ASNE_BIT BIT(29)
#define OUTSTAT0_RFS_BIT BIT(28)
#define OUTSTAT0_ROR_BIT BIT(27)
#define OUTSTAT0_FL_MASK MASK(6, 0)
/* OUTSTAT1 bits */
#define OUTSTAT1_AFE_BIT BIT(31)
#define OUTSTAT1_ASNE_BIT BIT(29)
#define OUTSTAT1_RFS_BIT BIT(28)
#define OUTSTAT1_ROR_BIT BIT(27)
#define OUTSTAT1_FL_MASK MASK(6, 0)
/* CIC_CONTROL bits */
#define CIC_CONTROL_SOFT_RESET_BIT BIT(16)
#define CIC_CONTROL_CIC_START_B_BIT BIT(15)
#define CIC_CONTROL_CIC_START_A_BIT BIT(14)
#define CIC_CONTROL_MIC_B_POLARITY_BIT BIT(3)
#define CIC_CONTROL_MIC_A_POLARITY_BIT BIT(2)
#define CIC_CONTROL_MIC_MUTE_BIT BIT(1)
#define CIC_CONTROL_STEREO_MODE_BIT BIT(0)
#define CIC_CONTROL_SOFT_RESET(x) SET_BIT(16, x)
#define CIC_CONTROL_CIC_START_B(x) SET_BIT(15, x)
#define CIC_CONTROL_CIC_START_A(x) SET_BIT(14, x)
#define CIC_CONTROL_MIC_B_POLARITY(x) SET_BIT(3, x)
#define CIC_CONTROL_MIC_A_POLARITY(x) SET_BIT(2, x)
#define CIC_CONTROL_MIC_MUTE(x) SET_BIT(1, x)
#define CIC_CONTROL_STEREO_MODE(x) SET_BIT(0, x)
#define CIC_CONTROL_SOFT_RESET_GET(x) GET_BIT(16, x)
#define CIC_CONTROL_CIC_START_B_GET(x) GET_BIT(15, x)
#define CIC_CONTROL_CIC_START_A_GET(x) GET_BIT(14, x)
#define CIC_CONTROL_MIC_B_POLARITY_GET(x) GET_BIT(3, x)
#define CIC_CONTROL_MIC_A_POLARITY_GET(x) GET_BIT(2, x)
#define CIC_CONTROL_MIC_MUTE_GET(x) GET_BIT(1, x)
#define CIC_CONTROL_STEREO_MODE_GET(x) GET_BIT(0, x)
/* CIC_CONFIG bits */
#define CIC_CONFIG_CIC_SHIFT(x) SET_BITS(27, 24, x)
#define CIC_CONFIG_COMB_COUNT(x) SET_BITS(15, 8, x)
/* CIC_CONFIG masks */
#define CIC_CONFIG_CIC_SHIFT_MASK MASK(27, 24)
#define CIC_CONFIG_COMB_COUNT_MASK MASK(15, 8)
#define CIC_CONFIG_CIC_SHIFT_GET(x) GET_BITS(27, 24, x)
#define CIC_CONFIG_COMB_COUNT_GET(x) GET_BITS(15, 8, x)
/* MIC_CONTROL bits */
#define MIC_CONTROL_PDM_EN_B_BIT BIT(1)
#define MIC_CONTROL_PDM_EN_A_BIT BIT(0)
#define MIC_CONTROL_PDM_CLKDIV(x) SET_BITS(15, 8, x)
#define MIC_CONTROL_PDM_SKEW(x) SET_BITS(7, 4, x)
#define MIC_CONTROL_CLK_EDGE(x) SET_BIT(3, x)
#define MIC_CONTROL_PDM_EN_B(x) SET_BIT(1, x)
#define MIC_CONTROL_PDM_EN_A(x) SET_BIT(0, x)
/* MIC_CONTROL masks */
#define MIC_CONTROL_PDM_CLKDIV_MASK MASK(15, 8)
#define MIC_CONTROL_PDM_CLKDIV_GET(x) GET_BITS(15, 8, x)
#define MIC_CONTROL_PDM_SKEW_GET(x) GET_BITS(7, 4, x)
#define MIC_CONTROL_PDM_CLK_EDGE_GET(x) GET_BIT(3, x)
#define MIC_CONTROL_PDM_EN_B_GET(x) GET_BIT(1, x)
#define MIC_CONTROL_PDM_EN_A_GET(x) GET_BIT(0, x)
/* FIR_CONTROL_A bits */
#define FIR_CONTROL_A_START_BIT BIT(7)
#define FIR_CONTROL_A_ARRAY_START_EN_BIT BIT(6)
#define FIR_CONTROL_A_MUTE_BIT BIT(1)
#define FIR_CONTROL_A_START(x) SET_BIT(7, x)
#define FIR_CONTROL_A_ARRAY_START_EN(x) SET_BIT(6, x)
#define FIR_CONTROL_A_DCCOMP(x) SET_BIT(4, x)
#define FIR_CONTROL_A_MUTE(x) SET_BIT(1, x)
#define FIR_CONTROL_A_STEREO(x) SET_BIT(0, x)
#define FIR_CONTROL_A_START_GET(x) GET_BIT(7, x)
#define FIR_CONTROL_A_ARRAY_START_EN_GET(x) GET_BIT(6, x)
#define FIR_CONTROL_A_DCCOMP_GET(x) GET_BIT(4, x)
#define FIR_CONTROL_A_MUTE_GET(x) GET_BIT(1, x)
#define FIR_CONTROL_A_STEREO_GET(x) GET_BIT(0, x)
/* FIR_CONFIG_A bits */
#define FIR_CONFIG_A_FIR_DECIMATION(x) SET_BITS(20, 16, x)
#define FIR_CONFIG_A_FIR_SHIFT(x) SET_BITS(11, 8, x)
#define FIR_CONFIG_A_FIR_LENGTH(x) SET_BITS(7, 0, x)
#define FIR_CONFIG_A_FIR_DECIMATION_GET(x) GET_BITS(20, 16, x)
#define FIR_CONFIG_A_FIR_SHIFT_GET(x) GET_BITS(11, 8, x)
#define FIR_CONFIG_A_FIR_LENGTH_GET(x) GET_BITS(7, 0, x)
/* DC offset compensation time constants */
#define DCCOMP_TC0 0
#define DCCOMP_TC1 1
#define DCCOMP_TC2 2
#define DCCOMP_TC3 3
#define DCCOMP_TC4 4
#define DCCOMP_TC5 5
#define DCCOMP_TC6 6
#define DCCOMP_TC7 7
/* DC_OFFSET_LEFT_A bits */
#define DC_OFFSET_LEFT_A_DC_OFFS(x) SET_BITS(21, 0, x)
/* DC_OFFSET_RIGHT_A bits */
#define DC_OFFSET_RIGHT_A_DC_OFFS(x) SET_BITS(21, 0, x)
/* OUT_GAIN_LEFT_A bits */
#define OUT_GAIN_LEFT_A_GAIN(x) SET_BITS(19, 0, x)
/* OUT_GAIN_RIGHT_A bits */
#define OUT_GAIN_RIGHT_A_GAIN(x) SET_BITS(19, 0, x)
/* FIR_CONTROL_B bits */
#define FIR_CONTROL_B_START_BIT BIT(7)
#define FIR_CONTROL_B_ARRAY_START_EN_BIT BIT(6)
#define FIR_CONTROL_B_MUTE_BIT BIT(1)
#define FIR_CONTROL_B_START(x) SET_BIT(7, x)
#define FIR_CONTROL_B_ARRAY_START_EN(x) SET_BIT(6, x)
#define FIR_CONTROL_B_DCCOMP(x) SET_BIT(4, x)
#define FIR_CONTROL_B_MUTE(x) SET_BIT(1, x)
#define FIR_CONTROL_B_STEREO(x) SET_BIT(0, x)
#define FIR_CONTROL_B_START_GET(x) GET_BIT(7, x)
#define FIR_CONTROL_B_ARRAY_START_EN_GET(x) GET_BIT(6, x)
#define FIR_CONTROL_B_DCCOMP_GET(x) GET_BIT(4, x)
#define FIR_CONTROL_B_MUTE_GET(x) GET_BIT(1, x)
#define FIR_CONTROL_B_STEREO_GET(x) GET_BIT(0, x)
/* FIR_CONFIG_B bits */
#define FIR_CONFIG_B_FIR_DECIMATION(x) SET_BITS(20, 16, x)
#define FIR_CONFIG_B_FIR_SHIFT(x) SET_BITS(11, 8, x)
#define FIR_CONFIG_B_FIR_LENGTH(x) SET_BITS(7, 0, x)
#define FIR_CONFIG_B_FIR_DECIMATION_GET(x) GET_BITS(20, 16, x)
#define FIR_CONFIG_B_FIR_SHIFT_GET(x) GET_BITS(11, 8, x)
#define FIR_CONFIG_B_FIR_LENGTH_GET(x) GET_BITS(7, 0, x)
/* DC_OFFSET_LEFT_B bits */
#define DC_OFFSET_LEFT_B_DC_OFFS(x) SET_BITS(21, 0, x)
/* DC_OFFSET_RIGHT_B bits */
#define DC_OFFSET_RIGHT_B_DC_OFFS(x) SET_BITS(21, 0, x)
/* OUT_GAIN_LEFT_B bits */
#define OUT_GAIN_LEFT_B_GAIN(x) SET_BITS(19, 0, x)
/* OUT_GAIN_RIGHT_B bits */
#define OUT_GAIN_RIGHT_B_GAIN(x) SET_BITS(19, 0, x)
/* FIR coefficients */
#define FIR_COEF_A(x) SET_BITS(19, 0, x)
#define FIR_COEF_B(x) SET_BITS(19, 0, x)
/* Used for scaling FIR coefficients 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
/* Used in unmute ramp values calculation */
#define DMIC_HW_FIR_GAIN_MAX ((1 << (DMIC_HW_BITS_FIR_GAIN - 1)) - 1)
/* Hardwired log ramp parameters. The first value is the initial gain in
* decibels. The default ramp time is provided by 1st order equation
* ramp time = coef * samplerate + offset. The default ramp is 200 ms for
* 48 kHz and 400 ms for 16 kHz.
*/
#define LOGRAMP_START_DB Q_CONVERT_FLOAT(-90, DB2LIN_FIXED_INPUT_QY)
#define LOGRAMP_TIME_COEF_Q15 -205 /* dy/dx (16000,400) (48000,200) */
#define LOGRAMP_TIME_OFFS_Q0 500 /* Offset for line slope */
/* Limits for ramp time from topology */
#define LOGRAMP_TIME_MIN_MS 10 /* Min. 10 ms */
#define LOGRAMP_TIME_MAX_MS 1000 /* Max. 1s */
/* Simplify log ramp step calculation equation with this constant term */
#define LOGRAMP_CONST_TERM ((int32_t) \
((int64_t)-LOGRAMP_START_DB * DMIC_UNMUTE_RAMP_US / 1000))
/* Fractional shift for gain update. Gain format is Q2.30. */
#define Q_SHIFT_GAIN_X_GAIN_COEF \
(Q_SHIFT_BITS_32(30, DB2LIN_FIXED_OUTPUT_QY, 30))
#define DMA_HANDSHAKE_DMIC_CH0 0
#define DMA_HANDSHAKE_DMIC_CH1 1
/* For NHLT DMIC configuration parsing */
#define DMIC_HW_CONTROLLERS_MAX 4
#define DMIC_HW_FIFOS_MAX 2
struct nhlt_dmic_gateway_attributes {
uint32_t dw;
};
struct nhlt_dmic_ts_group {
uint32_t ts_group[4];
};
struct nhlt_dmic_clock_on_delay {
uint32_t clock_on_delay;
};
struct nhlt_dmic_channel_ctrl_mask {
uint32_t channel_ctrl_mask;
};
struct nhlt_pdm_ctrl_mask {
uint32_t pdm_ctrl_mask;
};
struct nhlt_pdm_ctrl_cfg {
uint32_t cic_control;
uint32_t cic_config;
uint32_t reserved0;
uint32_t mic_control;
uint32_t pdm_sdw_map;
uint32_t reuse_fir_from_pdm;
uint32_t reserved1[2];
};
struct nhlt_pdm_ctrl_fir_cfg {
uint32_t fir_control;
uint32_t fir_config;
int32_t dc_offset_left;
int32_t dc_offset_right;
int32_t out_gain_left;
int32_t out_gain_right;
uint32_t reserved[2];
};
struct nhlt_pdm_fir_coeffs {
int32_t fir_coeffs[0];
};
enum dai_dmic_frame_format {
DAI_DMIC_FRAME_S16_LE = 0,
DAI_DMIC_FRAME_S24_4LE,
DAI_DMIC_FRAME_S32_LE,
DAI_DMIC_FRAME_FLOAT,
/* other formats here */
DAI_DMIC_FRAME_S24_3LE,
};
/* Common data for all DMIC DAI instances */
struct dai_dmic_global_shared {
uint32_t active_fifos_mask; /* Bits (dai->index) are set to indicate active FIFO */
uint32_t pause_mask; /* Bits (dai->index) are set to indicate driver pause */
};
struct dai_dmic_plat_fifo_data {
uint32_t offset;
uint32_t width;
uint32_t depth;
uint32_t watermark;
uint32_t handshake;
};
struct dai_intel_dmic {
struct dai_config dai_config_params;
struct k_spinlock lock; /**< locking mechanism */
int sref; /**< simple ref counter, guarded by lock */
enum dai_state state; /* Driver component state */
uint16_t enable[CONFIG_DAI_DMIC_HW_CONTROLLERS];/* Mic 0 and 1 enable bits array for PDMx */
struct dai_dmic_plat_fifo_data fifo; /* dmic capture fifo stream */
/* hardware parameters */
uint32_t reg_base;
uint32_t shim_base;
int irq;
uint32_t flags;
};
#endif /* __INTEL_DAI_DRIVER_DMIC_H__ */

View file

@ -0,0 +1,503 @@
/*
* Copyright (c) 2022 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <errno.h>
#include <zephyr/spinlock.h>
#define LOG_DOMAIN dai_intel_dmic_nhlt
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_DOMAIN);
#include <zephyr/drivers/dai.h>
#include "dmic.h"
extern struct dai_dmic_global_shared dai_dmic_global;
/* 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};
static inline void dai_dmic_write(const struct dai_intel_dmic *dmic,
uint32_t reg, uint32_t val)
{
sys_write32(val, dmic->reg_base + reg);
}
static int dai_ipm_source_to_enable(struct dai_intel_dmic *dmic,
struct nhlt_pdm_ctrl_cfg **pdm_cfg,
int *count, int pdm_count, int stereo,
int source_pdm)
{
int mic_swap;
if (source_pdm >= CONFIG_DAI_DMIC_HW_CONTROLLERS)
return -EINVAL;
if (*count < pdm_count) {
(*count)++;
mic_swap = MIC_CONTROL_PDM_CLK_EDGE_GET(pdm_cfg[source_pdm]->mic_control);
if (stereo)
dmic->enable[source_pdm] = 0x3; /* PDMi MIC A and B */
else
dmic->enable[source_pdm] = mic_swap ? 0x2 : 0x1; /* PDMi MIC B or MIC A */
}
return 0;
}
static int dai_nhlt_dmic_dai_params_get(struct dai_intel_dmic *dmic,
int32_t *outcontrol,
struct nhlt_pdm_ctrl_cfg **pdm_cfg,
struct nhlt_pdm_ctrl_fir_cfg **fir_cfg)
{
uint32_t outcontrol_val = outcontrol[dmic->dai_config_params.dai_index];
int num_pdm;
int source_pdm;
int ret;
int n;
bool stereo_pdm;
switch (OUTCONTROL0_OF_GET(outcontrol_val)) {
case 0:
case 1:
dmic->dai_config_params.format = DAI_DMIC_FRAME_S16_LE;
dmic->dai_config_params.word_size = 16;
break;
case 2:
dmic->dai_config_params.format = DAI_DMIC_FRAME_S32_LE;
dmic->dai_config_params.word_size = 32;
break;
default:
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal OF bit field");
return -EINVAL;
}
num_pdm = OUTCONTROL0_IPM_GET(outcontrol_val);
if (num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM PDM controllers count");
return -EINVAL;
}
stereo_pdm = 1;
dmic->dai_config_params.channels = (stereo_pdm + 1) * num_pdm;
for (n = 0; n < CONFIG_DAI_DMIC_HW_CONTROLLERS; n++)
dmic->enable[n] = 0;
n = 0;
source_pdm = OUTCONTROL0_IPM_SOURCE_1_GET(outcontrol_val);
ret = dai_ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm);
if (ret) {
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_1");
return -EINVAL;
}
source_pdm = OUTCONTROL0_IPM_SOURCE_2_GET(outcontrol_val);
ret = dai_ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm);
if (ret) {
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_2");
return -EINVAL;
}
source_pdm = OUTCONTROL0_IPM_SOURCE_3_GET(outcontrol_val);
ret = dai_ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm);
if (ret) {
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_3");
return -EINVAL;
}
source_pdm = OUTCONTROL0_IPM_SOURCE_4_GET(outcontrol_val);
ret = dai_ipm_source_to_enable(dmic, pdm_cfg, &n, num_pdm, stereo_pdm, source_pdm);
if (ret) {
LOG_ERR("nhlt_dmic_dai_params_get(): Illegal IPM_SOURCE_4");
return -EINVAL;
}
return 0;
}
int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *bespoke_cfg)
{
struct nhlt_pdm_ctrl_cfg *pdm_cfg[DMIC_HW_CONTROLLERS_MAX];
struct nhlt_pdm_ctrl_fir_cfg *fir_cfg_a[DMIC_HW_CONTROLLERS_MAX];
struct nhlt_pdm_ctrl_fir_cfg *fir_cfg_b[DMIC_HW_CONTROLLERS_MAX];
struct nhlt_pdm_fir_coeffs *fir_a[DMIC_HW_CONTROLLERS_MAX] = {NULL};
struct nhlt_pdm_fir_coeffs *fir_b[DMIC_HW_CONTROLLERS_MAX];
uint32_t out_control[DMIC_HW_FIFOS_MAX] = {0};
uint32_t channel_ctrl_mask;
uint32_t fir_control;
uint32_t pdm_ctrl_mask;
uint32_t ref = 0;
uint32_t val;
const uint8_t *p = bespoke_cfg;
int num_fifos;
int num_pdm;
int fir_length_a;
int fir_length_b;
int n;
int i;
int rate_div;
int clk_div;
int comb_count;
int fir_decimation, fir_shift, fir_length;
int bf1, bf2, bf3, bf4, bf5, bf6, bf7, bf8;
int bf9, bf10, bf11, bf12, bf13;
int bfth;
int ret;
int p_mcic = 0;
int p_mfira = 0;
int p_mfirb = 0;
int p_clkdiv = 0;
if (dmic->dai_config_params.dai_index >= DMIC_HW_FIFOS_MAX) {
LOG_ERR("dmic_set_config_nhlt(): illegal DAI index %d",
dmic->dai_config_params.dai_index);
return -EINVAL;
}
/* Skip not used headers */
p += sizeof(struct nhlt_dmic_gateway_attributes);
p += sizeof(struct nhlt_dmic_ts_group);
p += sizeof(struct nhlt_dmic_clock_on_delay);
/* Channel_ctlr_mask bits indicate the FIFOs enabled*/
channel_ctrl_mask = ((struct nhlt_dmic_channel_ctrl_mask *)p)->channel_ctrl_mask;
num_fifos = popcount(channel_ctrl_mask); /* Count set bits */
p += sizeof(struct nhlt_dmic_channel_ctrl_mask);
LOG_DBG("dmic_set_config_nhlt(): channel_ctrl_mask = %d", channel_ctrl_mask);
/* Get OUTCONTROLx configuration */
if (num_fifos < 1 || num_fifos > DMIC_HW_FIFOS_MAX) {
LOG_ERR("dmic_set_config_nhlt(): illegal number of FIFOs %d", num_fifos);
return -EINVAL;
}
for (n = 0; n < DMIC_HW_FIFOS_MAX; n++) {
if (!(channel_ctrl_mask & (1 << n)))
continue;
val = *(uint32_t *)p;
out_control[n] = val;
bf1 = OUTCONTROL0_TIE_GET(val);
bf2 = OUTCONTROL0_SIP_GET(val);
bf3 = OUTCONTROL0_FINIT_GET(val);
bf4 = OUTCONTROL0_FCI_GET(val);
bf5 = OUTCONTROL0_BFTH_GET(val);
bf6 = OUTCONTROL0_OF_GET(val);
bf7 = OUTCONTROL0_IPM_GET(val);
bf8 = OUTCONTROL0_TH_GET(val);
LOG_INF("dmic_set_config_nhlt(): OUTCONTROL%d = %08x", n, out_control[n]);
LOG_INF(" tie=%d, sip=%d, finit=%d, fci=%d", bf1, bf2, bf3, bf4);
LOG_INF(" bfth=%d, of=%d, ipm=%d, th=%d", bf5, bf6, bf7, bf8);
if (bf5 > OUTCONTROL0_BFTH_MAX) {
LOG_ERR("dmic_set_config_nhlt(): illegal BFTH value");
return -EINVAL;
}
bf9 = OUTCONTROL0_IPM_SOURCE_1_GET(val);
bf10 = OUTCONTROL0_IPM_SOURCE_2_GET(val);
bf11 = OUTCONTROL0_IPM_SOURCE_3_GET(val);
bf12 = OUTCONTROL0_IPM_SOURCE_4_GET(val);
bf13 = OUTCONTROL0_IPM_SOURCE_MODE_GET(val);
LOG_INF(" ipms1=%d, ipms2=%d, ipms3=%d, ipms4=%d", bf9, bf10, bf11, bf12);
LOG_INF(" ipms_mode=%d", bf13);
ref = OUTCONTROL0_TIE(bf1) | OUTCONTROL0_SIP(bf2) | OUTCONTROL0_FINIT(bf3) |
OUTCONTROL0_FCI(bf4) | OUTCONTROL0_BFTH(bf5) | OUTCONTROL0_OF(bf6) |
OUTCONTROL0_IPM(bf7) | OUTCONTROL0_IPM_SOURCE_1(bf9) |
OUTCONTROL0_IPM_SOURCE_2(bf10) | OUTCONTROL0_IPM_SOURCE_3(bf11) |
OUTCONTROL0_IPM_SOURCE_4(bf12) | OUTCONTROL0_TH(bf8) |
OUTCONTROL0_IPM_SOURCE_MODE(bf13);
if (ref != val) {
LOG_ERR("dmic_set_config_nhlt(): illegal OUTCONTROL%d = 0x%08x",
n, val);
return -EINVAL;
}
p += sizeof(uint32_t);
}
/* Write the FIFO control registers. The clear/set of bits is the same for
* all DMIC_HW_VERSION
*/
/* Clear TIE, SIP, FCI, set FINIT, the rest of bits as such */
val = (out_control[dmic->dai_config_params.dai_index] &
~(OUTCONTROL0_TIE_BIT | OUTCONTROL0_SIP_BIT | OUTCONTROL0_FCI_BIT)) |
OUTCONTROL0_FINIT_BIT;
if (dmic->dai_config_params.dai_index == 0)
dai_dmic_write(dmic, OUTCONTROL0, val);
else
dai_dmic_write(dmic, OUTCONTROL1, val);
LOG_INF("dmic_set_config_nhlt(): OUTCONTROL%d = %08x",
dmic->dai_config_params.dai_index, val);
/* Pass 2^BFTH to plat_data fifo depth. It will be used later in DMA
* configuration
*/
bfth = OUTCONTROL0_BFTH_GET(val);
dmic->fifo.depth = 1 << bfth;
/* Get PDMx registers */
pdm_ctrl_mask = ((struct nhlt_pdm_ctrl_mask *)p)->pdm_ctrl_mask;
num_pdm = popcount(pdm_ctrl_mask); /* Count set bits */
p += sizeof(struct nhlt_pdm_ctrl_mask);
LOG_DBG("dmic_set_config_nhlt(): pdm_ctrl_mask = %d", pdm_ctrl_mask);
if (num_pdm < 1 || num_pdm > CONFIG_DAI_DMIC_HW_CONTROLLERS) {
LOG_ERR("dmic_set_config_nhlt(): illegal number of PDMs %d", num_pdm);
return -EINVAL;
}
for (n = 0; n < CONFIG_DAI_DMIC_HW_CONTROLLERS; n++) {
fir_cfg_a[n] = NULL;
fir_cfg_b[n] = NULL;
if (!(pdm_ctrl_mask & (1 << n)))
continue;
LOG_DBG("dmic_set_config_nhlt(): PDM%d", n);
/* Get CIC configuration */
pdm_cfg[n] = (struct nhlt_pdm_ctrl_cfg *)p;
p += sizeof(struct nhlt_pdm_ctrl_cfg);
comb_count = CIC_CONFIG_COMB_COUNT_GET(pdm_cfg[n]->cic_config);
p_mcic = comb_count + 1;
clk_div = MIC_CONTROL_PDM_CLKDIV_GET(pdm_cfg[n]->mic_control);
p_clkdiv = clk_div + 2;
if (dai_dmic_global.active_fifos_mask == 0) {
val = pdm_cfg[n]->cic_control;
bf1 = CIC_CONTROL_SOFT_RESET_GET(val);
bf2 = CIC_CONTROL_CIC_START_B_GET(val);
bf3 = CIC_CONTROL_CIC_START_A_GET(val);
bf4 = CIC_CONTROL_MIC_B_POLARITY_GET(val);
bf5 = CIC_CONTROL_MIC_A_POLARITY_GET(val);
bf6 = CIC_CONTROL_MIC_MUTE_GET(val);
bf7 = CIC_CONTROL_STEREO_MODE_GET(val);
LOG_DBG("dmic_set_config_nhlt(): CIC_CONTROL = %08x", val);
LOG_DBG(" soft_reset=%d, cic_start_b=%d, cic_start_a=%d",
bf1, bf2, bf3);
LOG_DBG(" mic_b_polarity=%d, mic_a_polarity=%d, mic_mute=%d",
bf4, bf5, bf6);
ref = CIC_CONTROL_SOFT_RESET(bf1) | CIC_CONTROL_CIC_START_B(bf2) |
CIC_CONTROL_CIC_START_A(bf3) | CIC_CONTROL_MIC_B_POLARITY(bf4) |
CIC_CONTROL_MIC_A_POLARITY(bf5) | CIC_CONTROL_MIC_MUTE(bf6) |
CIC_CONTROL_STEREO_MODE(bf7);
LOG_DBG(" stereo_mode=%d", bf7);
if (ref != val) {
LOG_ERR("dmic_set_config_nhlt(): illegal CIC_CONTROL = 0x%08x",
val);
return -EINVAL;
}
/* Clear CIC_START_A and CIC_START_B, set SOF_RESET and MIC_MUTE*/
val = (val & ~(CIC_CONTROL_CIC_START_A_BIT | CIC_CONTROL_CIC_START_A_BIT)) |
CIC_CONTROL_SOFT_RESET_BIT | CIC_CONTROL_MIC_MUTE_BIT;
dai_dmic_write(dmic, base[n] + CIC_CONTROL, val);
LOG_DBG("dmic_set_config_nhlt(): CIC_CONTROL = %08x", val);
val = pdm_cfg[n]->cic_config;
bf1 = CIC_CONFIG_CIC_SHIFT_GET(val);
LOG_DBG("dmic_set_config_nhlt(): CIC_CONFIG = %08x", val);
LOG_DBG(" cic_shift=%d, comb_count=%d", bf1, comb_count);
/* Use CIC_CONFIG as such */
dai_dmic_write(dmic, base[n] + CIC_CONFIG, val);
LOG_DBG("dmic_set_config_nhlt(): CIC_CONFIG = %08x", val);
val = pdm_cfg[n]->mic_control;
bf1 = MIC_CONTROL_PDM_SKEW_GET(val);
bf2 = MIC_CONTROL_PDM_CLK_EDGE_GET(val);
bf3 = MIC_CONTROL_PDM_EN_B_GET(val);
bf4 = MIC_CONTROL_PDM_EN_A_GET(val);
LOG_DBG("dmic_set_config_nhlt(): MIC_CONTROL = %08x", val);
LOG_DBG(" clkdiv=%d, skew=%d, clk_edge=%d", clk_div, bf1, bf2);
LOG_DBG(" en_b=%d, en_a=%d", bf3, bf4);
/* Clear PDM_EN_A and PDM_EN_B */
val &= ~(MIC_CONTROL_PDM_EN_A_BIT | MIC_CONTROL_PDM_EN_B_BIT);
dai_dmic_write(dmic, base[n] + MIC_CONTROL, val);
LOG_DBG("dmic_set_config_nhlt(): MIC_CONTROL = %08x", val);
}
/* FIR A */
fir_cfg_a[n] = (struct nhlt_pdm_ctrl_fir_cfg *)p;
p += sizeof(struct nhlt_pdm_ctrl_fir_cfg);
val = fir_cfg_a[n]->fir_config;
fir_length = FIR_CONFIG_A_FIR_LENGTH_GET(val);
fir_length_a = fir_length + 1; /* Need for parsing */
fir_decimation = FIR_CONFIG_A_FIR_DECIMATION_GET(val);
p_mfira = fir_decimation + 1;
if (dmic->dai_config_params.dai_index == 0) {
fir_shift = FIR_CONFIG_A_FIR_SHIFT_GET(val);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONFIG_A = %08x", val);
LOG_DBG(" fir_decimation=%d, fir_shift=%d, fir_length=%d",
fir_decimation, fir_shift, fir_length);
/* Use FIR_CONFIG_A as such */
dai_dmic_write(dmic, base[n] + FIR_CONFIG_A, val);
LOG_DBG("configure_registers(), FIR_CONFIG_A = %08x", val);
val = fir_cfg_a[n]->fir_control;
bf1 = FIR_CONTROL_A_START_GET(val);
bf2 = FIR_CONTROL_A_ARRAY_START_EN_GET(val);
bf3 = FIR_CONTROL_A_DCCOMP_GET(val);
bf4 = FIR_CONTROL_A_MUTE_GET(val);
bf5 = FIR_CONTROL_A_STEREO_GET(val);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONTROL_A = %08x", val);
LOG_DBG(" start=%d, array_start_en=%d, dccomp=%d", bf1, bf2, bf3);
LOG_DBG(" mute=%d, stereo=%d", bf4, bf5);
ref = FIR_CONTROL_A_START(bf1) | FIR_CONTROL_A_ARRAY_START_EN(bf2) |
FIR_CONTROL_A_DCCOMP(bf3) | FIR_CONTROL_A_MUTE(bf4) |
FIR_CONTROL_A_STEREO(bf5);
if (ref != val) {
LOG_ERR("dmic_set_config_nhlt(): illegal FIR_CONTROL = 0x%08x",
val);
return -EINVAL;
}
/* Clear START, set MUTE */
fir_control = (val & ~FIR_CONTROL_A_START_BIT) | FIR_CONTROL_A_MUTE_BIT;
dai_dmic_write(dmic, base[n] + FIR_CONTROL_A, fir_control);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONTROL_A = %08x", fir_control);
/* Use DC_OFFSET and GAIN as such */
val = fir_cfg_a[n]->dc_offset_left;
dai_dmic_write(dmic, base[n] + DC_OFFSET_LEFT_A, val);
LOG_DBG("dmic_set_config_nhlt(): DC_OFFSET_LEFT_A = %08x", val);
val = fir_cfg_a[n]->dc_offset_right;
dai_dmic_write(dmic, base[n] + DC_OFFSET_RIGHT_A, val);
LOG_DBG("dmic_set_config_nhlt(): DC_OFFSET_RIGHT_A = %08x", val);
val = fir_cfg_a[n]->out_gain_left;
dai_dmic_write(dmic, base[n] + OUT_GAIN_LEFT_A, val);
LOG_DBG("dmic_set_config_nhlt(): OUT_GAIN_LEFT_A = %08x", val);
val = fir_cfg_a[n]->out_gain_right;
dai_dmic_write(dmic, base[n] + OUT_GAIN_RIGHT_A, val);
LOG_DBG("dmic_set_config_nhlt(): OUT_GAIN_RIGHT_A = %08x", val);
}
/* FIR B */
fir_cfg_b[n] = (struct nhlt_pdm_ctrl_fir_cfg *)p;
p += sizeof(struct nhlt_pdm_ctrl_fir_cfg);
val = fir_cfg_b[n]->fir_config;
fir_length = FIR_CONFIG_B_FIR_LENGTH_GET(val);
fir_length_b = fir_length + 1; /* Need for parsing */
fir_decimation = FIR_CONFIG_B_FIR_DECIMATION_GET(val);
p_mfirb = fir_decimation + 1;
if (dmic->dai_config_params.dai_index == 1) {
fir_shift = FIR_CONFIG_B_FIR_SHIFT_GET(val);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONFIG_B = %08x", val);
LOG_DBG(" fir_decimation=%d, fir_shift=%d, fir_length=%d",
fir_decimation, fir_shift, fir_length);
/* Use FIR_CONFIG_B as such */
dai_dmic_write(dmic, base[n] + FIR_CONFIG_B, val);
LOG_DBG("configure_registers(), FIR_CONFIG_B = %08x", val);
val = fir_cfg_b[n]->fir_control;
bf1 = FIR_CONTROL_B_START_GET(val);
bf2 = FIR_CONTROL_B_ARRAY_START_EN_GET(val);
bf3 = FIR_CONTROL_B_DCCOMP_GET(val);
bf5 = FIR_CONTROL_B_MUTE_GET(val);
bf6 = FIR_CONTROL_B_STEREO_GET(val);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONTROL_B = %08x", val);
LOG_DBG(" start=%d, array_start_en=%d, dccomp=%d", bf1, bf2, bf3);
LOG_DBG(" mute=%d, stereo=%d", bf5, bf6);
/* Clear START, set MUTE */
fir_control = (val & ~FIR_CONTROL_B_START_BIT) | FIR_CONTROL_B_MUTE_BIT;
dai_dmic_write(dmic, base[n] + FIR_CONTROL_B, fir_control);
LOG_DBG("dmic_set_config_nhlt(): FIR_CONTROL_B = %08x", fir_control);
/* Use DC_OFFSET and GAIN as such */
val = fir_cfg_b[n]->dc_offset_left;
dai_dmic_write(dmic, base[n] + DC_OFFSET_LEFT_B, val);
LOG_DBG("dmic_set_config_nhlt(): DC_OFFSET_LEFT_B = %08x", val);
val = fir_cfg_b[n]->dc_offset_right;
dai_dmic_write(dmic, base[n] + DC_OFFSET_RIGHT_B, val);
LOG_DBG("dmic_set_config_nhlt(): DC_OFFSET_RIGHT_B = %08x", val);
val = fir_cfg_b[n]->out_gain_left;
dai_dmic_write(dmic, base[n] + OUT_GAIN_LEFT_B, val);
LOG_DBG("dmic_set_config_nhlt(): OUT_GAIN_LEFT_B = %08x", val);
val = fir_cfg_b[n]->out_gain_right;
dai_dmic_write(dmic, base[n] + OUT_GAIN_RIGHT_B, val);
LOG_DBG("dmic_set_config_nhlt(): OUT_GAIN_RIGHT_B = %08x", val);
}
/* Set up FIR coefficients RAM */
val = pdm_cfg[n]->reuse_fir_from_pdm;
if (val == 0) {
fir_a[n] = (struct nhlt_pdm_fir_coeffs *)p;
p += sizeof(int32_t) * fir_length_a;
fir_b[n] = (struct nhlt_pdm_fir_coeffs *)p;
p += sizeof(int32_t) * fir_length_b;
} else {
val--;
if (val >= n) {
LOG_ERR("dmic_set_config_nhlt(): Illegal FIR reuse 0x%x", val);
return -EINVAL;
}
if (!fir_a[val]) {
LOG_ERR("dmic_set_config_nhlt(): PDM%d FIR reuse from %d fail",
n, val);
return -EINVAL;
}
fir_a[n] = fir_a[val];
fir_b[n] = fir_b[val];
}
if (dmic->dai_config_params.dai_index == 0) {
LOG_INF(
"dmic_set_config_nhlt(): clkdiv = %d, mcic = %d, mfir_a = %d, len = %d",
p_clkdiv, p_mcic, p_mfira, fir_length_a);
for (i = 0; i < fir_length_a; i++)
dai_dmic_write(dmic,
coef_base_a[n] + (i << 2), fir_a[n]->fir_coeffs[i]);
} else {
LOG_INF(
"dmic_set_config_nhlt(): clkdiv = %d, mcic = %d, mfir_b = %d, len = %d",
p_clkdiv, p_mcic, p_mfirb, fir_length_b);
for (i = 0; i < fir_length_b; i++)
dai_dmic_write(dmic,
coef_base_b[n] + (i << 2), fir_b[n]->fir_coeffs[i]);
}
}
if (dmic->dai_config_params.dai_index == 0)
ret = dai_nhlt_dmic_dai_params_get(dmic, out_control, pdm_cfg, fir_cfg_a);
else
ret = dai_nhlt_dmic_dai_params_get(dmic, out_control, pdm_cfg, fir_cfg_b);
if (ret)
return ret;
if (dmic->dai_config_params.dai_index == 0)
rate_div = p_clkdiv * p_mcic * p_mfira;
else
rate_div = p_clkdiv * p_mcic * p_mfirb;
if (!rate_div) {
LOG_ERR("dmic_set_config_nhlt(): zero clock divide or decimation factor");
return -EINVAL;
}
dmic->dai_config_params.rate = CONFIG_DAI_DMIC_HW_IOCLK / rate_div;
LOG_INF("dmic_set_config_nhlt(): rate = %d, channels = %d, format = %d",
dmic->dai_config_params.rate, dmic->dai_config_params.channels,
dmic->dai_config_params.format);
return 0;
}

View file

@ -0,0 +1,19 @@
# Copyright (c) 2022 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
description: Intel Digital PDM Microphone (DMIC) node
compatible: "intel,dai,dmic"
include: base.yaml
properties:
reg:
required: true
label:
required: true
shim:
type: array
required: true