|
25 | 25 | #include "soc/soc_caps.h" |
26 | 26 | #include "hal/gpio_hal.h" |
27 | 27 | #include "hal/i2s_hal.h" |
| 28 | +#include "hal/hal_utils.h" |
28 | 29 | #include "hal/dma_types.h" |
29 | 30 | #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE |
30 | 31 | #include "hal/cache_hal.h" |
@@ -1374,6 +1375,95 @@ esp_err_t i2s_channel_read(i2s_chan_handle_t handle, void *dest, size_t size, si |
1374 | 1375 | return ret; |
1375 | 1376 | } |
1376 | 1377 |
|
| 1378 | +esp_err_t i2s_channel_tune_rate(i2s_chan_handle_t handle, const i2s_tuning_config_t *tune_cfg, i2s_tuning_info_t *tune_info) |
| 1379 | +{ |
| 1380 | + /** We tune the sample rate via the MCLK clock. |
| 1381 | + * Because the sample rate is decided by MCLK eventually, |
| 1382 | + * and MCLK has a higher resolution which can be tuned more precisely. |
| 1383 | + */ |
| 1384 | + |
| 1385 | + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "NULL pointer"); |
| 1386 | + ESP_RETURN_ON_FALSE(!handle->is_external, ESP_ERR_NOT_SUPPORTED, TAG, "Not support to tune rate for external mclk"); |
| 1387 | + |
| 1388 | + /* If no tuning configuration given, just return the current information */ |
| 1389 | + if (tune_cfg == NULL) { |
| 1390 | + xSemaphoreTake(handle->mutex, portMAX_DELAY); |
| 1391 | + goto result; |
| 1392 | + } |
| 1393 | + ESP_RETURN_ON_FALSE(tune_cfg->max_delta_mclk >= tune_cfg->min_delta_mclk, ESP_ERR_INVALID_ARG, TAG, "invalid range"); |
| 1394 | + |
| 1395 | + uint32_t new_mclk = 0; |
| 1396 | + |
| 1397 | + /* Get the new MCLK according to the tuning operation */ |
| 1398 | + switch (tune_cfg->tune_mode) { |
| 1399 | + case I2S_TUNING_MODE_ADDSUB: |
| 1400 | + new_mclk = handle->curr_mclk_hz + tune_cfg->tune_mclk_val; |
| 1401 | + break; |
| 1402 | + case I2S_TUNING_MODE_SET: |
| 1403 | + new_mclk = tune_cfg->tune_mclk_val; |
| 1404 | + break; |
| 1405 | + case I2S_TUNING_MODE_RESET: |
| 1406 | + new_mclk = handle->origin_mclk_hz; |
| 1407 | + break; |
| 1408 | + default: |
| 1409 | + return ESP_ERR_INVALID_ARG; |
| 1410 | + } |
| 1411 | + /* Check if the tuned mclk is within the supposed range */ |
| 1412 | + if ((int32_t)new_mclk - (int32_t)handle->origin_mclk_hz > tune_cfg->max_delta_mclk) { |
| 1413 | + new_mclk = handle->origin_mclk_hz + tune_cfg->max_delta_mclk; |
| 1414 | + } else if ((int32_t)new_mclk - (int32_t)handle->origin_mclk_hz < tune_cfg->min_delta_mclk) { |
| 1415 | + new_mclk = handle->origin_mclk_hz + tune_cfg->min_delta_mclk; |
| 1416 | + } |
| 1417 | + xSemaphoreTake(handle->mutex, portMAX_DELAY); |
| 1418 | +#if SOC_CLK_APLL_SUPPORTED |
| 1419 | + if (handle->clk_src == I2S_CLK_SRC_APLL) { |
| 1420 | + periph_rtc_apll_release(); |
| 1421 | + handle->sclk_hz = i2s_set_get_apll_freq(new_mclk); |
| 1422 | + periph_rtc_apll_acquire(); |
| 1423 | + } |
| 1424 | +#endif |
| 1425 | + /* Calculate the new divider */ |
| 1426 | + hal_utils_clk_div_t mclk_div = {}; |
| 1427 | + i2s_hal_calc_mclk_precise_division(handle->sclk_hz, new_mclk, &mclk_div); |
| 1428 | + /* mclk_div = sclk / mclk >= 2 */ |
| 1429 | + if (mclk_div.integer < 2) { |
| 1430 | + return ESP_ERR_INVALID_ARG; |
| 1431 | + } |
| 1432 | + /* Set the new divider for MCLK */ |
| 1433 | + I2S_CLOCK_SRC_ATOMIC() { |
| 1434 | + if (handle->dir == I2S_DIR_TX) { |
| 1435 | + i2s_ll_tx_set_mclk(handle->controller->hal.dev, &mclk_div); |
| 1436 | +#if SOC_I2S_HW_VERSION_2 |
| 1437 | + i2s_ll_tx_update(handle->controller->hal.dev); |
| 1438 | +#endif |
| 1439 | + } else { |
| 1440 | + i2s_ll_rx_set_mclk(handle->controller->hal.dev, &mclk_div); |
| 1441 | +#if SOC_I2S_HW_VERSION_2 |
| 1442 | + i2s_ll_rx_update(handle->controller->hal.dev); |
| 1443 | +#endif |
| 1444 | + } |
| 1445 | + } |
| 1446 | + /* Save the current mclk frequency */ |
| 1447 | + handle->curr_mclk_hz = (uint32_t)(((uint64_t)handle->sclk_hz * mclk_div.denominator) / |
| 1448 | + (mclk_div.integer * mclk_div.denominator + mclk_div.numerator)); |
| 1449 | +result: |
| 1450 | + /* Assign the information if needed */ |
| 1451 | + if (tune_info) { |
| 1452 | + tune_info->curr_mclk_hz = handle->curr_mclk_hz; |
| 1453 | + tune_info->delta_mclk_hz = (int32_t)handle->curr_mclk_hz - (int32_t)handle->origin_mclk_hz; |
| 1454 | + uint32_t tot_size = handle->dma.buf_size * handle->dma.desc_num; |
| 1455 | + uint32_t used_size = 0; |
| 1456 | + if (handle->dir == I2S_DIR_TX) { |
| 1457 | + used_size = uxQueueSpacesAvailable(handle->msg_queue) * handle->dma.buf_size + handle->dma.rw_pos; |
| 1458 | + } else { |
| 1459 | + used_size = uxQueueMessagesWaiting(handle->msg_queue) * handle->dma.buf_size + handle->dma.buf_size - handle->dma.rw_pos; |
| 1460 | + } |
| 1461 | + tune_info->water_mark = used_size * 100 / tot_size; |
| 1462 | + } |
| 1463 | + xSemaphoreGive(handle->mutex); |
| 1464 | + return ESP_OK; |
| 1465 | +} |
| 1466 | + |
1377 | 1467 | #if SOC_I2S_SUPPORTS_TX_SYNC_CNT |
1378 | 1468 | uint32_t i2s_sync_get_bclk_count(i2s_chan_handle_t tx_handle) |
1379 | 1469 | { |
|
0 commit comments