diff --git a/drivers/dai/CMakeLists.txt b/drivers/dai/CMakeLists.txt index 506c2a54335b..2196c11485f6 100644 --- a/drivers/dai/CMakeLists.txt +++ b/drivers/dai/CMakeLists.txt @@ -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) diff --git a/drivers/dai/Kconfig b/drivers/dai/Kconfig index 05877efd88ed..fc3972a98966 100644 --- a/drivers/dai/Kconfig +++ b/drivers/dai/Kconfig @@ -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 diff --git a/drivers/dai/nxp/micfil/CMakeLists.txt b/drivers/dai/nxp/micfil/CMakeLists.txt new file mode 100644 index 000000000000..50ea62e93279 --- /dev/null +++ b/drivers/dai/nxp/micfil/CMakeLists.txt @@ -0,0 +1,5 @@ +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(micfil.c) diff --git a/drivers/dai/nxp/micfil/Kconfig.micfil b/drivers/dai/nxp/micfil/Kconfig.micfil new file mode 100644 index 000000000000..7e5e0cd8bebe --- /dev/null +++ b/drivers/dai/nxp/micfil/Kconfig.micfil @@ -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. diff --git a/drivers/dai/nxp/micfil/micfil.c b/drivers/dai/nxp/micfil/micfil.c new file mode 100644 index 000000000000..0834a0359e63 --- /dev/null +++ b/drivers/dai/nxp/micfil/micfil.c @@ -0,0 +1,210 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#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) diff --git a/dts/bindings/dai/nxp,dai-micfil.yaml b/dts/bindings/dai/nxp,dai-micfil.yaml new file mode 100644 index 000000000000..3c0f5bf7f217 --- /dev/null +++ b/dts/bindings/dai/nxp,dai-micfil.yaml @@ -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.