drivers: dai: Add initial support for NXP MICFIL PDM IP
Introduce new DAI driver used for NXP's PDM MICFIL IP. This block implements required digital interface to provide a 24-bits audio signal from a PDM microphone bitstream in a configurable output sampling rate. Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
This commit is contained in:
parent
da2f5a8b34
commit
715fbd1f81
@ -6,3 +6,4 @@ add_subdirectory_ifdef(CONFIG_DAI_INTEL_DMIC intel/dmic)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_INTEL_HDA intel/hda)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_NXP_SAI nxp/sai)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_NXP_ESAI nxp/esai)
|
||||
add_subdirectory_ifdef(CONFIG_DAI_NXP_MICFIL nxp/micfil)
|
||||
|
||||
@ -31,5 +31,6 @@ source "drivers/dai/intel/dmic/Kconfig.dmic"
|
||||
source "drivers/dai/intel/hda/Kconfig.hda"
|
||||
source "drivers/dai/nxp/sai/Kconfig.sai"
|
||||
source "drivers/dai/nxp/esai/Kconfig.esai"
|
||||
source "drivers/dai/nxp/micfil/Kconfig.micfil"
|
||||
|
||||
endif # DAI
|
||||
|
||||
5
drivers/dai/nxp/micfil/CMakeLists.txt
Normal file
5
drivers/dai/nxp/micfil/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright 2025 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
zephyr_library_sources(micfil.c)
|
||||
10
drivers/dai/nxp/micfil/Kconfig.micfil
Normal file
10
drivers/dai/nxp/micfil/Kconfig.micfil
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright 2025 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config DAI_NXP_MICFIL
|
||||
bool "NXP Pulse Density Modulation Microphone Interface (MICFIL) driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_DAI_MICFIL_ENABLED
|
||||
select PINCTRL
|
||||
help
|
||||
Select this to enable NXP PDM MICFIL driver.
|
||||
209
drivers/dai/nxp/micfil/micfil.c
Normal file
209
drivers/dai/nxp/micfil/micfil.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright 2025 NXP
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr/drivers/dai.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "fsl_pdm.h"
|
||||
|
||||
#define DT_DRV_COMPAT nxp_dai_micfil
|
||||
LOG_MODULE_REGISTER(nxp_dai_micfil);
|
||||
|
||||
#define MICFIL_CLK_ROOT 24576000
|
||||
#define MICFIL_OSR_DEFAULT 16
|
||||
|
||||
#define UINT_TO_MICFIL(x) ((PDM_Type *)(uintptr_t)(x))
|
||||
|
||||
#define MICFIL_FIFO_BASE(inst) \
|
||||
POINTER_TO_UINT(&(UINT_TO_MICFIL(DT_INST_REG_ADDR(inst))->DATACH[0]))
|
||||
|
||||
#define MICFIL_DMA_HANDSHAKE(inst) \
|
||||
((DT_INST_DMAS_CELL_BY_IDX(inst, 0, channel) & GENMASK(7, 0)) | \
|
||||
((DT_INST_DMAS_CELL_BY_IDX(inst, 0, mux) << 8) & GENMASK(15, 8)))
|
||||
|
||||
struct dai_nxp_micfil_data {
|
||||
struct dai_config cfg;
|
||||
};
|
||||
|
||||
struct dai_nxp_micfil_config {
|
||||
PDM_Type *base;
|
||||
const struct dai_properties *rx_props;
|
||||
const struct pinctrl_dev_config *pincfg;
|
||||
};
|
||||
|
||||
/* this needs to match SOF struct sof_ipc_dai_micfil_params */
|
||||
struct micfil_bespoke_config {
|
||||
uint32_t pdm_rate;
|
||||
uint32_t pdm_ch;
|
||||
};
|
||||
|
||||
static void dai_nxp_micfil_trigger_start(const struct device *dev)
|
||||
{
|
||||
const struct dai_nxp_micfil_config *cfg = dev->config;
|
||||
|
||||
/* enable DMA requests */
|
||||
PDM_EnableDMA(cfg->base, true);
|
||||
/* enable the module */
|
||||
PDM_Enable(cfg->base, true);
|
||||
}
|
||||
|
||||
static void dai_nxp_micfil_trigger_stop(const struct device *dev)
|
||||
{
|
||||
const struct dai_nxp_micfil_config *cfg = dev->config;
|
||||
|
||||
/* disable DMA requests */
|
||||
PDM_EnableDMA(cfg->base, false);
|
||||
/* disable module */
|
||||
PDM_Enable(cfg->base, false);
|
||||
}
|
||||
|
||||
static const struct dai_properties
|
||||
*dai_nxp_micfil_get_properties(const struct device *dev, enum dai_dir dir, int stream_id)
|
||||
{
|
||||
const struct dai_nxp_micfil_config *cfg = dev->config;
|
||||
|
||||
if (dir == DAI_DIR_RX) {
|
||||
return cfg->rx_props;
|
||||
}
|
||||
|
||||
LOG_ERR("invalid direction %d", dir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dai_nxp_micfil_trigger(const struct device *dev, enum dai_dir dir,
|
||||
enum dai_trigger_cmd cmd)
|
||||
{
|
||||
if (dir != DAI_DIR_RX) {
|
||||
LOG_ERR("invalid direction %d", dir);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case DAI_TRIGGER_START:
|
||||
dai_nxp_micfil_trigger_start(dev);
|
||||
break;
|
||||
case DAI_TRIGGER_STOP:
|
||||
case DAI_TRIGGER_PAUSE:
|
||||
dai_nxp_micfil_trigger_stop(dev);
|
||||
break;
|
||||
case DAI_TRIGGER_PRE_START:
|
||||
case DAI_TRIGGER_COPY:
|
||||
return 0;
|
||||
default:
|
||||
LOG_ERR("invalid trigger cmd %d", cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_nxp_micfil_get_config(const struct device *dev, struct dai_config *cfg,
|
||||
enum dai_dir dir)
|
||||
{
|
||||
struct dai_nxp_micfil_data *micfil_data = dev->data;
|
||||
|
||||
memcpy(cfg, &micfil_data->cfg, sizeof(*cfg));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_nxp_micfil_set_config(const struct device *dev,
|
||||
const struct dai_config *cfg, const void *bespoke_cfg)
|
||||
|
||||
{
|
||||
const struct micfil_bespoke_config *bespoke = bespoke_cfg;
|
||||
const struct dai_nxp_micfil_config *micfil_cfg = dev->config;
|
||||
pdm_channel_config_t chan_config = { 0 };
|
||||
pdm_config_t global_config = { 0 };
|
||||
int ret, i;
|
||||
|
||||
if (cfg->type != DAI_IMX_MICFIL) {
|
||||
LOG_ERR("wrong DAI type: %d", cfg->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
global_config.fifoWatermark = micfil_cfg->rx_props->fifo_depth - 1;
|
||||
global_config.qualityMode = kPDM_QualityModeVeryLow0;
|
||||
global_config.cicOverSampleRate = MICFIL_OSR_DEFAULT;
|
||||
|
||||
PDM_Init(micfil_cfg->base, &global_config);
|
||||
|
||||
for (i = 0; i < bespoke->pdm_ch; i++) {
|
||||
chan_config.gain = kPDM_DfOutputGain2;
|
||||
chan_config.cutOffFreq = kPDM_DcRemoverBypass;
|
||||
PDM_SetChannelConfig(micfil_cfg->base, i, &chan_config);
|
||||
}
|
||||
|
||||
ret = PDM_SetSampleRateConfig(micfil_cfg->base, MICFIL_CLK_ROOT, bespoke->pdm_rate);
|
||||
if (ret == kStatus_Fail) {
|
||||
LOG_ERR("Failure to set samplerate config rate %d", bespoke->pdm_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_nxp_micfil_probe(const struct device *dev)
|
||||
{
|
||||
/* nothing do to here, but mandatory to exist */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dai_nxp_micfil_remove(const struct device *dev)
|
||||
{
|
||||
/* nothing do to here, but mandatory to exist */
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dai_driver_api dai_nxp_micfil_ops = {
|
||||
.probe = dai_nxp_micfil_probe,
|
||||
.remove = dai_nxp_micfil_remove,
|
||||
.config_set = dai_nxp_micfil_set_config,
|
||||
.config_get = dai_nxp_micfil_get_config,
|
||||
.get_properties = dai_nxp_micfil_get_properties,
|
||||
.trigger = dai_nxp_micfil_trigger,
|
||||
};
|
||||
|
||||
static int micfil_init(const struct device *dev)
|
||||
{
|
||||
const struct dai_nxp_micfil_config *cfg = dev->config;
|
||||
int ret;
|
||||
|
||||
/* pinctrl is optional so do not return an error if not defined */
|
||||
ret = pinctrl_apply_state(cfg->pincfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0 && ret != -ENOENT) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DAI_NXP_MICFIL_INIT(inst) \
|
||||
PINCTRL_DT_INST_DEFINE(inst); \
|
||||
static struct dai_nxp_micfil_data dai_nxp_micfil_data_##inst = { \
|
||||
.cfg.type = DAI_IMX_MICFIL, \
|
||||
.cfg.dai_index = DT_INST_PROP_OR(inst, dai_index, 0), \
|
||||
}; \
|
||||
\
|
||||
static const struct dai_properties micfil_rx_props_##inst = { \
|
||||
.fifo_address = MICFIL_FIFO_BASE(inst), \
|
||||
.fifo_depth = DT_INST_PROP(inst, fifo_depth), \
|
||||
.dma_hs_id = MICFIL_DMA_HANDSHAKE(inst), \
|
||||
}; \
|
||||
\
|
||||
static const struct dai_nxp_micfil_config dai_nxp_micfil_config_##inst = { \
|
||||
.base = UINT_TO_MICFIL(DT_INST_REG_ADDR(inst)), \
|
||||
.rx_props = &micfil_rx_props_##inst, \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(inst, &micfil_init, NULL, \
|
||||
&dai_nxp_micfil_data_##inst, &dai_nxp_micfil_config_##inst, \
|
||||
POST_KERNEL, CONFIG_DAI_INIT_PRIORITY, \
|
||||
&dai_nxp_micfil_ops); \
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(DAI_NXP_MICFIL_INIT)
|
||||
25
dts/bindings/dai/nxp,dai-micfil.yaml
Normal file
25
dts/bindings/dai/nxp,dai-micfil.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright 2023 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: NXP PDM MICFIL node
|
||||
|
||||
compatible: "nxp,dai-micfil"
|
||||
|
||||
include: [base.yaml, pinctrl-device.yaml]
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
dai-index:
|
||||
type: int
|
||||
description: |
|
||||
Use this property to specify the index of the DAI. At the
|
||||
moment, this is only used by SOF to fetch the "struct device"
|
||||
associated with the DAI whose index Linux passes to SOF
|
||||
through an IPC. If this property is not specified, the DAI
|
||||
index will be considered 0.
|
||||
fifo-depth:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Depth (in words) for each channel's FIFO.
|
||||
Loading…
Reference in New Issue
Block a user