diff --git a/drivers/audio/CMakeLists.txt b/drivers/audio/CMakeLists.txt index 49b05d66bf3..ffa93bdd1a5 100644 --- a/drivers/audio/CMakeLists.txt +++ b/drivers/audio/CMakeLists.txt @@ -11,3 +11,5 @@ zephyr_library_sources_ifdef(CONFIG_AUDIO_INTEL_DMIC decimation/pdm_decim_int32_ zephyr_library_sources_ifdef(CONFIG_AUDIO_INTEL_DMIC decimation/pdm_decim_int32_06_4156_5100_010_095.c) zephyr_library_sources_ifdef(CONFIG_AUDIO_INTEL_DMIC decimation/pdm_decim_int32_08_4156_5380_010_090.c) zephyr_library_sources_ifdef(CONFIG_AUDIO_INTEL_DMIC decimation/pdm_decim_table.c) +zephyr_library_sources_ifdef(CONFIG_AUDIO_MPXXDTYY mpxxdtyy.c) +zephyr_library_sources_ifdef(CONFIG_AUDIO_MPXXDTYY mpxxdtyy-i2s.c) diff --git a/drivers/audio/Kconfig b/drivers/audio/Kconfig index bfeea859d14..821c69cc2fa 100644 --- a/drivers/audio/Kconfig +++ b/drivers/audio/Kconfig @@ -54,6 +54,7 @@ module-str = audio_dmic source "subsys/logging/Kconfig.template.log_config" source "drivers/audio/Kconfig.intel_dmic" +source "drivers/audio/Kconfig.mpxxdtyy" endif # AUDIO_DMIC diff --git a/drivers/audio/Kconfig.mpxxdtyy b/drivers/audio/Kconfig.mpxxdtyy new file mode 100644 index 00000000000..8ebc9237466 --- /dev/null +++ b/drivers/audio/Kconfig.mpxxdtyy @@ -0,0 +1,13 @@ +# +# Copyright (c) 2018 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig AUDIO_MPXXDTYY + bool "ST Digital PDM microphone attached to I2S support" + depends on AUDIO_DMIC + depends on I2S + select HAS_STLIB + help + Enable MPXXDTYY microphone support on the selected board diff --git a/drivers/audio/mpxxdtyy-i2s.c b/drivers/audio/mpxxdtyy-i2s.c new file mode 100644 index 00000000000..f96b927a7b9 --- /dev/null +++ b/drivers/audio/mpxxdtyy-i2s.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mpxxdtyy.h" +#include + +#define LOG_LEVEL CONFIG_MPXXDTYY_LOG_LEVEL +#include +LOG_MODULE_DECLARE(mpxxdtyy); + +#ifdef DT_ST_MPXXDTYY_BUS_I2S + +#define NUM_RX_BLOCKS 4 +#define PDM_BLOCK_MAX_SIZE_BYTES 512 + +K_MEM_SLAB_DEFINE(rx_pdm_i2s_mslab, PDM_BLOCK_MAX_SIZE_BYTES, NUM_RX_BLOCKS, 1); + +int mpxxdtyy_i2s_read(struct device *dev, u8_t stream, void **buffer, + size_t *size, s32_t timeout) +{ + int ret; + struct mpxxdtyy_data *const data = DEV_DATA(dev); + void *pdm_block, *pcm_block; + size_t pdm_size; + TPDMFilter_InitStruct *pdm_filter = &data->pdm_filter; + + ret = i2s_read(data->comm_master, &pdm_block, &pdm_size); + if (ret != 0) { + LOG_ERR("read failed (%d)", ret); + return ret; + } + + ret = k_mem_slab_alloc(data->pcm_mem_slab, + &pcm_block, K_NO_WAIT); + if (ret < 0) { + return ret; + } + + sw_filter_lib_run(pdm_filter, pdm_block, pcm_block, pdm_size, + data->pcm_mem_size); + k_mem_slab_free(&rx_pdm_i2s_mslab, &pdm_block); + + *buffer = pcm_block; + *size = data->pcm_mem_size; + + return 0; +} + +int mpxxdtyy_i2s_trigger(struct device *dev, enum dmic_trigger cmd) +{ + int ret; + struct mpxxdtyy_data *const data = DEV_DATA(dev); + enum i2s_trigger_cmd i2s_cmd; + enum dmic_state tmp_state; + + switch (cmd) { + case DMIC_TRIGGER_START: + if (data->state == DMIC_STATE_CONFIGURED) { + tmp_state = DMIC_STATE_ACTIVE; + i2s_cmd = I2S_TRIGGER_START; + } else { + return 0; + } + break; + case DMIC_TRIGGER_STOP: + if (data->state == DMIC_STATE_ACTIVE) { + tmp_state = DMIC_STATE_CONFIGURED; + i2s_cmd = I2S_TRIGGER_STOP; + } else { + return 0; + } + break; + default: + return -EINVAL; + } + + ret = i2s_trigger(data->comm_master, I2S_DIR_RX, i2s_cmd); + if (ret != 0) { + LOG_ERR("trigger failed with %d error", ret); + return ret; + } + + data->state = tmp_state; + return 0; +} + +int mpxxdtyy_i2s_configure(struct device *dev, struct dmic_cfg *cfg) +{ + int ret; + struct mpxxdtyy_data *const data = DEV_DATA(dev); + u8_t chan_size = cfg->streams->pcm_width; + u32_t audio_freq = cfg->streams->pcm_rate; + u16_t factor; + + /* PCM buffer size */ + data->pcm_mem_slab = cfg->streams->mem_slab; + data->pcm_mem_size = cfg->streams->block_size; + + /* check requested min pdm frequency */ + if (cfg->io.min_pdm_clk_freq < MPXXDTYY_MIN_PDM_FREQ || + cfg->io.min_pdm_clk_freq > cfg->io.max_pdm_clk_freq) { + return -EINVAL; + } + + /* check requested max pdm frequency */ + if (cfg->io.max_pdm_clk_freq > MPXXDTYY_MAX_PDM_FREQ || + cfg->io.max_pdm_clk_freq < cfg->io.min_pdm_clk_freq) { + return -EINVAL; + } + + factor = sw_filter_lib_init(dev, cfg); + if (factor == 0) { + return -EINVAL; + } + + /* configure I2S channels */ + struct i2s_config i2s_cfg; + + i2s_cfg.word_size = chan_size; + i2s_cfg.channels = cfg->channel.req_num_chan; + i2s_cfg.format = I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED | + I2S_FMT_BIT_CLK_INV; + i2s_cfg.options = I2S_OPT_FRAME_CLK_MASTER | I2S_OPT_BIT_CLK_MASTER; + i2s_cfg.frame_clk_freq = audio_freq * factor / chan_size; + i2s_cfg.block_size = data->pcm_mem_size * (factor / chan_size); + i2s_cfg.mem_slab = &rx_pdm_i2s_mslab; + i2s_cfg.timeout = 2000; + + ret = i2s_configure(data->comm_master, I2S_DIR_RX, &i2s_cfg); + if (ret != 0) { + LOG_ERR("I2S device configuration error"); + return ret; + } + + data->state = DMIC_STATE_CONFIGURED; + return 0; +} +#endif /* DT_ST_MPXXDTYY_BUS_I2S */ diff --git a/drivers/audio/mpxxdtyy.c b/drivers/audio/mpxxdtyy.c new file mode 100644 index 00000000000..aa538724d6f --- /dev/null +++ b/drivers/audio/mpxxdtyy.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "mpxxdtyy.h" + +#define LOG_LEVEL CONFIG_MPXXDTYY_LOG_LEVEL +#include +LOG_MODULE_REGISTER(mpxxdtyy); + +u16_t sw_filter_lib_init(struct device *dev, struct dmic_cfg *cfg) +{ + struct mpxxdtyy_data *const data = DEV_DATA(dev); + TPDMFilter_InitStruct *pdm_filter = &data->pdm_filter; + u16_t factor; + u32_t audio_freq = cfg->streams->pcm_rate; + + /* calculate oversampling factor based on pdm clock */ + for (factor = 64; factor <= 128; factor += 64) { + u32_t pdm_bit_clk = (audio_freq * factor * + cfg->channel.req_num_chan); + + if (pdm_bit_clk >= cfg->io.min_pdm_clk_freq && + pdm_bit_clk <= cfg->io.max_pdm_clk_freq) { + break; + } + } + + if (factor != 64 && factor != 128) { + return 0; + } + + /* init the filter lib */ + pdm_filter->LP_HZ = audio_freq / 2; + pdm_filter->HP_HZ = 10; + pdm_filter->Fs = audio_freq; + pdm_filter->Out_MicChannels = 1; + pdm_filter->In_MicChannels = 1; + pdm_filter->Decimation = factor; + pdm_filter->MaxVolume = 64; + + Open_PDM_Filter_Init(pdm_filter); + + return factor; +} + +int sw_filter_lib_run(TPDMFilter_InitStruct *pdm_filter, + void *pdm_block, void *pcm_block, + size_t pdm_size, size_t pcm_size) +{ + int i; + + if (pdm_block == NULL || pcm_block == NULL || pdm_filter == NULL) { + return -EINVAL; + } + + for (i = 0; i < pdm_size/2; i++) { + ((u16_t *)pdm_block)[i] = HTONS(((u16_t *)pdm_block)[i]); + } + + switch (pdm_filter->Decimation) { + case 64: + Open_PDM_Filter_64((u8_t *) pdm_block, pcm_block, + pcm_size, pdm_filter); + break; + case 128: + Open_PDM_Filter_128((u8_t *) pdm_block, pcm_block, + pcm_size, pdm_filter); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct _dmic_ops mpxxdtyy_driver_api = { +#ifdef DT_ST_MPXXDTYY_BUS_I2S + .configure = mpxxdtyy_i2s_configure, + .trigger = mpxxdtyy_i2s_trigger, + .read = mpxxdtyy_i2s_read, +#endif /* DT_ST_MPXXDTYY_BUS_I2S */ +}; + +static int mpxxdtyy_initialize(struct device *dev) +{ + struct mpxxdtyy_data *const data = DEV_DATA(dev); + + data->comm_master = device_get_binding(DT_MPXXDTYY_MASTER_DEV_NAME); + + if (data->comm_master == NULL) { + LOG_ERR("master %s not found", DT_MPXXDTYY_MASTER_DEV_NAME); + return -EINVAL; + } + + data->state = DMIC_STATE_INITIALIZED; + return 0; +} + +static struct mpxxdtyy_data mpxxdtyy_data; + +DEVICE_AND_API_INIT(mpxxdtyy, DT_MPXXDTYY_DEV_NAME, mpxxdtyy_initialize, + &mpxxdtyy_data, NULL, POST_KERNEL, + CONFIG_AUDIO_DMIC_INIT_PRIORITY, &mpxxdtyy_driver_api); diff --git a/drivers/audio/mpxxdtyy.h b/drivers/audio/mpxxdtyy.h new file mode 100644 index 00000000000..38f99c93a98 --- /dev/null +++ b/drivers/audio/mpxxdtyy.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef MPXXDTYY_H +#define MPXXDTYY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include