Skip to content

Commit

Permalink
Merge branch 'feature/i2s_support_mclk_input' into 'master'
Browse files Browse the repository at this point in the history
feat(i2s): supported external clock source input

Closes IDF-7889

See merge request espressif/esp-idf!24942
  • Loading branch information
L-KAYA committed Aug 16, 2023
2 parents d8b34c7 + 22bb572 commit f629c4b
Show file tree
Hide file tree
Showing 30 changed files with 289 additions and 184 deletions.
4 changes: 2 additions & 2 deletions components/driver/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ components/driver/test_apps/i2s_test_apps/i2s:
disable:
- if: SOC_I2S_SUPPORTED != 1

components/driver/test_apps/i2s_test_apps/i2s_tdm:
components/driver/test_apps/i2s_test_apps/i2s_multi_dev:
disable:
- if: SOC_I2S_SUPPORTS_TDM != 1
- if: SOC_I2S_HW_VERSION_2 != 1

components/driver/test_apps/i2s_test_apps/legacy_i2s_adc_dac:
disable:
Expand Down
50 changes: 18 additions & 32 deletions components/driver/i2s/i2s_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "i2s_private.h"

#include "clk_ctrl_os.h"
#include "esp_clk_tree.h"
#include "esp_intr_alloc.h"
#include "esp_check.h"
#include "esp_attr.h"
Expand Down Expand Up @@ -435,39 +436,16 @@ static uint32_t i2s_set_get_apll_freq(uint32_t mclk_freq_hz)
}
#endif

// [clk_tree] TODO: replace the following switch table by clk_tree API
uint32_t i2s_get_source_clk_freq(i2s_clock_src_t clk_src, uint32_t mclk_freq_hz)
{
switch (clk_src)
{
uint32_t clk_freq = 0;
#if SOC_I2S_SUPPORTS_APLL
case I2S_CLK_SRC_APLL:
if (clk_src == I2S_CLK_SRC_APLL) {
return i2s_set_get_apll_freq(mclk_freq_hz);
#endif
#if SOC_I2S_SUPPORTS_XTAL
case I2S_CLK_SRC_XTAL:
(void)mclk_freq_hz;
return esp_clk_xtal_freq();
#endif
#if SOC_I2S_SUPPORTS_PLL_F160M
case I2S_CLK_SRC_PLL_160M:
(void)mclk_freq_hz;
return I2S_LL_PLL_F160M_CLK_FREQ;
#endif
#if SOC_I2S_SUPPORTS_PLL_F96M
case I2S_CLK_SRC_PLL_96M:
(void)mclk_freq_hz;
return I2S_LL_PLL_F96M_CLK_FREQ;
#endif
#if SOC_I2S_SUPPORTS_PLL_F64M
case I2S_CLK_SRC_PLL_64M:
(void)mclk_freq_hz;
return I2S_LL_PLL_F64M_CLK_FREQ;
#endif
default:
// Invalid clock source
return 0;
}
#endif
esp_clk_tree_src_get_freq_hz(clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &clk_freq);
return clk_freq;
}

#if SOC_GDMA_SUPPORTED
Expand Down Expand Up @@ -711,7 +689,7 @@ void i2s_gpio_loopback_set(gpio_num_t gpio, uint32_t out_sig_idx, uint32_t in_si
}
}

esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert)
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, i2s_clock_src_t clk_src, bool is_invert)
{
if (gpio_num == I2S_GPIO_UNUSED) {
return ESP_OK;
Expand All @@ -721,6 +699,7 @@ esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, b
ESP_ERR_INVALID_ARG, TAG,
"ESP32 only support to set GPIO0/GPIO1/GPIO3 as mclk signal, error GPIO number:%d", gpio_num);
bool is_i2s0 = id == I2S_NUM_0;
bool is_apll = clk_src == I2S_CLK_SRC_APLL;
if (gpio_num == GPIO_NUM_0) {
gpio_hal_iomux_func_sel(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
gpio_ll_iomux_pin_ctrl(is_apll ? 0xFFF6 : (is_i2s0 ? 0xFFF0 : 0xFFFF));
Expand All @@ -733,9 +712,16 @@ esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, b
}
#else
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "mck_io_num invalid");
i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, false, is_invert);
#endif
ESP_LOGD(TAG, "MCLK is pinned to GPIO%d on I2S%d", id, gpio_num);
#if SOC_I2S_HW_VERSION_2
if (clk_src == I2S_CLK_SRC_EXTERNAL) {
i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_in_sig, true, is_invert);
} else
#endif // SOC_I2S_HW_VERSION_2
{
i2s_gpio_check_and_set(gpio_num, i2s_periph_signal[id].mck_out_sig, false, is_invert);
}
#endif // CONFIG_IDF_TARGET_ESP32
ESP_LOGD(TAG, "MCLK is pinned to GPIO%d on I2S%d", gpio_num, id);
return ESP_OK;
}

Expand Down
6 changes: 6 additions & 0 deletions components/driver/i2s/i2s_pdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ static esp_err_t i2s_pdm_tx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_tx
{
esp_err_t ret = ESP_OK;
i2s_pdm_tx_config_t *pdm_tx_cfg = (i2s_pdm_tx_config_t *)(handle->mode_info);
#if SOC_I2S_HW_VERSION_2
ESP_RETURN_ON_FALSE(clk_cfg->clk_src != I2S_CLK_SRC_EXTERNAL, ESP_ERR_INVALID_ARG, TAG, "not support external clock source in pdm mode");
#endif
ESP_RETURN_ON_FALSE(clk_cfg->up_sample_fs <= 480, ESP_ERR_INVALID_ARG, TAG, "up_sample_fs should be within 480");

i2s_hal_clock_info_t clk_info;
Expand Down Expand Up @@ -342,6 +345,9 @@ static esp_err_t i2s_pdm_rx_set_clock(i2s_chan_handle_t handle, const i2s_pdm_rx
{
esp_err_t ret = ESP_OK;
i2s_pdm_rx_config_t *pdm_rx_cfg = (i2s_pdm_rx_config_t *)(handle->mode_info);
#if SOC_I2S_HW_VERSION_2
ESP_RETURN_ON_FALSE(clk_cfg->clk_src != I2S_CLK_SRC_EXTERNAL, ESP_ERR_INVALID_ARG, TAG, "not support external clock source in pdm mode");
#endif

i2s_hal_clock_info_t clk_info;
/* Calculate clock parameters */
Expand Down
4 changes: 2 additions & 2 deletions components/driver/i2s/i2s_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,13 @@ void i2s_gpio_check_and_set(gpio_num_t gpio, uint32_t signal_idx, bool is_input,
*
* @param id I2S port id
* @param gpio_num GPIO number
* @param is_apll Is using APLL as clock source
* @param clk_src The clock source of this I2S port
* @param is_invert Is invert the GPIO
* @return
* - ESP_OK Set mclk output gpio success
* - ESP_ERR_INVALID_ARG Invalid GPIO number
*/
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, bool is_apll, bool is_invert);
esp_err_t i2s_check_set_mclk(i2s_port_t id, gpio_num_t gpio_num, i2s_clock_src_t clk_src, bool is_invert);

/**
* @brief Attach data out signal and data in signal to a same gpio
Expand Down
20 changes: 11 additions & 9 deletions components/driver/i2s/i2s_std.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@ static esp_err_t i2s_std_calculate_clock(i2s_chan_handle_t handle, const i2s_std
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
#if SOC_I2S_HW_VERSION_2
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ?
clk_cfg->ext_clk_freq_hz : i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
#else
clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
#endif
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;

/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large for the current clock source");

return ESP_OK;
}
Expand Down Expand Up @@ -151,6 +156,9 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
}

/* Set mclk pin */
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, std_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");

if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
Expand All @@ -165,13 +173,6 @@ static esp_err_t i2s_std_set_gpio(i2s_chan_handle_t handle, const i2s_std_gpio_c
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
}
} else {
/* mclk only available in master mode */
#if SOC_I2S_SUPPORTS_APLL
bool is_apll = std_cfg->clk_cfg.clk_src == I2S_CLK_SRC_APLL;
#else
bool is_apll = false;
#endif
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, is_apll, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
/* For "rx + master" mode, select RX signal index for ws and bck */
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
Expand Down Expand Up @@ -208,7 +209,6 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
handle->mode_info = calloc(1, sizeof(i2s_std_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_FALSE(handle->state == I2S_CHAN_STATE_REGISTER, ESP_ERR_INVALID_STATE, err, TAG, "the channel has initialized already");
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_std_slot should be called before i2s_set_std_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_std_set_slot(handle, &std_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
Expand All @@ -219,6 +219,8 @@ esp_err_t i2s_channel_init_std_mode(i2s_chan_handle_t handle, const i2s_std_conf
}
#endif
ESP_GOTO_ON_ERROR(i2s_std_set_clock(handle, &std_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
/* i2s_std_set_gpio should be called after i2s_std_set_clock as mclk relies on the clock source */
ESP_GOTO_ON_ERROR(i2s_std_set_gpio(handle, &std_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed");
#if SOC_I2S_HW_VERSION_2
/* Enable clock to start outputting mclk signal. Some codecs will reset once mclk stop */
Expand Down
15 changes: 9 additions & 6 deletions components/driver/i2s/i2s_tdm.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ static esp_err_t i2s_tdm_calculate_clock(i2s_chan_handle_t handle, const i2s_tdm
clk_info->bclk = rate * handle->total_slot * slot_bits;
clk_info->mclk = clk_info->bclk * clk_info->bclk_div;
}
clk_info->sclk = i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
clk_info->sclk = clk_cfg->clk_src == I2S_CLK_SRC_EXTERNAL ?
clk_cfg->ext_clk_freq_hz : i2s_get_source_clk_freq(clk_cfg->clk_src, clk_info->mclk);
clk_info->mclk_div = clk_info->sclk / clk_info->mclk;

/* Check if the configuration is correct */
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large");
ESP_RETURN_ON_FALSE(clk_info->mclk_div, ESP_ERR_INVALID_ARG, TAG, "sample rate is too large for the current clock source");

return ESP_OK;
}
Expand Down Expand Up @@ -146,6 +147,7 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
ESP_ERR_INVALID_ARG, TAG, "bclk invalid");
ESP_RETURN_ON_FALSE((gpio_cfg->ws == -1 || GPIO_IS_VALID_GPIO(gpio_cfg->ws)),
ESP_ERR_INVALID_ARG, TAG, "ws invalid");
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
/* Loopback if dout = din */
if (gpio_cfg->dout != -1 &&
gpio_cfg->dout == gpio_cfg->din) {
Expand All @@ -158,6 +160,9 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
i2s_gpio_check_and_set(gpio_cfg->din, i2s_periph_signal[id].data_in_sig, true, false);
}

/* Set mclk pin */
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, tdm_cfg->clk_cfg.clk_src, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");

if (handle->role == I2S_ROLE_SLAVE) {
/* For "tx + slave" mode, select TX signal index for ws and bck */
if (handle->dir == I2S_DIR_TX && !handle->controller->full_duplex) {
Expand All @@ -172,8 +177,6 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
i2s_gpio_check_and_set(gpio_cfg->bclk, i2s_periph_signal[id].s_rx_bck_sig, true, gpio_cfg->invert_flags.bclk_inv);
}
} else {
/* mclk only available in master mode */
ESP_RETURN_ON_ERROR(i2s_check_set_mclk(id, gpio_cfg->mclk, false, gpio_cfg->invert_flags.mclk_inv), TAG, "mclk config failed");
/* For "rx + master" mode, select RX signal index for ws and bck */
if (handle->dir == I2S_DIR_RX && !handle->controller->full_duplex) {
#if SOC_I2S_HW_VERSION_2
Expand All @@ -188,7 +191,6 @@ static esp_err_t i2s_tdm_set_gpio(i2s_chan_handle_t handle, const i2s_tdm_gpio_c
}
}
/* Update the mode info: gpio configuration */
i2s_tdm_config_t *tdm_cfg = (i2s_tdm_config_t *)(handle->mode_info);
memcpy(&(tdm_cfg->gpio_cfg), gpio_cfg, sizeof(i2s_tdm_gpio_config_t));

return ESP_OK;
Expand All @@ -212,7 +214,6 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
}
handle->mode_info = calloc(1, sizeof(i2s_tdm_config_t));
ESP_GOTO_ON_FALSE(handle->mode_info, ESP_ERR_NO_MEM, err, TAG, "no memory for storing the configurations");
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
/* i2s_set_tdm_slot should be called before i2s_set_tdm_clock while initializing, because clock is relay on the slot */
ESP_GOTO_ON_ERROR(i2s_tdm_set_slot(handle, &tdm_cfg->slot_cfg), err, TAG, "initialize channel failed while setting slot");
#if SOC_I2S_SUPPORTS_APLL
Expand All @@ -223,6 +224,8 @@ esp_err_t i2s_channel_init_tdm_mode(i2s_chan_handle_t handle, const i2s_tdm_conf
}
#endif
ESP_GOTO_ON_ERROR(i2s_tdm_set_clock(handle, &tdm_cfg->clk_cfg), err, TAG, "initialize channel failed while setting clock");
/* i2s_tdm_set_gpio should be called after i2s_tdm_set_clock as mclk relies on the clock source */
ESP_GOTO_ON_ERROR(i2s_tdm_set_gpio(handle, &tdm_cfg->gpio_cfg), err, TAG, "initialize channel failed while setting gpio pins");
ESP_GOTO_ON_ERROR(i2s_init_dma_intr(handle, I2S_INTR_ALLOC_FLAGS), err, TAG, "initialize dma interrupt failed");

#if SOC_I2S_HW_VERSION_2
Expand Down
11 changes: 9 additions & 2 deletions components/driver/i2s/include/driver/i2s_std.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,14 @@ typedef struct {
typedef struct {
/* General fields */
uint32_t sample_rate_hz; /*!< I2S sample rate */
i2s_clock_src_t clk_src; /*!< Choose clock source */
i2s_clock_src_t clk_src; /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources.
* selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock input via MCLK pin,
*/
#if SOC_I2S_HW_VERSION_2
uint32_t ext_clk_freq_hz; /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored,
* Please make sure the frequency input is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * 2'
*/
#endif
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate
* Default is 256 in the helper macro, it can satisfy most of cases,
* but please set this field a multiple of '3' (like 384) when using 24-bit data width,
Expand All @@ -249,7 +256,7 @@ typedef struct {
* @brief I2S standard mode GPIO pins configuration
*/
typedef struct {
gpio_num_t mclk; /*!< MCK pin, output */
gpio_num_t mclk; /*!< MCK pin, output by default, input if the clock source is selected to 'I2S_CLK_SRC_EXTERNAL' */
gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */
gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */
gpio_num_t dout; /*!< DATA pin, output */
Expand Down
10 changes: 8 additions & 2 deletions components/driver/i2s/include/driver/i2s_tdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,13 @@ typedef struct {
typedef struct {
/* General fields */
uint32_t sample_rate_hz; /*!< I2S sample rate */
i2s_clock_src_t clk_src; /*!< Choose clock source */
i2s_clock_src_t clk_src; /*!< Choose clock source, see 'soc_periph_i2s_clk_src_t' for the supported clock sources.
* selected 'I2S_CLK_SRC_EXTERNAL'(if supports) to enable the external source clock inputted via MCLK pin,
* please make sure the frequency inputted is equal or greater than 'sample_rate_hz * mclk_multiple'
*/
uint32_t ext_clk_freq_hz; /*!< External clock source frequency in Hz, only take effect when 'clk_src = I2S_CLK_SRC_EXTERNAL', otherwise this field will be ignored
* Please make sure the frequency inputted is equal or greater than bclk, i.e. 'sample_rate_hz * slot_bits * slot_num'
*/
i2s_mclk_multiple_t mclk_multiple; /*!< The multiple of mclk to the sample rate, only take effect for master role */
uint32_t bclk_div; /*!< The division from mclk to bclk, only take effect for slave role, it shouldn't be smaller than 8. Increase this field when data sent by slave lag behind */
} i2s_tdm_clk_config_t;
Expand All @@ -165,7 +171,7 @@ typedef struct {
* @brief I2S TDM mode GPIO pins configuration
*/
typedef struct {
gpio_num_t mclk; /*!< MCK pin, output */
gpio_num_t mclk; /*!< MCK pin, output by default, input if the clock source is selected to 'I2S_CLK_SRC_EXTERNAL' */
gpio_num_t bclk; /*!< BCK pin, input in slave role, output in master role */
gpio_num_t ws; /*!< WS pin, input in slave role, output in master role */
gpio_num_t dout; /*!< DATA pin, output */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ set(EXTRA_COMPONENT_DIRS
)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(i2s_tdm_full_duplex_test)
project(i2s_multi_dev_test)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
idf_component_register(SRCS "test_app_main.c" "test_i2s_tdm_full_duplex.c"
idf_component_register(SRCS "test_app_main.c" "test_i2s_multi_dev.c"
PRIV_REQUIRES unity driver test_utils
INCLUDE_DIRS "."
WHOLE_ARCHIVE
Expand Down
Loading

0 comments on commit f629c4b

Please sign in to comment.