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:
parent
c8e1cba8bc
commit
ae8b17d2bb
8 changed files with 1773 additions and 0 deletions
|
@ -2,3 +2,4 @@
|
|||
|
||||
add_subdirectory_ifdef(CONFIG_DAI_INTEL_SSP intel/ssp)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_INTEL_ALH intel/alh)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_INTEL_DMIC intel/dmic)
|
||||
|
|
|
@ -27,5 +27,6 @@ comment "Device Drivers"
|
|||
|
||||
source "drivers/dai/intel/ssp/Kconfig.ssp"
|
||||
source "drivers/dai/intel/alh/Kconfig.alh"
|
||||
source "drivers/dai/intel/dmic/Kconfig.dmic"
|
||||
|
||||
endif # DAI
|
||||
|
|
5
drivers/dai/intel/dmic/CMakeLists.txt
Normal file
5
drivers/dai/intel/dmic/CMakeLists.txt
Normal 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)
|
76
drivers/dai/intel/dmic/Kconfig.dmic
Normal file
76
drivers/dai/intel/dmic/Kconfig.dmic
Normal 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
|
682
drivers/dai/intel/dmic/dmic.c
Normal file
682
drivers/dai/intel/dmic/dmic.c
Normal 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);
|
486
drivers/dai/intel/dmic/dmic.h
Normal file
486
drivers/dai/intel/dmic/dmic.h
Normal 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__ */
|
503
drivers/dai/intel/dmic/dmic_nhlt.c
Normal file
503
drivers/dai/intel/dmic/dmic_nhlt.c
Normal 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;
|
||||
}
|
19
dts/bindings/dai/dai,intel,dmic.yaml
Normal file
19
dts/bindings/dai/dai,intel,dmic.yaml
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue