#include #include #include "driver/i2c.h" #include "esp_idf_version.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) #include "driver/i2s_std.h" #include "driver/i2s_tdm.h" #include "driver/i2s_pdm.h" #include "soc/soc_caps.h" #else #include "driver/i2s.h" #endif #include "esp_codec_dev.h" #include "esp_codec_dev_defaults.h" #include "esp_codec_dev_os.h" #include "codec_board.h" #include "codec_init.h" #include "esp_log.h" #include "dummy_codec.h" #include "driver/i2c_master.h" #include "esp_vfs_fat.h" #include "driver/sdmmc_host.h" #include "driver/sdmmc_defs.h" #define TAG "CODEC_INIT" typedef struct { bool inited; const audio_codec_data_if_t *data_if; const audio_codec_data_if_t *data_in_if; const audio_codec_gpio_if_t *gpio_if; const audio_codec_ctrl_if_t *in_ctrl_if; const audio_codec_ctrl_if_t *out_ctrl_if; const audio_codec_if_t *out_codec_if; const audio_codec_if_t *in_codec_if; esp_codec_dev_handle_t play_dev; esp_codec_dev_handle_t record_dev; } codec_res_t; #define USE_I2C_MASTER typedef struct { bool inited; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) i2s_chan_handle_t tx_handle; i2s_chan_handle_t rx_handle; #endif } i2s_keep_t; static bool i2c_inited[MAX_I2C_NUM]; static i2c_master_bus_handle_t i2c_bus_handle[MAX_I2C_NUM]; static i2s_keep_t *i2s_keep[MAX_I2S_NUM]; static codec_res_t codec_res; static int _i2c_init(uint8_t port) { if (port >= MAX_I2C_NUM) { return -1; } if (i2c_inited[port]) { return 0; } codec_i2c_pin_t i2c_pin; if (get_i2c_pin(port, &i2c_pin)) { ESP_LOGE(TAG, "Fail to get i2c pin"); return -1; } // workaround to check i2c initialized already int ret = 0; #ifdef USE_I2C_MASTER i2c_master_bus_config_t i2c_bus_config = { 0 }; i2c_bus_config.clk_source = I2C_CLK_SRC_DEFAULT; i2c_bus_config.i2c_port = port; i2c_bus_config.scl_io_num = i2c_pin.scl; i2c_bus_config.sda_io_num = i2c_pin.sda; i2c_bus_config.glitch_ignore_cnt = 7; i2c_bus_config.flags.enable_internal_pullup = true; ret = i2c_new_master_bus(&i2c_bus_config, &i2c_bus_handle[port]); if (ret != ESP_OK) { ESP_LOGE(TAG, "failed to initialize I2C master bus port %d", port); return -1; } ESP_LOGI(TAG, "Set mater handle %d %p", port, i2c_bus_handle[port]); #else i2c_config_t i2c_cfg = { .mode = I2C_MODE_MASTER, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master.clk_speed = 100000, }; i2c_cfg.sda_io_num = i2c_pin.sda; i2c_cfg.scl_io_num = i2c_pin.scl; ret = i2c_param_config(port, &i2c_cfg); if (ret != ESP_OK) { return -1; } ret = i2c_driver_install(port, i2c_cfg.mode, 0, 0, 0); #endif if (ret == 0) { i2c_inited[port] = true; } return ret; } void *get_i2c_bus_handle(uint8_t port) { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) // Try to get port from I2C driver directly i2c_master_bus_handle_t bus_handle = NULL; i2c_master_get_bus_handle(port, &bus_handle); return bus_handle; #endif return i2c_bus_handle[port]; } static int _i2c_deinit(uint8_t port) { if (port >= MAX_I2C_NUM || i2c_inited[port] == false) { return -1; } #ifdef USE_I2C_MASTER i2c_del_master_bus(i2c_bus_handle[port]); #else i2c_driver_delete(port); #endif i2c_bus_handle[port] = NULL; i2c_inited[port] = false; return 0; } static int _i2s_init(uint8_t port, esp_codec_dev_type_t dev_type, codec_init_cfg_t *init_cfg) { if (port >= MAX_I2S_NUM) { return -1; } if (i2s_keep[port]) { return 0; } codec_i2s_pin_t i2s_cfg; if (get_i2s_pin(port, &i2s_cfg)) { ESP_LOGE(TAG, "Fail to get i2s pin"); return -1; } ESP_LOGI(TAG, "Init i2s %d type: %d mclk:%d bclk:%d ws:%d din:%d dout:%d", port, dev_type, i2s_cfg.mclk, i2s_cfg.bclk, i2s_cfg.ws, i2s_cfg.din, i2s_cfg.dout); i2s_keep[port] = (i2s_keep_t *)calloc(1, sizeof(i2s_keep_t)); if (i2s_keep[port] == NULL) { return -1; } #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); chan_cfg.auto_clear = true; i2s_std_config_t std_cfg = { .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(16000), .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO), .gpio_cfg = { .mclk = i2s_cfg.mclk, .bclk = i2s_cfg.bclk, .ws = i2s_cfg.ws, .dout = i2s_cfg.dout, .din = i2s_cfg.din, }, }; bool output = (dev_type & ESP_CODEC_DEV_TYPE_OUT) > 0; bool input = (dev_type & ESP_CODEC_DEV_TYPE_IN) > 0; // Same I2S have tx and rx but use rx or tx only if (output && init_cfg->out_mode == CODEC_I2S_MODE_NONE) { output = false; } if (input && init_cfg->in_mode == CODEC_I2S_MODE_NONE) { input = false; } if (input == false && output == false) { return 0; } #ifdef SOC_I2S_SUPPORTS_TDM i2s_tdm_slot_mask_t slot_mask = I2S_TDM_SLOT0 | I2S_TDM_SLOT1 | I2S_TDM_SLOT2 | I2S_TDM_SLOT3; i2s_tdm_config_t tdm_cfg = { .slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(32, I2S_SLOT_MODE_STEREO, slot_mask), .clk_cfg = I2S_TDM_CLK_DEFAULT_CONFIG(16000), .gpio_cfg = { .mclk = i2s_cfg.mclk, .bclk = i2s_cfg.bclk, .ws = i2s_cfg.ws, .dout = i2s_cfg.dout, .din = i2s_cfg.din, }, }; tdm_cfg.slot_cfg.total_slot = 4; #endif chan_cfg.id = I2S_NUM_AUTO; // Use auto ID int ret = i2s_new_channel(&chan_cfg, output == false ? NULL : &i2s_keep[port]->tx_handle, input == false ? NULL : &i2s_keep[port]->rx_handle); ESP_LOGI(TAG, "tx:%p rx:%p", i2s_keep[port]->tx_handle, i2s_keep[port]->rx_handle); if (i2s_keep[port]->tx_handle) { if (init_cfg->out_mode == CODEC_I2S_MODE_STD) { ret = i2s_channel_init_std_mode(i2s_keep[port]->tx_handle, &std_cfg); ESP_LOGI(TAG, "output init std ret %d", ret); } #ifdef SOC_I2S_SUPPORTS_TDM else if (init_cfg->out_mode == CODEC_I2S_MODE_TDM) { ret = i2s_channel_init_tdm_mode(i2s_keep[port]->tx_handle, &tdm_cfg); ESP_LOGI(TAG, "output init tdm ret %d", ret); } #endif #ifdef SOC_I2S_SUPPORTS_PDM_TX else if (init_cfg->out_mode == CODEC_I2S_MODE_PDM) { i2s_pdm_tx_config_t pdm_cfg = { .clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(16000), .slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO), .gpio_cfg = { .dout = i2s_cfg.dout, .clk = i2s_cfg.bclk, .invert_flags = { .clk_inv = false, }, }, }; ret = i2s_channel_init_pdm_tx_mode(i2s_keep[port]->tx_handle, &pdm_cfg); ESP_LOGI(TAG, "output init pdm ret %d", ret); } #endif } if (i2s_keep[port]->rx_handle) { if (init_cfg->in_mode == CODEC_I2S_MODE_STD) { ret = i2s_channel_init_std_mode(i2s_keep[port]->rx_handle, &std_cfg); ESP_LOGI(TAG, "Input init std ret %d", ret); } #ifdef SOC_I2S_SUPPORTS_TDM else if (init_cfg->in_mode == CODEC_I2S_MODE_TDM) { ret = i2s_channel_init_tdm_mode(i2s_keep[port]->rx_handle, &tdm_cfg); ESP_LOGI(TAG, "Input init tdm ret %d", ret); } #endif #ifdef SOC_I2S_SUPPORTS_PDM_RX else if (init_cfg->in_mode == CODEC_I2S_MODE_PDM) { i2s_pdm_rx_config_t pdm_cfg = { .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(16000), .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(16, I2S_SLOT_MODE_STEREO), .gpio_cfg = { .din = i2s_cfg.din, .clk = i2s_cfg.bclk, .invert_flags = { .clk_inv = false, }, }, }; ret = i2s_channel_init_pdm_rx_mode(i2s_keep[port]->rx_handle, &pdm_cfg); ESP_LOGI(TAG, "Input init pdm ret %d", ret); } #endif } // Enable I2S here for maybe some codec need I2S clock to set register correctly if (i2s_keep[port]->tx_handle) { i2s_channel_enable(i2s_keep[port]->tx_handle); } if (i2s_keep[port]->rx_handle) { i2s_channel_enable(i2s_keep[port]->rx_handle); } #else i2s_config_t i2s_config = { .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), .sample_rate = 44100, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = I2S_COMM_FORMAT_STAND_I2S, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_IRAM, .dma_buf_count = 3, .dma_buf_len = 300, .use_apll = true, .tx_desc_auto_clear = true, .fixed_mclk = 0 //.mclk_multiple = 256, }; int ret = i2s_driver_install(port, &i2s_config, 0, NULL); i2s_pin_config_t i2s_pin_cfg = { .mck_io_num = i2s_cfg.mclk, .bck_io_num = i2s_cfg.bclk, .ws_io_num = i2s_cfg.ws, .data_out_num = i2s_cfg.dout, .data_in_num = i2s_cfg.din, }; i2s_set_pin(port, &i2s_pin_cfg); #endif ESP_LOGI(TAG, "Init i2s %d ok", port); i2s_keep[port]->inited = true; return ret; } static int _i2s_deinit(uint8_t port) { if (port >= MAX_I2S_NUM) { return -1; } if (i2s_keep[port] == NULL) { return 0; } #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) if (i2s_keep[port]->tx_handle) { i2s_channel_disable(i2s_keep[port]->tx_handle); } if (i2s_keep[port]->rx_handle) { i2s_channel_disable(i2s_keep[port]->rx_handle); } if (i2s_keep[port]->tx_handle) { i2s_del_channel(i2s_keep[port]->tx_handle); } if (i2s_keep[port]->rx_handle) { i2s_del_channel(i2s_keep[port]->rx_handle); } #else i2s_driver_uninstall(port); #endif free(i2s_keep[port]); i2s_keep[port] = NULL; return 0; } int init_i2c(uint8_t port) { return _i2c_init(port); } int deinit_i2c(uint8_t port) { return _i2c_deinit(port); } static int check_i2c_inited(int8_t port) { if (port < 0) { return 0; } // Already installed if (get_i2c_bus_handle(port)) { return 0; } return _i2c_init(port); } int init_codec(codec_init_cfg_t *cfg) { if (cfg == NULL) { return -1; } if (codec_res.inited) { ESP_LOGI(TAG, "Already initialized"); return 0; } codec_cfg_t out_cfg = { 0 }; codec_cfg_t in_cfg = { 0 }; bool has_out = false; bool has_in = false; // Get codec configuration if (get_out_codec_cfg(&out_cfg) == 0) { has_out = true; } if (get_in_codec_cfg(&in_cfg) == 0) { has_in = true; } if (has_out == false && has_in == false) { ESP_LOGE(TAG, "No codec device found"); return -1; } // Try to get I2C handle check_i2c_inited(0); // Init i2c and i2s bool same_i2s = (has_in && has_out && out_cfg.i2s_port == in_cfg.i2s_port); ESP_LOGI(TAG, "in:%d out:%d port: %d", has_in, has_out, out_cfg.i2s_port == in_cfg.i2s_port); if (has_out) { if (check_i2c_inited(out_cfg.i2c_port) < 0) { ESP_LOGE(TAG, "Fail to int i2c: %d", out_cfg.i2c_port); return -1; } ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port); if (_i2s_init(out_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_OUT, cfg)) { ESP_LOGE(TAG, "Fail to init i2s: %d", out_cfg.i2s_port); return -1; } ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port); } if (has_in) { if (check_i2c_inited(in_cfg.i2c_port) < 0) { ESP_LOGE(TAG, "Fail to int i2c: %d", in_cfg.i2c_port); return -1; } ESP_LOGI(TAG, "Success to int i2c: %d", in_cfg.i2c_port); if (_i2s_init(in_cfg.i2s_port, same_i2s ? ESP_CODEC_DEV_TYPE_IN_OUT : ESP_CODEC_DEV_TYPE_IN, cfg)) { ESP_LOGE(TAG, "Fail to init i2s: %d", in_cfg.i2s_port); return -1; } ESP_LOGI(TAG, "Success to init i2s: %d", in_cfg.i2s_port); } // Create gpio interface codec_res.gpio_if = audio_codec_new_gpio(); bool same_codec = same_i2s && (in_cfg.codec_type == out_cfg.codec_type); if (has_out) { audio_codec_i2s_cfg_t i2s_out_cfg = { .port = out_cfg.i2s_port, #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) .tx_handle = i2s_keep[out_cfg.i2s_port]->tx_handle, .rx_handle = i2s_keep[out_cfg.i2s_port]->rx_handle, #endif }; ESP_LOGI(TAG, "Get out handle %p port %d", i2s_out_cfg.tx_handle, out_cfg.i2s_port); codec_res.data_if = audio_codec_new_i2s_data(&i2s_out_cfg); audio_codec_i2c_cfg_t i2c_cfg = { .port = out_cfg.i2c_port, #ifdef USE_I2C_MASTER .bus_handle = get_i2c_bus_handle(out_cfg.i2c_port), #endif }; // TODO add other codec support switch (out_cfg.codec_type) { case CODEC_TYPE_ES8311: { i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8311_CODEC_DEFAULT_ADDR; codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); es8311_codec_cfg_t es8311_cfg = { .codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC, .ctrl_if = codec_res.out_ctrl_if, .gpio_if = codec_res.gpio_if, .pa_pin = out_cfg.pa_pin, .use_mclk = out_cfg.use_mclk, .hw_gain.pa_gain = out_cfg.pa_gain, }; codec_res.out_codec_if = es8311_codec_new(&es8311_cfg); } break; case CODEC_TYPE_ES8388: { i2c_cfg.addr = out_cfg.i2c_addr ? out_cfg.i2c_addr : ES8388_CODEC_DEFAULT_ADDR; codec_res.out_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); es8388_codec_cfg_t es8388_cfg = { .codec_mode = same_codec ? ESP_CODEC_DEV_WORK_MODE_BOTH : ESP_CODEC_DEV_WORK_MODE_DAC, .ctrl_if = codec_res.out_ctrl_if, .gpio_if = codec_res.gpio_if, .pa_pin = out_cfg.pa_pin, .hw_gain.pa_gain = out_cfg.pa_gain, }; codec_res.out_codec_if = es8388_codec_new(&es8388_cfg); } break; case CODEC_TYPE_DUMMY: { dummy_codec_cfg_t dummy_cfg = { .gpio_if = codec_res.gpio_if, .enable_gpio = out_cfg.pa_pin, }; codec_res.out_codec_if = dummy_codec_new(&dummy_cfg); } break; default: ESP_LOGE(TAG, "TODO not supported output codec type %d", out_cfg.codec_type); break; } esp_codec_dev_cfg_t dev_cfg = { .codec_if = codec_res.out_codec_if, .data_if = codec_res.data_if, .dev_type = ESP_CODEC_DEV_TYPE_OUT, }; if (same_codec && cfg->reuse_dev) { dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN_OUT; } codec_res.play_dev = esp_codec_dev_new(&dev_cfg); if (same_codec) { if (cfg->reuse_dev) { codec_res.record_dev = codec_res.play_dev; } else { // separate record from playback so that can set different fs dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_IN; codec_res.record_dev = esp_codec_dev_new(&dev_cfg); } } } if (same_codec == false && has_in) { if (same_i2s == false) { audio_codec_i2s_cfg_t i2s_in_cfg = { .port = in_cfg.i2s_port, #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) .rx_handle = i2s_keep[in_cfg.i2s_port]->rx_handle, #endif }; ESP_LOGI(TAG, "Get in handle %p port %d", i2s_in_cfg.rx_handle, i2s_in_cfg.port); codec_res.data_in_if = audio_codec_new_i2s_data(&i2s_in_cfg); } audio_codec_i2c_cfg_t i2c_cfg = { .port = in_cfg.i2c_port, #ifdef USE_I2C_MASTER .bus_handle = get_i2c_bus_handle(in_cfg.i2c_port), #endif }; // TODO add other codec support switch (in_cfg.codec_type) { case CODEC_TYPE_ES7210: { i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7210_CODEC_DEFAULT_ADDR; codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); es7210_codec_cfg_t es7210_cfg = { .ctrl_if = codec_res.in_ctrl_if, .mic_selected = ES7210_SEL_MIC1 | ES7210_SEL_MIC3, }; if (cfg->in_use_tdm || (cfg->in_mode == CODEC_I2S_MODE_TDM)) { es7210_cfg.mic_selected |= ES7210_SEL_MIC2 | ES7210_SEL_MIC4; } codec_res.in_codec_if = es7210_codec_new(&es7210_cfg); } break; case CODEC_TYPE_ES7243: { i2c_cfg.addr = in_cfg.i2c_addr ? in_cfg.i2c_addr : ES7243_CODEC_DEFAULT_ADDR; codec_res.in_ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg); es7243_codec_cfg_t es7243_cfg = { .ctrl_if = codec_res.in_ctrl_if, }; codec_res.in_codec_if = es7243_codec_new(&es7243_cfg); } break; case CODEC_TYPE_DUMMY: { dummy_codec_cfg_t dummy_cfg = { .gpio_if = codec_res.gpio_if, .enable_gpio = out_cfg.pa_pin, }; codec_res.in_codec_if = dummy_codec_new(&dummy_cfg); } default: break; } esp_codec_dev_cfg_t dev_cfg = { .codec_if = codec_res.in_codec_if, .data_if = same_i2s ? codec_res.data_if : codec_res.data_in_if, .dev_type = ESP_CODEC_DEV_TYPE_IN, }; codec_res.record_dev = esp_codec_dev_new(&dev_cfg); } // Set default volume and gain for play and record if (codec_res.play_dev) { esp_codec_dev_set_out_vol(codec_res.play_dev, 60.0); } if (codec_res.record_dev) { esp_codec_dev_set_in_gain(codec_res.record_dev, 30.0); } if ((codec_res.play_dev != NULL) || (codec_res.record_dev != NULL)) { codec_res.inited = true; return 0; } return -1; } esp_codec_dev_handle_t get_playback_handle(void) { return codec_res.play_dev; } esp_codec_dev_handle_t get_record_handle(void) { return codec_res.record_dev; } void deinit_codec(void) { if (codec_res.play_dev) { esp_codec_dev_delete(codec_res.play_dev); if (codec_res.record_dev == codec_res.play_dev) { codec_res.record_dev = NULL; } codec_res.play_dev = NULL; } if (codec_res.record_dev) { esp_codec_dev_delete(codec_res.record_dev); codec_res.record_dev = NULL; } // Delete codec interface if (codec_res.in_codec_if) { audio_codec_delete_codec_if(codec_res.in_codec_if); codec_res.in_codec_if = NULL; } if (codec_res.out_codec_if) { audio_codec_delete_codec_if(codec_res.out_codec_if); codec_res.out_codec_if = NULL; } // Delete codec control interface if (codec_res.in_ctrl_if) { audio_codec_delete_ctrl_if(codec_res.in_ctrl_if); codec_res.in_ctrl_if = NULL; } if (codec_res.out_ctrl_if) { audio_codec_delete_ctrl_if(codec_res.out_ctrl_if); codec_res.out_ctrl_if = NULL; } if (codec_res.data_if) { audio_codec_delete_data_if(codec_res.data_if); codec_res.data_if = NULL; } if (codec_res.data_in_if) { audio_codec_delete_data_if(codec_res.data_in_if); codec_res.data_in_if = NULL; } if (codec_res.gpio_if) { audio_codec_delete_gpio_if(codec_res.gpio_if); codec_res.gpio_if = NULL; } for (int i = 0; i < MAX_I2C_NUM; i++) { _i2c_deinit(i); _i2s_deinit(i); } codec_res.inited = false; } static sdmmc_card_t *card = NULL; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #if CONFIG_IDF_TARGET_ESP32P4 #include "esp_ldo_regulator.h" #endif #endif static void enable_mmc_phy_power(void) { #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #if CONFIG_IDF_TARGET_ESP32P4 esp_ldo_channel_config_t ldo_cfg = { .chan_id = 4, .voltage_mv = 3300, }; esp_ldo_channel_handle_t ldo_phy_chan; esp_ldo_acquire_channel(&ldo_cfg, &ldo_phy_chan); #endif #endif } #if CONFIG_IDF_TARGET_ESP32P4 static void sdmmc_get_slot(const int slot, sdmmc_slot_config_t *config) { memset(config, 0, sizeof(sdmmc_slot_config_t)); config->cd = SDMMC_SLOT_NO_CD; config->wp = SDMMC_SLOT_NO_WP; config->width = 4; config->flags = 0; } #endif int mount_sdcard(void) { sdcard_cfg_t cfg = { 0 }; enable_mmc_phy_power(); int ret = get_sdcard_config(&cfg); if (ret != 0) { return ret; } #if defined CONFIG_IDF_TARGET_ESP32 gpio_config_t sdcard_pwr_pin_cfg = { .pin_bit_mask = 1UL << GPIO_NUM_13, .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE, }; gpio_config(&sdcard_pwr_pin_cfg); gpio_set_level(GPIO_NUM_13, 0); #endif #if SOC_SDMMC_HOST_SUPPORTED esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, .max_files = 5, }; sdmmc_host_t host = SDMMC_HOST_DEFAULT(); #if CONFIG_IDF_TARGET_ESP32P4 host.slot = 0; #endif #if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; #endif sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); slot_config.width = cfg.d3 ? 4 : 1; #if SOC_SDMMC_USE_GPIO_MATRIX slot_config.width = cfg.d3 ? 4 : 1; slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; slot_config.d4 = -1; slot_config.d5 = -1; slot_config.d6 = -1; slot_config.d7 = -1; slot_config.cd = -1; slot_config.wp = -1; slot_config.clk = cfg.clk; slot_config.cmd = cfg.cmd; slot_config.d0 = cfg.d0; slot_config.d1 = cfg.d1 ? cfg.d1 : -1; slot_config.d2 = cfg.d2 ? cfg.d2 : -1; slot_config.d3 = cfg.d3 ? cfg.d3 : -1; #endif /* SOC_SDMMC_USE_GPIO_MATRIX */ #if CONFIG_IDF_TARGET_ESP32P4 sdmmc_get_slot(0, &slot_config); #endif printf("use %d %d %d %d\n", cfg.d0, cfg.d1, cfg.d2, cfg.d3); return esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card); #else return -1; #endif } void *get_sdcard_handle(void) { return card; } void unmount_sdcard(void) { if (card) { esp_vfs_fat_sdcard_unmount("/sdcard", card); card = NULL; } }