From 3bcd9278fa092cee44951c8591ee571bd873b511 Mon Sep 17 00:00:00 2001 From: morris Date: Wed, 4 Aug 2021 16:47:56 +0800 Subject: [PATCH] i2s: expose resource object to other component --- components/driver/i2s.c | 203 +++++++++--------- components/driver/include/driver/i2s.h | 32 +-- .../driver/include/esp_private/i2s_platform.h | 48 +++++ components/driver/test/test_i2s.c | 2 +- 4 files changed, 173 insertions(+), 112 deletions(-) create mode 100644 components/driver/include/esp_private/i2s_platform.h diff --git a/components/driver/i2s.c b/components/driver/i2s.c index f0dc634ac3..f2085bc5ee 100644 --- a/components/driver/i2s.c +++ b/components/driver/i2s.c @@ -37,6 +37,7 @@ #include "esp_pm.h" #include "esp_efuse.h" #include "esp_rom_gpio.h" +#include "esp_private/i2s_platform.h" #include "sdkconfig.h" @@ -107,9 +108,12 @@ typedef struct { i2s_hal_config_t hal_cfg; /*!< I2S hal configurations*/ } i2s_obj_t; -static i2s_obj_t *p_i2s[I2S_NUM_MAX] = {0}; +static i2s_obj_t *p_i2s[SOC_I2S_NUM]; +static portMUX_TYPE i2s_platform_spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; +static portMUX_TYPE i2s_spinlock[SOC_I2S_NUM] = { + [0 ... SOC_I2S_NUM - 1] = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED, +}; -static portMUX_TYPE i2s_spinlock[I2S_NUM_MAX]; #if SOC_I2S_SUPPORTS_ADC_DAC static int _i2s_adc_unit = -1; static int _i2s_adc_channel = -1; @@ -939,7 +943,7 @@ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg) I2S_ENTER_CRITICAL(i2s_num); if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { i2s_hal_tx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); - } else if(p_i2s[i2s_num]->mode & I2S_MODE_RX) { + } else if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { i2s_hal_rx_pcm_cfg(&(p_i2s[i2s_num]->hal), pcm_cfg->pcm_type); } I2S_EXIT_CRITICAL(i2s_num); @@ -1174,97 +1178,86 @@ esp_err_t i2s_stop(i2s_port_t i2s_num) esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue) { esp_err_t ret = ESP_FAIL; + i2s_obj_t *pre_alloc_i2s_obj = NULL; ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); ESP_RETURN_ON_FALSE((i2s_config != NULL), ESP_ERR_INVALID_ARG, TAG, "I2S configuration must not NULL"); ESP_RETURN_ON_FALSE((i2s_config->dma_buf_count >= 2 && i2s_config->dma_buf_count <= 128), ESP_ERR_INVALID_ARG, TAG, "I2S buffer count less than 128 and more than 2"); ESP_RETURN_ON_FALSE((i2s_config->dma_buf_len >= 8 && i2s_config->dma_buf_len <= 1024), ESP_ERR_INVALID_ARG, TAG, "I2S buffer length at most 1024 and more than 8"); - if (p_i2s[i2s_num] != NULL) { - ESP_LOGW(TAG, "I2S driver already installed"); - return ESP_OK; - } - p_i2s[i2s_num] = (i2s_obj_t *) calloc(1, sizeof(i2s_obj_t)); - if (p_i2s[i2s_num] == NULL) { - ESP_LOGE(TAG, "Malloc I2S driver error"); - return ESP_ERR_NO_MEM; + // alloc driver object and register to platform + pre_alloc_i2s_obj = calloc(1, sizeof(i2s_obj_t)); + ESP_RETURN_ON_FALSE(pre_alloc_i2s_obj, ESP_ERR_NO_MEM, TAG, "no mem for I2S driver"); + ret = i2s_priv_register_object(pre_alloc_i2s_obj, i2s_num); + if (ret != ESP_OK) { + free(pre_alloc_i2s_obj); + ESP_LOGE(TAG, "register I2S object to platform failed"); + return ret; } - - portMUX_TYPE i2s_spinlock_unlocked[1] = {portMUX_INITIALIZER_UNLOCKED}; - for (int x = 0; x < I2S_NUM_MAX; x++) { - i2s_spinlock[x] = i2s_spinlock_unlocked[0]; - } - //To make sure hardware is enabled before any hardware register operations. - periph_module_enable(i2s_periph_signal[i2s_num].module); - i2s_hal_init(&(p_i2s[i2s_num]->hal), i2s_num); + // initialize HAL layer + i2s_hal_init(&(pre_alloc_i2s_obj->hal), i2s_num); // Set I2S HAL configurations - p_i2s[i2s_num]->hal_cfg.mode = i2s_config->mode; - p_i2s[i2s_num]->hal_cfg.sample_rate = i2s_config->sample_rate; - p_i2s[i2s_num]->hal_cfg.comm_fmt = i2s_config->communication_format; - p_i2s[i2s_num]->hal_cfg.chan_fmt = i2s_config->channel_format; - p_i2s[i2s_num]->hal_cfg.bits_cfg.sample_bits = i2s_config->bits_per_sample; - p_i2s[i2s_num]->hal_cfg.bits_cfg.chan_bits = i2s_config->bits_per_chan; + pre_alloc_i2s_obj->hal_cfg.mode = i2s_config->mode; + pre_alloc_i2s_obj->hal_cfg.sample_rate = i2s_config->sample_rate; + pre_alloc_i2s_obj->hal_cfg.comm_fmt = i2s_config->communication_format; + pre_alloc_i2s_obj->hal_cfg.chan_fmt = i2s_config->channel_format; + pre_alloc_i2s_obj->hal_cfg.bits_cfg.sample_bits = i2s_config->bits_per_sample; + pre_alloc_i2s_obj->hal_cfg.bits_cfg.chan_bits = i2s_config->bits_per_chan; #if SOC_I2S_SUPPORTS_TDM int active_chan = 0; switch (i2s_config->channel_format) { case I2S_CHANNEL_FMT_RIGHT_LEFT: case I2S_CHANNEL_FMT_ALL_RIGHT: case I2S_CHANNEL_FMT_ALL_LEFT: - p_i2s[i2s_num]->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; - p_i2s[i2s_num]->hal_cfg.total_chan = 2; + pre_alloc_i2s_obj->hal_cfg.chan_mask = I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1; + pre_alloc_i2s_obj->hal_cfg.total_chan = 2; active_chan = 2; break; case I2S_CHANNEL_FMT_ONLY_RIGHT: - p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; - p_i2s[i2s_num]->hal_cfg.total_chan = 1; + pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH1 : I2S_TDM_ACTIVE_CH0; + pre_alloc_i2s_obj->hal_cfg.total_chan = 1; active_chan = 1; break; case I2S_CHANNEL_FMT_ONLY_LEFT: - p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; - p_i2s[i2s_num]->hal_cfg.total_chan = 1; + pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->left_align ? I2S_TDM_ACTIVE_CH0 : I2S_TDM_ACTIVE_CH1; + pre_alloc_i2s_obj->hal_cfg.total_chan = 1; active_chan = 1; break; case I2S_CHANNEL_FMT_MULTIPLE: - ESP_RETURN_ON_FALSE((i2s_config->chan_mask != 0), ESP_ERR_INVALID_ARG, TAG, "i2s all channel are disabled"); - p_i2s[i2s_num]->hal_cfg.chan_mask = i2s_config->chan_mask; - i2s_get_active_chan_num(&p_i2s[i2s_num]->hal_cfg); + ESP_GOTO_ON_FALSE(i2s_config->chan_mask != 0, ESP_ERR_INVALID_ARG, err, TAG, "i2s all channel are disabled"); + pre_alloc_i2s_obj->hal_cfg.chan_mask = i2s_config->chan_mask; + i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); break; default: - ESP_LOGE(TAG, "wrong i2s channel format, uninstalled i2s."); - goto err; + ESP_GOTO_ON_FALSE(false, ESP_ERR_INVALID_ARG, err, TAG, "invalid I2S channel format:%d", i2s_config->channel_format); } - p_i2s[i2s_num]->hal_cfg.left_align = i2s_config->left_align; - p_i2s[i2s_num]->hal_cfg.big_edin = i2s_config->big_edin; - p_i2s[i2s_num]->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; - p_i2s[i2s_num]->hal_cfg.skip_msk = i2s_config->skip_msk; + pre_alloc_i2s_obj->hal_cfg.left_align = i2s_config->left_align; + pre_alloc_i2s_obj->hal_cfg.big_edin = i2s_config->big_edin; + pre_alloc_i2s_obj->hal_cfg.bit_order_msb = i2s_config->bit_order_msb; + pre_alloc_i2s_obj->hal_cfg.skip_msk = i2s_config->skip_msk; #endif // Set I2S driver configurations - p_i2s[i2s_num]->i2s_num = i2s_num; - p_i2s[i2s_num]->mode = i2s_config->mode; - p_i2s[i2s_num]->channel_num = i2s_get_active_chan_num(&p_i2s[i2s_num]->hal_cfg); - p_i2s[i2s_num]->i2s_queue = i2s_queue; - p_i2s[i2s_num]->bits_per_sample = 0; - p_i2s[i2s_num]->bytes_per_sample = 0; // Not initialized yet - p_i2s[i2s_num]->dma_buf_count = i2s_config->dma_buf_count; - p_i2s[i2s_num]->dma_buf_len = i2s_config->dma_buf_len; - p_i2s[i2s_num]->mclk_multiple = i2s_config->mclk_multiple; + pre_alloc_i2s_obj->i2s_num = i2s_num; + pre_alloc_i2s_obj->mode = i2s_config->mode; + pre_alloc_i2s_obj->channel_num = i2s_get_active_chan_num(&pre_alloc_i2s_obj->hal_cfg); + pre_alloc_i2s_obj->i2s_queue = i2s_queue; + pre_alloc_i2s_obj->bits_per_sample = 0; + pre_alloc_i2s_obj->bytes_per_sample = 0; // Not initialized yet + pre_alloc_i2s_obj->dma_buf_count = i2s_config->dma_buf_count; + pre_alloc_i2s_obj->dma_buf_len = i2s_config->dma_buf_len; + pre_alloc_i2s_obj->mclk_multiple = i2s_config->mclk_multiple; #ifdef CONFIG_PM_ENABLE #if SOC_I2S_SUPPORTS_APLL if (i2s_config->use_apll) { - ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock); + ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); } else #endif // SOC_I2S_SUPPORTS_APLL { - ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &p_i2s[i2s_num]->pm_lock); - } - if (ret != ESP_OK) { - free(p_i2s[i2s_num]); - p_i2s[i2s_num] = NULL; - ESP_LOGE(TAG, "I2S pm lock error"); - return ret; + ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "i2s_driver", &pre_alloc_i2s_obj->pm_lock); } + ESP_GOTO_ON_ERROR(ret, err, TAG, "create PM lock failed"); #endif //CONFIG_PM_ENABLE #if SOC_GDMA_SUPPORTED ret = ESP_OK; @@ -1275,85 +1268,79 @@ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, trig.instance_id = SOC_GDMA_TRIG_PERIPH_I2S0; #endif gdma_channel_alloc_config_t dma_cfg = {.flags.reserve_sibling = 1}; - if ( p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (pre_alloc_i2s_obj->mode & I2S_MODE_RX) { dma_cfg.direction = GDMA_CHANNEL_DIRECTION_RX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->rx_dma_chan), err, TAG, "Register rx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(p_i2s[i2s_num]->rx_dma_chan, trig), err, TAG, "Connect rx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->rx_dma_chan), err, TAG, "Register rx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->rx_dma_chan, trig), err, TAG, "Connect rx dma channel error"); gdma_rx_event_callbacks_t cb = {.on_recv_eof = i2s_dma_rx_callback}; - gdma_register_rx_event_callbacks(p_i2s[i2s_num]->rx_dma_chan, &cb, p_i2s[i2s_num]); + gdma_register_rx_event_callbacks(pre_alloc_i2s_obj->rx_dma_chan, &cb, pre_alloc_i2s_obj); } - if ( p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (pre_alloc_i2s_obj->mode & I2S_MODE_TX) { dma_cfg.direction = GDMA_CHANNEL_DIRECTION_TX; - ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &p_i2s[i2s_num]->tx_dma_chan), err, TAG, "Register tx dma channel error"); - ESP_GOTO_ON_ERROR(gdma_connect(p_i2s[i2s_num]->tx_dma_chan, trig), err, TAG, "Connect tx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_new_channel(&dma_cfg, &pre_alloc_i2s_obj->tx_dma_chan), err, TAG, "Register tx dma channel error"); + ESP_GOTO_ON_ERROR(gdma_connect(pre_alloc_i2s_obj->tx_dma_chan, trig), err, TAG, "Connect tx dma channel error"); gdma_tx_event_callbacks_t cb = {.on_trans_eof = i2s_dma_tx_callback}; - gdma_register_tx_event_callbacks(p_i2s[i2s_num]->tx_dma_chan, &cb, p_i2s[i2s_num]); + gdma_register_tx_event_callbacks(pre_alloc_i2s_obj->tx_dma_chan, &cb, pre_alloc_i2s_obj); } #else //initial interrupt - ret = esp_intr_alloc(i2s_periph_signal[i2s_num].irq, i2s_config->intr_alloc_flags, i2s_intr_handler_default, p_i2s[i2s_num], &p_i2s[i2s_num]->i2s_isr_handle); + ret = esp_intr_alloc(i2s_periph_signal[i2s_num].irq, i2s_config->intr_alloc_flags, i2s_intr_handler_default, pre_alloc_i2s_obj, &pre_alloc_i2s_obj->i2s_isr_handle); ESP_GOTO_ON_ERROR(ret, err, TAG, "Register I2S Interrupt error"); #endif // SOC_GDMA_SUPPORTED i2s_stop(i2s_num); - p_i2s[i2s_num]->use_apll = i2s_config->use_apll; - p_i2s[i2s_num]->fixed_mclk = i2s_config->fixed_mclk; - p_i2s[i2s_num]->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; + pre_alloc_i2s_obj->use_apll = i2s_config->use_apll; + pre_alloc_i2s_obj->fixed_mclk = i2s_config->fixed_mclk; + pre_alloc_i2s_obj->tx_desc_auto_clear = i2s_config->tx_desc_auto_clear; ret = i2s_param_config(i2s_num); ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S param configure error"); if (i2s_queue) { - p_i2s[i2s_num]->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); - ESP_GOTO_ON_ERROR((p_i2s[i2s_num]->i2s_queue != NULL), err, TAG, "I2S queue create failed"); - *((QueueHandle_t *) i2s_queue) = p_i2s[i2s_num]->i2s_queue; - ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(p_i2s[i2s_num]->i2s_queue)); + pre_alloc_i2s_obj->i2s_queue = xQueueCreate(queue_size, sizeof(i2s_event_t)); + ESP_GOTO_ON_ERROR((pre_alloc_i2s_obj->i2s_queue != NULL), err, TAG, "I2S queue create failed"); + *((QueueHandle_t *) i2s_queue) = pre_alloc_i2s_obj->i2s_queue; + ESP_LOGI(TAG, "queue free spaces: %d", uxQueueSpacesAvailable(pre_alloc_i2s_obj->i2s_queue)); } else { - p_i2s[i2s_num]->i2s_queue = NULL; + pre_alloc_i2s_obj->i2s_queue = NULL; } //set clock and start #if SOC_I2S_SUPPORTS_TDM ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - p_i2s[i2s_num]->hal_cfg.bits_cfg.val, + pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, (i2s_channel_t)active_chan); #else ret = i2s_set_clk(i2s_num, i2s_config->sample_rate, - p_i2s[i2s_num]->hal_cfg.bits_cfg.val, + pre_alloc_i2s_obj->hal_cfg.bits_cfg.val, I2S_CHANNEL_STEREO); #endif ESP_GOTO_ON_ERROR(ret, err, TAG, "I2S set clock failed"); return ret; - err: -#ifdef CONFIG_PM_ENABLE - if (p_i2s[i2s_num]->pm_lock) { - esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); - } -#endif i2s_driver_uninstall(i2s_num); return ret; } esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) { - ESP_RETURN_ON_FALSE((i2s_num < I2S_NUM_MAX), ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); - if (p_i2s[i2s_num] == NULL) { - ESP_LOGI(TAG, "already uninstalled"); - return ESP_OK; - } + ESP_RETURN_ON_FALSE(i2s_num < I2S_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "i2s_num error"); + ESP_RETURN_ON_FALSE(p_i2s[i2s_num], ESP_ERR_INVALID_STATE, TAG, "I2S port %d has not installed", i2s_num); + i2s_obj_t *obj = p_i2s[i2s_num]; i2s_stop(i2s_num); #if SOC_I2S_SUPPORTS_ADC_DAC i2s_set_dac_mode(I2S_DAC_CHANNEL_DISABLE); #endif #if SOC_GDMA_SUPPORTED - if (p_i2s[i2s_num]->mode & I2S_MODE_TX) { + if (p_i2s[i2s_num]->tx_dma_chan) { gdma_disconnect(p_i2s[i2s_num]->tx_dma_chan); gdma_del_channel(p_i2s[i2s_num]->tx_dma_chan); } - if (p_i2s[i2s_num]->mode & I2S_MODE_RX) { + if (p_i2s[i2s_num]->rx_dma_chan) { gdma_disconnect(p_i2s[i2s_num]->rx_dma_chan); gdma_del_channel(p_i2s[i2s_num]->rx_dma_chan); } #else - esp_intr_free(p_i2s[i2s_num]->i2s_isr_handle); + if (p_i2s[i2s_num]->i2s_isr_handle) { + esp_intr_free(p_i2s[i2s_num]->i2s_isr_handle); + } #endif if (p_i2s[i2s_num]->tx != NULL && p_i2s[i2s_num]->mode & I2S_MODE_TX) { i2s_destroy_dma_queue(i2s_num, p_i2s[i2s_num]->tx); @@ -1382,12 +1369,8 @@ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num) esp_pm_lock_delete(p_i2s[i2s_num]->pm_lock); } #endif - - free(p_i2s[i2s_num]); - p_i2s[i2s_num] = NULL; -#if !SOC_GDMA_SUPPORTED - periph_module_disable(i2s_periph_signal[i2s_num].module); -#endif + i2s_priv_deregister_object(i2s_num); + free(obj); return ESP_OK; } @@ -1532,3 +1515,31 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re xSemaphoreGive(p_i2s[i2s_num]->rx->mux); return ret; } + +esp_err_t i2s_priv_register_object(void *driver_obj, int port_id) +{ + esp_err_t ret = ESP_ERR_NOT_FOUND; + ESP_RETURN_ON_FALSE(driver_obj && (port_id < SOC_I2S_NUM), ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + portENTER_CRITICAL(&i2s_platform_spinlock); + if (!p_i2s[port_id]) { + ret = ESP_OK; + p_i2s[port_id] = driver_obj; + periph_module_enable(i2s_periph_signal[port_id].module); + } + portEXIT_CRITICAL(&i2s_platform_spinlock); + return ret; +} + +esp_err_t i2s_priv_deregister_object(int port_id) +{ + esp_err_t ret = ESP_ERR_INVALID_STATE; + ESP_RETURN_ON_FALSE(port_id < SOC_I2S_NUM, ESP_ERR_INVALID_ARG, TAG, "invalid arguments"); + portENTER_CRITICAL(&i2s_platform_spinlock); + if (p_i2s[port_id]) { + ret = ESP_OK; + p_i2s[port_id] = NULL; + periph_module_disable(i2s_periph_signal[port_id].module); + } + portEXIT_CRITICAL(&i2s_platform_spinlock); + return ret; +} diff --git a/components/driver/include/driver/i2s.h b/components/driver/include/driver/i2s.h index 3f6031ed26..0c8a43db0d 100644 --- a/components/driver/include/driver/i2s.h +++ b/components/driver/include/driver/i2s.h @@ -148,7 +148,7 @@ typedef struct { * The I2S peripheral output signals can be connected to multiple GPIO pads. * However, the I2S peripheral input signal can only be connected to one GPIO pad. * - * @param i2s_num I2S_NUM_0 or I2S_NUM_1 + * @param i2s_num I2S port number * * @param pin I2S Pin structure, or NULL to set 2-channel 8-bit internal DAC pin configuration (GPIO25 & GPIO26) * @@ -172,7 +172,7 @@ esp_err_t i2s_set_pin(i2s_port_t i2s_num, const i2s_pin_config_t *pin); * In the first downsample process, the sampling number can be 16 or 8. * In the second downsample process, the sampling number is fixed as 8. * So the clock frequency in PDM RX mode would be (fpcm * 64) or (fpcm * 128) accordingly. - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * @param downsample i2s RX down sample rate for PDM mode. * * @note After calling this function, it would call i2s_set_clk inside to update the clock frequency. @@ -193,7 +193,7 @@ esp_err_t i2s_set_pdm_rx_down_sample(i2s_port_t i2s_num, i2s_pdm_dsr_t downsampl * default PDM TX upsample parameters have already been set, * no need to call this function again if you don't have to change the default configuration * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * @param upsample_cfg Set I2S PDM up-sample rate configuration * * @return @@ -207,7 +207,7 @@ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample /** * @brief Install and start I2S driver. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param i2s_config I2S configurations - see i2s_config_t struct * @@ -221,24 +221,26 @@ esp_err_t i2s_set_pdm_tx_up_sample(i2s_port_t i2s_num, const i2s_pdm_tx_upsample * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error * - ESP_ERR_NO_MEM Out of memory + * - ESP_ERR_NOT_FOUND I2S port is not found or has been installed by others (e.g. LCD i80) */ esp_err_t i2s_driver_install(i2s_port_t i2s_num, const i2s_config_t *i2s_config, int queue_size, void *i2s_queue); /** * @brief Uninstall I2S driver. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error + * - ESP_ERR_INVALID_STATE I2S port has been uninstalled by others (e.g. LCD i80) */ esp_err_t i2s_driver_uninstall(i2s_port_t i2s_num); /** * @brief Write data to I2S DMA transmit buffer. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param src Source address to write from * @@ -262,7 +264,7 @@ esp_err_t i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *by /** * @brief Write data to I2S DMA transmit buffer while expanding the number of bits per sample. For example, expanding 16-bit PCM to 32-bit PCM. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param src Source address to write from * @@ -293,7 +295,7 @@ esp_err_t i2s_write_expand(i2s_port_t i2s_num, const void *src, size_t size, siz /** * @brief Read data from I2S DMA receive buffer * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param dest Destination address to read into * @@ -319,7 +321,7 @@ esp_err_t i2s_read(i2s_port_t i2s_num, void *dest, size_t size, size_t *bytes_re * * `bit_clock = rate * (number of channels) * bits_per_sample` * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param rate I2S sample rate (ex: 8000, 44100...) * @@ -337,7 +339,7 @@ esp_err_t i2s_set_sample_rates(i2s_port_t i2s_num, uint32_t rate); * * Disables I2S TX/RX, until i2s_start() is called. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @return * - ESP_OK Success @@ -351,7 +353,7 @@ esp_err_t i2s_stop(i2s_port_t i2s_num); * It is not necessary to call this function after i2s_driver_install() (it is started automatically), however it is necessary to call it after i2s_stop(). * * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @return * - ESP_OK Success @@ -364,7 +366,7 @@ esp_err_t i2s_start(i2s_port_t i2s_num); * * Pushes zero-byte samples into the TX DMA buffer, until it is full. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @return * - ESP_OK Success @@ -379,7 +381,7 @@ esp_err_t i2s_zero_dma_buffer(i2s_port_t i2s_num); * @note This function should be called after i2s driver installed * Only take effecttive when the i2s 'communication_format' is set to 'I2S_COMM_FORMAT_STAND_PCM_SHORT' or 'I2S_COMM_FORMAT_STAND_PCM_LONG' * - * @param i2s_num I2S_NUM_0 + * @param i2s_num I2S port number * * @param pcm_cfg including mode selection and a/u-law decompress or compress configuration paramater * @@ -400,7 +402,7 @@ esp_err_t i2s_pcm_config(i2s_port_t i2s_num, const i2s_pcm_cfg_t *pcm_cfg); * 3. malloc dma buffer; * 4. start i2s * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @param rate I2S sample rate (ex: 8000, 44100...) * @@ -421,7 +423,7 @@ esp_err_t i2s_set_clk(i2s_port_t i2s_num, uint32_t rate, uint32_t bits_cfg, i2s_ /** * @brief get clock set on particular port number. * - * @param i2s_num I2S_NUM_0, I2S_NUM_1 + * @param i2s_num I2S port number * * @return * - actual clock set by i2s driver diff --git a/components/driver/include/esp_private/i2s_platform.h b/components/driver/include/esp_private/i2s_platform.h new file mode 100644 index 0000000000..ff1ebdf17a --- /dev/null +++ b/components/driver/include/esp_private/i2s_platform.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// DO NOT USE THESE APIS IN YOUR APPLICATIONS +// The following APIs are for internal use, public to other IDF components, but not for users' applications. + +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Register an I2S or I2S variant driver object to platform + * + * @note This private API is used to avoid applications from using the same I2S instance for different purpose. + * @note This function will help enable the peripheral APB clock as well. + * + * @param driver_obj Driver object + * @param port_id I2S port number + * @return + * - ESP_OK: The specific I2S port is free and register the new device object successfully + * - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id + * - ESP_ERR_NOT_FOUND: Specific I2S port is not available + */ +esp_err_t i2s_priv_register_object(void *driver_obj, int port_id); + +/** + * @brief Deregister I2S or I2S variant driver object from platform + * + * @note This function will help disable the peripheral APB clock as well. + * + * @param port_id I2S port number + * @return + * - ESP_OK: Deregister I2S port successfully (i.e. that I2S port can used used by other users after this function returns) + * - ESP_ERR_INVALID_ARG: Invalid argument, e.g. wrong port_id + * - ESP_ERR_INVALID_STATE: Specific I2S port is free already + */ +esp_err_t i2s_priv_deregister_object(int port_id); + +#ifdef __cplusplus +} +#endif diff --git a/components/driver/test/test_i2s.c b/components/driver/test/test_i2s.c index 5fc601ab49..2e303b6e3d 100644 --- a/components/driver/test/test_i2s.c +++ b/components/driver/test/test_i2s.c @@ -165,7 +165,7 @@ TEST_CASE("I2S basic driver install, uninstall, set pin test", "[i2s]") TEST_ASSERT(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG); i2s_config.dma_buf_count = 129; TEST_ASSERT(i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL) == ESP_ERR_INVALID_ARG); - TEST_ESP_OK(i2s_driver_uninstall(I2S_NUM_0)); + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, i2s_driver_uninstall(I2S_NUM_0)); } TEST_CASE("I2S Loopback test(master tx and rx)", "[i2s]")