Эх сурвалжийг харах

i2s: expose resource object to other component

morris 4 жил өмнө
parent
commit
3bcd9278fa

+ 107 - 96
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;
-    }
-
-    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];
+    // 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;
     }
-    //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;
+}

+ 17 - 15
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

+ 48 - 0
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

+ 1 - 1
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]")