From 5d2f7bb7c50c9d96c44b3bb6f162b5913f889f41 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Wed, 8 Apr 2026 11:14:17 +0200 Subject: [PATCH 01/13] make SPIRAM optional and for all Implementations first implementation - not tested --- components/hub75/Kconfig | 12 +++++ components/hub75/include/hub75_config.h | 21 ++++++++ .../hub75/src/platforms/gdma/gdma_dma.cpp | 51 +++++++++++++++++-- .../hub75/src/platforms/gdma/gdma_dma.h | 4 ++ .../hub75/src/platforms/i2s/i2s_dma.cpp | 51 +++++++++++++++++-- components/hub75/src/platforms/i2s/i2s_dma.h | 4 ++ .../hub75/src/platforms/parlio/parlio_dma.cpp | 9 ++-- 7 files changed, 142 insertions(+), 10 deletions(-) diff --git a/components/hub75/Kconfig b/components/hub75/Kconfig index 665d726..062b885 100644 --- a/components/hub75/Kconfig +++ b/components/hub75/Kconfig @@ -724,6 +724,18 @@ menu "HUB75 RGB LED Matrix Driver" Recommended for best performance. Uses ~2-4 KB of IRAM. Disable only if IRAM space is critically constrained. + config HUB75_EXTERNAL_FRAMEBUFFERS + bool "Use External Framebuffers" + default y if IDF_TARGET_ESP32P4 + default n + help + Allocates the Framebuffers in the external memory. + It reduces internal memory usage. + + If enabled, the HUB75 clock speed may need to be lowered to + avoid overloading the SPIRAM bandwidth, especially since it + may also be shared with other modules such as WiFi. + endmenu # ======================================== diff --git a/components/hub75/include/hub75_config.h b/components/hub75/include/hub75_config.h index 7d0a395..a7a79b1 100644 --- a/components/hub75/include/hub75_config.h +++ b/components/hub75/include/hub75_config.h @@ -10,6 +10,8 @@ extern "C" { #endif +#include "sdkconfig.h" + /** * IRAM optimization * Place hot-path code in instruction RAM to prevent flash cache stalls @@ -78,6 +80,25 @@ extern "C" { #endif #endif + +/** + * Use external framebuffer in PSRAM / SPIRAM + * Ser via menuconfig or override: -D HUB75_EXTERNAL_FRAMEBUFFERS + */ +#ifndef HUB75_EXTERNAL_FRAMEBUFFERS +#ifdef CONFIG_HUB75_EXTERNAL_FRAMEBUFFERS +#define HUB75_EXTERNAL_FRAMEBUFFERS CONFIG_HUB75_EXTERNAL_FRAMEBUFFERS +#elif defined(CONFIG_SPIRAM) && defined(CONFIG_IDF_TARGET_ESP32P4) +#define HUB75_EXTERNAL_FRAMEBUFFERS 1 +#else +#define HUB75_EXTERNAL_FRAMEBUFFERS 0 +#endif +#endif //HUB75_EXTERNAL_FRAMEBUFFERS + + + + + #ifdef __cplusplus } #endif diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index 5f07714..cc57215 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -41,6 +41,8 @@ #include #endif #include +#include +#include static const char *const TAG = "GdmaDma"; @@ -387,10 +389,17 @@ bool GdmaDma::allocate_row_buffers() { size_t pixels_per_bitplane = dma_width_; // DMA buffer width (all panels chained horizontally) size_t buffer_size_per_row = pixels_per_bitplane * bit_depth_ * 2; // uint16_t = 2 bytes size_t total_buffer_size = num_rows_ * buffer_size_per_row; + total_buffer_bytes_ = total_buffer_size; // Always allocate first buffer (buffer A, index 0) - ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows", total_buffer_size, num_rows_); - dma_buffers_[0] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, MALLOC_CAP_DMA); +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 + static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM; + ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows (PSRAM)", total_buffer_size, num_rows_); +#else + static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL; + ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows (internal RAM)", total_buffer_size, num_rows_); +#endif + dma_buffers_[0] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, DMA_MEM_CAPS); if (!dma_buffers_[0]) { ESP_LOGE(TAG, "Failed to allocate %zu bytes for buffer A", total_buffer_size); return false; @@ -417,7 +426,7 @@ bool GdmaDma::allocate_row_buffers() { // Conditionally allocate second buffer (buffer B, index 1) if (config_.double_buffer) { ESP_LOGI(TAG, "Allocating buffer B: %zu bytes (double buffering enabled)", total_buffer_size); - dma_buffers_[1] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, MALLOC_CAP_DMA); + dma_buffers_[1] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, DMA_MEM_CAPS); if (!dma_buffers_[1]) { ESP_LOGE(TAG, "Failed to allocate %zu bytes for buffer B", total_buffer_size); // Continue in single-buffer mode @@ -671,6 +680,9 @@ HUB75_IRAM void GdmaDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_ HUB75_PROFILE_PIXEL(); } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void GdmaDma::clear() { @@ -692,6 +704,9 @@ void GdmaDma::clear() { } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } HUB75_IRAM void GdmaDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b) { @@ -783,6 +798,9 @@ HUB75_IRAM void GdmaDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, ui } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void GdmaDma::flip_buffer() { @@ -791,6 +809,10 @@ void GdmaDma::flip_buffer() { return; } + // Flush CPU cache for active buffer BEFORE swap (buffer we were drawing to) + // Only needed in double buffer mode (draw/clear skip flush, defer to here) + flush_cache_to_dma(); + // Seamless descriptor chain redirection (no stop/start!) // // DMA is continuously traversing a circular descriptor chain (front buffer). @@ -979,6 +1001,9 @@ void GdmaDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t bri } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } return; } @@ -1066,6 +1091,9 @@ void GdmaDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t bri } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void GdmaDma::set_brightness_oe() { @@ -1279,6 +1307,23 @@ void GdmaDma::calculate_bcm_timings() { ESP_LOGI(TAG, "BCM timing calculated (lsbMsbTransitionBit used by set_brightness_oe for OE control)"); } +void GdmaDma::flush_cache_to_dma() { + // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync + // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() + // returns false and we skip the msync (which would be unnecessary overhead). + if (!dma_buffers_[active_idx_] || !esp_ptr_external_ram(dma_buffers_[active_idx_])) { + return; + } + + // Flush cache: CPU cache → PSRAM (C2M = Cache to Memory) + // Flush the active buffer (CPU drawing buffer) + esp_err_t err = esp_cache_msync(dma_buffers_[active_idx_], total_buffer_bytes_, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); + } +} + // ============================================================================ // Compile-Time Validation (ESP-IDF 5.x only - requires consteval/GCC 9+) // ============================================================================ diff --git a/components/hub75/src/platforms/gdma/gdma_dma.h b/components/hub75/src/platforms/gdma/gdma_dma.h index 144ee2a..0227b36 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.h +++ b/components/hub75/src/platforms/gdma/gdma_dma.h @@ -130,6 +130,10 @@ class GdmaDma : public PlatformDma { // BCM timing calculation (calculates lsbMsbTransitionBit for OE control) void calculate_bcm_timings(); + void flush_cache_to_dma(); + + size_t total_buffer_bytes_; // Cached total buffer size per buffer (computed once, never changes) + gdma_channel_handle_t dma_chan_; const uint8_t bit_depth_; // Bit depth from config (6, 7, 8, 10, or 12) uint8_t lsbMsbTransitionBit_; // BCM optimization threshold (calculated at init) diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index 1a455e4..ccfb769 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -39,6 +39,8 @@ #endif #include +#include +#include static const char *const TAG = "I2sDma"; @@ -443,10 +445,17 @@ bool I2sDma::allocate_row_buffers() { size_t pixels_per_bitplane = dma_width_; // DMA buffer width (all panels chained horizontally) size_t buffer_size_per_row = pixels_per_bitplane * bit_depth_ * 2; // uint16_t = 2 bytes size_t total_buffer_size = num_rows_ * buffer_size_per_row; + total_buffer_bytes_ = total_buffer_size; // Always allocate first buffer (buffer A, index 0) - ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows", total_buffer_size, num_rows_); - dma_buffers_[0] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, MALLOC_CAP_DMA); +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 + static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM; + ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows (PSRAM)", total_buffer_size, num_rows_); +#else + static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL; + ESP_LOGI(TAG, "Allocating buffer A: %zu bytes for %d rows (internal RAM)", total_buffer_size, num_rows_); +#endif + dma_buffers_[0] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, DMA_MEM_CAPS); if (!dma_buffers_[0]) { ESP_LOGE(TAG, "Failed to allocate %zu bytes for buffer A", total_buffer_size); return false; @@ -473,7 +482,7 @@ bool I2sDma::allocate_row_buffers() { // Conditionally allocate second buffer (buffer B, index 1) if (config_.double_buffer) { ESP_LOGI(TAG, "Allocating buffer B: %zu bytes (double buffering enabled)", total_buffer_size); - dma_buffers_[1] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, MALLOC_CAP_DMA); + dma_buffers_[1] = (uint8_t *) heap_caps_calloc(1, total_buffer_size, DMA_MEM_CAPS); if (!dma_buffers_[1]) { ESP_LOGE(TAG, "Failed to allocate %zu bytes for buffer B", total_buffer_size); // Continue in single-buffer mode @@ -804,6 +813,9 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } return; } @@ -883,6 +895,9 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void I2sDma::set_brightness_oe() { @@ -1138,6 +1153,9 @@ HUB75_IRAM void I2sDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t HUB75_PROFILE_PIXEL(); } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void I2sDma::clear() { @@ -1159,6 +1177,9 @@ void I2sDma::clear() { } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } HUB75_IRAM void I2sDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t r, uint8_t g, uint8_t b) { @@ -1245,6 +1266,9 @@ HUB75_IRAM void I2sDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uin } } } + if(!config_.double_buffer){ + flush_cache_to_dma(); + } } void I2sDma::flip_buffer() { @@ -1253,6 +1277,10 @@ void I2sDma::flip_buffer() { return; } + // Flush CPU cache for active buffer BEFORE swap (buffer we were drawing to) + // Only needed in double buffer mode (draw/clear skip flush, defer to here) + flush_cache_to_dma(); + // Seamless descriptor chain redirection (no stop/start!) // // DMA is continuously traversing a circular descriptor chain (front buffer). @@ -1276,6 +1304,23 @@ void I2sDma::flip_buffer() { // DMA seamlessly transitions at next frame boundary - no interruption! } +void I2sDma::flush_cache_to_dma() { + // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync + // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() + // returns false and we skip the msync (which would be unnecessary overhead). + if (!dma_buffers_[active_idx_] || !esp_ptr_external_ram(dma_buffers_[active_idx_])) { + return; + } + + // Flush cache: CPU cache → PSRAM (C2M = Cache to Memory) + // Flush the active buffer (CPU drawing buffer) + esp_err_t err = esp_cache_msync(dma_buffers_[active_idx_], total_buffer_bytes_, + ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); + } +} + // ============================================================================ // Compile-Time Validation (ESP-IDF 5.x only - requires consteval/GCC 9+) // ============================================================================ diff --git a/components/hub75/src/platforms/i2s/i2s_dma.h b/components/hub75/src/platforms/i2s/i2s_dma.h index bd3ba1b..6b16188 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.h +++ b/components/hub75/src/platforms/i2s/i2s_dma.h @@ -129,6 +129,10 @@ class I2sDma : public PlatformDma { // BCM timing calculation (calculates lsbMsbTransitionBit for OE control) void calculate_bcm_timings(); + void flush_cache_to_dma(); + + size_t total_buffer_bytes_; // Cached total buffer size per buffer (computed once, never changes) + volatile i2s_dev_t *i2s_dev_; const uint8_t bit_depth_; // Bit depth from config (6, 7, 8, 10, or 12) uint8_t lsbMsbTransitionBit_; // BCM optimization threshold (calculated at init) diff --git a/components/hub75/src/platforms/parlio/parlio_dma.cpp b/components/hub75/src/platforms/parlio/parlio_dma.cpp index 11afc53..e3793a0 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.cpp +++ b/components/hub75/src/platforms/parlio/parlio_dma.cpp @@ -436,14 +436,15 @@ bool ParlioDma::allocate_row_buffers() { total_buffer_bytes_ = total_bytes; // Cache for flush_cache_to_dma() and build_transaction_queue() // Always allocate first buffer (buffer 0) + +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 + static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM; + ESP_LOGI(TAG, "Allocating buffer [0]: %zu bytes for %d rows × %d bits (PSRAM)", total_bytes, num_rows_, bit_depth_); +#else // ESP32-C6 has no PSRAM, so use internal DMA-capable memory -#ifdef CONFIG_IDF_TARGET_ESP32C6 static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL; ESP_LOGI(TAG, "Allocating buffer [0]: %zu bytes for %d rows × %d bits (internal RAM)", total_bytes, num_rows_, bit_depth_); -#else - static constexpr uint32_t DMA_MEM_CAPS = MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM; - ESP_LOGI(TAG, "Allocating buffer [0]: %zu bytes for %d rows × %d bits (PSRAM)", total_bytes, num_rows_, bit_depth_); #endif dma_buffers_[0] = (uint16_t *) heap_caps_calloc(total_words, sizeof(uint16_t), DMA_MEM_CAPS); From 7c3562eae644f6ef8d464e37f74bd096fc369e49 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Wed, 8 Apr 2026 12:47:30 +0200 Subject: [PATCH 02/13] disable code for cache_write_back, if no SPIRAM is used --- components/hub75/src/platforms/gdma/gdma_dma.cpp | 2 ++ components/hub75/src/platforms/i2s/i2s_dma.cpp | 2 ++ components/hub75/src/platforms/parlio/parlio_dma.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index cc57215..985d931 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -1308,6 +1308,7 @@ void GdmaDma::calculate_bcm_timings() { } void GdmaDma::flush_cache_to_dma() { +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() // returns false and we skip the msync (which would be unnecessary overhead). @@ -1322,6 +1323,7 @@ void GdmaDma::flush_cache_to_dma() { if (err != ESP_OK) { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } +#endif } // ============================================================================ diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index ccfb769..2eec818 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -1305,6 +1305,7 @@ void I2sDma::flip_buffer() { } void I2sDma::flush_cache_to_dma() { +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() // returns false and we skip the msync (which would be unnecessary overhead). @@ -1319,6 +1320,7 @@ void I2sDma::flush_cache_to_dma() { if (err != ESP_OK) { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } +#endif } // ============================================================================ diff --git a/components/hub75/src/platforms/parlio/parlio_dma.cpp b/components/hub75/src/platforms/parlio/parlio_dma.cpp index e3793a0..68d6976 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.cpp +++ b/components/hub75/src/platforms/parlio/parlio_dma.cpp @@ -776,6 +776,7 @@ void ParlioDma::set_brightness_oe() { } void ParlioDma::flush_cache_to_dma() { +#if HUB75_EXTERNAL_FRAMEBUFFERS == 1 // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() // returns false and we skip the msync (which would be unnecessary overhead). @@ -790,6 +791,7 @@ void ParlioDma::flush_cache_to_dma() { if (err != ESP_OK) { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } +#endif } bool ParlioDma::build_transaction_queue() { From bef6d2a8f983b5a909750c6ff063956895cb27e7 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 10:26:39 +0200 Subject: [PATCH 03/13] fix caching neccessarity --- components/hub75/src/platforms/gdma/gdma_dma.cpp | 10 +++------- components/hub75/src/platforms/i2s/i2s_dma.cpp | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index 985d931..0e72429 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -1001,9 +1001,6 @@ void GdmaDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t bri } } } - if(!config_.double_buffer){ - flush_cache_to_dma(); - } return; } @@ -1091,9 +1088,6 @@ void GdmaDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t bri } } } - if(!config_.double_buffer){ - flush_cache_to_dma(); - } } void GdmaDma::set_brightness_oe() { @@ -1113,7 +1107,9 @@ void GdmaDma::set_brightness_oe() { set_brightness_oe_internal(row_buffer, brightness); } } - + + flush_cache_to_dma(); + ESP_LOGD(TAG, "Brightness OE configuration complete"); } diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index 2eec818..32ae3b6 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -813,9 +813,6 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig } } } - if(!config_.double_buffer){ - flush_cache_to_dma(); - } return; } @@ -895,9 +892,6 @@ void I2sDma::set_brightness_oe_internal(RowBitPlaneBuffer *buffers, uint8_t brig } } } - if(!config_.double_buffer){ - flush_cache_to_dma(); - } } void I2sDma::set_brightness_oe() { @@ -917,7 +911,9 @@ void I2sDma::set_brightness_oe() { set_brightness_oe_internal(row_buffer, brightness); } } - + + flush_cache_to_dma(); + ESP_LOGD(TAG, "Brightness OE configuration complete"); } From 129186fa06324f7a9d62f02e0f0776067034e93d Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 13:48:35 +0200 Subject: [PATCH 04/13] fix for esp32-c6 or soc without looping parlio tx in general --- components/hub75/include/hub75_config.h | 6 ++++ .../hub75/src/platforms/parlio/parlio_dma.cpp | 29 ++++++++++++++++++- .../hub75/src/platforms/parlio/parlio_dma.h | 5 ++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/components/hub75/include/hub75_config.h b/components/hub75/include/hub75_config.h index a7a79b1..a0300ce 100644 --- a/components/hub75/include/hub75_config.h +++ b/components/hub75/include/hub75_config.h @@ -93,6 +93,12 @@ extern "C" { #else #define HUB75_EXTERNAL_FRAMEBUFFERS 0 #endif +#else // HUB75_EXTERNAL_FRAMEBUFFERS +// maybe use SOC_SPIRAM_SUPPORTED +#if defined(CONFIG_IDF_TARGET_ESP32C6) && HUB75_EXTERNAL_FRAMEBUFFERS != 0 +#undef HUB75_EXTERNAL_FRAMEBUFFERS +#define HUB75_EXTERNAL_FRAMEBUFFERS 0 +#endif #endif //HUB75_EXTERNAL_FRAMEBUFFERS diff --git a/components/hub75/src/platforms/parlio/parlio_dma.cpp b/components/hub75/src/platforms/parlio/parlio_dma.cpp index 68d6976..ff6259d 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.cpp +++ b/components/hub75/src/platforms/parlio/parlio_dma.cpp @@ -61,6 +61,15 @@ constexpr uint16_t RGB_MASK = RGB_UPPER_MASK | RGB_LOWER_MASK; // 0x003F constexpr uint16_t OE_CLEAR_MASK = ~(1 << OE_BIT); constexpr uint16_t RGB_CLEAR_MASK = ~RGB_MASK; // Clear RGB bits 0-5 +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION +IRAM_ATTR bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx){ + ParlioDma* dma = (ParlioDma*)user_ctx; + size_t total_bits = dma->total_buffer_bytes_ * 8; + esp_err_t err = parlio_tx_unit_transmit(dma->tx_unit_, dma->dma_buffers_[dma->front_idx_], total_bits, &dma->transmit_config_); + return false; // No Higher level Task woken +} +#endif + ParlioDma::ParlioDma(const Hub75Config &config) : PlatformDma(config), tx_unit_(nullptr), @@ -94,10 +103,15 @@ ParlioDma::ParlioDma(const Hub75Config &config) // Initialize transmit config // Note: For four-scan panels, dma_width_ is doubled and num_rows_ is halved // to match the physical shift register layout - transmit_config_.idle_value = 0x00; + transmit_config_.idle_value = 1 << OE_BIT; // make sure nothing lights up when in idle transmit_config_.bitscrambler_program = nullptr; +#ifdef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION transmit_config_.flags.queue_nonblocking = 0; transmit_config_.flags.loop_transmission = 1; // Continuous refresh +#else + transmit_config_.flags.queue_nonblocking = 1; // enable the restart loop + transmit_config_.flags.loop_transmission = 0; +#endif } ParlioDma::~ParlioDma() { ParlioDma::shutdown(); } @@ -288,6 +302,17 @@ void ParlioDma::configure_parlio() { ESP_LOGI(TAG, " Clock gating: NOT SUPPORTED"); #endif ESP_LOGI(TAG, " Transaction queue depth: %zu", config.trans_queue_depth); + +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION + parlio_event_cbs.on_trans_done = parlio_trans_done_callback; + parlio_event_cbs.on_buffer_switched = nullptr; + err = parlio_tx_unit_register_event_callbacks(tx_unit_, &parlio_event_cbs, this); + if(err != ESP_OK){ + ESP_LOGE(TAG, "Failed to register event callbacks: %s", esp_err_to_name(err)); + return; + } + ESP_LOGI(TAG, "Event callbacks registered"); +#endif } HUB75_CONST uint32_t ParlioDma::resolve_actual_clock_speed(Hub75ClockSpeed clock_speed) const { @@ -1090,12 +1115,14 @@ void ParlioDma::flip_buffer() { // Swap indices (front ↔ active) std::swap(front_idx_, active_idx_); +#ifdef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION // Queue new front buffer (hardware switches seamlessly after current frame) size_t total_bits = total_buffer_bytes_ * 8; esp_err_t err = parlio_tx_unit_transmit(tx_unit_, dma_buffers_[front_idx_], total_bits, &transmit_config_); if (err != ESP_OK) { ESP_LOGW(TAG, "flip_buffer: Failed to queue buffer: %s", esp_err_to_name(err)); } +#endif } } // namespace hub75 diff --git a/components/hub75/src/platforms/parlio/parlio_dma.h b/components/hub75/src/platforms/parlio/parlio_dma.h index 3d64f01..f060986 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.h +++ b/components/hub75/src/platforms/parlio/parlio_dma.h @@ -117,6 +117,11 @@ class ParlioDma : public PlatformDma { uint8_t basis_brightness_; float intensity_; bool transfer_started_; + +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION + friend bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx); + parlio_tx_event_callbacks_t parlio_event_cbs; +#endif }; } // namespace hub75 From 34f06fff5579ee7923f253411da723ed24aa409f Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 14:04:39 +0200 Subject: [PATCH 05/13] make the HUB75_EXTERNAL_FRAMEBUFFERS flag depend on SOC_SPIRAM_SUPPORTED --- components/hub75/Kconfig | 1 + components/hub75/include/hub75_config.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/components/hub75/Kconfig b/components/hub75/Kconfig index 062b885..5b69852 100644 --- a/components/hub75/Kconfig +++ b/components/hub75/Kconfig @@ -726,6 +726,7 @@ menu "HUB75 RGB LED Matrix Driver" config HUB75_EXTERNAL_FRAMEBUFFERS bool "Use External Framebuffers" + depends on SOC_SPIRAM_SUPPORTED default y if IDF_TARGET_ESP32P4 default n help diff --git a/components/hub75/include/hub75_config.h b/components/hub75/include/hub75_config.h index a0300ce..61da23e 100644 --- a/components/hub75/include/hub75_config.h +++ b/components/hub75/include/hub75_config.h @@ -94,8 +94,8 @@ extern "C" { #define HUB75_EXTERNAL_FRAMEBUFFERS 0 #endif #else // HUB75_EXTERNAL_FRAMEBUFFERS -// maybe use SOC_SPIRAM_SUPPORTED -#if defined(CONFIG_IDF_TARGET_ESP32C6) && HUB75_EXTERNAL_FRAMEBUFFERS != 0 +#if !defined(SOC_SPIRAM_SUPPORTED) && HUB75_EXTERNAL_FRAMEBUFFERS != 0 +#pragma message "SOC does not support external framebuffer, disabling..." #undef HUB75_EXTERNAL_FRAMEBUFFERS #define HUB75_EXTERNAL_FRAMEBUFFERS 0 #endif From 1a6edb2e326c323c14228eaaae8c0e289f8e1c9b Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 15:34:07 +0200 Subject: [PATCH 06/13] increase esp32 max speed to 20mhz --- components/hub75/src/platforms/i2s/i2s_dma.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index 32ae3b6..b073e9b 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -322,12 +322,12 @@ HUB75_CONST uint32_t I2sDma::resolve_actual_clock_speed(Hub75ClockSpeed clock_sp // // The ESP32's lower base clock (80 MHz vs 160 MHz) combined with the same // divider constraints results in half the max frequency of ESP32-S2. - if (requested_hz > 10000000) { + if (requested_hz > 20000000) { ESP_LOGW(TAG, "Requested %u Hz exceeds ESP32 max (10 MHz), using 10 MHz", (unsigned int) requested_hz); - return 10000000; + return 20000000; } uint32_t divider = (80000000 + requested_hz * 2) / (requested_hz * 4); // Round to nearest - return 80000000 / (std::max(divider, uint32_t{2}) * 4); + return 80000000 / (std::max(divider, uint32_t{1}) * 4); #endif } From cd1baca78cad9288c17243d8288dfae9c3fa3228 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 16:25:12 +0200 Subject: [PATCH 07/13] fix build for the esp32c6 --- components/hub75/src/platforms/parlio/parlio_dma.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/hub75/src/platforms/parlio/parlio_dma.cpp b/components/hub75/src/platforms/parlio/parlio_dma.cpp index ff6259d..9ce7a37 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.cpp +++ b/components/hub75/src/platforms/parlio/parlio_dma.cpp @@ -65,7 +65,7 @@ constexpr uint16_t RGB_CLEAR_MASK = ~RGB_MASK; // Clear RGB bits 0-5 IRAM_ATTR bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx){ ParlioDma* dma = (ParlioDma*)user_ctx; size_t total_bits = dma->total_buffer_bytes_ * 8; - esp_err_t err = parlio_tx_unit_transmit(dma->tx_unit_, dma->dma_buffers_[dma->front_idx_], total_bits, &dma->transmit_config_); + parlio_tx_unit_transmit(dma->tx_unit_, dma->dma_buffers_[dma->front_idx_], total_bits, &dma->transmit_config_); return false; // No Higher level Task woken } #endif From a42f299fd8b75e46711da9d21bb5d56d5fb7e4f2 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 16:31:15 +0200 Subject: [PATCH 08/13] try to fix builds, by adding esp_mm in CMakeLists --- components/hub75/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/hub75/CMakeLists.txt b/components/hub75/CMakeLists.txt index 2ab2dd4..8a8252b 100644 --- a/components/hub75/CMakeLists.txt +++ b/components/hub75/CMakeLists.txt @@ -8,10 +8,8 @@ set(HUB75_REQUIRES esp_timer esp_hw_support freertos + esp_mm ) -if(IDF_TARGET STREQUAL "esp32p4" OR IDF_TARGET STREQUAL "esp32c6") - list(APPEND HUB75_REQUIRES esp_mm) -endif() # ESP-IDF 6.0+ split drivers into separate components if(IDF_VERSION_MAJOR GREATER_EQUAL 6) list(REMOVE_ITEM HUB75_REQUIRES driver) # Remove old "driver" component From 413146e22e209c2ea50ba95fab6fe203f17740b2 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 17:16:17 +0200 Subject: [PATCH 09/13] apply .clang-format --- components/hub75/include/hub75_config.h | 9 ++------- components/hub75/src/platforms/gdma/gdma_dma.cpp | 10 +++++----- components/hub75/src/platforms/i2s/i2s_dma.cpp | 10 +++++----- .../hub75/src/platforms/parlio/parlio_dma.cpp | 15 ++++++++------- .../hub75/src/platforms/parlio/parlio_dma.h | 5 +++-- 5 files changed, 23 insertions(+), 26 deletions(-) diff --git a/components/hub75/include/hub75_config.h b/components/hub75/include/hub75_config.h index 61da23e..897b558 100644 --- a/components/hub75/include/hub75_config.h +++ b/components/hub75/include/hub75_config.h @@ -80,7 +80,6 @@ extern "C" { #endif #endif - /** * Use external framebuffer in PSRAM / SPIRAM * Ser via menuconfig or override: -D HUB75_EXTERNAL_FRAMEBUFFERS @@ -93,17 +92,13 @@ extern "C" { #else #define HUB75_EXTERNAL_FRAMEBUFFERS 0 #endif -#else // HUB75_EXTERNAL_FRAMEBUFFERS +#else // HUB75_EXTERNAL_FRAMEBUFFERS #if !defined(SOC_SPIRAM_SUPPORTED) && HUB75_EXTERNAL_FRAMEBUFFERS != 0 #pragma message "SOC does not support external framebuffer, disabling..." #undef HUB75_EXTERNAL_FRAMEBUFFERS #define HUB75_EXTERNAL_FRAMEBUFFERS 0 #endif -#endif //HUB75_EXTERNAL_FRAMEBUFFERS - - - - +#endif // HUB75_EXTERNAL_FRAMEBUFFERS #ifdef __cplusplus } diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index 0e72429..92f949c 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -680,7 +680,7 @@ HUB75_IRAM void GdmaDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_ HUB75_PROFILE_PIXEL(); } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } @@ -704,7 +704,7 @@ void GdmaDma::clear() { } } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } @@ -798,7 +798,7 @@ HUB75_IRAM void GdmaDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, ui } } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } @@ -1107,9 +1107,9 @@ void GdmaDma::set_brightness_oe() { set_brightness_oe_internal(row_buffer, brightness); } } - + flush_cache_to_dma(); - + ESP_LOGD(TAG, "Brightness OE configuration complete"); } diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index b073e9b..1514c1d 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -911,9 +911,9 @@ void I2sDma::set_brightness_oe() { set_brightness_oe_internal(row_buffer, brightness); } } - + flush_cache_to_dma(); - + ESP_LOGD(TAG, "Brightness OE configuration complete"); } @@ -1149,7 +1149,7 @@ HUB75_IRAM void I2sDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t HUB75_PROFILE_PIXEL(); } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } @@ -1173,7 +1173,7 @@ void I2sDma::clear() { } } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } @@ -1262,7 +1262,7 @@ HUB75_IRAM void I2sDma::fill(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uin } } } - if(!config_.double_buffer){ + if (!config_.double_buffer) { flush_cache_to_dma(); } } diff --git a/components/hub75/src/platforms/parlio/parlio_dma.cpp b/components/hub75/src/platforms/parlio/parlio_dma.cpp index 9ce7a37..428ee83 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.cpp +++ b/components/hub75/src/platforms/parlio/parlio_dma.cpp @@ -61,12 +61,13 @@ constexpr uint16_t RGB_MASK = RGB_UPPER_MASK | RGB_LOWER_MASK; // 0x003F constexpr uint16_t OE_CLEAR_MASK = ~(1 << OE_BIT); constexpr uint16_t RGB_CLEAR_MASK = ~RGB_MASK; // Clear RGB bits 0-5 -#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION -IRAM_ATTR bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx){ - ParlioDma* dma = (ParlioDma*)user_ctx; +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION +IRAM_ATTR bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, + void *user_ctx) { + ParlioDma *dma = (ParlioDma *) user_ctx; size_t total_bits = dma->total_buffer_bytes_ * 8; parlio_tx_unit_transmit(dma->tx_unit_, dma->dma_buffers_[dma->front_idx_], total_bits, &dma->transmit_config_); - return false; // No Higher level Task woken + return false; // No Higher level Task woken } #endif @@ -109,7 +110,7 @@ ParlioDma::ParlioDma(const Hub75Config &config) transmit_config_.flags.queue_nonblocking = 0; transmit_config_.flags.loop_transmission = 1; // Continuous refresh #else - transmit_config_.flags.queue_nonblocking = 1; // enable the restart loop + transmit_config_.flags.queue_nonblocking = 1; // enable the restart loop transmit_config_.flags.loop_transmission = 0; #endif } @@ -303,11 +304,11 @@ void ParlioDma::configure_parlio() { #endif ESP_LOGI(TAG, " Transaction queue depth: %zu", config.trans_queue_depth); -#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION parlio_event_cbs.on_trans_done = parlio_trans_done_callback; parlio_event_cbs.on_buffer_switched = nullptr; err = parlio_tx_unit_register_event_callbacks(tx_unit_, &parlio_event_cbs, this); - if(err != ESP_OK){ + if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to register event callbacks: %s", esp_err_to_name(err)); return; } diff --git a/components/hub75/src/platforms/parlio/parlio_dma.h b/components/hub75/src/platforms/parlio/parlio_dma.h index f060986..879d8e9 100644 --- a/components/hub75/src/platforms/parlio/parlio_dma.h +++ b/components/hub75/src/platforms/parlio/parlio_dma.h @@ -118,8 +118,9 @@ class ParlioDma : public PlatformDma { float intensity_; bool transfer_started_; -#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION - friend bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx); +#ifndef SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION + friend bool parlio_trans_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, + void *user_ctx); parlio_tx_event_callbacks_t parlio_event_cbs; #endif }; From 7a8233678f9d2f58b7f4c815a6661543d9b25fce Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 17:32:05 +0200 Subject: [PATCH 10/13] fix missing dependency in idf 4.4.8 --- components/hub75/CMakeLists.txt | 4 +++- components/hub75/src/platforms/gdma/gdma_dma.cpp | 9 +++++++-- components/hub75/src/platforms/i2s/i2s_dma.cpp | 11 +++++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/components/hub75/CMakeLists.txt b/components/hub75/CMakeLists.txt index 8a8252b..e80ebaf 100644 --- a/components/hub75/CMakeLists.txt +++ b/components/hub75/CMakeLists.txt @@ -8,8 +8,10 @@ set(HUB75_REQUIRES esp_timer esp_hw_support freertos - esp_mm ) +if(IDF_VERSION_MAJOR GREATER_EQUAL 6) + list(APPEND HUB75_REQUIRES esp_mm) +endif() # ESP-IDF 6.0+ split drivers into separate components if(IDF_VERSION_MAJOR GREATER_EQUAL 6) list(REMOVE_ITEM HUB75_REQUIRES driver) # Remove old "driver" component diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index 92f949c..b4d78a9 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -37,12 +37,13 @@ // Header location changed in ESP-IDF 5.0 #if (ESP_IDF_VERSION_MAJOR >= 5) #include +#include +#include #else +#include "rom/cache.h" #include #endif #include -#include -#include static const char *const TAG = "GdmaDma"; @@ -1308,6 +1309,7 @@ void GdmaDma::flush_cache_to_dma() { // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() // returns false and we skip the msync (which would be unnecessary overhead). +#if (ESP_IDF_VERSION_MAJOR >= 5) if (!dma_buffers_[active_idx_] || !esp_ptr_external_ram(dma_buffers_[active_idx_])) { return; } @@ -1319,6 +1321,9 @@ void GdmaDma::flush_cache_to_dma() { if (err != ESP_OK) { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } +#else + Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); +#endif #endif } diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index 1514c1d..32b203b 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -26,7 +26,10 @@ // Header location changed in ESP-IDF 5.0 #if (ESP_IDF_VERSION_MAJOR >= 5) #include +#include +#include #else +#include "rom/cache.h" #include #endif #include @@ -39,8 +42,6 @@ #endif #include -#include -#include static const char *const TAG = "I2sDma"; @@ -1305,6 +1306,7 @@ void I2sDma::flush_cache_to_dma() { // Only flush for PSRAM (external RAM) - internal SRAM doesn't need cache sync // This handles ESP32-C6 automatically: C6 uses internal RAM, so esp_ptr_external_ram() // returns false and we skip the msync (which would be unnecessary overhead). +#if (ESP_IDF_VERSION_MAJOR >= 5) if (!dma_buffers_[active_idx_] || !esp_ptr_external_ram(dma_buffers_[active_idx_])) { return; } @@ -1316,6 +1318,11 @@ void I2sDma::flush_cache_to_dma() { if (err != ESP_OK) { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } +#else +#ifndef CONFIG_IDF_TARGET_ESP32 // ESP32 has no cache writeback in IDF < v5 + Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); +#endif +#endif #endif } From 65fdbae1189418bca6b3189b8bb3c7c7492feda2 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 17:35:48 +0200 Subject: [PATCH 11/13] fix .clang-format --- components/hub75/src/platforms/gdma/gdma_dma.cpp | 2 +- components/hub75/src/platforms/i2s/i2s_dma.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/hub75/src/platforms/gdma/gdma_dma.cpp b/components/hub75/src/platforms/gdma/gdma_dma.cpp index b4d78a9..476329b 100644 --- a/components/hub75/src/platforms/gdma/gdma_dma.cpp +++ b/components/hub75/src/platforms/gdma/gdma_dma.cpp @@ -1322,7 +1322,7 @@ void GdmaDma::flush_cache_to_dma() { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } #else - Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); + Cache_WriteBack_Addr((uint32_t) &p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); #endif #endif } diff --git a/components/hub75/src/platforms/i2s/i2s_dma.cpp b/components/hub75/src/platforms/i2s/i2s_dma.cpp index 32b203b..1903037 100644 --- a/components/hub75/src/platforms/i2s/i2s_dma.cpp +++ b/components/hub75/src/platforms/i2s/i2s_dma.cpp @@ -1319,8 +1319,8 @@ void I2sDma::flush_cache_to_dma() { ESP_LOGW(TAG, "Cache sync failed: %s", esp_err_to_name(err)); } #else -#ifndef CONFIG_IDF_TARGET_ESP32 // ESP32 has no cache writeback in IDF < v5 - Cache_WriteBack_Addr((uint32_t)&p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); +#ifndef CONFIG_IDF_TARGET_ESP32 // ESP32 has no cache writeback in IDF < v5 + Cache_WriteBack_Addr((uint32_t) &p[x_coord], sizeof(ESP32_I2S_DMA_STORAGE_TYPE)); #endif #endif #endif From ba767977d08da7638d487a06e8ad93782787a3eb Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Thu, 9 Apr 2026 17:40:00 +0200 Subject: [PATCH 12/13] fix correct version check in CMakeLists.txt for esp_mm component --- components/hub75/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/hub75/CMakeLists.txt b/components/hub75/CMakeLists.txt index e80ebaf..51a133f 100644 --- a/components/hub75/CMakeLists.txt +++ b/components/hub75/CMakeLists.txt @@ -9,7 +9,7 @@ set(HUB75_REQUIRES esp_hw_support freertos ) -if(IDF_VERSION_MAJOR GREATER_EQUAL 6) +if(IDF_VERSION_MAJOR GREATER_EQUAL 5) list(APPEND HUB75_REQUIRES esp_mm) endif() # ESP-IDF 6.0+ split drivers into separate components From 53e997c6355c538beda91f9f0d06171fd2518f87 Mon Sep 17 00:00:00 2001 From: Lukaswnd Date: Fri, 19 Jun 2026 11:02:52 +0200 Subject: [PATCH 13/13] fix missing include for HUB75_EXTERNAL_FRAMEBUFFERS --- components/hub75/include/hub75_config.h | 1 + 1 file changed, 1 insertion(+) diff --git a/components/hub75/include/hub75_config.h b/components/hub75/include/hub75_config.h index 897b558..3611d17 100644 --- a/components/hub75/include/hub75_config.h +++ b/components/hub75/include/hub75_config.h @@ -11,6 +11,7 @@ extern "C" { #endif #include "sdkconfig.h" +#include "soc/soc_caps.h" /** * IRAM optimization