diff --git a/drivers/audio/dmic_mcux.c b/drivers/audio/dmic_mcux.c index 751ecaba04a69..1b7b1e05af5c3 100644 --- a/drivers/audio/dmic_mcux.c +++ b/drivers/audio/dmic_mcux.c @@ -61,21 +61,18 @@ static int dmic_mcux_get_osr(uint32_t pcm_rate, uint32_t bit_clk, bool use_2fs) } /* Gets hardware channel index from logical channel */ -static uint8_t dmic_mcux_hw_chan(struct mcux_dmic_drv_data *drv_data, - uint8_t log_chan) +static uint8_t dmic_mcux_hw_chan(struct mcux_dmic_drv_data *drv_data, uint8_t log_chan) { enum pdm_lr lr; uint8_t hw_chan; - dmic_parse_channel_map(drv_data->chan_map_lo, - drv_data->chan_map_hi, - log_chan, &hw_chan, &lr); + dmic_parse_channel_map(drv_data->chan_map_lo, drv_data->chan_map_hi, log_chan, &hw_chan, + &lr); return hw_chan; } -static void dmic_mcux_activate_channels(struct mcux_dmic_drv_data *drv_data, - bool enable) +static void dmic_mcux_activate_channels(struct mcux_dmic_drv_data *drv_data, bool enable) { /* PDM channel 0 must always be enabled, as the RM states: @@ -112,27 +109,23 @@ static int dmic_mcux_enable_dma(struct mcux_dmic_drv_data *drv_data, bool enable if (enable) { ret = dma_start(pdm_channel->dma, pdm_channel->dma_chan); if (ret < 0) { - LOG_ERR("Could not start DMA for HW channel %d", - hw_chan); + LOG_ERR("Could not start DMA for HW channel %d", hw_chan); return ret; } } else { if (dma_stop(pdm_channel->dma, pdm_channel->dma_chan)) { - LOG_ERR("Error stopping DMA for HW channel %d", - hw_chan); + LOG_ERR("Error stopping DMA for HW channel %d", hw_chan); ret = -EIO; } } - DMIC_EnableChannelDma(drv_data->base_address, - (dmic_channel_t)hw_chan, enable); + DMIC_EnableChannelDma(drv_data->base_address, (dmic_channel_t)hw_chan, enable); } return ret; } /* Helper to reload DMA engine for all active channels with new buffer */ -static void dmic_mcux_reload_dma(struct mcux_dmic_drv_data *drv_data, - void *buffer) +static void dmic_mcux_reload_dma(struct mcux_dmic_drv_data *drv_data, void *buffer) { int ret; uint8_t hw_chan; @@ -151,8 +144,7 @@ static void dmic_mcux_reload_dma(struct mcux_dmic_drv_data *drv_data, pdm_channel = drv_data->pdm_channels[hw_chan]; src = DMIC_FifoGetAddress(drv_data->base_address, hw_chan); dst = (uint32_t)(((uint16_t *)buffer) + chan); - ret = dma_reload(pdm_channel->dma, pdm_channel->dma_chan, - src, dst, dma_buf_size); + ret = dma_reload(pdm_channel->dma, pdm_channel->dma_chan, src, dst, dma_buf_size); if (ret < 0) { LOG_ERR("Could not reload DMIC HW channel %d", hw_chan); return; @@ -192,8 +184,8 @@ static int dmic_mcux_stop(struct mcux_dmic_drv_data *drv_data) return 0; } -static void dmic_mcux_dma_cb(const struct device *dev, void *user_data, - uint32_t channel, int status) +static void dmic_mcux_dma_cb(const struct device *dev, void *user_data, uint32_t channel, + int status) { struct mcux_dmic_drv_data *drv_data = (struct mcux_dmic_drv_data *)user_data; @@ -230,8 +222,7 @@ static void dmic_mcux_dma_cb(const struct device *dev, void *user_data, /* Reload DMA */ dmic_mcux_reload_dma(drv_data, done_buffer); /* Advance active buffer index */ - drv_data->active_buf_idx = - dmic_mcux_next_buf_idx(drv_data->active_buf_idx); + drv_data->active_buf_idx = dmic_mcux_next_buf_idx(drv_data->active_buf_idx); return; } @@ -253,8 +244,7 @@ static void dmic_mcux_dma_cb(const struct device *dev, void *user_data, /* Reload DMA */ dmic_mcux_reload_dma(drv_data, done_buffer); /* Advance active buffer index */ - drv_data->active_buf_idx = - dmic_mcux_next_buf_idx(drv_data->active_buf_idx); + drv_data->active_buf_idx = dmic_mcux_next_buf_idx(drv_data->active_buf_idx); return; } @@ -314,10 +304,8 @@ static int dmic_mcux_setup_dma(const struct device *dev) * pdm0_l_s1, pdm0_r_s1, pdm1_r_s1, pdm1_l_s1, ...] * Each sample is 16 bits wide. */ - blk_cfg[blk].dest_address = - (uint32_t)(((uint16_t *)dma_buf) + chan); - blk_cfg[blk].dest_scatter_interval = - num_chan * sizeof(uint16_t); + blk_cfg[blk].dest_address = (uint32_t)(((uint16_t *)dma_buf) + chan); + blk_cfg[blk].dest_scatter_interval = num_chan * sizeof(uint16_t); blk_cfg[blk].dest_scatter_en = 1; blk_cfg[blk].source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; blk_cfg[blk].dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; @@ -355,8 +343,8 @@ static int dmic_mcux_setup_dma(const struct device *dev) } /* Initializes a DMIC hardware channel */ -static int dmic_mcux_init_channel(const struct device *dev, uint32_t osr, - uint8_t chan, enum pdm_lr lr) +static int dmic_mcux_init_channel(const struct device *dev, uint32_t osr, uint8_t chan, + enum pdm_lr lr) { struct mcux_dmic_drv_data *drv_data = dev->data; @@ -399,8 +387,7 @@ static int mcux_dmic_init(const struct device *dev) return 0; } -static int dmic_mcux_configure(const struct device *dev, - struct dmic_cfg *config) +static int dmic_mcux_configure(const struct device *dev, struct dmic_cfg *config) { const struct mcux_dmic_cfg *drv_config = dev->config; @@ -460,8 +447,7 @@ static int dmic_mcux_configure(const struct device *dev, return -ENOTSUP; } - ret = clock_control_get_rate(drv_config->clock_dev, - drv_config->clock_name, &bit_clk_rate); + ret = clock_control_get_rate(drv_config->clock_dev, drv_config->clock_name, &bit_clk_rate); if (ret < 0) { return ret; } @@ -486,24 +472,19 @@ static int dmic_mcux_configure(const struct device *dev, drv_data->chan_map_hi = channel->req_chan_map_hi; for (uint8_t chan = 0; chan < channel->req_num_chan; chan += 2) { /* Get the channel map data for channel pair */ - dmic_parse_channel_map(channel->req_chan_map_lo, - channel->req_chan_map_hi, - chan, &hw_chan_0, &lr_0); + dmic_parse_channel_map(channel->req_chan_map_lo, channel->req_chan_map_hi, chan, + &hw_chan_0, &lr_0); if ((chan + 1) < channel->req_num_chan) { /* Paired channel is enabled */ - dmic_parse_channel_map(channel->req_chan_map_lo, - channel->req_chan_map_hi, + dmic_parse_channel_map(channel->req_chan_map_lo, channel->req_chan_map_hi, chan + 1, &hw_chan_1, &lr_1); /* Verify that paired channels use consecutive hardware index */ - if ((lr_0 == lr_1) || - (hw_chan_1 != (hw_chan_0 + 1))) { + if ((lr_0 == lr_1) || (hw_chan_1 != (hw_chan_0 + 1))) { return -EINVAL; } } /* Configure selected channels in DMIC */ - ret = dmic_mcux_init_channel(dev, osr, - dmic_mcux_hw_chan(drv_data, chan), - lr_0); + ret = dmic_mcux_init_channel(dev, osr, dmic_mcux_hw_chan(drv_data, chan), lr_0); if (ret < 0) { return ret; } @@ -511,9 +492,7 @@ static int dmic_mcux_configure(const struct device *dev, if ((chan + 1) < channel->req_num_chan) { /* Paired channel is enabled */ ret = dmic_mcux_init_channel(dev, osr, - dmic_mcux_hw_chan(drv_data, - chan + 1), - lr_1); + dmic_mcux_hw_chan(drv_data, chan + 1), lr_1); if (ret < 0) { return ret; } @@ -524,8 +503,8 @@ static int dmic_mcux_configure(const struct device *dev, channel->act_chan_map_lo = channel->req_chan_map_lo; channel->act_chan_map_hi = channel->req_chan_map_hi; - drv_data->mem_slab = stream->mem_slab; - drv_data->block_size = stream->block_size; + drv_data->mem_slab = stream->mem_slab; + drv_data->block_size = stream->block_size; drv_data->act_num_chan = channel->act_num_chan; drv_data->dmic_state = DMIC_STATE_CONFIGURED; @@ -547,8 +526,7 @@ static int dmic_mcux_start(const struct device *dev) for (uint32_t i = 0; i < CONFIG_DMIC_MCUX_DMA_BUFFERS; i++) { /* Allocate buffers for DMA */ - ret = k_mem_slab_alloc(drv_data->mem_slab, - &drv_data->dma_bufs[i], K_NO_WAIT); + ret = k_mem_slab_alloc(drv_data->mem_slab, &drv_data->dma_bufs[i], K_NO_WAIT); if (ret < 0) { LOG_ERR("failed to allocate buffer"); return -ENOBUFS; @@ -569,8 +547,7 @@ static int dmic_mcux_start(const struct device *dev) return 0; } -static int dmic_mcux_trigger(const struct device *dev, - enum dmic_trigger cmd) +static int dmic_mcux_trigger(const struct device *dev, enum dmic_trigger cmd) { struct mcux_dmic_drv_data *drv_data = dev->data; @@ -620,9 +597,8 @@ static int dmic_mcux_trigger(const struct device *dev, return 0; } -static int dmic_mcux_read(const struct device *dev, - uint8_t stream, - void **buffer, size_t *size, int32_t timeout) +static int dmic_mcux_read(const struct device *dev, uint8_t stream, void **buffer, size_t *size, + int32_t timeout) { struct mcux_dmic_drv_data *drv_data = dev->data; int ret; @@ -651,76 +627,87 @@ static int dmic_mcux_read(const struct device *dev, return 0; } +static int dmic_mcux_get_caps(const struct device *dev, struct audio_caps *caps) +{ + memset(caps, 0, sizeof(struct audio_caps)); + + caps->min_total_channels = 1; + caps->max_total_channels = FSL_FEATURE_DMIC_CHANNEL_NUM; + caps->supported_sample_rates = AUDIO_SAMPLE_RATE_16000 | AUDIO_SAMPLE_RATE_48000; + /* Currently, driver supports only 16-bit samples */ + caps->supported_bit_widths = AUDIO_BIT_WIDTH_16; + caps->min_num_buffers = CONFIG_DMIC_MCUX_DMA_BUFFERS; + /* Workaround: Set to 10ms because MediaPipe library does not have a cap filter implemented + * yet */ + caps->min_frame_interval = 10000; /* 10ms minimum */ + caps->max_frame_interval = 100000; /* 100ms maximum */ + caps->interleaved = true; + + return 0; +} + static const struct _dmic_ops dmic_ops = { .configure = dmic_mcux_configure, .trigger = dmic_mcux_trigger, .read = dmic_mcux_read, + .get_caps = dmic_mcux_get_caps, }; /* Converts integer gainshift into 5 bit 2's complement value for GAINSHIFT reg */ -#define PDM_DMIC_GAINSHIFT(val) \ - (val >= 0) ? (val & 0xF) : (BIT(4) | (0x10 - (val & 0xF))) +#define PDM_DMIC_GAINSHIFT(val) (val >= 0) ? (val & 0xF) : (BIT(4) | (0x10 - (val & 0xF))) /* Defines structure for a given PDM channel node */ -#define PDM_DMIC_CHAN_DEFINE(pdm_node) \ - static struct mcux_dmic_pdm_chan \ - pdm_channel_##pdm_node = { \ - .dma = DEVICE_DT_GET(DT_DMAS_CTLR(pdm_node)), \ - .dma_chan = DT_DMAS_CELL_BY_IDX(pdm_node, 0, channel), \ - .dmic_channel_cfg = { \ - .gainshft = PDM_DMIC_GAINSHIFT(DT_PROP(pdm_node, \ - gainshift)), \ - .preac2coef = DT_ENUM_IDX(pdm_node, compensation_2fs), \ - .preac4coef = DT_ENUM_IDX(pdm_node, compensation_4fs), \ - .dc_cut_level = DT_ENUM_IDX(pdm_node, dc_cutoff), \ - .post_dc_gain_reduce = DT_PROP(pdm_node, dc_gain), \ - .sample_rate = kDMIC_PhyFullSpeed, \ - .saturate16bit = 1U, \ - }, \ +#define PDM_DMIC_CHAN_DEFINE(pdm_node) \ + static struct mcux_dmic_pdm_chan pdm_channel_##pdm_node = { \ + .dma = DEVICE_DT_GET(DT_DMAS_CTLR(pdm_node)), \ + .dma_chan = DT_DMAS_CELL_BY_IDX(pdm_node, 0, channel), \ + .dmic_channel_cfg = \ + { \ + .gainshft = PDM_DMIC_GAINSHIFT(DT_PROP(pdm_node, gainshift)), \ + .preac2coef = DT_ENUM_IDX(pdm_node, compensation_2fs), \ + .preac4coef = DT_ENUM_IDX(pdm_node, compensation_4fs), \ + .dc_cut_level = DT_ENUM_IDX(pdm_node, dc_cutoff), \ + .post_dc_gain_reduce = DT_PROP(pdm_node, dc_gain), \ + .sample_rate = kDMIC_PhyFullSpeed, \ + .saturate16bit = 1U, \ + }, \ }; /* Defines structures for all enabled PDM channels */ -#define PDM_DMIC_CHANNELS_DEFINE(idx) \ - DT_INST_FOREACH_CHILD_STATUS_OKAY(idx, PDM_DMIC_CHAN_DEFINE) +#define PDM_DMIC_CHANNELS_DEFINE(idx) DT_INST_FOREACH_CHILD_STATUS_OKAY(idx, PDM_DMIC_CHAN_DEFINE) /* Gets pointer for a given PDM channel node */ -#define PDM_DMIC_CHAN_GET(pdm_node) \ +#define PDM_DMIC_CHAN_GET(pdm_node) \ COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(pdm_node), \ (&pdm_channel_##pdm_node), (NULL)), /* Gets array of pointers to PDM channels */ -#define PDM_DMIC_CHANNELS_GET(idx) \ - DT_INST_FOREACH_CHILD(idx, PDM_DMIC_CHAN_GET) - -#define MCUX_DMIC_DEVICE(idx) \ - PDM_DMIC_CHANNELS_DEFINE(idx); \ - static struct mcux_dmic_pdm_chan \ - *pdm_channels##idx[FSL_FEATURE_DMIC_CHANNEL_NUM] = { \ - PDM_DMIC_CHANNELS_GET(idx) \ - }; \ - K_MSGQ_DEFINE(dmic_msgq##idx, sizeof(void *), \ - CONFIG_DMIC_MCUX_QUEUE_SIZE, 1); \ - static struct mcux_dmic_drv_data mcux_dmic_data##idx = { \ - .pdm_channels = pdm_channels##idx, \ - .base_address = (DMIC_Type *) DT_INST_REG_ADDR(idx), \ - .dmic_state = DMIC_STATE_UNINIT, \ - .rx_queue = &dmic_msgq##idx, \ - .active_buf_idx = 0U, \ - }; \ - \ - PINCTRL_DT_INST_DEFINE(idx); \ - static struct mcux_dmic_cfg mcux_dmic_cfg##idx = { \ - .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ - .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ - .clock_name = (clock_control_subsys_t) \ - DT_INST_CLOCKS_CELL(idx, name), \ - .use2fs = DT_INST_PROP(idx, use2fs), \ - }; \ - \ - DEVICE_DT_INST_DEFINE(idx, mcux_dmic_init, NULL, \ - &mcux_dmic_data##idx, &mcux_dmic_cfg##idx, \ - POST_KERNEL, CONFIG_AUDIO_DMIC_INIT_PRIORITY, \ - &dmic_ops); +#define PDM_DMIC_CHANNELS_GET(idx) DT_INST_FOREACH_CHILD(idx, PDM_DMIC_CHAN_GET) + +#define MCUX_DMIC_DEVICE(idx) \ + PDM_DMIC_CHANNELS_DEFINE(idx); \ + static struct mcux_dmic_pdm_chan *pdm_channels##idx[FSL_FEATURE_DMIC_CHANNEL_NUM] = { \ + PDM_DMIC_CHANNELS_GET(idx)}; \ + K_MSGQ_DEFINE(dmic_msgq##idx, sizeof(void *), CONFIG_DMIC_MCUX_QUEUE_SIZE, 1); \ + static struct mcux_dmic_drv_data mcux_dmic_data##idx = { \ + .pdm_channels = pdm_channels##idx, \ + .base_address = (DMIC_Type *)DT_INST_REG_ADDR(idx), \ + .dmic_state = DMIC_STATE_UNINIT, \ + .rx_queue = &dmic_msgq##idx, \ + .active_buf_idx = 0U, \ + }; \ + \ + PINCTRL_DT_INST_DEFINE(idx); \ + static struct mcux_dmic_cfg mcux_dmic_cfg##idx = { \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \ + .clock_name = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(idx, name), \ + .use2fs = DT_INST_PROP(idx, use2fs), \ + }; \ + \ + DEVICE_DT_INST_DEFINE(idx, mcux_dmic_init, NULL, &mcux_dmic_data##idx, \ + &mcux_dmic_cfg##idx, POST_KERNEL, CONFIG_AUDIO_DMIC_INIT_PRIORITY, \ + &dmic_ops); /* Existing SoCs only have one PDM instance. */ DT_INST_FOREACH_STATUS_OKAY(MCUX_DMIC_DEVICE) diff --git a/drivers/audio/wm8904.c b/drivers/audio/wm8904.c index 075ff29de6105..84eb30bf6f96d 100644 --- a/drivers/audio/wm8904.c +++ b/drivers/audio/wm8904.c @@ -180,12 +180,8 @@ static int wm8904_audio_fmt_config(const struct device *dev, audio_dai_cfg_t *cf return 0; } -static int wm8904_out_update( - const struct device *dev, - audio_channel_t channel, - uint16_t val, - uint16_t mask -) +static int wm8904_out_update(const struct device *dev, audio_channel_t channel, uint16_t val, + uint16_t mask) { switch (channel) { case AUDIO_CHANNEL_FRONT_LEFT: @@ -220,9 +216,8 @@ static int wm8904_out_volume_config(const struct device *dev, audio_channel_t ch { /* Set volume values with VU = 0 */ const uint16_t val = WM8904_REGVAL_OUT_VOL(0, 0, 1, volume); - const uint16_t mask = WM8904_REGMASK_OUT_VU - | WM8904_REGMASK_OUT_ZC - | WM8904_REGMASK_OUT_VOL; + const uint16_t mask = + WM8904_REGMASK_OUT_VU | WM8904_REGMASK_OUT_ZC | WM8904_REGMASK_OUT_VOL; return wm8904_out_update(dev, channel, val, mask); } @@ -235,12 +230,8 @@ static int wm8904_out_mute_config(const struct device *dev, audio_channel_t chan return wm8904_out_update(dev, channel, val, mask); } -static int wm8904_in_update( - const struct device *dev, - audio_channel_t channel, - uint16_t mask, - uint16_t val -) +static int wm8904_in_update(const struct device *dev, audio_channel_t channel, uint16_t mask, + uint16_t val) { switch (channel) { case AUDIO_CHANNEL_FRONT_LEFT: @@ -284,10 +275,8 @@ static int wm8904_route_input(const struct device *dev, audio_channel_t channel, } uint8_t val = WM8904_REGVAL_INSEL(0, input - 1, input - 1, 0); - uint8_t mask = WM8904_REGMASK_INSEL_CMENA - | WM8904_REGMASK_INSEL_IP_SEL_P - | WM8904_REGMASK_INSEL_IP_SEL_N - | WM8904_REGMASK_INSEL_MODE; + uint8_t mask = WM8904_REGMASK_INSEL_CMENA | WM8904_REGMASK_INSEL_IP_SEL_P | + WM8904_REGMASK_INSEL_IP_SEL_N | WM8904_REGMASK_INSEL_MODE; uint8_t reg; switch (channel) { @@ -567,17 +556,10 @@ static int wm8904_apply_properties(const struct device *dev) * Set VU = 1 for all output channels, VU takes effect for the whole * channel pair. */ - wm8904_update_reg( - dev, - WM8904_REG_ANALOG_OUT1_LEFT, - WM8904_REGVAL_OUT_VOL(0, 1, 0, 0), - WM8904_REGMASK_OUT_MUTE - ); - wm8904_update_reg(dev, - WM8904_REG_ANALOG_OUT2_LEFT, - WM8904_REGVAL_OUT_VOL(0, 1, 0, 0), - WM8904_REGMASK_OUT_MUTE - ); + wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT1_LEFT, WM8904_REGVAL_OUT_VOL(0, 1, 0, 0), + WM8904_REGMASK_OUT_MUTE); + wm8904_update_reg(dev, WM8904_REG_ANALOG_OUT2_LEFT, WM8904_REGVAL_OUT_VOL(0, 1, 0, 0), + WM8904_REGMASK_OUT_MUTE); return 0; } @@ -659,23 +641,44 @@ static void wm8904_configure_input(const struct device *dev) wm8904_in_mute_config(dev, AUDIO_CHANNEL_ALL, false); } +static int wm8904_get_caps(const struct device *dev, struct audio_caps *caps) +{ + memset(caps, 0, sizeof(struct audio_caps)); + + caps->min_total_channels = 1; + caps->max_total_channels = 2; + caps->supported_sample_rates = + AUDIO_SAMPLE_RATE_8000 | AUDIO_SAMPLE_RATE_11025 | AUDIO_SAMPLE_RATE_12000 | + AUDIO_SAMPLE_RATE_16000 | AUDIO_SAMPLE_RATE_22050 | AUDIO_SAMPLE_RATE_24000 | + AUDIO_SAMPLE_RATE_32000 | AUDIO_SAMPLE_RATE_44100 | AUDIO_SAMPLE_RATE_48000; + caps->supported_bit_widths = + AUDIO_BIT_WIDTH_16 | AUDIO_BIT_WIDTH_20 | AUDIO_BIT_WIDTH_24 | AUDIO_BIT_WIDTH_32; + caps->min_num_buffers = 1; + caps->min_frame_interval = 1000; /* 10ms minimum */ + caps->max_frame_interval = 100000; /* 100ms maximum */ + caps->interleaved = true; + + return 0; +} + static const struct audio_codec_api wm8904_driver_api = { .configure = wm8904_configure, .start_output = wm8904_start_output, .stop_output = wm8904_stop_output, .set_property = wm8904_set_property, .apply_properties = wm8904_apply_properties, - .route_input = wm8904_route_input + .route_input = wm8904_route_input, + .get_caps = wm8904_get_caps, }; #define WM8904_INIT(n) \ static const struct wm8904_driver_config wm8904_device_config_##n = { \ .i2c = I2C_DT_SPEC_INST_GET(n), \ - .clock_source = DT_INST_ENUM_IDX(n, clock_source), \ + .clock_source = DT_INST_ENUM_IDX(n, clock_source), \ .mclk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(n, mclk)), \ .mclk_name = (clock_control_subsys_t)DT_INST_CLOCKS_CELL_BY_NAME(n, mclk, name)}; \ \ - DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &wm8904_device_config_##n, \ - POST_KERNEL, CONFIG_AUDIO_CODEC_INIT_PRIORITY, &wm8904_driver_api); + DEVICE_DT_INST_DEFINE(n, NULL, NULL, NULL, &wm8904_device_config_##n, POST_KERNEL, \ + CONFIG_AUDIO_CODEC_INIT_PRIORITY, &wm8904_driver_api); DT_INST_FOREACH_STATUS_OKAY(WM8904_INIT) diff --git a/drivers/i2s/i2s_mcux_flexcomm.c b/drivers/i2s/i2s_mcux_flexcomm.c index c292eeb0650d3..69dc14ff5ab64 100644 --- a/drivers/i2s/i2s_mcux_flexcomm.c +++ b/drivers/i2s/i2s_mcux_flexcomm.c @@ -4,7 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ - #define DT_DRV_COMPAT nxp_lpc_i2s #include @@ -19,7 +18,7 @@ LOG_MODULE_REGISTER(i2s_mcux_flexcomm); -#define NUM_RX_DMA_BLOCKS 2 +#define NUM_RX_DMA_BLOCKS 2 /* Device constant configuration parameters */ struct i2s_mcux_config { @@ -64,10 +63,8 @@ struct i2s_mcux_data { struct dma_block_config tx_dma_block; }; -static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, - enum i2s_dir dir, - const struct i2s_config *i2s_cfg, - i2s_config_t *fsl_cfg) +static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, enum i2s_dir dir, + const struct i2s_config *i2s_cfg, i2s_config_t *fsl_cfg) { if (dir == I2S_DIR_RX) { I2S_RxGetDefaultConfig(fsl_cfg); @@ -76,8 +73,7 @@ static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, } fsl_cfg->dataLength = i2s_cfg->word_size; - if ((i2s_cfg->format & I2S_FMT_DATA_FORMAT_MASK) == - I2S_FMT_DATA_FORMAT_I2S) { + if ((i2s_cfg->format & I2S_FMT_DATA_FORMAT_MASK) == I2S_FMT_DATA_FORMAT_I2S) { /* Classic I2S. We always use 2 channels */ fsl_cfg->frameLength = 2 * i2s_cfg->word_size; } else { @@ -95,8 +91,7 @@ static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, } /* Set master/slave configuration */ - switch (i2s_cfg->options & (I2S_OPT_BIT_CLK_SLAVE | - I2S_OPT_FRAME_CLK_SLAVE)) { + switch (i2s_cfg->options & (I2S_OPT_BIT_CLK_SLAVE | I2S_OPT_FRAME_CLK_SLAVE)) { case I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER: fsl_cfg->masterSlave = kI2S_MasterSlaveNormalMaster; break; @@ -135,10 +130,8 @@ static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, } if (fsl_cfg->masterSlave == kI2S_MasterSlaveNormalMaster || - fsl_cfg->masterSlave == kI2S_MasterSlaveWsSyncMaster) { - fsl_cfg->divider = base_frequency / - i2s_cfg->frame_clk_freq / - fsl_cfg->frameLength; + fsl_cfg->masterSlave == kI2S_MasterSlaveWsSyncMaster) { + fsl_cfg->divider = base_frequency / i2s_cfg->frame_clk_freq / fsl_cfg->frameLength; } /* @@ -166,8 +159,7 @@ static int i2s_mcux_flexcomm_cfg_convert(uint32_t base_frequency, return 0; } -static const struct i2s_config *i2s_mcux_config_get(const struct device *dev, - enum i2s_dir dir) +static const struct i2s_config *i2s_mcux_config_get(const struct device *dev, enum i2s_dir dir) { struct i2s_mcux_data *dev_data = dev->data; struct stream *stream; @@ -206,8 +198,7 @@ static int i2s_mcux_configure(const struct device *dev, enum i2s_dir dir, return -EINVAL; } - if (stream->state != I2S_STATE_NOT_READY && - stream->state != I2S_STATE_READY) { + if (stream->state != I2S_STATE_NOT_READY && stream->state != I2S_STATE_READY) { LOG_ERR("invalid state"); return -EINVAL; } @@ -235,8 +226,7 @@ static int i2s_mcux_configure(const struct device *dev, enum i2s_dir dir, } /* Figure out function base clock */ - if (clock_control_get_rate(cfg->clock_dev, - cfg->clock_subsys, &base_frequency)) { + if (clock_control_get_rate(cfg->clock_dev, cfg->clock_subsys, &base_frequency)) { return -EINVAL; } @@ -244,8 +234,7 @@ static int i2s_mcux_configure(const struct device *dev, enum i2s_dir dir, * Validate the configuration by converting it to SDK * format. */ - result = i2s_mcux_flexcomm_cfg_convert(base_frequency, dir, i2s_cfg, - &fsl_cfg); + result = i2s_mcux_flexcomm_cfg_convert(base_frequency, dir, i2s_cfg, &fsl_cfg); if (result != 0) { return result; } @@ -258,14 +247,13 @@ static int i2s_mcux_configure(const struct device *dev, enum i2s_dir dir, } if ((i2s_cfg->channels > 2) && - (i2s_cfg->format & I2S_FMT_DATA_FORMAT_MASK) != - I2S_FMT_DATA_FORMAT_I2S) { + (i2s_cfg->format & I2S_FMT_DATA_FORMAT_MASK) != I2S_FMT_DATA_FORMAT_I2S) { /* * More than 2 channels are enabled, so we need to enable * secondary channel pairs. */ -#if (defined(FSL_FEATURE_I2S_SUPPORT_SECONDARY_CHANNEL) && \ - FSL_FEATURE_I2S_SUPPORT_SECONDARY_CHANNEL) +#if (defined(FSL_FEATURE_I2S_SUPPORT_SECONDARY_CHANNEL) && \ + FSL_FEATURE_I2S_SUPPORT_SECONDARY_CHANNEL) for (uint32_t slot = 1; slot < i2s_cfg->channels / 2; slot++) { /* Position must be set so that data does not overlap * with previous channel pair. Each channel pair @@ -311,8 +299,7 @@ static int i2s_mcux_configure(const struct device *dev, enum i2s_dir dir, return 0; } -static inline void i2s_purge_stream_buffers(struct stream *stream, - struct k_mem_slab *mem_slab, +static inline void i2s_purge_stream_buffers(struct stream *stream, struct k_mem_slab *mem_slab, bool tx) { void *buffer; @@ -401,8 +388,7 @@ static void i2s_mcux_rx_stream_disable(const struct device *dev, bool drop) } } -static void i2s_mcux_config_dma_blocks(const struct device *dev, - enum i2s_dir dir, uint32_t *buffer, +static void i2s_mcux_config_dma_blocks(const struct device *dev, enum i2s_dir dir, uint32_t *buffer, size_t block_size) { const struct i2s_mcux_config *cfg = dev->config; @@ -450,8 +436,7 @@ static void i2s_mcux_config_dma_blocks(const struct device *dev, LOG_DBG("dma_slot is %d", stream->dma_cfg.dma_slot); LOG_DBG("channel_direction is %d", stream->dma_cfg.channel_direction); - LOG_DBG("complete_callback_en is %d", - stream->dma_cfg.complete_callback_en); + LOG_DBG("complete_callback_en is %d", stream->dma_cfg.complete_callback_en); LOG_DBG("error_callback_dis is %d", stream->dma_cfg.error_callback_dis); LOG_DBG("source_handshake is %d", stream->dma_cfg.source_handshake); LOG_DBG("dest_handshake is %d", stream->dma_cfg.dest_handshake); @@ -467,8 +452,8 @@ static void i2s_mcux_config_dma_blocks(const struct device *dev, } /* This function is executed in the interrupt context */ -static void i2s_mcux_dma_tx_callback(const struct device *dma_dev, void *arg, - uint32_t channel, int status) +static void i2s_mcux_dma_tx_callback(const struct device *dma_dev, void *arg, uint32_t channel, + int status) { const struct device *dev = (const struct device *)arg; struct i2s_mcux_data *dev_data = dev->data; @@ -514,8 +499,8 @@ static void i2s_mcux_dma_tx_callback(const struct device *dma_dev, void *arg, * or * No buffers in input queue */ - LOG_DBG("DMA status %08x channel %u k_msgq_get ret %d", - status, channel, ret); + LOG_DBG("DMA status %08x channel %u k_msgq_get ret %d", status, channel, + ret); if (stream->state == I2S_STATE_STOPPING) { stream->state = I2S_STATE_READY; } else { @@ -530,8 +515,8 @@ static void i2s_mcux_dma_tx_callback(const struct device *dma_dev, void *arg, } } -static void i2s_mcux_dma_rx_callback(const struct device *dma_dev, void *arg, - uint32_t channel, int status) +static void i2s_mcux_dma_rx_callback(const struct device *dma_dev, void *arg, uint32_t channel, + int status) { const struct device *dev = (const struct device *)arg; struct i2s_mcux_data *dev_data = dev->data; @@ -557,8 +542,8 @@ static void i2s_mcux_dma_rx_callback(const struct device *dma_dev, void *arg, /* put buffer to output queue */ ret = k_msgq_put(&stream->out_queue, &buffer, K_NO_WAIT); if (ret != 0) { - LOG_ERR("buffer %p -> out_queue %p err %d", buffer, - &stream->out_queue, ret); + LOG_ERR("buffer %p -> out_queue %p err %d", buffer, &stream->out_queue, + ret); i2s_mcux_rx_stream_disable(dev, false); stream->state = I2S_STATE_ERROR; } @@ -566,8 +551,8 @@ static void i2s_mcux_dma_rx_callback(const struct device *dma_dev, void *arg, /* allocate new buffer for next audio frame */ ret = k_mem_slab_alloc(stream->cfg.mem_slab, &buffer, K_NO_WAIT); if (ret != 0) { - LOG_ERR("buffer alloc from slab %p err %d", - stream->cfg.mem_slab, ret); + LOG_ERR("buffer alloc from slab %p err %d", stream->cfg.mem_slab, + ret); i2s_mcux_rx_stream_disable(dev, false); stream->state = I2S_STATE_ERROR; } else { @@ -580,8 +565,8 @@ static void i2s_mcux_dma_rx_callback(const struct device *dma_dev, void *arg, /* put buffer in input queue */ ret = k_msgq_put(&stream->in_queue, &buffer, K_NO_WAIT); if (ret != 0) { - LOG_ERR("buffer %p -> in_queue %p err %d", - buffer, &stream->in_queue, ret); + LOG_ERR("buffer %p -> in_queue %p err %d", buffer, + &stream->in_queue, ret); } dma_start(stream->dev_dma, stream->channel); } @@ -613,8 +598,7 @@ static int i2s_mcux_tx_stream_start(const struct device *dev) return ret; } - i2s_mcux_config_dma_blocks(dev, I2S_DIR_TX, - (uint32_t *)queue_entry.mem_block, + i2s_mcux_config_dma_blocks(dev, I2S_DIR_TX, (uint32_t *)queue_entry.mem_block, queue_entry.size); /* put buffer in output queue */ @@ -660,16 +644,14 @@ static int i2s_mcux_rx_stream_start(const struct device *dev) } for (int i = 0; i < NUM_RX_DMA_BLOCKS; i++) { - ret = k_mem_slab_alloc(stream->cfg.mem_slab, &buffer[i], - K_NO_WAIT); + ret = k_mem_slab_alloc(stream->cfg.mem_slab, &buffer[i], K_NO_WAIT); if (ret != 0) { LOG_ERR("buffer alloc from mem_slab failed (%d)", ret); return ret; } } - i2s_mcux_config_dma_blocks(dev, I2S_DIR_RX, (uint32_t *)buffer, - stream->cfg.block_size); + i2s_mcux_config_dma_blocks(dev, I2S_DIR_RX, (uint32_t *)buffer, stream->cfg.block_size); /* put buffers in input queue */ for (int i = 0; i < NUM_RX_DMA_BLOCKS; i++) { @@ -695,8 +677,7 @@ static int i2s_mcux_rx_stream_start(const struct device *dev) return 0; } -static int i2s_mcux_trigger(const struct device *dev, enum i2s_dir dir, - enum i2s_trigger_cmd cmd) +static int i2s_mcux_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) { struct i2s_mcux_data *dev_data = dev->data; struct stream *stream; @@ -719,8 +700,7 @@ static int i2s_mcux_trigger(const struct device *dev, enum i2s_dir dir, switch (cmd) { case I2S_TRIGGER_START: if (stream->state != I2S_STATE_READY) { - LOG_ERR("START trigger: invalid state %d", - stream->state); + LOG_ERR("START trigger: invalid state %d", stream->state); ret = -EIO; break; } @@ -797,8 +777,7 @@ static int i2s_mcux_trigger(const struct device *dev, enum i2s_dir dir, return ret; } -static int i2s_mcux_read(const struct device *dev, void **mem_block, - size_t *size) +static int i2s_mcux_read(const struct device *dev, void **mem_block, size_t *size) { struct i2s_mcux_data *dev_data = dev->data; struct stream *stream = &dev_data->rx; @@ -810,8 +789,7 @@ static int i2s_mcux_read(const struct device *dev, void **mem_block, return -EIO; } - ret = k_msgq_get(&stream->out_queue, &buffer, - SYS_TIMEOUT_MS(stream->cfg.timeout)); + ret = k_msgq_get(&stream->out_queue, &buffer, SYS_TIMEOUT_MS(stream->cfg.timeout)); if (ret != 0) { if (stream->state == I2S_STATE_ERROR) { @@ -826,8 +804,7 @@ static int i2s_mcux_read(const struct device *dev, void **mem_block, return 0; } -static int i2s_mcux_write(const struct device *dev, void *mem_block, - size_t size) +static int i2s_mcux_write(const struct device *dev, void *mem_block, size_t size) { struct i2s_mcux_data *dev_data = dev->data; struct stream *stream = &dev_data->tx; @@ -837,14 +814,12 @@ static int i2s_mcux_write(const struct device *dev, void *mem_block, .size = size, }; - if (stream->state != I2S_STATE_RUNNING && - stream->state != I2S_STATE_READY) { + if (stream->state != I2S_STATE_RUNNING && stream->state != I2S_STATE_READY) { LOG_ERR("invalid state (%d)", stream->state); return -EIO; } - ret = k_msgq_put(&stream->in_queue, &queue_entry, - SYS_TIMEOUT_MS(stream->cfg.timeout)); + ret = k_msgq_put(&stream->in_queue, &queue_entry, SYS_TIMEOUT_MS(stream->cfg.timeout)); if (ret) { LOG_ERR("k_msgq_put failed %d", ret); @@ -854,12 +829,35 @@ static int i2s_mcux_write(const struct device *dev, void *mem_block, return ret; } +static int i2s_mcux_get_caps(const struct device *dev, struct audio_caps *caps) +{ + memset(caps, 0, sizeof(struct audio_caps)); + + caps->min_total_channels = 2; /* Stereo minimum */ + caps->max_total_channels = 8; /* Up to 4 stereo pairs in TDM mode */ + caps->supported_sample_rates = + AUDIO_SAMPLE_RATE_8000 | AUDIO_SAMPLE_RATE_11025 | AUDIO_SAMPLE_RATE_16000 | + AUDIO_SAMPLE_RATE_22050 | AUDIO_SAMPLE_RATE_32000 | AUDIO_SAMPLE_RATE_44100 | + AUDIO_SAMPLE_RATE_48000 | AUDIO_SAMPLE_RATE_88200 | AUDIO_SAMPLE_RATE_96000; + caps->supported_bit_widths = + AUDIO_BIT_WIDTH_8 | AUDIO_BIT_WIDTH_16 | AUDIO_BIT_WIDTH_24 | AUDIO_BIT_WIDTH_32; + caps->min_num_buffers = NUM_RX_DMA_BLOCKS; + /* Workaround: Set to 10ms because MediaPipe library does not have a cap filter implemented + * yet */ + caps->min_frame_interval = 10000; /* 10ms minimum */ + caps->max_frame_interval = 100000; /* 100ms maximum */ + caps->interleaved = true; + + return 0; +} + static DEVICE_API(i2s, i2s_mcux_driver_api) = { .configure = i2s_mcux_configure, .config_get = i2s_mcux_config_get, .read = i2s_mcux_read, .write = i2s_mcux_write, .trigger = i2s_mcux_trigger, + .get_caps = i2s_mcux_get_caps, }; static void i2s_mcux_isr(const struct device *dev) @@ -899,14 +897,14 @@ static int i2s_mcux_init(const struct device *dev) cfg->irq_config(dev); /* Initialize the buffer queues */ - k_msgq_init(&data->tx.in_queue, (char *)data->tx_in_msgs, - sizeof(struct i2s_txq_entry), CONFIG_I2S_MCUX_FLEXCOMM_TX_BLOCK_COUNT); - k_msgq_init(&data->rx.in_queue, (char *)data->rx_in_msgs, - sizeof(void *), CONFIG_I2S_MCUX_FLEXCOMM_RX_BLOCK_COUNT); - k_msgq_init(&data->tx.out_queue, (char *)data->tx_out_msgs, - sizeof(void *), CONFIG_I2S_MCUX_FLEXCOMM_TX_BLOCK_COUNT); - k_msgq_init(&data->rx.out_queue, (char *)data->rx_out_msgs, - sizeof(void *), CONFIG_I2S_MCUX_FLEXCOMM_RX_BLOCK_COUNT); + k_msgq_init(&data->tx.in_queue, (char *)data->tx_in_msgs, sizeof(struct i2s_txq_entry), + CONFIG_I2S_MCUX_FLEXCOMM_TX_BLOCK_COUNT); + k_msgq_init(&data->rx.in_queue, (char *)data->rx_in_msgs, sizeof(void *), + CONFIG_I2S_MCUX_FLEXCOMM_RX_BLOCK_COUNT); + k_msgq_init(&data->tx.out_queue, (char *)data->tx_out_msgs, sizeof(void *), + CONFIG_I2S_MCUX_FLEXCOMM_TX_BLOCK_COUNT); + k_msgq_init(&data->rx.out_queue, (char *)data->rx_out_msgs, sizeof(void *), + CONFIG_I2S_MCUX_FLEXCOMM_RX_BLOCK_COUNT); if (data->tx.dev_dma != NULL) { if (!device_is_ready(data->tx.dev_dma)) { @@ -930,66 +928,47 @@ static int i2s_mcux_init(const struct device *dev) return 0; } -#define I2S_DMA_CHANNELS(id) \ - .tx = { \ - .dev_dma = UTIL_AND( \ - DT_INST_DMAS_HAS_NAME(id, tx), \ - DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, tx))), \ - .channel = UTIL_AND( \ - DT_INST_DMAS_HAS_NAME(id, tx), \ - DT_INST_DMAS_CELL_BY_NAME(id, tx, channel)), \ - .dma_cfg = { \ - .channel_direction = MEMORY_TO_PERIPHERAL, \ - .dma_callback = i2s_mcux_dma_tx_callback, \ - .block_count = 1, \ - } \ - }, \ - .rx = { \ - .dev_dma = UTIL_AND( \ - DT_INST_DMAS_HAS_NAME(id, rx), \ - DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, rx))), \ - .channel = UTIL_AND( \ - DT_INST_DMAS_HAS_NAME(id, rx), \ - DT_INST_DMAS_CELL_BY_NAME(id, rx, channel)), \ - .dma_cfg = { \ - .channel_direction = PERIPHERAL_TO_MEMORY, \ - .dma_callback = i2s_mcux_dma_rx_callback, \ - .complete_callback_en = true, \ - .block_count = NUM_RX_DMA_BLOCKS, \ - } \ - } - -#define I2S_MCUX_FLEXCOMM_DEVICE(id) \ - PINCTRL_DT_INST_DEFINE(id); \ - static void i2s_mcux_config_func_##id(const struct device *dev); \ - static const struct i2s_mcux_config i2s_mcux_config_##id = { \ - .base = \ - (I2S_Type *)DT_INST_REG_ADDR(id), \ - .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \ - .clock_subsys = \ - (clock_control_subsys_t)DT_INST_CLOCKS_CELL(id, name),\ - .irq_config = i2s_mcux_config_func_##id, \ - .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ - }; \ - static struct i2s_mcux_data i2s_mcux_data_##id = { \ - I2S_DMA_CHANNELS(id) \ - }; \ - DEVICE_DT_INST_DEFINE(id, \ - &i2s_mcux_init, \ - NULL, \ - &i2s_mcux_data_##id, \ - &i2s_mcux_config_##id, \ - POST_KERNEL, \ - CONFIG_I2S_INIT_PRIORITY, \ - &i2s_mcux_driver_api); \ - static void i2s_mcux_config_func_##id(const struct device *dev) \ - { \ - IRQ_CONNECT(DT_INST_IRQN(id), \ - DT_INST_IRQ(id, priority), \ - i2s_mcux_isr, \ - DEVICE_DT_INST_GET(id), \ - 0); \ - irq_enable(DT_INST_IRQN(id)); \ +#define I2S_DMA_CHANNELS(id) \ + .tx = {.dev_dma = UTIL_AND(DT_INST_DMAS_HAS_NAME(id, tx), \ + DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, tx))), \ + .channel = UTIL_AND(DT_INST_DMAS_HAS_NAME(id, tx), \ + DT_INST_DMAS_CELL_BY_NAME(id, tx, channel)), \ + .dma_cfg = \ + { \ + .channel_direction = MEMORY_TO_PERIPHERAL, \ + .dma_callback = i2s_mcux_dma_tx_callback, \ + .block_count = 1, \ + }}, \ + .rx = {.dev_dma = UTIL_AND(DT_INST_DMAS_HAS_NAME(id, rx), \ + DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(id, rx))), \ + .channel = UTIL_AND(DT_INST_DMAS_HAS_NAME(id, rx), \ + DT_INST_DMAS_CELL_BY_NAME(id, rx, channel)), \ + .dma_cfg = { \ + .channel_direction = PERIPHERAL_TO_MEMORY, \ + .dma_callback = i2s_mcux_dma_rx_callback, \ + .complete_callback_en = true, \ + .block_count = NUM_RX_DMA_BLOCKS, \ + }} + +#define I2S_MCUX_FLEXCOMM_DEVICE(id) \ + PINCTRL_DT_INST_DEFINE(id); \ + static void i2s_mcux_config_func_##id(const struct device *dev); \ + static const struct i2s_mcux_config i2s_mcux_config_##id = { \ + .base = (I2S_Type *)DT_INST_REG_ADDR(id), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \ + .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(id, name), \ + .irq_config = i2s_mcux_config_func_##id, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ + }; \ + static struct i2s_mcux_data i2s_mcux_data_##id = {I2S_DMA_CHANNELS(id)}; \ + DEVICE_DT_INST_DEFINE(id, &i2s_mcux_init, NULL, &i2s_mcux_data_##id, \ + &i2s_mcux_config_##id, POST_KERNEL, CONFIG_I2S_INIT_PRIORITY, \ + &i2s_mcux_driver_api); \ + static void i2s_mcux_config_func_##id(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), i2s_mcux_isr, \ + DEVICE_DT_INST_GET(id), 0); \ + irq_enable(DT_INST_IRQN(id)); \ } DT_INST_FOREACH_STATUS_OKAY(I2S_MCUX_FLEXCOMM_DEVICE) diff --git a/include/zephyr/audio/audio_caps.h b/include/zephyr/audio/audio_caps.h new file mode 100644 index 0000000000000..4b3a0cd729223 --- /dev/null +++ b/include/zephyr/audio/audio_caps.h @@ -0,0 +1,136 @@ +/* + * Copyright 2025, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Common Audio Capabilities Definitions + * + * This file contains common audio format and sample rate definitions used across + * all audio subsystems (DMIC, I2S, Audio Codec, etc.) + */ + +#ifndef ZEPHYR_INCLUDE_AUDIO_CAPS_H_ +#define ZEPHYR_INCLUDE_AUDIO_CAPS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup audio_bit_width Audio Bit Width Definitions + * @brief Common audio bit width definitions - can be used across DMIC, I2S, Audio Codec APIs + * @{ + */ +/** @brief 8 bit width */ +#define AUDIO_BIT_WIDTH_8 BIT(0) +/** @brief 16 bit width */ +#define AUDIO_BIT_WIDTH_16 BIT(1) +/** @brief 18 bit width */ +#define AUDIO_BIT_WIDTH_18 BIT(2) +/** @brief 20 bit width */ +#define AUDIO_BIT_WIDTH_20 BIT(3) +/** @brief 24 bit width */ +#define AUDIO_BIT_WIDTH_24 BIT(4) +/** @brief 32 bit width */ +#define AUDIO_BIT_WIDTH_32 BIT(5) +/** @brief 64 bit width */ +#define AUDIO_BIT_WIDTH_64 BIT(6) +/** @} */ + +/** + * @defgroup audio_sample_rate Audio Sample Rate Definitions + * @brief Common audio sample rate definitions - can be used across DMIC, I2S, Audio Codec APIs + * @{ + */ +/** @brief 7.35 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_7350 BIT(0) +/** @brief 8 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_8000 BIT(1) +/** @brief 11.025 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_11025 BIT(2) +/** @brief 12 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_12000 BIT(3) +/** @brief 14.7 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_14700 BIT(4) +/** @brief 16 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_16000 BIT(5) +/** @brief 20 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_20000 BIT(6) +/** @brief 22.05 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_22050 BIT(7) +/** @brief 24 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_24000 BIT(8) +/** @brief 29.4 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_29400 BIT(9) +/** @brief 32 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_32000 BIT(10) +/** @brief 44.1 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_44100 BIT(11) +/** @brief 48 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_48000 BIT(12) +/** @brief 50 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_50000 BIT(13) +/** @brief 50.4 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_50400 BIT(14) +/** @brief 64 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_64000 BIT(15) +/** @brief 88.2 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_88200 BIT(16) +/** @brief 96 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_96000 BIT(17) +/** @brief 100.8 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_100800 BIT(18) +/** @brief 128 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_128000 BIT(19) +/** @brief 176.4 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_176400 BIT(20) +/** @brief 192 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_192000 BIT(21) +/** @brief 352.8 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_352800 BIT(22) +/** @brief 384 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_384000 BIT(23) +/** @brief 705.6 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_705600 BIT(24) +/** @brief 768 kHz sample rate */ +#define AUDIO_SAMPLE_RATE_768000 BIT(25) +/** @} */ + +/** + * @struct audio_caps + * @brief Audio capabilities structure + * + * Structure that describes the audio capabilities of a device, including + * supported channels, sample rates, bit widths, buffering requirements, + * and data layout format. + */ +struct audio_caps { + /** Minimum supported number of channels */ + uint8_t min_total_channels; + /** Maximum supported number of channels */ + uint8_t max_total_channels; + /** Bitwise OR of supported PCM sample rates */ + uint32_t supported_sample_rates; + /** Bitwise OR of supported PCM bit width */ + uint32_t supported_bit_widths; + /** Minimum number of data buffers required */ + uint8_t min_num_buffers; + /** Minimum supported frame interval in microseconds */ + uint32_t min_frame_interval; + /** Maximum supported frame interval in microseconds */ + uint32_t max_frame_interval; + /** The layout of channels within a buffer: interleaved (LRLRLRLR) and non-interleaved + * (LLLLRRRR) */ + bool interleaved; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_AUDIO_CAPS_H_ */ diff --git a/include/zephyr/audio/codec.h b/include/zephyr/audio/codec.h index 6c4cddb782259..40c9fb81f5b29 100644 --- a/include/zephyr/audio/codec.h +++ b/include/zephyr/audio/codec.h @@ -27,6 +27,8 @@ #include #include +#include /* Include common audio caps */ + #ifdef __cplusplus extern "C" { #endif @@ -35,66 +37,66 @@ extern "C" { * PCM audio sample rates */ typedef enum { - AUDIO_PCM_RATE_8K = 8000, /**< 8 kHz sample rate */ - AUDIO_PCM_RATE_11P025K = 11025, /**< 11.025 kHz sample rate */ - AUDIO_PCM_RATE_16K = 16000, /**< 16 kHz sample rate */ - AUDIO_PCM_RATE_22P05K = 22050, /**< 22.05 kHz sample rate */ - AUDIO_PCM_RATE_24K = 24000, /**< 24 kHz sample rate */ - AUDIO_PCM_RATE_32K = 32000, /**< 32 kHz sample rate */ - AUDIO_PCM_RATE_44P1K = 44100, /**< 44.1 kHz sample rate */ - AUDIO_PCM_RATE_48K = 48000, /**< 48 kHz sample rate */ - AUDIO_PCM_RATE_96K = 96000, /**< 96 kHz sample rate */ - AUDIO_PCM_RATE_192K = 192000, /**< 192 kHz sample rate */ + AUDIO_PCM_RATE_8K = 8000, /**< 8 kHz sample rate */ + AUDIO_PCM_RATE_11P025K = 11025, /**< 11.025 kHz sample rate */ + AUDIO_PCM_RATE_16K = 16000, /**< 16 kHz sample rate */ + AUDIO_PCM_RATE_22P05K = 22050, /**< 22.05 kHz sample rate */ + AUDIO_PCM_RATE_24K = 24000, /**< 24 kHz sample rate */ + AUDIO_PCM_RATE_32K = 32000, /**< 32 kHz sample rate */ + AUDIO_PCM_RATE_44P1K = 44100, /**< 44.1 kHz sample rate */ + AUDIO_PCM_RATE_48K = 48000, /**< 48 kHz sample rate */ + AUDIO_PCM_RATE_96K = 96000, /**< 96 kHz sample rate */ + AUDIO_PCM_RATE_192K = 192000, /**< 192 kHz sample rate */ } audio_pcm_rate_t; /** * PCM audio sample bit widths */ typedef enum { - AUDIO_PCM_WIDTH_16_BITS = 16, /**< 16-bit sample width */ - AUDIO_PCM_WIDTH_20_BITS = 20, /**< 20-bit sample width */ - AUDIO_PCM_WIDTH_24_BITS = 24, /**< 24-bit sample width */ - AUDIO_PCM_WIDTH_32_BITS = 32, /**< 32-bit sample width */ + AUDIO_PCM_WIDTH_16_BITS = 16, /**< 16-bit sample width */ + AUDIO_PCM_WIDTH_20_BITS = 20, /**< 20-bit sample width */ + AUDIO_PCM_WIDTH_24_BITS = 24, /**< 24-bit sample width */ + AUDIO_PCM_WIDTH_32_BITS = 32, /**< 32-bit sample width */ } audio_pcm_width_t; /** * Digital Audio Interface (DAI) type */ typedef enum { - AUDIO_DAI_TYPE_I2S, /**< I2S Interface */ - AUDIO_DAI_TYPE_LEFT_JUSTIFIED, /**< I2S Interface, left justified */ - AUDIO_DAI_TYPE_RIGHT_JUSTIFIED, /**< I2S Interface, right justified */ - AUDIO_DAI_TYPE_PCMA, /**< PCM Interface, variant A */ - AUDIO_DAI_TYPE_PCMB, /**< PCM Interface, variant B */ - AUDIO_DAI_TYPE_INVALID, /**< Other interfaces can be added here */ + AUDIO_DAI_TYPE_I2S, /**< I2S Interface */ + AUDIO_DAI_TYPE_LEFT_JUSTIFIED, /**< I2S Interface, left justified */ + AUDIO_DAI_TYPE_RIGHT_JUSTIFIED, /**< I2S Interface, right justified */ + AUDIO_DAI_TYPE_PCMA, /**< PCM Interface, variant A */ + AUDIO_DAI_TYPE_PCMB, /**< PCM Interface, variant B */ + AUDIO_DAI_TYPE_INVALID, /**< Other interfaces can be added here */ } audio_dai_type_t; /** * Codec properties that can be set by audio_codec_set_property(). */ typedef enum { - AUDIO_PROPERTY_OUTPUT_VOLUME, /**< Output volume */ - AUDIO_PROPERTY_OUTPUT_MUTE, /**< Output mute/unmute */ - AUDIO_PROPERTY_INPUT_VOLUME, /**< Input volume */ - AUDIO_PROPERTY_INPUT_MUTE /**< Input mute/unmute */ + AUDIO_PROPERTY_OUTPUT_VOLUME, /**< Output volume */ + AUDIO_PROPERTY_OUTPUT_MUTE, /**< Output mute/unmute */ + AUDIO_PROPERTY_INPUT_VOLUME, /**< Input volume */ + AUDIO_PROPERTY_INPUT_MUTE /**< Input mute/unmute */ } audio_property_t; /** * Audio channel identifiers to use in audio_codec_set_property(). */ typedef enum { - AUDIO_CHANNEL_FRONT_LEFT, /**< Front left channel */ - AUDIO_CHANNEL_FRONT_RIGHT, /**< Front right channel */ - AUDIO_CHANNEL_LFE, /**< Low frequency effect channel */ - AUDIO_CHANNEL_FRONT_CENTER, /**< Front center channel */ - AUDIO_CHANNEL_REAR_LEFT, /**< Rear left channel */ - AUDIO_CHANNEL_REAR_RIGHT, /**< Rear right channel */ - AUDIO_CHANNEL_REAR_CENTER, /**< Rear center channel */ - AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */ - AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */ - AUDIO_CHANNEL_HEADPHONE_LEFT, /**< Headphone left */ - AUDIO_CHANNEL_HEADPHONE_RIGHT, /**< Headphone right */ - AUDIO_CHANNEL_ALL, /**< All channels */ + AUDIO_CHANNEL_FRONT_LEFT, /**< Front left channel */ + AUDIO_CHANNEL_FRONT_RIGHT, /**< Front right channel */ + AUDIO_CHANNEL_LFE, /**< Low frequency effect channel */ + AUDIO_CHANNEL_FRONT_CENTER, /**< Front center channel */ + AUDIO_CHANNEL_REAR_LEFT, /**< Rear left channel */ + AUDIO_CHANNEL_REAR_RIGHT, /**< Rear right channel */ + AUDIO_CHANNEL_REAR_CENTER, /**< Rear center channel */ + AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */ + AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */ + AUDIO_CHANNEL_HEADPHONE_LEFT, /**< Headphone left */ + AUDIO_CHANNEL_HEADPHONE_RIGHT, /**< Headphone right */ + AUDIO_CHANNEL_ALL, /**< All channels */ } audio_channel_t; /** @@ -103,8 +105,8 @@ typedef enum { * Configuration is dependent on DAI type */ typedef union { - struct i2s_config i2s; /**< I2S configuration */ - /* Other DAI types go here */ + struct i2s_config i2s; /**< I2S configuration */ + /* Other DAI types go here */ } audio_dai_cfg_t; /* @@ -121,18 +123,18 @@ typedef enum { * Codec configuration parameters */ struct audio_codec_cfg { - uint32_t mclk_freq; /**< MCLK input frequency in Hz */ - audio_dai_type_t dai_type; /**< Digital interface type */ - audio_dai_cfg_t dai_cfg; /**< DAI configuration info */ - audio_route_t dai_route; /**< Codec route type */ + uint32_t mclk_freq; /**< MCLK input frequency in Hz */ + audio_dai_type_t dai_type; /**< Digital interface type */ + audio_dai_cfg_t dai_cfg; /**< DAI configuration info */ + audio_route_t dai_route; /**< Codec route type */ }; /** * Codec property values */ typedef union { - int vol; /**< Volume level (codec-specific) */ - bool mute; /**< Mute if @a true, unmute if @a false */ + int vol; /**< Volume level (codec-specific) */ + bool mute; /**< Mute if @a true, unmute if @a false */ } audio_property_value_t; /** @@ -170,20 +172,17 @@ typedef void (*audio_codec_error_callback_t)(const struct device *dev, uint32_t * For internal use only, skip these in public documentation. */ struct audio_codec_api { - int (*configure)(const struct device *dev, - struct audio_codec_cfg *cfg); + int (*configure)(const struct device *dev, struct audio_codec_cfg *cfg); void (*start_output)(const struct device *dev); void (*stop_output)(const struct device *dev); - int (*set_property)(const struct device *dev, - audio_property_t property, - audio_channel_t channel, - audio_property_value_t val); + int (*set_property)(const struct device *dev, audio_property_t property, + audio_channel_t channel, audio_property_value_t val); int (*apply_properties)(const struct device *dev); int (*clear_errors)(const struct device *dev); - int (*register_error_callback)(const struct device *dev, - audio_codec_error_callback_t cb); + int (*register_error_callback)(const struct device *dev, audio_codec_error_callback_t cb); int (*route_input)(const struct device *dev, audio_channel_t channel, uint32_t input); int (*route_output)(const struct device *dev, audio_channel_t channel, uint32_t output); + int (*get_caps)(const struct device *dev, struct audio_caps *caps); }; /** * @endcond @@ -200,11 +199,9 @@ struct audio_codec_api { * * @return 0 on success, negative error code on failure */ -static inline int audio_codec_configure(const struct device *dev, - struct audio_codec_cfg *cfg) +static inline int audio_codec_configure(const struct device *dev, struct audio_codec_cfg *cfg) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; return api->configure(dev, cfg); } @@ -218,8 +215,7 @@ static inline int audio_codec_configure(const struct device *dev, */ static inline void audio_codec_start_output(const struct device *dev) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; api->start_output(dev); } @@ -233,8 +229,7 @@ static inline void audio_codec_start_output(const struct device *dev) */ static inline void audio_codec_stop_output(const struct device *dev) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; api->stop_output(dev); } @@ -251,13 +246,10 @@ static inline void audio_codec_stop_output(const struct device *dev) * * @return 0 on success, negative error code on failure */ -static inline int audio_codec_set_property(const struct device *dev, - audio_property_t property, - audio_channel_t channel, - audio_property_value_t val) +static inline int audio_codec_set_property(const struct device *dev, audio_property_t property, + audio_channel_t channel, audio_property_value_t val) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; return api->set_property(dev, property, channel, val); } @@ -275,8 +267,7 @@ static inline int audio_codec_set_property(const struct device *dev, */ static inline int audio_codec_apply_properties(const struct device *dev) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; return api->apply_properties(dev); } @@ -293,8 +284,7 @@ static inline int audio_codec_apply_properties(const struct device *dev) */ static inline int audio_codec_clear_errors(const struct device *dev) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; if (api->clear_errors == NULL) { return -ENOSYS; @@ -318,10 +308,9 @@ static inline int audio_codec_clear_errors(const struct device *dev) * @return 0 if successful, negative errno code if failure. */ static inline int audio_codec_register_error_callback(const struct device *dev, - audio_codec_error_callback_t cb) + audio_codec_error_callback_t cb) { - const struct audio_codec_api *api = - (const struct audio_codec_api *)dev->api; + const struct audio_codec_api *api = (const struct audio_codec_api *)dev->api; if (api->register_error_callback == NULL) { return -ENOSYS; @@ -380,6 +369,31 @@ static inline int audio_codec_route_output(const struct device *dev, audio_chann return api->route_output(dev, channel, output); } +/** + * @brief Get i2s capabilities + * + * @param dev Pointer to device structure + * @param caps Pointer to capabilities structure to populate + * + * @retval 0 on success + * @retval -ENOSYS if the get_caps is not implemented by the driver + * @retval -EINVAL if invalid parameters are provided (e.g., caps is NULL) + */ +static inline int audio_codec_get_caps(const struct device *dev, struct audio_caps *caps) +{ + struct audio_codec_api *api = (struct audio_codec_api *)dev->api; + + if (api->get_caps == NULL) { + return -ENOSYS; + } + + if (caps == NULL) { + return -EINVAL; + } + + return api->get_caps(dev, caps); +} + #ifdef __cplusplus } #endif diff --git a/include/zephyr/audio/dmic.h b/include/zephyr/audio/dmic.h index bb46e008ddde3..d31dd674af940 100644 --- a/include/zephyr/audio/dmic.h +++ b/include/zephyr/audio/dmic.h @@ -17,7 +17,6 @@ #ifndef ZEPHYR_INCLUDE_AUDIO_DMIC_H_ #define ZEPHYR_INCLUDE_AUDIO_DMIC_H_ - /** * @defgroup audio_interface Audio * @{ @@ -37,6 +36,8 @@ #include #include +#include /* Include common audio caps */ + #ifdef __cplusplus extern "C" { #endif @@ -45,30 +46,30 @@ extern "C" { * DMIC driver states */ enum dmic_state { - DMIC_STATE_UNINIT, /**< Uninitialized */ - DMIC_STATE_INITIALIZED, /**< Initialized */ - DMIC_STATE_CONFIGURED, /**< Configured */ - DMIC_STATE_ACTIVE, /**< Active */ - DMIC_STATE_PAUSED, /**< Paused */ - DMIC_STATE_ERROR, /**< Error */ + DMIC_STATE_UNINIT, /**< Uninitialized */ + DMIC_STATE_INITIALIZED, /**< Initialized */ + DMIC_STATE_CONFIGURED, /**< Configured */ + DMIC_STATE_ACTIVE, /**< Active */ + DMIC_STATE_PAUSED, /**< Paused */ + DMIC_STATE_ERROR, /**< Error */ }; /** * DMIC driver trigger commands */ enum dmic_trigger { - DMIC_TRIGGER_STOP, /**< Stop stream */ - DMIC_TRIGGER_START, /**< Start stream */ - DMIC_TRIGGER_PAUSE, /**< Pause stream */ - DMIC_TRIGGER_RELEASE, /**< Release paused stream */ - DMIC_TRIGGER_RESET, /**< Reset stream */ + DMIC_TRIGGER_STOP, /**< Stop stream */ + DMIC_TRIGGER_START, /**< Start stream */ + DMIC_TRIGGER_PAUSE, /**< Pause stream */ + DMIC_TRIGGER_RELEASE, /**< Release paused stream */ + DMIC_TRIGGER_RESET, /**< Reset stream */ }; /** * PDM Channels LEFT / RIGHT */ enum pdm_lr { - PDM_CHAN_LEFT, /**< Left channel */ + PDM_CHAN_LEFT, /**< Left channel */ PDM_CHAN_RIGHT, /**< Right channel */ }; @@ -150,16 +151,16 @@ struct pdm_chan_cfg { * @name Requested channel map * @{ */ - uint32_t req_chan_map_lo; /**< Channels 0 to 7 */ - uint32_t req_chan_map_hi; /**< Channels 8 to 15 */ + uint32_t req_chan_map_lo; /**< Channels 0 to 7 */ + uint32_t req_chan_map_hi; /**< Channels 8 to 15 */ /** @} */ /** * @name Actual channel map that the driver could configure * @{ */ - uint32_t act_chan_map_lo; /**< Channels 0 to 7 */ - uint32_t act_chan_map_hi; /**< Channels 8 to 15 */ + uint32_t act_chan_map_lo; /**< Channels 0 to 7 */ + uint32_t act_chan_map_hi; /**< Channels 8 to 15 */ /** @} */ /** Requested number of channels */ @@ -191,8 +192,9 @@ struct dmic_cfg { struct _dmic_ops { int (*configure)(const struct device *dev, struct dmic_cfg *config); int (*trigger)(const struct device *dev, enum dmic_trigger cmd); - int (*read)(const struct device *dev, uint8_t stream, void **buffer, - size_t *size, int32_t timeout); + int (*read)(const struct device *dev, uint8_t stream, void **buffer, size_t *size, + int32_t timeout); + int (*get_caps)(const struct device *dev, struct audio_caps *caps); }; /** @@ -207,11 +209,9 @@ struct _dmic_ops { * * @return Bit-map containing the PDM and L/R channel information */ -static inline uint32_t dmic_build_channel_map(uint8_t channel, uint8_t pdm, - enum pdm_lr lr) +static inline uint32_t dmic_build_channel_map(uint8_t channel, uint8_t pdm, enum pdm_lr lr) { - return ((((pdm & BIT_MASK(3)) << 1) | lr) << - ((channel & BIT_MASK(3)) * 4U)); + return ((((pdm & BIT_MASK(3)) << 1) | lr) << ((channel & BIT_MASK(3)) * 4U)); } /** @@ -226,8 +226,8 @@ static inline uint32_t dmic_build_channel_map(uint8_t channel, uint8_t pdm, * @param pdm Pointer to the PDM hardware controller number * @param lr Pointer to the LEFT/RIGHT channel within the PDM controller */ -static inline void dmic_parse_channel_map(uint32_t channel_map_lo, - uint32_t channel_map_hi, uint8_t channel, uint8_t *pdm, enum pdm_lr *lr) +static inline void dmic_parse_channel_map(uint32_t channel_map_lo, uint32_t channel_map_hi, + uint8_t channel, uint8_t *pdm, enum pdm_lr *lr) { uint32_t channel_map; @@ -235,7 +235,7 @@ static inline void dmic_parse_channel_map(uint32_t channel_map_lo, channel_map >>= ((channel & BIT_MASK(3)) * 4U); *pdm = (channel_map >> 1) & BIT_MASK(3); - *lr = (enum pdm_lr) (channel_map & BIT(0)); + *lr = (enum pdm_lr)(channel_map & BIT(0)); } /** @@ -265,11 +265,9 @@ static inline uint32_t dmic_build_clk_skew_map(uint8_t pdm, uint8_t skew) * * @return 0 on success, a negative error code on failure */ -static inline int dmic_configure(const struct device *dev, - struct dmic_cfg *cfg) +static inline int dmic_configure(const struct device *dev, struct dmic_cfg *cfg) { - const struct _dmic_ops *api = - (const struct _dmic_ops *)dev->api; + const struct _dmic_ops *api = (const struct _dmic_ops *)dev->api; return api->configure(dev, cfg); } @@ -284,11 +282,9 @@ static inline int dmic_configure(const struct device *dev, * * @return 0 on success, a negative error code on failure */ -static inline int dmic_trigger(const struct device *dev, - enum dmic_trigger cmd) +static inline int dmic_trigger(const struct device *dev, enum dmic_trigger cmd) { - const struct _dmic_ops *api = - (const struct _dmic_ops *)dev->api; + const struct _dmic_ops *api = (const struct _dmic_ops *)dev->api; return api->trigger(dev, cmd); } @@ -308,16 +304,39 @@ static inline int dmic_trigger(const struct device *dev, * * @return 0 on success, a negative error code on failure */ -static inline int dmic_read(const struct device *dev, uint8_t stream, - void **buffer, - size_t *size, int32_t timeout) +static inline int dmic_read(const struct device *dev, uint8_t stream, void **buffer, size_t *size, + int32_t timeout) { - const struct _dmic_ops *api = - (const struct _dmic_ops *)dev->api; + const struct _dmic_ops *api = (const struct _dmic_ops *)dev->api; return api->read(dev, stream, buffer, size, timeout); } +/** + * @brief Get dmic capabilities + * + * @param dev Pointer to device structure + * @param caps Pointer to capabilities structure to populate + * + * @retval 0 on success + * @retval -ENOSYS if the get_caps is not implemented by the driver + * @retval -EINVAL if invalid parameters are provided (e.g., caps is NULL) + */ +static inline int dmic_get_caps(const struct device *dev, struct audio_caps *caps) +{ + struct _dmic_ops *api = (struct _dmic_ops *)dev->api; + + if (api->get_caps == NULL) { + return -ENOSYS; + } + + if (caps == NULL) { + return -EINVAL; + } + + return api->get_caps(dev, caps); +} + #ifdef __cplusplus } #endif diff --git a/include/zephyr/drivers/i2s.h b/include/zephyr/drivers/i2s.h index 406789c365da9..6a42a66e73e9a 100644 --- a/include/zephyr/drivers/i2s.h +++ b/include/zephyr/drivers/i2s.h @@ -29,6 +29,8 @@ #include #include +#include /* Include common audio caps */ + #ifdef __cplusplus extern "C" { #endif @@ -41,9 +43,9 @@ extern "C" { typedef uint8_t i2s_fmt_t; /** Data Format bit field position. */ -#define I2S_FMT_DATA_FORMAT_SHIFT 0 +#define I2S_FMT_DATA_FORMAT_SHIFT 0 /** Data Format bit field mask. */ -#define I2S_FMT_DATA_FORMAT_MASK (0x7 << I2S_FMT_DATA_FORMAT_SHIFT) +#define I2S_FMT_DATA_FORMAT_MASK (0x7 << I2S_FMT_DATA_FORMAT_SHIFT) /** @brief Standard I2S Data Format. * @@ -62,7 +64,7 @@ typedef uint8_t i2s_fmt_t; * -'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---' * | Left channel | Right channel | */ -#define I2S_FMT_DATA_FORMAT_I2S (0 << I2S_FMT_DATA_FORMAT_SHIFT) +#define I2S_FMT_DATA_FORMAT_I2S (0 << I2S_FMT_DATA_FORMAT_SHIFT) /** @brief PCM Short Frame Sync Data Format. * @@ -81,7 +83,7 @@ typedef uint8_t i2s_fmt_t; * -'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'--- * | Word 1 | Word 2 | Word 3 | Word n | */ -#define I2S_FMT_DATA_FORMAT_PCM_SHORT (1 << I2S_FMT_DATA_FORMAT_SHIFT) +#define I2S_FMT_DATA_FORMAT_PCM_SHORT (1 << I2S_FMT_DATA_FORMAT_SHIFT) /** @brief PCM Long Frame Sync Data Format. * @@ -101,7 +103,7 @@ typedef uint8_t i2s_fmt_t; * -'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'--- * | Word 1 | Word 2 | Word 3 | Word n | */ -#define I2S_FMT_DATA_FORMAT_PCM_LONG (2 << I2S_FMT_DATA_FORMAT_SHIFT) +#define I2S_FMT_DATA_FORMAT_PCM_LONG (2 << I2S_FMT_DATA_FORMAT_SHIFT) /** * @brief Left Justified Data Format. @@ -122,7 +124,7 @@ typedef uint8_t i2s_fmt_t; * ---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'---'- * | Left channel | Right channel | */ -#define I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED (3 << I2S_FMT_DATA_FORMAT_SHIFT) +#define I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED (3 << I2S_FMT_DATA_FORMAT_SHIFT) /** * @brief Right Justified Data Format. @@ -146,53 +148,53 @@ typedef uint8_t i2s_fmt_t; #define I2S_FMT_DATA_FORMAT_RIGHT_JUSTIFIED (4 << I2S_FMT_DATA_FORMAT_SHIFT) /** Send MSB first */ -#define I2S_FMT_DATA_ORDER_MSB (0 << 3) +#define I2S_FMT_DATA_ORDER_MSB (0 << 3) /** Send LSB first */ -#define I2S_FMT_DATA_ORDER_LSB BIT(3) +#define I2S_FMT_DATA_ORDER_LSB BIT(3) /** Invert bit ordering, send LSB first */ -#define I2S_FMT_DATA_ORDER_INV I2S_FMT_DATA_ORDER_LSB +#define I2S_FMT_DATA_ORDER_INV I2S_FMT_DATA_ORDER_LSB /** Data Format bit field position. */ -#define I2S_FMT_CLK_FORMAT_SHIFT 4 +#define I2S_FMT_CLK_FORMAT_SHIFT 4 /** Data Format bit field mask. */ -#define I2S_FMT_CLK_FORMAT_MASK (0x3 << I2S_FMT_CLK_FORMAT_SHIFT) +#define I2S_FMT_CLK_FORMAT_MASK (0x3 << I2S_FMT_CLK_FORMAT_SHIFT) /** Invert bit clock */ -#define I2S_FMT_BIT_CLK_INV BIT(4) +#define I2S_FMT_BIT_CLK_INV BIT(4) /** Invert frame clock */ -#define I2S_FMT_FRAME_CLK_INV BIT(5) +#define I2S_FMT_FRAME_CLK_INV BIT(5) /** Normal Frame, Normal Bit Clk */ -#define I2S_FMT_CLK_NF_NB (0 << I2S_FMT_CLK_FORMAT_SHIFT) +#define I2S_FMT_CLK_NF_NB (0 << I2S_FMT_CLK_FORMAT_SHIFT) /** Normal Frame, Inverted Bit Clk */ -#define I2S_FMT_CLK_NF_IB (1 << I2S_FMT_CLK_FORMAT_SHIFT) +#define I2S_FMT_CLK_NF_IB (1 << I2S_FMT_CLK_FORMAT_SHIFT) /** Inverted Frame, Normal Bit Clk */ -#define I2S_FMT_CLK_IF_NB (2 << I2S_FMT_CLK_FORMAT_SHIFT) +#define I2S_FMT_CLK_IF_NB (2 << I2S_FMT_CLK_FORMAT_SHIFT) /** Inverted Frame, Inverted Bit Clk */ -#define I2S_FMT_CLK_IF_IB (3 << I2S_FMT_CLK_FORMAT_SHIFT) +#define I2S_FMT_CLK_IF_IB (3 << I2S_FMT_CLK_FORMAT_SHIFT) /** I2S configuration options */ typedef uint8_t i2s_opt_t; /** Run bit clock continuously */ -#define I2S_OPT_BIT_CLK_CONT (0 << 0) +#define I2S_OPT_BIT_CLK_CONT (0 << 0) /** Run bit clock when sending data only */ -#define I2S_OPT_BIT_CLK_GATED BIT(0) +#define I2S_OPT_BIT_CLK_GATED BIT(0) /** I2S driver is bit clock master */ -#define I2S_OPT_BIT_CLK_MASTER (0 << 1) +#define I2S_OPT_BIT_CLK_MASTER (0 << 1) /** I2S driver is bit clock slave */ -#define I2S_OPT_BIT_CLK_SLAVE BIT(1) +#define I2S_OPT_BIT_CLK_SLAVE BIT(1) /** I2S driver is frame clock master */ -#define I2S_OPT_FRAME_CLK_MASTER (0 << 2) +#define I2S_OPT_FRAME_CLK_MASTER (0 << 2) /** I2S driver is frame clock slave */ -#define I2S_OPT_FRAME_CLK_SLAVE BIT(2) +#define I2S_OPT_FRAME_CLK_SLAVE BIT(2) /** @brief Loop back mode. * * In loop back mode RX input will be connected internally to TX output. * This is used primarily for testing. */ -#define I2S_OPT_LOOPBACK BIT(7) +#define I2S_OPT_LOOPBACK BIT(7) /** @brief Ping pong mode * @@ -202,7 +204,7 @@ typedef uint8_t i2s_opt_t; * So, in this mode, 2 sets of buffers fixed in size are used. Static Arrays * are used to achieve this and hence they are never freed. */ -#define I2S_OPT_PINGPONG BIT(6) +#define I2S_OPT_PINGPONG BIT(6) /** * @brief I2C Direction @@ -320,14 +322,12 @@ struct i2s_config { * For internal use only, skip these in public documentation. */ __subsystem struct i2s_driver_api { - int (*configure)(const struct device *dev, enum i2s_dir dir, - const struct i2s_config *cfg); - const struct i2s_config *(*config_get)(const struct device *dev, - enum i2s_dir dir); + int (*configure)(const struct device *dev, enum i2s_dir dir, const struct i2s_config *cfg); + const struct i2s_config *(*config_get)(const struct device *dev, enum i2s_dir dir); int (*read)(const struct device *dev, void **mem_block, size_t *size); int (*write)(const struct device *dev, void *mem_block, size_t size); - int (*trigger)(const struct device *dev, enum i2s_dir dir, - enum i2s_trigger_cmd cmd); + int (*trigger)(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd); + int (*get_caps)(const struct device *dev, struct audio_caps *caps); }; /** * @endcond @@ -358,12 +358,10 @@ __subsystem struct i2s_driver_api { __syscall int i2s_configure(const struct device *dev, enum i2s_dir dir, const struct i2s_config *cfg); -static inline int z_impl_i2s_configure(const struct device *dev, - enum i2s_dir dir, +static inline int z_impl_i2s_configure(const struct device *dev, enum i2s_dir dir, const struct i2s_config *cfg) { - const struct i2s_driver_api *api = - (const struct i2s_driver_api *)dev->api; + const struct i2s_driver_api *api = (const struct i2s_driver_api *)dev->api; return api->configure(dev, dir, cfg); } @@ -376,11 +374,9 @@ static inline int z_impl_i2s_configure(const struct device *dev, * @retval Pointer to the structure containing configuration parameters, * or NULL if un-configured */ -static inline const struct i2s_config *i2s_config_get(const struct device *dev, - enum i2s_dir dir) +static inline const struct i2s_config *i2s_config_get(const struct device *dev, enum i2s_dir dir) { - const struct i2s_driver_api *api = - (const struct i2s_driver_api *)dev->api; + const struct i2s_driver_api *api = (const struct i2s_driver_api *)dev->api; return api->config_get(dev, dir); } @@ -416,11 +412,9 @@ static inline const struct i2s_config *i2s_config_get(const struct device *dev, * @retval -EBUSY Returned without waiting. * @retval -EAGAIN Waiting period timed out. */ -static inline int i2s_read(const struct device *dev, void **mem_block, - size_t *size) +static inline int i2s_read(const struct device *dev, void **mem_block, size_t *size) { - const struct i2s_driver_api *api = - (const struct i2s_driver_api *)dev->api; + const struct i2s_driver_api *api = (const struct i2s_driver_api *)dev->api; return api->read(dev, mem_block, size); } @@ -477,11 +471,9 @@ __syscall int i2s_buf_read(const struct device *dev, void *buf, size_t *size); * @retval -EBUSY Returned without waiting. * @retval -EAGAIN Waiting period timed out. */ -static inline int i2s_write(const struct device *dev, void *mem_block, - size_t size) +static inline int i2s_write(const struct device *dev, void *mem_block, size_t size) { - const struct i2s_driver_api *api = - (const struct i2s_driver_api *)dev->api; + const struct i2s_driver_api *api = (const struct i2s_driver_api *)dev->api; return api->write(dev, mem_block, size); } @@ -524,19 +516,41 @@ __syscall int i2s_buf_write(const struct device *dev, void *buf, size_t size); * @retval -ENOMEM RX/TX memory block not available. * @retval -ENOSYS I2S_DIR_BOTH value is not supported. */ -__syscall int i2s_trigger(const struct device *dev, enum i2s_dir dir, - enum i2s_trigger_cmd cmd); +__syscall int i2s_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd); -static inline int z_impl_i2s_trigger(const struct device *dev, - enum i2s_dir dir, +static inline int z_impl_i2s_trigger(const struct device *dev, enum i2s_dir dir, enum i2s_trigger_cmd cmd) { - const struct i2s_driver_api *api = - (const struct i2s_driver_api *)dev->api; + const struct i2s_driver_api *api = (const struct i2s_driver_api *)dev->api; return api->trigger(dev, dir, cmd); } +/** + * @brief Get i2s capabilities + * + * @param dev Pointer to device structure + * @param caps Pointer to capabilities structure to populate + * + * @retval 0 on success + * @retval -ENOSYS if the get_caps is not implemented by the driver + * @retval -EINVAL if invalid parameters are provided (e.g., caps is NULL) + */ +static inline int i2s_get_caps(const struct device *dev, struct audio_caps *caps) +{ + struct i2s_driver_api *api = (struct i2s_driver_api *)dev->api; + + if (api->get_caps == NULL) { + return -ENOSYS; + } + + if (caps == NULL) { + return -EINVAL; + } + + return api->get_caps(dev, caps); +} + /** * @} */ diff --git a/tests/drivers/audio/dmic_api/src/main.c b/tests/drivers/audio/dmic_api/src/main.c index 27a549212351e..4c443f7d725f9 100644 --- a/tests/drivers/audio/dmic_api/src/main.c +++ b/tests/drivers/audio/dmic_api/src/main.c @@ -10,6 +10,7 @@ */ #include +#include #include #include @@ -291,4 +292,64 @@ ZTEST(dmic, test_bad_pair) "non adjacent channels in map"); } +/** + * @brief Test the dmic_get_caps API function + * + * This test validates the DMIC get_caps API functionality by testing both + * successful operation and error handling scenarios. + */ +ZTEST(dmic, test_get_caps) +{ + int ret; + struct audio_caps caps; + + zassert_true(device_is_ready(dmic_dev), "DMIC device is not ready"); + + /** + * Test Case 1: Normal operation - Valid parameters + * Expected: Function should return 0 (success) or -ENOSYS (not implemented) + */ + ret = dmic_get_caps(dmic_dev, &caps); + + /* Handle case where driver doesn't implement get_caps */ + if (ret == -ENOSYS) { + TC_PRINT("DMIC get_caps not implemented by driver\n"); + ztest_test_skip(); + return; + } + + /* Verify successful execution */ + zassert_equal(ret, 0, "dmic_get_caps should return 0, got %d", ret); + + /** + * Test Case 2: Capability value validation + * Verify that returned capability values are within reasonable ranges + */ + zassert_true(caps.min_total_channels >= 1, "min_total_channels should be >= 1, got %d", + caps.min_total_channels); + + zassert_true(caps.max_total_channels >= caps.min_total_channels, + "max_total_channels (%u) should be >= min_total_channels (%u)", + caps.max_total_channels, caps.min_total_channels); + + zassert_not_equal(caps.supported_sample_rates, 0, "supported_sample_rates should not be 0"); + + zassert_not_equal(caps.supported_bit_widths, 0, "supported_bit_widths should not be 0"); + + zassert_true(caps.min_num_buffers >= 1, "min_num_buffers should be >= 1, got %u", + caps.min_num_buffers); + + zassert_true(caps.max_frame_interval >= caps.min_frame_interval, + "max_frame_interval (%u) should be >= min_frame_interval (%u)", + caps.max_frame_interval, caps.min_frame_interval); + + /** + * Test Case 3: Error handling - NULL caps pointer + * Expected: Function should return -EINVAL for invalid parameter + */ + ret = dmic_get_caps(dmic_dev, NULL); + zassert_equal(ret, -EINVAL, + "dmic_get_caps should return -EINVAL for NULL caps pointer, got %d", ret); +} + ZTEST_SUITE(dmic, NULL, NULL, NULL, NULL, NULL); diff --git a/tests/drivers/i2s/i2s_api/src/test_i2s_errors.c b/tests/drivers/i2s/i2s_api/src/test_i2s_errors.c index 230a71e95d4de..bedd9eef0394c 100644 --- a/tests/drivers/i2s/i2s_api/src/test_i2s_errors.c +++ b/tests/drivers/i2s/i2s_api/src/test_i2s_errors.c @@ -6,7 +6,10 @@ #include #include + +#include #include + #include "i2s_api_test.h" #define INVALID_TRIGGER_SETTING 7 @@ -154,3 +157,61 @@ ZTEST_USER(i2s_errors, test_i2s_improper_block_size_write) err, 0, "I2S attempting write with incorrect block size did not raise error, err=%d", err); } + +/** + * @brief Test the i2s_get_caps API function + * + * This test validates the I2S get_caps API functionality by testing both + * successful operation and error handling scenarios. + */ +ZTEST_USER(i2s_errors, test_i2s_get_caps) +{ + int ret; + struct audio_caps caps; + + /** + * Test Case 1: Normal operation - Valid parameters + * Expected: Function should return 0 (success) or -ENOSYS (not implemented) + */ + ret = i2s_get_caps(dev_i2s, &caps); + + /* Handle case where driver doesn't implement get_caps */ + if (ret == -ENOSYS) { + TC_PRINT("I2S get_caps not implemented by driver\n"); + ztest_test_skip(); + return; + } + + /* Verify successful execution */ + zassert_equal(ret, 0, "i2s_get_caps should return 0, got %d", ret); + + /** + * Test Case 2: Capability value validation + * Verify that returned capability values are within reasonable ranges + */ + zassert_true(caps.min_total_channels >= 1, "min_total_channels should be >= 1, got %u", + caps.min_total_channels); + + zassert_true(caps.max_total_channels >= caps.min_total_channels, + "max_total_channels (%u) should be >= min_total_channels (%u)", + caps.max_total_channels, caps.min_total_channels); + + zassert_not_equal(caps.supported_sample_rates, 0, "supported_sample_rates should not be 0"); + + zassert_not_equal(caps.supported_bit_widths, 0, "supported_bit_widths should not be 0"); + + zassert_true(caps.min_num_buffers >= 1, "min_num_buffers should be >= 1, got %u", + caps.min_num_buffers); + + zassert_true(caps.max_frame_interval >= caps.min_frame_interval, + "max_frame_interval (%u) should be >= min_frame_interval (%u)", + caps.max_frame_interval, caps.min_frame_interval); + + /** + * Test Case 3: Error handling - NULL caps pointer + * Expected: Function should return -EINVAL for invalid parameter + */ + ret = i2s_get_caps(dev_i2s, NULL); + zassert_equal(ret, -EINVAL, + "i2s_get_caps should return -EINVAL for NULL caps pointer, got %d", ret); +}