drivers: audio: dmic_nrfx: Add DMM support to driver

Added support for DMM in PDM drivers in order to use it
with nRF54H20.

Signed-off-by: Michał Stasiak <michal.stasiak@nordicsemi.no>
This commit is contained in:
Michał Stasiak 2024-11-19 14:25:05 +01:00 committed by Benjamin Cabé
parent ab001888d3
commit ff405eaebd

View file

@ -8,17 +8,25 @@
#include <zephyr/drivers/clock_control/nrf_clock_control.h> #include <zephyr/drivers/clock_control/nrf_clock_control.h>
#include <zephyr/drivers/pinctrl.h> #include <zephyr/drivers/pinctrl.h>
#include <soc.h> #include <soc.h>
#include <dmm.h>
#include <nrfx_pdm.h> #include <nrfx_pdm.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/irq.h> #include <zephyr/irq.h>
LOG_MODULE_REGISTER(dmic_nrfx_pdm, CONFIG_AUDIO_DMIC_LOG_LEVEL); LOG_MODULE_REGISTER(dmic_nrfx_pdm, CONFIG_AUDIO_DMIC_LOG_LEVEL);
#if CONFIG_SOC_SERIES_NRF54HX
#define DMIC_NRFX_CLOCK_FREQ 8*1000*1000UL
#else
#define DMIC_NRFX_CLOCK_FREQ 32*1000*1000UL
#endif
struct dmic_nrfx_pdm_drv_data { struct dmic_nrfx_pdm_drv_data {
const nrfx_pdm_t *pdm; const nrfx_pdm_t *pdm;
struct onoff_manager *clk_mgr; struct onoff_manager *clk_mgr;
struct onoff_client clk_cli; struct onoff_client clk_cli;
struct k_mem_slab *mem_slab; struct k_mem_slab *mem_slab;
void *mem_slab_buffer;
uint32_t block_size; uint32_t block_size;
struct k_msgq rx_queue; struct k_msgq rx_queue;
bool request_clock : 1; bool request_clock : 1;
@ -36,17 +44,25 @@ struct dmic_nrfx_pdm_drv_cfg {
PCLK32M_HFXO, PCLK32M_HFXO,
ACLK ACLK
} clk_src; } clk_src;
void *mem_reg;
}; };
static void free_buffer(struct dmic_nrfx_pdm_drv_data *drv_data, void *buffer) static void free_buffer(struct dmic_nrfx_pdm_drv_data *drv_data)
{ {
k_mem_slab_free(drv_data->mem_slab, buffer); k_mem_slab_free(drv_data->mem_slab, drv_data->mem_slab_buffer);
LOG_DBG("Freed buffer %p", buffer); LOG_DBG("Freed buffer %p", drv_data->mem_slab_buffer);
}
static void stop_pdm(struct dmic_nrfx_pdm_drv_data *drv_data)
{
drv_data->stopping = true;
nrfx_pdm_stop(drv_data->pdm);
} }
static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt) static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
{ {
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data; struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
const struct dmic_nrfx_pdm_drv_cfg *drv_cfg = dev->config;
int ret; int ret;
bool stop = false; bool stop = false;
@ -54,11 +70,18 @@ static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
void *buffer; void *buffer;
nrfx_err_t err; nrfx_err_t err;
ret = k_mem_slab_alloc(drv_data->mem_slab, &buffer, K_NO_WAIT); ret = k_mem_slab_alloc(drv_data->mem_slab, &drv_data->mem_slab_buffer, K_NO_WAIT);
if (ret < 0) { if (ret < 0) {
LOG_ERR("Failed to allocate buffer: %d", ret); LOG_ERR("Failed to allocate buffer: %d", ret);
stop = true; stop = true;
} else { } else {
ret = dmm_buffer_in_prepare(drv_cfg->mem_reg, drv_data->mem_slab_buffer,
drv_data->block_size, &buffer);
if (ret < 0) {
LOG_ERR("Failed to prepare buffer: %d", ret);
stop_pdm(drv_data);
return;
}
err = nrfx_pdm_buffer_set(drv_data->pdm, buffer, drv_data->block_size / 2); err = nrfx_pdm_buffer_set(drv_data->pdm, buffer, drv_data->block_size / 2);
if (err != NRFX_SUCCESS) { if (err != NRFX_SUCCESS) {
LOG_ERR("Failed to set buffer: 0x%08x", err); LOG_ERR("Failed to set buffer: 0x%08x", err);
@ -69,7 +92,14 @@ static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
if (drv_data->stopping) { if (drv_data->stopping) {
if (evt->buffer_released) { if (evt->buffer_released) {
free_buffer(drv_data, evt->buffer_released); ret = dmm_buffer_in_release(drv_cfg->mem_reg, drv_data->mem_slab_buffer,
drv_data->block_size, evt->buffer_released);
if (ret < 0) {
LOG_ERR("Failed to release buffer: %d", ret);
stop_pdm(drv_data);
return;
}
free_buffer(drv_data);
} }
if (drv_data->active) { if (drv_data->active) {
@ -79,22 +109,26 @@ static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
} }
} }
} else if (evt->buffer_released) { } else if (evt->buffer_released) {
ret = dmm_buffer_in_release(drv_cfg->mem_reg, drv_data->mem_slab_buffer,
drv_data->block_size, evt->buffer_released);
if (ret < 0) {
LOG_ERR("Failed to release buffer: %d", ret);
stop_pdm(drv_data);
return;
}
ret = k_msgq_put(&drv_data->rx_queue, ret = k_msgq_put(&drv_data->rx_queue,
&evt->buffer_released, &drv_data->mem_slab_buffer,
K_NO_WAIT); K_NO_WAIT);
if (ret < 0) { if (ret < 0) {
LOG_ERR("No room in RX queue"); LOG_ERR("No room in RX queue");
stop = true; stop = true;
free_buffer(drv_data);
free_buffer(drv_data, evt->buffer_released);
} else { } else {
LOG_DBG("Queued buffer %p", evt->buffer_released); LOG_DBG("Queued buffer %p", evt->buffer_released);
} }
} }
if (stop) { if (stop) {
drv_data->stopping = true; stop_pdm(drv_data);
nrfx_pdm_stop(drv_data->pdm);
} }
} }
@ -168,7 +202,7 @@ static bool check_pdm_frequencies(const struct dmic_nrfx_pdm_drv_cfg *drv_cfg,
better_found = true; better_found = true;
} }
#else #else
if (IS_ENABLED(CONFIG_SOC_SERIES_NRF53X)) { if (IS_ENABLED(CONFIG_SOC_SERIES_NRF53X) || IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX)) {
const uint32_t src_freq = const uint32_t src_freq =
(NRF_PDM_HAS_MCLKCONFIG && drv_cfg->clk_src == ACLK) (NRF_PDM_HAS_MCLKCONFIG && drv_cfg->clk_src == ACLK)
/* The DMIC_NRFX_PDM_DEVICE() macro contains build /* The DMIC_NRFX_PDM_DEVICE() macro contains build
@ -180,9 +214,13 @@ static bool check_pdm_frequencies(const struct dmic_nrfx_pdm_drv_cfg *drv_cfg,
* not defined (this expression will be eventually * not defined (this expression will be eventually
* optimized away then). * optimized away then).
*/ */
/* TODO : PS does not provide correct formula for nRF54H20 PDM_CLK.
* Assume that master clock source frequency is 8 MHz. Remove once
* correct formula is found.
*/
? DT_PROP_OR(DT_NODELABEL(clock), hfclkaudio_frequency, ? DT_PROP_OR(DT_NODELABEL(clock), hfclkaudio_frequency,
0) 0)
: 32*1000*1000UL; : DMIC_NRFX_CLOCK_FREQ;
uint32_t req_freq = req_rate * ratio; uint32_t req_freq = req_rate * ratio;
/* As specified in the nRF5340 PS: /* As specified in the nRF5340 PS:
* *
@ -562,6 +600,7 @@ static int dmic_nrfx_pdm_read(const struct device *dev,
return ret; return ret;
} }
#if CONFIG_CLOCK_CONTROL_NRF
static void init_clock_manager(const struct device *dev) static void init_clock_manager(const struct device *dev)
{ {
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data; struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
@ -581,6 +620,7 @@ static void init_clock_manager(const struct device *dev)
drv_data->clk_mgr = z_nrf_clock_control_get_onoff(subsys); drv_data->clk_mgr = z_nrf_clock_control_get_onoff(subsys);
__ASSERT_NO_MSG(drv_data->clk_mgr != NULL); __ASSERT_NO_MSG(drv_data->clk_mgr != NULL);
} }
#endif
static const struct _dmic_ops dmic_ops = { static const struct _dmic_ops dmic_ops = {
.configure = dmic_nrfx_pdm_configure, .configure = dmic_nrfx_pdm_configure,
@ -609,7 +649,8 @@ static const struct _dmic_ops dmic_ops = {
k_msgq_init(&dmic_nrfx_pdm_data##idx.rx_queue, \ k_msgq_init(&dmic_nrfx_pdm_data##idx.rx_queue, \
(char *)rx_msgs##idx, sizeof(void *), \ (char *)rx_msgs##idx, sizeof(void *), \
ARRAY_SIZE(rx_msgs##idx)); \ ARRAY_SIZE(rx_msgs##idx)); \
init_clock_manager(dev); \ IF_ENABLED(CONFIG_CLOCK_CONTROL_NRF, \
(init_clock_manager(dev);)) \
return 0; \ return 0; \
} \ } \
static void event_handler##idx(const nrfx_pdm_evt_t *evt) \ static void event_handler##idx(const nrfx_pdm_evt_t *evt) \
@ -624,6 +665,7 @@ static const struct _dmic_ops dmic_ops = {
.nrfx_def_cfg.skip_psel_cfg = true, \ .nrfx_def_cfg.skip_psel_cfg = true, \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(PDM(idx)), \ .pcfg = PINCTRL_DT_DEV_CONFIG_GET(PDM(idx)), \
.clk_src = PDM_CLK_SRC(idx), \ .clk_src = PDM_CLK_SRC(idx), \
.mem_reg = DMM_DEV_TO_REG(PDM(idx)), \
}; \ }; \
BUILD_ASSERT(PDM_CLK_SRC(idx) != ACLK || NRF_PDM_HAS_MCLKCONFIG, \ BUILD_ASSERT(PDM_CLK_SRC(idx) != ACLK || NRF_PDM_HAS_MCLKCONFIG, \
"Clock source ACLK is not available."); \ "Clock source ACLK is not available."); \