drivers: audio: dmic_nrfx: add support for audio clocks on nRF54
Added support for audio clock for nRF54L20 and AudioPLL for nRF54H20 in DMIC PDM driver. Signed-off-by: Michał Stasiak <michal.stasiak@nordicsemi.no>
This commit is contained in:
parent
894cbed016
commit
16b9f60432
1 changed files with 77 additions and 37 deletions
|
@ -18,14 +18,21 @@ LOG_MODULE_REGISTER(dmic_nrfx_pdm, CONFIG_AUDIO_DMIC_LOG_LEVEL);
|
|||
#if CONFIG_SOC_SERIES_NRF54HX
|
||||
#define DMIC_NRFX_CLOCK_FREQ MHZ(16)
|
||||
#define DMIC_NRFX_CLOCK_FACTOR 8192
|
||||
#define DMIC_NRFX_AUDIO_CLOCK_FREQ DT_PROP_OR(DT_NODELABEL(audiopll), frequency, 0)
|
||||
#else
|
||||
#define DMIC_NRFX_CLOCK_FREQ MHZ(32)
|
||||
#define DMIC_NRFX_CLOCK_FACTOR 4096
|
||||
#define DMIC_NRFX_AUDIO_CLOCK_FREQ DT_PROP_OR(DT_NODELABEL(aclk), clock_frequency, \
|
||||
DT_PROP_OR(DT_NODELABEL(clock), hfclkaudio_frequency, 0))
|
||||
#endif
|
||||
|
||||
struct dmic_nrfx_pdm_drv_data {
|
||||
const nrfx_pdm_t *pdm;
|
||||
#if CONFIG_CLOCK_CONTROL_NRF
|
||||
struct onoff_manager *clk_mgr;
|
||||
#elif CONFIG_CLOCK_CONTROL_NRF2_AUDIOPLL
|
||||
const struct device *audiopll_dev;
|
||||
#endif
|
||||
struct onoff_client clk_cli;
|
||||
struct k_mem_slab *mem_slab;
|
||||
uint32_t block_size;
|
||||
|
@ -61,6 +68,35 @@ static void stop_pdm(struct dmic_nrfx_pdm_drv_data *drv_data)
|
|||
nrfx_pdm_stop(drv_data->pdm);
|
||||
}
|
||||
|
||||
static int request_clock(struct dmic_nrfx_pdm_drv_data *drv_data)
|
||||
{
|
||||
if (!drv_data->request_clock) {
|
||||
return 0;
|
||||
}
|
||||
#if CONFIG_CLOCK_CONTROL_NRF
|
||||
return onoff_request(drv_data->clk_mgr, &drv_data->clk_cli);
|
||||
#elif CONFIG_CLOCK_CONTROL_NRF2_AUDIOPLL
|
||||
return nrf_clock_control_request(drv_data->audiopll_dev, NULL, &drv_data->clk_cli);
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int release_clock(struct dmic_nrfx_pdm_drv_data *drv_data)
|
||||
{
|
||||
if (!drv_data->request_clock) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_CLOCK_CONTROL_NRF
|
||||
return onoff_release(drv_data->clk_mgr);
|
||||
#elif CONFIG_CLOCK_CONTROL_NRF2_AUDIOPLL
|
||||
return nrf_clock_control_release(drv_data->audiopll_dev, NULL);
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
|
||||
{
|
||||
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
|
||||
|
@ -119,8 +155,10 @@ static void event_handler(const struct device *dev, const nrfx_pdm_evt_t *evt)
|
|||
|
||||
if (drv_data->active) {
|
||||
drv_data->active = false;
|
||||
if (drv_data->request_clock) {
|
||||
(void)onoff_release(drv_data->clk_mgr);
|
||||
ret = release_clock(drv_data);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to release clock: %d", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (evt->buffer_released) {
|
||||
|
@ -191,9 +229,11 @@ static bool check_pdm_frequencies(const struct dmic_nrfx_pdm_drv_cfg *drv_cfg,
|
|||
{
|
||||
uint32_t req_rate = pdm_cfg->streams[0].pcm_rate;
|
||||
bool better_found = false;
|
||||
|
||||
const uint32_t src_freq =
|
||||
(NRF_PDM_HAS_SELECTABLE_CLOCK && drv_cfg->clk_src == ACLK)
|
||||
? DMIC_NRFX_AUDIO_CLOCK_FREQ
|
||||
: DMIC_NRFX_CLOCK_FREQ;
|
||||
#if NRF_PDM_HAS_PRESCALER
|
||||
uint32_t src_freq = 32 * 1000 * 1000UL;
|
||||
uint32_t req_freq = req_rate * ratio;
|
||||
uint32_t prescaler = src_freq / req_freq;
|
||||
uint32_t act_freq = src_freq / prescaler;
|
||||
|
@ -224,24 +264,6 @@ static bool check_pdm_frequencies(const struct dmic_nrfx_pdm_drv_cfg *drv_cfg,
|
|||
}
|
||||
#else
|
||||
if (IS_ENABLED(CONFIG_SOC_SERIES_NRF53X) || IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX)) {
|
||||
const uint32_t src_freq =
|
||||
(NRF_PDM_HAS_MCLKCONFIG && drv_cfg->clk_src == ACLK)
|
||||
/* The DMIC_NRFX_PDM_DEVICE() macro contains build
|
||||
* assertions that make sure that the ACLK clock
|
||||
* source is only used when it is available and only
|
||||
* with the "hfclkaudio-frequency" property defined,
|
||||
* but the default value of 0 here needs to be used
|
||||
* to prevent compilation errors when the property is
|
||||
* not defined (this expression will be eventually
|
||||
* 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,
|
||||
0)
|
||||
: DMIC_NRFX_CLOCK_FREQ;
|
||||
uint32_t req_freq = req_rate * ratio;
|
||||
/* As specified in the nRF5340 PS:
|
||||
*
|
||||
|
@ -461,7 +483,7 @@ static int dmic_nrfx_pdm_configure(const struct device *dev,
|
|||
nrfx_cfg.edge = NRF_PDM_EDGE_LEFTRISING;
|
||||
channel->act_chan_map_lo = alt_map;
|
||||
}
|
||||
#if NRF_PDM_HAS_MCLKCONFIG
|
||||
#if NRF_PDM_HAS_SELECTABLE_CLOCK
|
||||
nrfx_cfg.mclksrc = drv_cfg->clk_src == ACLK
|
||||
? NRF_PDM_MCLKSRC_ACLK
|
||||
: NRF_PDM_MCLKSRC_PCLK32M;
|
||||
|
@ -489,8 +511,10 @@ static int dmic_nrfx_pdm_configure(const struct device *dev,
|
|||
* (which is always available without any additional actions),
|
||||
* it is required to request the proper clock to be running
|
||||
* before starting the transfer itself.
|
||||
* Targets using CLKSELECT register to select clock source
|
||||
* do not need to request audio clock.
|
||||
*/
|
||||
drv_data->request_clock = (drv_cfg->clk_src != PCLK32M);
|
||||
drv_data->request_clock = (drv_cfg->clk_src != PCLK32M && !NRF_PDM_HAS_CLKSELECT);
|
||||
drv_data->configured = true;
|
||||
return 0;
|
||||
}
|
||||
|
@ -508,8 +532,10 @@ static int start_transfer(struct dmic_nrfx_pdm_drv_data *drv_data)
|
|||
LOG_ERR("Failed to start PDM: 0x%08x", err);
|
||||
ret = -EIO;
|
||||
|
||||
if (drv_data->request_clock) {
|
||||
(void)onoff_release(drv_data->clk_mgr);
|
||||
ret = release_clock(drv_data);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to release clock: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
drv_data->active = false;
|
||||
|
@ -529,7 +555,12 @@ static void clock_started_callback(struct onoff_manager *mgr,
|
|||
* the actual transfer in such case.
|
||||
*/
|
||||
if (!drv_data->active) {
|
||||
(void)onoff_release(drv_data->clk_mgr);
|
||||
int ret = release_clock(drv_data);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Failed to release clock: %d", ret);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
(void)start_transfer(drv_data);
|
||||
}
|
||||
|
@ -548,7 +579,7 @@ static int trigger_start(const struct device *dev)
|
|||
if (drv_data->request_clock) {
|
||||
sys_notify_init_callback(&drv_data->clk_cli.notify,
|
||||
clock_started_callback);
|
||||
ret = onoff_request(drv_data->clk_mgr, &drv_data->clk_cli);
|
||||
ret = request_clock(drv_data);
|
||||
if (ret < 0) {
|
||||
drv_data->active = false;
|
||||
|
||||
|
@ -624,12 +655,11 @@ static int dmic_nrfx_pdm_read(const struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_CLOCK_CONTROL_NRF
|
||||
static void init_clock_manager(const struct device *dev)
|
||||
{
|
||||
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
|
||||
#if CONFIG_CLOCK_CONTROL_NRF
|
||||
clock_control_subsys_t subsys;
|
||||
|
||||
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
|
||||
#if NRF_CLOCK_HAS_HFCLKAUDIO
|
||||
const struct dmic_nrfx_pdm_drv_cfg *drv_cfg = dev->config;
|
||||
|
||||
|
@ -643,8 +673,12 @@ static void init_clock_manager(const struct device *dev)
|
|||
|
||||
drv_data->clk_mgr = z_nrf_clock_control_get_onoff(subsys);
|
||||
__ASSERT_NO_MSG(drv_data->clk_mgr != NULL);
|
||||
}
|
||||
#elif CONFIG_CLOCK_CONTROL_NRF2_AUDIOPLL
|
||||
struct dmic_nrfx_pdm_drv_data *drv_data = dev->data;
|
||||
|
||||
drv_data->audiopll_dev = DEVICE_DT_GET(DT_NODELABEL(audiopll));
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct _dmic_ops dmic_ops = {
|
||||
.configure = dmic_nrfx_pdm_configure,
|
||||
|
@ -677,8 +711,7 @@ static const struct _dmic_ops dmic_ops = {
|
|||
k_msgq_init(&dmic_nrfx_pdm_data##idx.mem_slab_queue, \
|
||||
(char *)mem_slab_msgs##idx, sizeof(void *), \
|
||||
ARRAY_SIZE(mem_slab_msgs##idx)); \
|
||||
IF_ENABLED(CONFIG_CLOCK_CONTROL_NRF, \
|
||||
(init_clock_manager(dev);)) \
|
||||
init_clock_manager(dev); \
|
||||
return 0; \
|
||||
} \
|
||||
static void event_handler##idx(const nrfx_pdm_evt_t *evt) \
|
||||
|
@ -695,13 +728,20 @@ static const struct _dmic_ops dmic_ops = {
|
|||
.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_SELECTABLE_CLOCK, \
|
||||
"Clock source ACLK is not available."); \
|
||||
BUILD_ASSERT(PDM_CLK_SRC(idx) != ACLK || \
|
||||
DT_NODE_HAS_PROP(DT_NODELABEL(clock), \
|
||||
hfclkaudio_frequency), \
|
||||
hfclkaudio_frequency) || \
|
||||
DT_NODE_HAS_PROP(DT_NODELABEL(aclk), \
|
||||
clock_frequency) || \
|
||||
DT_NODE_HAS_PROP(DT_NODELABEL(audiopll), \
|
||||
frequency), \
|
||||
"Clock source ACLK requires the hfclkaudio-frequency " \
|
||||
"property to be defined in the nordic,nrf-clock node."); \
|
||||
"property to be defined in the nordic,nrf-clock node " \
|
||||
"or clock-frequency property to be defined in aclk node" \
|
||||
"or frequency property to be defined in audiopll node"); \
|
||||
DEVICE_DT_DEFINE(PDM(idx), pdm_nrfx_init##idx, NULL, \
|
||||
&dmic_nrfx_pdm_data##idx, &dmic_nrfx_pdm_cfg##idx, \
|
||||
POST_KERNEL, CONFIG_AUDIO_DMIC_INIT_PRIORITY, \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue