-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
6 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Copyright 2025 NXP | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
zephyr_library() | ||
zephyr_library_sources(micfil.c) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
/* | ||
* 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |