Bläddra i källkod

Merge branch 'bugfix/i2s_output_wrong_freq_after_switch' into 'master'

i2s: workaround for inaccurate PLL frequency after switching

Closes IDF-6705 and IDFCI-1669

See merge request espressif/esp-idf!22975
Kevin (Lao Kaiyao) 2 år sedan
förälder
incheckning
96f2d215a2

+ 1 - 3
components/driver/test_apps/i2s_test_apps/i2s/main/test_i2s.c

@@ -746,10 +746,8 @@ static void i2s_test_common_sample_rate(i2s_chan_handle_t rx_chan, i2s_std_clk_c
     esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_rx_ws_sig, 0, 0);
     esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
 
-    /* Test common sample rate
-     * Workaround: set 12000 as 12001 to bypass the unknown failure, TODO: IDF-6705 */
     const uint32_t test_freq[] = {
-        8000,  10001, 11025, 12001, 16000, 22050,
+        8000,  10000, 11025, 12000, 16000, 22050,
         24000, 32000, 44100, 48000, 64000, 88200,
         96000, 128000,144000,196000};
     int real_pulse = 0;

+ 1 - 3
components/driver/test_apps/i2s_test_apps/legacy_i2s_driver/main/test_legacy_i2s.c

@@ -874,10 +874,8 @@ static void i2s_test_common_sample_rate(i2s_port_t id)
     esp_rom_gpio_connect_out_signal(MASTER_WS_IO, i2s_periph_signal[0].m_tx_ws_sig, 0, 0);
     esp_rom_gpio_connect_in_signal(MASTER_WS_IO, pcnt_periph_signals.groups[0].units[0].channels[0].pulse_sig, 0);
 
-    /* Test common sample rate
-     * Workaround: set 12000 as 12001 to bypass the unknown failure, TODO: IDF-6705 */
     const uint32_t test_freq[] = {
-        8000,  10000,  11025, 12001, 16000, 22050,
+        8000,  10000,  11025, 12000, 16000, 22050,
         24000, 32000,  44100, 48000, 64000, 88200,
         96000, 128000, 144000,196000};
     int real_pulse = 0;

+ 23 - 20
components/hal/esp32/include/hal/i2s_ll.h

@@ -285,6 +285,23 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
     hw->sample_rate_conf.tx_bck_div_num = val;
 }
 
+/**
+ * @brief Configure I2S module clock divider
+ * @note mclk on ESP32 is shared by both TX and RX channel
+ *       mclk = sclk / (mclk_div + b/a)
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param mclk_div integer part of the division from sclk to mclk
+ * @param a Denominator of decimal part
+ * @param b Numerator of decimal part
+ */
+static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
+    hw->clkm_conf.clkm_div_b = b;
+    hw->clkm_conf.clkm_div_a = a;
+}
+
 /**
  * @brief Configure I2S TX module clock divider
  * @note mclk on ESP32 is shared by both TX and RX channel
@@ -328,26 +345,12 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
-    hw->clkm_conf.clkm_div_b = numerator;
-    hw->clkm_conf.clkm_div_a = denominator;
-}
-
-/**
- * @brief Configure I2S module clock divider
- * @note mclk on ESP32 is shared by both TX and RX channel
- *       mclk = sclk / (mclk_div + b/a)
- *
- * @param hw Peripheral I2S hardware instance address.
- * @param mclk_div integer part of the division from sclk to mclk
- * @param a Denominator of decimal part
- * @param b Numerator of decimal part
- */
-static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b)
-{
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
-    hw->clkm_conf.clkm_div_b = b;
-    hw->clkm_conf.clkm_div_a = a;
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_set_raw_mclk_div(hw, 13, 48, 1);
+    i2s_ll_set_raw_mclk_div(hw, mclk_div, denominator, numerator);
 }
 
 /**

+ 48 - 40
components/hal/esp32c3/include/hal/i2s_ll.h

@@ -254,10 +254,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
  */
 static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
-    hw->tx_clkm_div_conf.tx_clkm_div_x = x;
-    hw->tx_clkm_div_conf.tx_clkm_div_y = y;
-    hw->tx_clkm_div_conf.tx_clkm_div_z = z;
-    hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1;
+    typeof(hw->tx_clkm_div_conf) div = {};
+    div.tx_clkm_div_x = x;
+    div.tx_clkm_div_y = y;
+    div.tx_clkm_div_z = z;
+    div.tx_clkm_div_yn1 = yn1;
+    hw->tx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -271,10 +273,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t
  */
 static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
-    hw->rx_clkm_div_conf.rx_clkm_div_x = x;
-    hw->rx_clkm_div_conf.rx_clkm_div_y = y;
-    hw->rx_clkm_div_conf.rx_clkm_div_z = z;
-    hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
+    typeof(hw->rx_clkm_div_conf) div = {};
+    div.rx_clkm_div_x = x;
+    div.rx_clkm_div_y = y;
+    div.rx_clkm_div_z = z;
+    div.rx_clkm_div_yn1 = yn1;
+    hw->rx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -319,23 +323,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        hw->tx_clkm_div_conf.tx_clkm_div_x = 0;
-        hw->tx_clkm_div_conf.tx_clkm_div_y = 0;
-        hw->tx_clkm_div_conf.tx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator);
-            hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
-        } else {
-            hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_z = numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div);
 }
 
@@ -393,23 +399,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        hw->rx_clkm_div_conf.rx_clkm_div_x = 0;
-        hw->rx_clkm_div_conf.rx_clkm_div_y = 0;
-        hw->rx_clkm_div_conf.rx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator);
-            hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
-        } else {
-            hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_z = numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div);
 }
 

+ 48 - 40
components/hal/esp32c6/include/hal/i2s_ll.h

@@ -266,10 +266,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
 static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
     (void)hw;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = x;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = y;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = z;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = yn1;
+    typeof(PCR.i2s_tx_clkm_div_conf) div = {};
+    div.i2s_tx_clkm_div_x = x;
+    div.i2s_tx_clkm_div_y = y;
+    div.i2s_tx_clkm_div_z = z;
+    div.i2s_tx_clkm_div_yn1 = yn1;
+    PCR.i2s_tx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -284,10 +286,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t
 static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
     (void)hw;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = x;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = y;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = z;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = yn1;
+    typeof(PCR.i2s_rx_clkm_div_conf) div = {};
+    div.i2s_rx_clkm_div_x = x;
+    div.i2s_rx_clkm_div_y = y;
+    div.i2s_rx_clkm_div_z = z;
+    div.i2s_rx_clkm_div_yn1 = yn1;
+    PCR.i2s_rx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -333,23 +337,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = 0;
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = 0;
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % (denominator - numerator);
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = denominator - numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 1;
-        } else {
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / numerator - 1;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, mclk_div);
 }
 
@@ -408,23 +414,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = 0;
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = 0;
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % (denominator - numerator);
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = denominator - numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 1;
-        } else {
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / numerator - 1;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, mclk_div);
 }
 

+ 48 - 40
components/hal/esp32h2/include/hal/i2s_ll.h

@@ -273,10 +273,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
 static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
     (void)hw;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = x;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = y;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = z;
-    PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = yn1;
+    typeof(PCR.i2s_tx_clkm_div_conf) div = {};
+    div.i2s_tx_clkm_div_x = x;
+    div.i2s_tx_clkm_div_y = y;
+    div.i2s_tx_clkm_div_z = z;
+    div.i2s_tx_clkm_div_yn1 = yn1;
+    PCR.i2s_tx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -291,10 +293,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t
 static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
     (void)hw;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = x;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = y;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = z;
-    PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = yn1;
+    typeof(PCR.i2s_rx_clkm_div_conf) div = {};
+    div.i2s_rx_clkm_div_x = x;
+    div.i2s_rx_clkm_div_y = y;
+    div.i2s_rx_clkm_div_z = z;
+    div.i2s_rx_clkm_div_yn1 = yn1;
+    PCR.i2s_rx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -340,23 +344,25 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = 0;
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = 0;
-        PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % (denominator - numerator);
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = denominator - numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 1;
-        } else {
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_x = denominator / numerator - 1;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_y = denominator % numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_z = numerator;
-            PCR.i2s_tx_clkm_div_conf.i2s_tx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @96MHz sclk */
+    i2s_ll_tx_set_raw_clk_div(hw, 1, 1, 73, 1);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, 8);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_tx_clkm_conf, i2s_tx_clkm_div_num, mclk_div);
 }
 
@@ -415,23 +421,25 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    if (denominator == 0 || numerator == 0) {
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = 0;
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = 0;
-        PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % (denominator - numerator);
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = denominator - numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 1;
-        } else {
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_x = denominator / numerator - 1;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_y = denominator % numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_z = numerator;
-            PCR.i2s_rx_clkm_div_conf.i2s_rx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @96MHz sclk */
+    i2s_ll_rx_set_raw_clk_div(hw, 1, 1, 73, 1);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, 8);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
     HAL_FORCE_MODIFY_U32_REG_FIELD(PCR.i2s_rx_clkm_conf, i2s_rx_clkm_div_num, mclk_div);
 }
 

+ 23 - 20
components/hal/esp32s2/include/hal/i2s_ll.h

@@ -276,6 +276,23 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
     hw->sample_rate_conf.tx_bck_div_num = val;
 }
 
+/**
+ * @brief Configure I2S module clock divider
+ * @note mclk on ESP32 is shared by both TX and RX channel
+ *       mclk = sclk / (mclk_div + b/a)
+ *
+ * @param hw Peripheral I2S hardware instance address.
+ * @param mclk_div integer part of the division from sclk to mclk
+ * @param a Denominator of decimal part
+ * @param b Numerator of decimal part
+ */
+static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b)
+{
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
+    hw->clkm_conf.clkm_div_b = b;
+    hw->clkm_conf.clkm_div_a = a;
+}
+
 /**
  * @brief Configure I2S TX module clock divider
  * @note mclk on ESP32S2 is shared by both TX and RX channel
@@ -319,26 +336,12 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
-    hw->clkm_conf.clkm_div_b = numerator;
-    hw->clkm_conf.clkm_div_a = denominator;
-}
-
-/**
- * @brief Configure I2S module clock divider
- * @note mclk on ESP32 is shared by both TX and RX channel
- *       mclk = sclk / (mclk_div + b/a)
- *
- * @param hw Peripheral I2S hardware instance address.
- * @param mclk_div integer part of the division from sclk to mclk
- * @param a Denominator of decimal part
- * @param b Numerator of decimal part
- */
-static inline void i2s_ll_set_raw_mclk_div(i2s_dev_t *hw, uint32_t mclk_div, uint32_t a, uint32_t b)
-{
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clkm_conf, clkm_div_num, mclk_div);
-    hw->clkm_conf.clkm_div_b = b;
-    hw->clkm_conf.clkm_div_a = a;
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_set_raw_mclk_div(hw, 13, 48, 1);
+    i2s_ll_set_raw_mclk_div(hw, mclk_div, denominator, numerator);
 }
 
 /**

+ 50 - 42
components/hal/esp32s3/include/hal/i2s_ll.h

@@ -254,10 +254,12 @@ static inline void i2s_ll_tx_set_bck_div_num(i2s_dev_t *hw, uint32_t val)
  */
 static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
-    hw->tx_clkm_div_conf.tx_clkm_div_x = x;
-    hw->tx_clkm_div_conf.tx_clkm_div_y = y;
-    hw->tx_clkm_div_conf.tx_clkm_div_z = z;
-    hw->tx_clkm_div_conf.tx_clkm_div_yn1 = yn1;
+    typeof(hw->tx_clkm_div_conf) div = {};
+    div.tx_clkm_div_x = x;
+    div.tx_clkm_div_y = y;
+    div.tx_clkm_div_z = z;
+    div.tx_clkm_div_yn1 = yn1;
+    hw->tx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -271,10 +273,12 @@ static inline void i2s_ll_tx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t
  */
 static inline void i2s_ll_rx_set_raw_clk_div(i2s_dev_t *hw, uint32_t x, uint32_t y, uint32_t z, uint32_t yn1)
 {
-    hw->rx_clkm_div_conf.rx_clkm_div_x = x;
-    hw->rx_clkm_div_conf.rx_clkm_div_y = y;
-    hw->rx_clkm_div_conf.rx_clkm_div_z = z;
-    hw->rx_clkm_div_conf.rx_clkm_div_yn1 = yn1;
+    typeof(hw->rx_clkm_div_conf) div = {};
+    div.rx_clkm_div_x = x;
+    div.rx_clkm_div_y = y;
+    div.rx_clkm_div_z = z;
+    div.rx_clkm_div_yn1 = yn1;
+    hw->rx_clkm_div_conf.val = div.val;
 }
 
 /**
@@ -319,24 +323,26 @@ static inline void i2s_ll_tx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div);
-    if (denominator == 0 || numerator == 0) {
-        hw->tx_clkm_div_conf.tx_clkm_div_x = 0;
-        hw->tx_clkm_div_conf.tx_clkm_div_y = 0;
-        hw->tx_clkm_div_conf.tx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % (denominator - numerator);
-            hw->tx_clkm_div_conf.tx_clkm_div_z = denominator - numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 1;
-        } else {
-            hw->tx_clkm_div_conf.tx_clkm_div_x = denominator / numerator - 1;
-            hw->tx_clkm_div_conf.tx_clkm_div_y = denominator % numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_z = numerator;
-            hw->tx_clkm_div_conf.tx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_tx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_tx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_clkm_conf, tx_clkm_div_num, mclk_div);
 }
 
 /**
@@ -393,24 +399,26 @@ static inline void i2s_ll_rx_set_mclk(i2s_dev_t *hw, uint32_t sclk, uint32_t mcl
         }
     }
 finish:
-    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div);
-    if (denominator == 0 || numerator == 0) {
-        hw->rx_clkm_div_conf.rx_clkm_div_x = 0;
-        hw->rx_clkm_div_conf.rx_clkm_div_y = 0;
-        hw->rx_clkm_div_conf.rx_clkm_div_z = 0;
-    } else {
-        if (numerator > denominator / 2) {
-            hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / (denominator - numerator) - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % (denominator - numerator);
-            hw->rx_clkm_div_conf.rx_clkm_div_z = denominator - numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 1;
-        } else {
-            hw->rx_clkm_div_conf.rx_clkm_div_x = denominator / numerator - 1;
-            hw->rx_clkm_div_conf.rx_clkm_div_y = denominator % numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_z = numerator;
-            hw->rx_clkm_div_conf.rx_clkm_div_yn1 = 0;
-        }
+    /* Workaround for inaccurate clock while switching from a relatively low sample rate to a high sample rate
+     * Set to particular coefficients first then update to the target coefficients,
+     * otherwise the clock division might be inaccurate.
+     * The particular coefficients here is calculated from 44100 Hz with 2 slots & 16-bit width @ 160MHz sclk */
+    i2s_ll_rx_set_raw_clk_div(hw, 47, 0, 1, 0);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, 13);
+
+    uint32_t div_x = 0;
+    uint32_t div_y = 0;
+    uint32_t div_z = 0;
+    uint32_t div_yn1 = 0;
+    /* If any of denominator and numerator is 0, set all the coefficients to 0 */
+    if (denominator && numerator) {
+        div_yn1 = numerator * 2 > denominator;
+        div_z = div_yn1 ? denominator - numerator : numerator;
+        div_x = denominator / div_z - 1;
+        div_y = denominator % div_z;
     }
+    i2s_ll_rx_set_raw_clk_div(hw, div_x, div_y, div_z, div_yn1);
+    HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_clkm_conf, rx_clkm_div_num, mclk_div);
 }
 
 /**