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:
parent
ab001888d3
commit
ff405eaebd
1 changed files with 56 additions and 14 deletions
|
|
@ -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."); \
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue