Skip to content

Commit bd3a784

Browse files
[dma] Refactor common parts of PlatformDMA implementations (#4)
1 parent fb71dd7 commit bd3a784

File tree

13 files changed

+220
-354
lines changed

13 files changed

+220
-354
lines changed

components/hub75/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ idf_component_register(
77
"src/color/color_lut.cpp"
88
"src/color/color_convert.cpp"
99
"src/platforms/platform_detect.cpp"
10+
"src/platforms/platform_dma.cpp"
1011
"src/drivers/fm6126a.cpp"
1112
# Platform-specific sources added conditionally below
1213
INCLUDE_DIRS

components/hub75/README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,12 @@ Double buffering doubles memory usage but enables tear-free animation. PARLIO us
140140
- `void set_brightness(uint8_t brightness)` - Set display brightness (0-255)
141141
- `void set_intensity(float intensity)` - Set intensity multiplier (0.0-1.0) for smooth dimming
142142
- `uint8_t get_brightness()` - Get current brightness value
143-
- `void set_gamma_mode(Hub75GammaMode mode)` - Set gamma correction mode
144-
- `Hub75GammaMode::NONE` - No gamma correction (linear)
145-
- `Hub75GammaMode::CIE1931` - CIE 1931 standard (recommended)
146-
- `Hub75GammaMode::GAMMA_2_2` - Gamma 2.2 correction
147-
- `Hub75GammaMode get_gamma_mode()` - Get current gamma mode
143+
144+
**Gamma Correction:**
145+
Gamma correction mode is set at initialization via `config.gamma_mode` and cannot be changed at runtime:
146+
- `Hub75GammaMode::NONE` - No gamma correction (linear)
147+
- `Hub75GammaMode::CIE1931` - CIE 1931 standard (recommended, default)
148+
- `Hub75GammaMode::GAMMA_2_2` - Gamma 2.2 correction
148149

149150
**Dual-Mode Brightness System:**
150151
- **Basis brightness** (0-255): Adjusts hardware OE (output enable) timing in DMA buffers

components/hub75/include/hub75.h

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,6 @@ class Hub75Driver {
149149
*/
150150
void set_intensity(float intensity);
151151

152-
/**
153-
* @brief Set gamma correction mode
154-
* @param mode Gamma mode
155-
*/
156-
void set_gamma_mode(Hub75GammaMode mode);
157-
158-
/**
159-
* @brief Get current gamma mode
160-
* @return Current gamma mode
161-
*/
162-
Hub75GammaMode get_gamma_mode() const;
163-
164152
// ========================================================================
165153
// Information
166154
// ========================================================================
@@ -187,15 +175,9 @@ class Hub75Driver {
187175
Hub75Config config_;
188176
bool running_;
189177

190-
// Color conversion LUTs
191-
const uint16_t *lut_;
192-
193178
// Platform-specific DMA engine
194179
hub75::PlatformDma *dma_;
195180

196-
// Helper methods
197-
void initialize_lut_();
198-
199181
HUB75_IRAM inline size_t get_pixel_offset_(uint16_t x, uint16_t y) const {
200182
// Total width includes all panels in layout
201183
return (y * (config_.panel_width * config_.layout_cols)) + x;

components/hub75/src/color/color_convert.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#pragma once
1111

1212
#include "hub75_config.h"
13+
#include "hub75_types.h" // For Hub75PixelFormat, Hub75ColorOrder
1314
#include <stdint.h>
15+
#include <stddef.h> // For size_t
1416

1517
namespace hub75 {
1618

@@ -40,6 +42,93 @@ HUB75_CONST HUB75_IRAM inline constexpr uint8_t scale_5bit_to_8bit(uint8_t val5)
4042
*/
4143
HUB75_CONST HUB75_IRAM inline constexpr uint8_t scale_6bit_to_8bit(uint8_t val6) { return (val6 << 2) | (val6 >> 4); }
4244

45+
// ============================================================================
46+
// Pixel Format Extraction
47+
// ============================================================================
48+
49+
/**
50+
* @brief Extract RGB888 values from various pixel formats
51+
*
52+
* Handles RGB565, RGB888, and RGB888_32 formats with endianness and color order.
53+
* This is a hot-path function used in draw_pixels() implementations.
54+
*
55+
* @param buffer Source pixel buffer
56+
* @param pixel_idx Index of pixel to extract (not byte offset)
57+
* @param format Pixel format (RGB565, RGB888, or RGB888_32)
58+
* @param color_order Color component order (RGB or BGR, for RGB888_32 only)
59+
* @param big_endian True if buffer is big-endian
60+
* @param r8 Output: 8-bit red component (0-255)
61+
* @param g8 Output: 8-bit green component (0-255)
62+
* @param b8 Output: 8-bit blue component (0-255)
63+
*/
64+
HUB75_IRAM inline void extract_rgb888_from_format(const uint8_t *buffer, size_t pixel_idx, Hub75PixelFormat format,
65+
Hub75ColorOrder color_order, bool big_endian, uint8_t &r8,
66+
uint8_t &g8, uint8_t &b8) {
67+
switch (format) {
68+
case Hub75PixelFormat::RGB565: {
69+
// 16-bit RGB565
70+
const uint8_t *p = buffer + (pixel_idx * 2);
71+
uint16_t rgb565;
72+
if (big_endian) {
73+
rgb565 = (uint16_t(p[0]) << 8) | p[1];
74+
} else {
75+
rgb565 = (uint16_t(p[1]) << 8) | p[0];
76+
}
77+
78+
// Extract RGB565 components
79+
const uint8_t r5 = (rgb565 >> 11) & 0x1F;
80+
const uint8_t g6 = (rgb565 >> 5) & 0x3F;
81+
const uint8_t b5 = rgb565 & 0x1F;
82+
83+
// Scale to 8-bit using color conversion helpers
84+
r8 = scale_5bit_to_8bit(r5);
85+
g8 = scale_6bit_to_8bit(g6);
86+
b8 = scale_5bit_to_8bit(b5);
87+
break;
88+
}
89+
90+
case Hub75PixelFormat::RGB888: {
91+
// 24-bit packed RGB
92+
const uint8_t *p = buffer + (pixel_idx * 3);
93+
r8 = p[0];
94+
g8 = p[1];
95+
b8 = p[2];
96+
break;
97+
}
98+
99+
case Hub75PixelFormat::RGB888_32: {
100+
// 32-bit RGB with padding
101+
const uint8_t *p = buffer + (pixel_idx * 4);
102+
if (color_order == Hub75ColorOrder::RGB) {
103+
if (big_endian) {
104+
// Big-endian xRGB: [x][R][G][B]
105+
r8 = p[1];
106+
g8 = p[2];
107+
b8 = p[3];
108+
} else {
109+
// Little-endian xRGB stored as BGRx: [B][G][R][x]
110+
r8 = p[2];
111+
g8 = p[1];
112+
b8 = p[0];
113+
}
114+
} else { // BGR
115+
if (big_endian) {
116+
// Big-endian xBGR: [x][B][G][R]
117+
r8 = p[3];
118+
g8 = p[2];
119+
b8 = p[1];
120+
} else {
121+
// Little-endian xBGR stored as RGBx: [R][G][B][x]
122+
r8 = p[0];
123+
g8 = p[1];
124+
b8 = p[2];
125+
}
126+
}
127+
break;
128+
}
129+
}
130+
}
131+
43132
// ============================================================================
44133
// Compile-Time Validation
45134
// ============================================================================

components/hub75/src/core/hub75_driver.cpp

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ using PlatformDMAImpl = ParlioDma;
4242
// Constructor / Destructor
4343
// ============================================================================
4444

45-
Hub75Driver::Hub75Driver(const Hub75Config &config) : config_(config), running_(false), lut_(nullptr), dma_(nullptr) {
45+
Hub75Driver::Hub75Driver(const Hub75Config &config) : config_(config), running_(false), dma_(nullptr) {
4646
ESP_LOGI(TAG, "Driver created for %s (%s)", getPlatformName(), getDMAEngineName());
4747
ESP_LOGI(TAG, "Panel: %dx%d, Layout: %dx%d, Virtual: %dx%d", (unsigned int) config_.panel_width,
4848
(unsigned int) config_.panel_height, (unsigned int) config_.layout_cols, (unsigned int) config_.layout_rows,
@@ -80,9 +80,6 @@ bool Hub75Driver::begin() {
8080
return false;
8181
}
8282

83-
// Initialize color LUT
84-
initialize_lut_();
85-
8683
// Initialize shift driver chips (panel-level, platform-agnostic)
8784
// Must happen before DMA starts transmitting data
8885
esp_err_t err = DriverInit::initialize(config_);
@@ -98,9 +95,6 @@ bool Hub75Driver::begin() {
9895
return false;
9996
}
10097

101-
// Pass LUT for gamma correction during pixel writes
102-
dma_->set_lut(lut_);
103-
10498
// Start DMA transfer
10599
dma_->start_transfer();
106100

@@ -191,13 +185,6 @@ void Hub75Driver::set_intensity(float intensity) {
191185
}
192186
}
193187

194-
void Hub75Driver::set_gamma_mode(Hub75GammaMode mode) {
195-
config_.gamma_mode = mode;
196-
initialize_lut_(); // Regenerate LUT
197-
}
198-
199-
Hub75GammaMode Hub75Driver::get_gamma_mode() const { return config_.gamma_mode; }
200-
201188
// ============================================================================
202189
// Information
203190
// ============================================================================
@@ -213,16 +200,3 @@ uint16_t Hub75Driver::get_height() const {
213200
}
214201

215202
bool Hub75Driver::is_running() const { return running_; }
216-
217-
// ============================================================================
218-
// Private Helper Methods
219-
// ============================================================================
220-
221-
void Hub75Driver::initialize_lut_() {
222-
lut_ = get_lut(config_.gamma_mode, config_.bit_depth);
223-
ESP_LOGI(TAG, "Initialized %s LUT for %d-bit depth",
224-
config_.gamma_mode == Hub75GammaMode::CIE1931 ? "CIE1931"
225-
: config_.gamma_mode == Hub75GammaMode::GAMMA_2_2 ? "Gamma2.2"
226-
: "Linear",
227-
config_.bit_depth);
228-
}

components/hub75/src/platforms/gdma/gdma_dma.cpp

Lines changed: 11 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ constexpr uint16_t RGB_LOWER_MASK = (1 << R2_BIT) | (1 << G2_BIT) | (1 << B2_BIT
6262
constexpr uint16_t RGB_MASK = RGB_UPPER_MASK | RGB_LOWER_MASK; // 0x003F
6363

6464
GdmaDma::GdmaDma(const Hub75Config &config)
65-
: config_(config),
65+
: PlatformDma(config),
6666
dma_chan_(nullptr),
6767
bit_depth_(config.bit_depth),
6868
lsbMsbTransitionBit_(0),
@@ -85,8 +85,7 @@ GdmaDma::GdmaDma(const Hub75Config &config)
8585
active_idx_(0),
8686
descriptor_count_(0),
8787
basis_brightness_(config.brightness), // Use config value (default: 128)
88-
intensity_(1.0f),
89-
lut_(nullptr) {
88+
intensity_(1.0f) {
9089
// Zero-copy architecture: DMA buffers ARE the display memory
9190
// Note: panel_width_, etc. will be set in init()
9291
}
@@ -466,8 +465,6 @@ void GdmaDma::set_intensity(float intensity) {
466465
// Pixel API (Direct DMA Buffer Writes)
467466
// ============================================================================
468467

469-
void GdmaDma::set_lut(const uint16_t *lut) { lut_ = lut; }
470-
471468
HUB75_IRAM void GdmaDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buffer,
472469
Hub75PixelFormat format, Hub75ColorOrder color_order, bool big_endian) {
473470
// Always write to active buffer (CPU drawing buffer)
@@ -496,102 +493,19 @@ HUB75_IRAM void GdmaDma::draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_
496493
uint16_t px = x + dx;
497494
uint16_t py = y + dy;
498495

499-
// ============================================================
500-
// COORDINATE TRANSFORMATION PIPELINE
501-
// ============================================================
502-
503-
Coords c = {.x = px, .y = py};
504-
505-
// Step 1: Panel layout remapping (if multi-panel grid)
506-
if (needs_layout_remap_) {
507-
c = PanelLayoutRemap::remap(c, layout_, panel_width_, panel_height_, layout_rows_, layout_cols_);
508-
}
509-
510-
// Step 2: Scan pattern remapping (if non-standard panel)
511-
if (needs_scan_remap_) {
512-
c = ScanPatternRemap::remap(c, scan_wiring_, panel_width_);
513-
}
514-
515-
// Use transformed coordinates for DMA buffer writes
516-
px = c.x;
517-
py = c.y;
518-
519-
// Debug assertions - automatically disabled in release builds
520-
assert(px < dma_width_ && "Transformed X exceeds DMA buffer width!");
521-
assert(py < panel_height_ && "Transformed Y exceeds row buffer height!");
522-
523-
const uint16_t row = py % (panel_height_ / 2);
524-
const bool is_lower = (py >= panel_height_ / 2);
496+
// Coordinate transformation pipeline (layout + scan remapping)
497+
auto transformed =
498+
transform_coordinate(px, py, needs_layout_remap_, needs_scan_remap_, layout_, scan_wiring_, panel_width_,
499+
panel_height_, layout_rows_, layout_cols_, dma_width_, num_rows_);
500+
px = transformed.x;
501+
const uint16_t row = transformed.row;
502+
const bool is_lower = transformed.is_lower;
525503

526504
const size_t pixel_idx = (dy * w) + dx;
527505
uint8_t r8 = 0, g8 = 0, b8 = 0;
528506

529-
// Extract RGB based on format
530-
switch (format) {
531-
case Hub75PixelFormat::RGB565: {
532-
// 16-bit RGB565
533-
const uint8_t *p = buffer + (pixel_idx * 2);
534-
uint16_t rgb565;
535-
// No endianness hint - LVGL default is big-endian, ESP32-native is little-endian
536-
if (big_endian) {
537-
rgb565 = (uint16_t(p[0]) << 8) | p[1];
538-
} else {
539-
rgb565 = (uint16_t(p[1]) << 8) | p[0];
540-
}
541-
542-
// Extract RGB565 components
543-
const uint8_t r5 = (rgb565 >> 11) & 0x1F;
544-
const uint8_t g6 = (rgb565 >> 5) & 0x3F;
545-
const uint8_t b5 = rgb565 & 0x1F;
546-
547-
// Scale to 8-bit using color conversion helpers
548-
r8 = scale_5bit_to_8bit(r5);
549-
g8 = scale_6bit_to_8bit(g6);
550-
b8 = scale_5bit_to_8bit(b5);
551-
break;
552-
}
553-
554-
case Hub75PixelFormat::RGB888: {
555-
// 24-bit packed RGB
556-
const uint8_t *p = buffer + (pixel_idx * 3);
557-
r8 = p[0];
558-
g8 = p[1];
559-
b8 = p[2];
560-
break;
561-
}
562-
563-
case Hub75PixelFormat::RGB888_32: {
564-
// 32-bit RGB with padding
565-
const uint8_t *p = buffer + (pixel_idx * 4);
566-
if (color_order == Hub75ColorOrder::RGB) {
567-
// No endianness hint - split between LVGL default and ESP32-native
568-
if (big_endian) {
569-
// Big-endian xRGB: [x][R][G][B]
570-
r8 = p[1];
571-
g8 = p[2];
572-
b8 = p[3];
573-
} else {
574-
// Little-endian xRGB stored as BGRx: [B][G][R][x]
575-
r8 = p[2];
576-
g8 = p[1];
577-
b8 = p[0];
578-
}
579-
} else { // BGR
580-
if (big_endian) {
581-
// Big-endian xBGR: [x][B][G][R]
582-
r8 = p[3];
583-
g8 = p[2];
584-
b8 = p[1];
585-
} else {
586-
// Little-endian xBGR stored as RGBx: [R][G][B][x]
587-
r8 = p[0];
588-
g8 = p[1];
589-
b8 = p[2];
590-
}
591-
}
592-
break;
593-
}
594-
}
507+
// Extract RGB888 from pixel format
508+
extract_rgb888_from_format(buffer, pixel_idx, format, color_order, big_endian, r8, g8, b8);
595509

596510
// Apply LUT correction
597511
const uint16_t r_corrected = lut_[r8];

components/hub75/src/platforms/gdma/gdma_dma.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ class GdmaDma : public PlatformDma {
7171
void draw_pixels(uint16_t x, uint16_t y, uint16_t w, uint16_t h, const uint8_t *buffer, Hub75PixelFormat format,
7272
Hub75ColorOrder color_order, bool big_endian) override;
7373

74-
/**
75-
* @brief Set gamma correction LUT
76-
*/
77-
void set_lut(const uint16_t *lut) override;
78-
7974
/**
8075
* @brief Clear all pixels to black
8176
*/
@@ -120,7 +115,6 @@ class GdmaDma : public PlatformDma {
120115
// BCM timing calculation (calculates lsbMsbTransitionBit for OE control)
121116
void calculate_bcm_timings();
122117

123-
const Hub75Config config_;
124118
gdma_channel_handle_t dma_chan_;
125119
const uint8_t bit_depth_; // Bit depth from config (6, 7, 8, 10, or 12)
126120
uint8_t lsbMsbTransitionBit_; // BCM optimization threshold (calculated at init)
@@ -158,9 +152,6 @@ class GdmaDma : public PlatformDma {
158152
// Brightness control (implementation of base class interface)
159153
uint8_t basis_brightness_; // 1-255
160154
float intensity_; // 0.0-1.0
161-
162-
// Gamma correction LUT (owned by Hub75Driver, just a pointer here)
163-
const uint16_t *lut_;
164155
};
165156

166157
} // namespace hub75

0 commit comments

Comments
 (0)