Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_MAX98091 max98091.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_AW88298 aw88298.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_CODEC_ES7210 es7210.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_AMBIQ_PDM dmic_ambiq_pdm.c)
zephyr_library_sources_ifdef(CONFIG_AUDIO_DMIC_ES7210 dmic_es7210.c)
1 change: 1 addition & 0 deletions drivers/audio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ 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"
source "drivers/audio/Kconfig.dmic_es7210"

endif # AUDIO_DMIC

Expand Down
23 changes: 23 additions & 0 deletions drivers/audio/Kconfig.dmic_es7210
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: Apache-2.0

config AUDIO_DMIC_ES7210
bool "Everest Semiconductor ES7210 digital microphone"
depends on I2C
depends on I2S
select AUDIO
select AUDIO_CODEC
select AUDIO_CODEC_ES7210
depends on DT_HAS_EVEREST_ES7210_DMIC_ENABLED
help
Enable support for using the Everest Semiconductor ES7210 audio

Check warning on line 12 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:12 please, no spaces at the start of a line
ADC as a digital microphone source. The device streams audio over

Check warning on line 13 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:13 please, no spaces at the start of a line
an I2S interface and is configured through the ES7210 audio codec

Check warning on line 14 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:14 please, no spaces at the start of a line
driver.

Check warning on line 15 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:15 please, no spaces at the start of a line

config ES7210_DMIC_I2S_TIMEOUT_MS
int "I2S read timeout (ms)"
depends on AUDIO_DMIC_ES7210
default 2000
help
Timeout, in milliseconds, used for I2S read operations performed

Check warning on line 22 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:22 please, no spaces at the start of a line
by the ES7210 digital microphone driver.

Check warning on line 23 in drivers/audio/Kconfig.dmic_es7210

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

LEADING_SPACE

drivers/audio/Kconfig.dmic_es7210:23 please, no spaces at the start of a line
284 changes: 284 additions & 0 deletions drivers/audio/dmic_es7210.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
/*
* Copyright (c) 2025 The Zephyr Authors
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT everest_es7210_dmic

#include <stdint.h>

#include <zephyr/audio/codec.h>
#include <zephyr/audio/dmic.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/i2s.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(es7210_dmic, CONFIG_AUDIO_DMIC_LOG_LEVEL);

struct es7210_dmic_config {
const struct device *i2s_dev;
const struct device *codec_dev;
i2s_opt_t i2s_options;
uint8_t data_format;
bool bit_clk_inverted;
int32_t timeout_ms;
};

struct es7210_dmic_data {
enum dmic_state state;
struct k_mem_slab *mem_slab;
size_t block_size;
uint32_t pcm_rate;
uint8_t pcm_width;
uint8_t num_channels;
};

static i2s_fmt_t es7210_dmic_format_from_idx(uint8_t idx)
{
switch (idx) {
case 1:
return I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED;
case 2:
return I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED;
case 0:
default:
return I2S_FMT_DATA_FORMAT_I2S;
}
}

static int es7210_dmic_configure(const struct device *dev, struct dmic_cfg *cfg)
{
const struct es7210_dmic_config *conf = dev->config;
struct es7210_dmic_data *data = dev->data;
struct pcm_stream_cfg *stream = &cfg->streams[0];
i2s_fmt_t format;
struct i2s_config i2s_cfg;
struct audio_codec_cfg codec_cfg = {0};
int ret;

if (cfg->channel.req_num_streams == 0U) {
return -EINVAL;
}

if (cfg->channel.req_num_streams > 1U) {
return -ENOTSUP;
}

if (stream->pcm_rate == 0U || stream->pcm_width == 0U || stream->block_size == 0U ||
stream->mem_slab == NULL) {
return -EINVAL;
}

data->mem_slab = stream->mem_slab;
data->block_size = stream->block_size;
data->pcm_rate = stream->pcm_rate;
data->pcm_width = stream->pcm_width;
data->num_channels = cfg->channel.req_num_chan;

format = es7210_dmic_format_from_idx(conf->data_format);
if (conf->bit_clk_inverted) {
format |= I2S_FMT_BIT_CLK_INV;
}

codec_cfg.dai_route = AUDIO_ROUTE_CAPTURE;
codec_cfg.dai_type = AUDIO_DAI_TYPE_I2S;
codec_cfg.dai_cfg.i2s.word_size = stream->pcm_width;
codec_cfg.dai_cfg.i2s.channels = cfg->channel.req_num_chan;
codec_cfg.dai_cfg.i2s.format = format;
codec_cfg.dai_cfg.i2s.options = conf->i2s_options;
codec_cfg.dai_cfg.i2s.frame_clk_freq = stream->pcm_rate;
codec_cfg.dai_cfg.i2s.mem_slab = stream->mem_slab;
codec_cfg.dai_cfg.i2s.block_size = stream->block_size;
codec_cfg.dai_cfg.i2s.timeout = conf->timeout_ms;

ret = audio_codec_configure(conf->codec_dev, &codec_cfg);
if (ret < 0) {
LOG_ERR("Failed to configure ES7210 codec (%d)", ret);
return ret;
}

audio_codec_stop_output(conf->codec_dev);

i2s_cfg.word_size = stream->pcm_width;
i2s_cfg.channels = cfg->channel.req_num_chan;
i2s_cfg.format = format;
i2s_cfg.options = conf->i2s_options;
i2s_cfg.frame_clk_freq = stream->pcm_rate;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Configure codec with inverted I²S master/slave role

The DMIC driver derives i2s_options from the devicetree properties and then applies the same flags to both the I²S controller and the ES7210 codec (codec_cfg.dai_cfg.i2s.options = conf->i2s_options; and later i2s_cfg.options = conf->i2s_options). However ES7210_DMIC_OPTIONS() produces settings from the SoC perspective: when the ES7210 is the bit/frame clock master, the macro returns _SLAVE so that the I²S peripheral behaves as a slave, and vice versa. Reusing those flags for the codec therefore programs the codec with the opposite master/slave role advertised in devicetree (e.g. the codec is set as master when the SoC should be master). On real hardware this results in both sides trying to act as the same bus role and the microphone never streams. The codec options need to mirror the devicetree properties while the I²S controller should receive the inverted options.

Useful? React with 👍 / 👎.

i2s_cfg.mem_slab = stream->mem_slab;
i2s_cfg.block_size = stream->block_size;
i2s_cfg.timeout = conf->timeout_ms;

ret = i2s_configure(conf->i2s_dev, I2S_DIR_RX, &i2s_cfg);
if (ret < 0) {
LOG_ERR("Failed to configure I2S RX (%d)", ret);
return ret;
}

cfg->channel.act_num_chan = cfg->channel.req_num_chan;
cfg->channel.act_chan_map_lo = cfg->channel.req_chan_map_lo;
cfg->channel.act_chan_map_hi = cfg->channel.req_chan_map_hi;
cfg->channel.act_num_streams = 1U;

data->state = DMIC_STATE_CONFIGURED;

return 0;
}

static int es7210_dmic_trigger(const struct device *dev, enum dmic_trigger cmd)
{
const struct es7210_dmic_config *conf = dev->config;
struct es7210_dmic_data *data = dev->data;
int ret = 0;

switch (cmd) {
case DMIC_TRIGGER_START:
if (data->state != DMIC_STATE_CONFIGURED && data->state != DMIC_STATE_PAUSED) {
return -EIO;
}

audio_codec_start_output(conf->codec_dev);
ret = i2s_trigger(conf->i2s_dev, I2S_DIR_RX, I2S_TRIGGER_START);
if (ret < 0) {
audio_codec_stop_output(conf->codec_dev);
return ret;
}

data->state = DMIC_STATE_ACTIVE;
break;

case DMIC_TRIGGER_STOP:
if (data->state != DMIC_STATE_ACTIVE && data->state != DMIC_STATE_PAUSED) {
return 0;
}

ret = i2s_trigger(conf->i2s_dev, I2S_DIR_RX, I2S_TRIGGER_STOP);
if (ret < 0) {
return ret;
}

audio_codec_stop_output(conf->codec_dev);
data->state = DMIC_STATE_CONFIGURED;
break;

case DMIC_TRIGGER_PAUSE:
if (data->state != DMIC_STATE_ACTIVE) {
return 0;
}

ret = i2s_trigger(conf->i2s_dev, I2S_DIR_RX, I2S_TRIGGER_STOP);
if (ret < 0) {
return ret;
}

audio_codec_stop_output(conf->codec_dev);
data->state = DMIC_STATE_PAUSED;
break;

case DMIC_TRIGGER_RELEASE:
if (data->state != DMIC_STATE_PAUSED) {
return -EIO;
}

audio_codec_start_output(conf->codec_dev);
ret = i2s_trigger(conf->i2s_dev, I2S_DIR_RX, I2S_TRIGGER_START);
if (ret < 0) {
audio_codec_stop_output(conf->codec_dev);
return ret;
}

data->state = DMIC_STATE_ACTIVE;
break;

case DMIC_TRIGGER_RESET:
i2s_trigger(conf->i2s_dev, I2S_DIR_RX, I2S_TRIGGER_DROP);
audio_codec_stop_output(conf->codec_dev);
data->state = DMIC_STATE_CONFIGURED;
break;

default:
return -EINVAL;
}

return ret;
}

static int es7210_dmic_read(const struct device *dev, uint8_t stream, void **buffer, size_t *size,
int32_t timeout)
{
const struct es7210_dmic_config *conf = dev->config;
struct es7210_dmic_data *data = dev->data;
int ret;
size_t block_size;

ARG_UNUSED(stream);

if (data->state != DMIC_STATE_ACTIVE && data->state != DMIC_STATE_PAUSED) {
return -EIO;
}

if ((timeout != SYS_FOREVER_MS) && (timeout != conf->timeout_ms)) {
LOG_DBG("Unsupported timeout %d ms", timeout);
}

ret = i2s_read(conf->i2s_dev, buffer, &block_size);
if (ret < 0) {
return ret;
}

if (size != NULL) {
*size = block_size;
}

return 0;
}

static const struct _dmic_ops es7210_dmic_api = {
.configure = es7210_dmic_configure,
.trigger = es7210_dmic_trigger,
.read = es7210_dmic_read,
};

static int es7210_dmic_init(const struct device *dev)
{
const struct es7210_dmic_config *conf = dev->config;
struct es7210_dmic_data *data = dev->data;

if (!device_is_ready(conf->i2s_dev)) {
LOG_ERR("I2S device is not ready");
return -ENODEV;
}

if (!device_is_ready(conf->codec_dev)) {
LOG_ERR("ES7210 codec device is not ready");
return -ENODEV;
}

data->state = DMIC_STATE_INITIALIZED;

return 0;
}

#define ES7210_DMIC_OPTIONS(inst) \
((DT_INST_PROP(inst, bit_clock_master) ? I2S_OPT_BIT_CLK_SLAVE : I2S_OPT_BIT_CLK_MASTER) | \
(DT_INST_PROP(inst, frame_clock_master) ? I2S_OPT_FRAME_CLK_SLAVE \
: I2S_OPT_FRAME_CLK_MASTER))

#define ES7210_DMIC_INIT(inst) \
static struct es7210_dmic_data es7210_dmic_data_##inst; \
static const struct es7210_dmic_config es7210_dmic_config_##inst = { \
.i2s_dev = DEVICE_DT_GET(DT_INST_BUS(inst)), \
.codec_dev = DEVICE_DT_GET(DT_INST_PHANDLE(inst, codec)), \
.i2s_options = ES7210_DMIC_OPTIONS(inst), \
.data_format = DT_INST_ENUM_IDX_OR(inst, data_format, 0), \
.bit_clk_inverted = DT_INST_PROP_OR(inst, bit_clock_inverted, false), \
.timeout_ms = \
DT_INST_PROP_OR(inst, timeout_ms, CONFIG_ES7210_DMIC_I2S_TIMEOUT_MS), \
}; \
DEVICE_DT_INST_DEFINE(inst, es7210_dmic_init, NULL, &es7210_dmic_data_##inst, \
&es7210_dmic_config_##inst, POST_KERNEL, \
CONFIG_AUDIO_DMIC_INIT_PRIORITY, &es7210_dmic_api);

DT_INST_FOREACH_STATUS_OKAY(ES7210_DMIC_INIT)
43 changes: 43 additions & 0 deletions dts/bindings/audio/everest,es7210-dmic.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright 2025 The Zephyr Authors
# SPDX-License-Identifier: Apache-2.0

description: Everest Semiconductor ES7210 digital microphone (I2S)

compatible: "everest,es7210-dmic"

include: i2s-device.yaml

properties:
codec:
type: phandle
required: true
description: Phandle to the ES7210 codec device controlling the ADC

bit-clock-master:
type: boolean
description: >
Set when the ES7210 provides the I2S bit clock. If unset the SoC drives
the clock.

frame-clock-master:
type: boolean
description: >
Set when the ES7210 provides the I2S frame clock. If unset the SoC drives
the clock.

bit-clock-inverted:
type: boolean
description: Invert the I2S bit clock polarity.

data-format:
type: string
enum:
- i2s
- left-justified
- right-justified
description: I2S data format provided by the ES7210.

timeout-ms:
type: int
default: 2000
description: Timeout in milliseconds used for I2S read operations.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_USE_DMIC=y
CONFIG_DMIC_CHANNELS=2
CONFIG_AUDIO_DMIC_ES7210=y
CONFIG_AUDIO_CODEC_ES7210=y
Loading
Loading