Browse Source

drivers: audio: dmic: Add Apollo510 PDM driver for DMIC.

Add PDM driver for DMIC.
This driver is compatible with samples/audio/dmic application.

Signed-off-by: Lewis Lee <llee@ambiq.com>
pull/92244/head
Lewis Lee 2 months ago committed by Benjamin Cabé
parent
commit
6d34549ff3
  1. 7
      boards/ambiq/apollo510_evb/apollo510_evb-pinctrl.dtsi
  2. 6
      boards/ambiq/apollo510_evb/apollo510_evb.dts
  3. 1
      boards/ambiq/apollo510_evb/apollo510_evb.yaml
  4. 1
      drivers/audio/CMakeLists.txt
  5. 1
      drivers/audio/Kconfig
  6. 29
      drivers/audio/Kconfig.dmic_ambiq_pdm
  7. 356
      drivers/audio/dmic_ambiq_pdm.c
  8. 7
      dts/arm/ambiq/ambiq_apollo510.dtsi
  9. 31
      dts/bindings/audio/ambiq,pdm.yaml
  10. 5
      modules/hal_ambiq/Kconfig.hal
  11. 5
      samples/drivers/audio/dmic/boards/apollo510_evb.overlay
  12. 1
      samples/drivers/audio/dmic/sample.yaml

7
boards/ambiq/apollo510_evb/apollo510_evb-pinctrl.dtsi

@ -265,6 +265,13 @@ @@ -265,6 +265,13 @@
};
};
pdm0_default: pdm0_default {
group0 {
pinmux = <PDM0_CLK_P50>,
<PDM0_DATA_P51>;
};
};
mspi0_default: mspi0_default {
group0 {
pinmux = <MSPI0_0_P64>,

6
boards/ambiq/apollo510_evb/apollo510_evb.dts

@ -205,6 +205,12 @@ zephyr_udc0: &usb { @@ -205,6 +205,12 @@ zephyr_udc0: &usb {
status = "okay";
};
&pdm0 {
pinctrl-0 = <&pdm0_default>;
pinctrl-names = "default";
status = "disabled";
};
&gpio0_31 {
status = "okay";
};

1
boards/ambiq/apollo510_evb/apollo510_evb.yaml

@ -19,6 +19,7 @@ supported: @@ -19,6 +19,7 @@ supported:
- hwinfo
- clock_control
- usbd
- pdm
- mspi
testing:
ignore_tags:

1
drivers/audio/CMakeLists.txt

@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_WM8962 wm8962.c) @@ -15,3 +15,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_WM8962 wm8962.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_CS43L22 cs43l22.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_PCM1681 pcm1681.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_MAX98091 max98091.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_AMBIQ_PDM dmic_ambiq_pdm.c)

1
drivers/audio/Kconfig

@ -66,6 +66,7 @@ source "subsys/logging/Kconfig.template.log_config" @@ -66,6 +66,7 @@ source "subsys/logging/Kconfig.template.log_config"
source "drivers/audio/Kconfig.mpxxdtyy"
source "drivers/audio/Kconfig.dmic_pdm_nrfx"
source "drivers/audio/Kconfig.dmic_mcux"
source "drivers/audio/Kconfig.dmic_ambiq_pdm"
endif # AUDIO_DMIC

29
drivers/audio/Kconfig.dmic_ambiq_pdm

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
# Copyright (c) 2025 Ambiq Micro Inc.
# SPDX-License-Identifier: Apache-2.0
config AUDIO_DMIC_AMBIQ_PDM
bool "Ambiq PDM driver"
default y
depends on DT_HAS_AMBIQ_PDM_ENABLED
select AMBIQ_HAL
select AMBIQ_HAL_USE_PDM
select PINCTRL
help
Enable support for Ambiq PDM driver for Apollo 5 MCU.
if AUDIO_DMIC_AMBIQ_PDM
config PDM_AMBIQ_HANDLE_CACHE
bool "Turn on cache handling in PDM driver"
default y
depends on CACHE_MANAGEMENT && DCACHE
help
Disable this if cache has been handled in upper layers.
config PDM_AMBIQ_BUFFER_ALIGNMENT
int "Set the PDM DMA TCB buffer alignment"
default DCACHE_LINE_SIZE if DCACHE
help
PDM buffer should be 32bytes aligned when placed in cachable region.
endif

356
drivers/audio/dmic_ambiq_pdm.c

@ -0,0 +1,356 @@ @@ -0,0 +1,356 @@
/*
* Copyright (c) 2025 Ambiq Micro Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#include <soc.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include <zephyr/audio/dmic.h>
#include <zephyr/cache.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
#include <zephyr/pm/device_runtime.h>
#include <am_mcu_apollo.h>
#define DT_DRV_COMPAT ambiq_pdm
LOG_MODULE_REGISTER(ambiq_pdm, CONFIG_AUDIO_DMIC_LOG_LEVEL);
struct dmic_ambiq_pdm_data {
void *pdm_handler;
struct k_mem_slab *mem_slab;
void *mem_slab_buffer;
struct k_sem dma_done_sem;
int inst_idx;
uint32_t block_size;
uint32_t sample_num;
uint8_t frame_size_bytes;
am_hal_pdm_config_t pdm_cfg;
am_hal_pdm_transfer_t pdm_transfer;
bool pm_policy_state_on;
enum dmic_state dmic_state;
};
struct dmic_ambiq_pdm_cfg {
void (*irq_config_func)(void);
const struct pinctrl_dev_config *pcfg;
};
static void dmic_ambiq_pdm_pm_policy_state_lock_get(const struct device *dev)
{
if (IS_ENABLED(CONFIG_PM)) {
struct dmic_ambiq_pdm_data *data = dev->data;
if (!data->pm_policy_state_on) {
data->pm_policy_state_on = true;
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES);
pm_device_runtime_get(dev);
}
}
}
static void dmic_ambiq_pdm_pm_policy_state_lock_put(const struct device *dev)
{
if (IS_ENABLED(CONFIG_PM)) {
struct dmic_ambiq_pdm_data *data = dev->data;
if (data->pm_policy_state_on) {
data->pm_policy_state_on = false;
pm_device_runtime_put(dev);
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_RAM, PM_ALL_SUBSTATES);
}
}
}
static void dmic_ambiq_pdm_isr(const struct device *dev)
{
uint32_t ui32Status;
struct dmic_ambiq_pdm_data *data = dev->data;
am_hal_pdm_interrupt_status_get(data->pdm_handler, &ui32Status, true);
am_hal_pdm_interrupt_clear(data->pdm_handler, ui32Status);
am_hal_pdm_interrupt_service(data->pdm_handler, ui32Status, &(data->pdm_transfer));
if (ui32Status & AM_HAL_PDM_INT_DCMP) {
k_sem_give(&data->dma_done_sem);
}
}
static int dmic_ambiq_pdm_init(const struct device *dev)
{
struct dmic_ambiq_pdm_data *data = dev->data;
const struct dmic_ambiq_pdm_cfg *config = dev->config;
int ret = 0;
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
LOG_ERR("Fail to config PDM pins\n");
return ret;
}
am_hal_pdm_initialize(data->inst_idx, &data->pdm_handler);
am_hal_pdm_power_control(data->pdm_handler, AM_HAL_PDM_POWER_ON, false);
data->dmic_state = DMIC_STATE_INITIALIZED;
return 0;
}
static int dmic_ambiq_pdm_configure(const struct device *dev, struct dmic_cfg *dev_config)
{
struct dmic_ambiq_pdm_data *data = dev->data;
const struct dmic_ambiq_pdm_cfg *config = dev->config;
struct pdm_chan_cfg *channel = &dev_config->channel;
struct pcm_stream_cfg *stream = &dev_config->streams[0];
if (data->dmic_state == DMIC_STATE_ACTIVE) {
LOG_ERR("Cannot configure device while it is active");
return -EBUSY;
}
if (channel->req_num_streams != 1 || channel->req_num_chan > 2 ||
channel->req_num_chan < 1 || channel->req_chan_map_hi != channel->act_chan_map_hi) {
LOG_ERR("Requested configuration is not supported");
return -EINVAL;
}
if ((stream->pcm_width != 16) && (stream->pcm_width != 24)) {
LOG_ERR("Only 16-bit or 24-bit samples are supported");
return -EINVAL;
}
channel->act_num_streams = 1;
channel->act_chan_map_hi = 0;
channel->act_chan_map_lo = channel->req_chan_map_lo;
data->pdm_cfg.ePDMClkSpeed = AM_HAL_PDM_CLK_HFRC_24MHZ;
/* 1.5MHz PDM CLK OUT:
* AM_HAL_PDM_CLK_24MHZ, AM_HAL_PDM_MCLKDIV_1, AM_HAL_PDM_PDMA_CLKO_DIV7
* 15.625KHz 24bit Sampling:
* DecimationRate = 48
*/
data->pdm_cfg.eClkDivider = AM_HAL_PDM_MCLKDIV_1;
data->pdm_cfg.ePDMAClkOutDivder = AM_HAL_PDM_PDMA_CLKO_DIV7;
data->pdm_cfg.ui32DecimationRate = 48;
data->pdm_cfg.eStepSize = AM_HAL_PDM_GAIN_STEP_0_13DB;
data->pdm_cfg.bHighPassEnable = AM_HAL_PDM_HIGH_PASS_ENABLE;
data->pdm_cfg.ui32HighPassCutoff = 0x3;
data->pdm_cfg.eLeftGain = AM_HAL_PDM_GAIN_0DB;
data->pdm_cfg.eRightGain = AM_HAL_PDM_GAIN_0DB;
data->pdm_cfg.bDataPacking = 1;
if (channel->req_num_chan == 1) {
data->pdm_cfg.ePCMChannels = AM_HAL_PDM_CHANNEL_LEFT;
} else {
data->pdm_cfg.ePCMChannels = AM_HAL_PDM_CHANNEL_STEREO;
}
data->pdm_cfg.bPDMSampleDelay = AM_HAL_PDM_CLKOUT_PHSDLY_NONE;
data->pdm_cfg.ui32GainChangeDelay = AM_HAL_PDM_CLKOUT_DELAY_NONE;
data->pdm_cfg.bSoftMute = 0;
data->pdm_cfg.bLRSwap = 1;
am_hal_pdm_configure(data->pdm_handler, &data->pdm_cfg);
am_hal_pdm_interrupt_clear(data->pdm_handler, (AM_HAL_PDM_INT_DERR | AM_HAL_PDM_INT_DCMP |
AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF));
am_hal_pdm_interrupt_enable(data->pdm_handler, (AM_HAL_PDM_INT_DERR | AM_HAL_PDM_INT_DCMP |
AM_HAL_PDM_INT_UNDFL | AM_HAL_PDM_INT_OVF));
config->irq_config_func();
data->block_size = stream->block_size;
if (stream->pcm_width == 16) {
data->frame_size_bytes = 2;
} else if (stream->pcm_width == 24) {
data->frame_size_bytes = 4;
}
data->sample_num = stream->block_size / data->frame_size_bytes;
data->mem_slab = stream->mem_slab;
data->dmic_state = DMIC_STATE_CONFIGURED;
/* Configure DMA and target address.*/
data->pdm_transfer.ui32TotalCount = data->sample_num * sizeof(uint32_t);
data->pdm_transfer.ui32TargetAddrReverse =
data->pdm_transfer.ui32TargetAddr + data->pdm_transfer.ui32TotalCount;
return 0;
}
static void am_pdm_dma_trigger(const struct device *dev)
{
struct dmic_ambiq_pdm_data *data = dev->data;
/* Start the data transfer. */
am_hal_pdm_dma_start(data->pdm_handler, &(data->pdm_transfer));
}
static int dmic_ambiq_pdm_trigger(const struct device *dev, enum dmic_trigger cmd)
{
struct dmic_ambiq_pdm_data *data = dev->data;
switch (cmd) {
case DMIC_TRIGGER_PAUSE:
case DMIC_TRIGGER_STOP:
if (data->dmic_state == DMIC_STATE_ACTIVE) {
am_hal_pdm_disable(data->pdm_handler);
data->dmic_state = DMIC_STATE_PAUSED;
}
break;
case DMIC_TRIGGER_RELEASE:
case DMIC_TRIGGER_START:
if (data->dmic_state == DMIC_STATE_PAUSED ||
data->dmic_state == DMIC_STATE_CONFIGURED) {
am_hal_pdm_enable(data->pdm_handler);
am_pdm_dma_trigger(dev);
data->dmic_state = DMIC_STATE_ACTIVE;
}
break;
default:
LOG_ERR("Invalid command: %d", cmd);
return -EINVAL;
}
return 0;
}
static int dmic_ambiq_pdm_read(const struct device *dev, uint8_t stream, void **buffer,
size_t *size, int32_t timeout)
{
struct dmic_ambiq_pdm_data *data = dev->data;
int ret;
ARG_UNUSED(stream);
if (data->dmic_state != DMIC_STATE_ACTIVE) {
LOG_ERR("Device is not activated");
return -EIO;
}
ret = k_sem_take(&data->dma_done_sem, SYS_TIMEOUT_MS(timeout));
dmic_ambiq_pdm_pm_policy_state_lock_get(dev);
if (ret != 0) {
LOG_DBG("No audio data to be read %d", ret);
} else {
ret = k_mem_slab_alloc(data->mem_slab, &data->mem_slab_buffer, K_NO_WAIT);
uint32_t *pdm_data_buf = (uint32_t *)am_hal_pdm_dma_get_buffer(data->pdm_handler);
if (data->frame_size_bytes == 2) {
/*
* PDM DMA is 32-bit datawidth for each sample, so we need to invalidate 2x
* block_size on 16 bit PCM data.
*/
#if CONFIG_PDM_AMBIQ_HANDLE_CACHE
if (!buf_in_nocache((uintptr_t)pdm_data_buf, data->block_size * 2)) {
sys_cache_data_invd_range(pdm_data_buf, data->block_size * 2);
}
#endif /* PDM_AMBIQ_HANDLE_CACHE */
uint8_t *temp1 = (uint8_t *)data->mem_slab_buffer;
/* Re-arrange data */
for (uint32_t i = 0; i < data->sample_num; i++) {
temp1[2 * i] = (pdm_data_buf[i] & 0xFF00) >> 8U;
temp1[2 * i + 1] = (pdm_data_buf[i] & 0xFF0000) >> 16U;
}
} else if (data->frame_size_bytes == 4) {
#if CONFIG_PDM_AMBIQ_HANDLE_CACHE
if (!buf_in_nocache((uintptr_t)pdm_data_buf, data->block_size)) {
sys_cache_data_invd_range(pdm_data_buf, data->block_size);
}
#endif /* PDM_AMBIQ_HANDLE_CACHE */
memcpy((void *)data->mem_slab_buffer, (void *)pdm_data_buf,
data->block_size);
}
*size = data->block_size;
*buffer = data->mem_slab_buffer;
}
dmic_ambiq_pdm_pm_policy_state_lock_put(dev);
return ret;
}
#ifdef CONFIG_PM_DEVICE
static int dmic_ambiq_pdm_pm_action(const struct device *dev, enum pm_device_action action)
{
struct dmic_ambiq_pdm_data *data = dev->data;
uint32_t ret;
am_hal_sysctrl_power_state_e status;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
status = AM_HAL_SYSCTRL_WAKE;
break;
case PM_DEVICE_ACTION_SUSPEND:
status = AM_HAL_SYSCTRL_DEEPSLEEP;
break;
default:
return -ENOTSUP;
}
ret = am_hal_pdm_power_control(data->pdm_handler, status, true);
if (ret != AM_HAL_STATUS_SUCCESS) {
LOG_ERR("am_hal_pdm_power_control failed: %d", ret);
return -EPERM;
} else {
return 0;
}
}
#endif /* CONFIG_PM_DEVICE */
static const struct _dmic_ops dmic_ambiq_ops = {
.configure = dmic_ambiq_pdm_configure,
.trigger = dmic_ambiq_pdm_trigger,
.read = dmic_ambiq_pdm_read,
};
#define AMBIQ_PDM_DEFINE(n) \
PINCTRL_DT_INST_DEFINE(n); \
static void pdm_irq_config_func_##n(void) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), dmic_ambiq_pdm_isr, \
DEVICE_DT_INST_GET(n), 0); \
irq_enable(DT_INST_IRQN(n)); \
} \
static uint32_t pdm_dma_tcb_buf##n[DT_INST_PROP_OR(n, pdm_buffer_size, 1536)] \
__attribute__((section(DT_INST_PROP_OR(n, pdm_buffer_location, ".data")))) \
__aligned(CONFIG_PDM_AMBIQ_BUFFER_ALIGNMENT); \
static struct dmic_ambiq_pdm_data dmic_ambiq_pdm_data##n = { \
.dma_done_sem = Z_SEM_INITIALIZER(dmic_ambiq_pdm_data##n.dma_done_sem, 0, 1), \
.inst_idx = n, \
.block_size = 0, \
.sample_num = 0, \
.dmic_state = DMIC_STATE_UNINIT, \
.pdm_transfer.ui32TargetAddr = (uint32_t)pdm_dma_tcb_buf##n, \
}; \
static const struct dmic_ambiq_pdm_cfg dmic_ambiq_pdm_cfg##n = { \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.irq_config_func = pdm_irq_config_func_##n, \
}; \
PM_DEVICE_DT_INST_DEFINE(n, dmic_ambiq_pdm_pm_action); \
DEVICE_DT_INST_DEFINE(n, dmic_ambiq_pdm_init, NULL, &dmic_ambiq_pdm_data##n, \
&dmic_ambiq_pdm_cfg##n, POST_KERNEL, \
CONFIG_AUDIO_DMIC_INIT_PRIORITY, &dmic_ambiq_ops);
DT_INST_FOREACH_STATUS_OKAY(AMBIQ_PDM_DEFINE)

7
dts/arm/ambiq/ambiq_apollo510.dtsi

@ -504,6 +504,13 @@ @@ -504,6 +504,13 @@
status = "disabled";
};
pdm0: pdm@PDM0_BASE_NAME {
compatible = "ambiq,pdm";
reg = <PDM0_REG_BASE PDM0_REG_SIZE>;
interrupts = <48 0>;
status = "disabled";
};
mspi0: mspi@MSPI0_BASE_NAME {
compatible = "ambiq,mspi-controller";
reg = <MSPI0_REG_BASE MSPI0_REG_SIZE>,

31
dts/bindings/audio/ambiq,pdm.yaml

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
# Copyright (c) 2024 Ambiq Micro Inc.
# SPDX-License-Identifier: Apache-2.0
description: Ambiq PDM
compatible: "ambiq,pdm"
include: ["base.yaml", "pinctrl-device.yaml"]
properties:
reg:
required: true
interrupts:
required: true
pinctrl-0:
required: true
pinctrl-names:
required: true
pdm-buffer-location:
type: string
description: |
Define PDM DMA buffer location section
pdm-buffer-size:
type: int
description: |
Define the PDM DMA buffer size in (4-byte) words

5
modules/hal_ambiq/Kconfig.hal

@ -116,4 +116,9 @@ config AMBIQ_HAL_USE_USB @@ -116,4 +116,9 @@ config AMBIQ_HAL_USE_USB
help
Use the USB driver from Ambiq HAL
config AMBIQ_HAL_USE_PDM
bool
help
Use the PDM driver from Ambiq HAL
endif # AMBIQ_HAL

5
samples/drivers/audio/dmic/boards/apollo510_evb.overlay

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
dmic_dev: &pdm0 {
pdm-buffer-location = "SRAM_NO_CACHE";
pdm-buffer-size = <640>;
status = "okay";
};

1
samples/drivers/audio/dmic/sample.yaml

@ -8,6 +8,7 @@ tests: @@ -8,6 +8,7 @@ tests:
- nrf52840dk/nrf52840
- nrf5340dk/nrf5340/cpuapp
- nrf54l15dk/nrf54l15/cpuapp
- apollo510_evb
harness: console
harness_config:
type: multi_line

Loading…
Cancel
Save