7#include "platforms/shared/spi_hw_1.h"
8#include "platforms/shared/spi_hw_2.h"
9#include "platforms/shared/spi_hw_4.h"
10#include "platforms/shared/spi_hw_8.h"
11#include "platforms/shared/spi_transposer.h"
33 size_t num_lanes =
config.data_pins.size();
34 for (
size_t i = 0; i < num_lanes; i++) {
64 if (num_lanes < 1 || num_lanes > 8) {
65 FL_WARN(
"MultiLaneDevice: Invalid number of data pins (" << num_lanes
69 FL_DBG(
"MultiLaneDevice: Created with " << num_lanes <<
" lane(s)");
83 if (
pImpl->initialized) {
88 size_t num_lanes =
pImpl->config.data_pins.size();
91 if (num_lanes < 1 || num_lanes > 8) {
98 const auto& controllers = SpiHw1::getAll();
99 if (controllers.empty()) {
100 FL_WARN(
"MultiLaneDevice: No Single-SPI hardware available");
106 for (
const auto& ctrl : controllers) {
107 if (!ctrl->isInitialized()) {
114 FL_WARN(
"MultiLaneDevice: All Single-SPI controllers in use");
119 SpiHw1::Config hw_config;
120 hw_config.bus_num =
static_cast<u8>(hw->getBusId());
121 hw_config.clock_speed_hz =
pImpl->config.clock_speed_hz;
122 hw_config.clock_pin =
pImpl->config.clock_pin;
123 hw_config.data_pin =
pImpl->config.data_pins[0];
125 if (!hw->begin(hw_config)) {
126 FL_WARN(
"MultiLaneDevice: Failed to initialize Single-SPI hardware");
131 pImpl->backend_type = 1;
132 FL_DBG(
"MultiLaneDevice: Initialized Single-SPI (" << hw->getName() <<
")");
134 }
else if (num_lanes == 2) {
136 const auto& controllers = SpiHw2::getAll();
137 if (controllers.empty()) {
138 FL_WARN(
"MultiLaneDevice: No Dual-SPI hardware available");
144 for (
const auto& ctrl : controllers) {
145 if (!ctrl->isInitialized()) {
152 FL_WARN(
"MultiLaneDevice: All Dual-SPI controllers in use");
157 SpiHw2::Config hw_config;
158 hw_config.bus_num =
static_cast<u8>(hw->getBusId());
159 hw_config.clock_speed_hz =
pImpl->config.clock_speed_hz;
160 hw_config.clock_pin =
pImpl->config.clock_pin;
161 hw_config.data0_pin =
pImpl->config.data_pins[0];
162 hw_config.data1_pin =
pImpl->config.data_pins[1];
164 if (!hw->begin(hw_config)) {
165 FL_WARN(
"MultiLaneDevice: Failed to initialize Dual-SPI hardware");
170 pImpl->backend_type = 2;
171 FL_DBG(
"MultiLaneDevice: Initialized Dual-SPI (" << hw->getName() <<
")");
173 }
else if (num_lanes >= 3 && num_lanes <= 4) {
175 const auto& controllers = SpiHw4::getAll();
176 if (controllers.empty()) {
177 FL_WARN(
"MultiLaneDevice: No Quad-SPI hardware available");
183 for (
const auto& ctrl : controllers) {
184 if (!ctrl->isInitialized()) {
191 FL_WARN(
"MultiLaneDevice: All Quad-SPI controllers in use");
196 SpiHw4::Config hw_config;
197 hw_config.bus_num =
static_cast<u8>(hw->getBusId());
198 hw_config.clock_speed_hz =
pImpl->config.clock_speed_hz;
199 hw_config.clock_pin =
pImpl->config.clock_pin;
200 hw_config.data0_pin =
pImpl->config.data_pins[0];
201 hw_config.data1_pin = (num_lanes > 1) ?
pImpl->config.data_pins[1] : -1;
202 hw_config.data2_pin = (num_lanes > 2) ?
pImpl->config.data_pins[2] : -1;
203 hw_config.data3_pin = (num_lanes > 3) ?
pImpl->config.data_pins[3] : -1;
205 if (!hw->begin(hw_config)) {
206 FL_WARN(
"MultiLaneDevice: Failed to initialize Quad-SPI hardware");
211 pImpl->backend_type = 4;
212 FL_DBG(
"MultiLaneDevice: Initialized Quad-SPI (" << hw->getName() <<
")");
214 }
else if (num_lanes >= 5 && num_lanes <= 8) {
216 const auto& controllers = SpiHw8::getAll();
217 if (controllers.empty()) {
218 FL_WARN(
"MultiLaneDevice: No Octal-SPI hardware available");
224 for (
const auto& ctrl : controllers) {
225 if (!ctrl->isInitialized()) {
232 FL_WARN(
"MultiLaneDevice: All Octal-SPI controllers in use");
237 SpiHw8::Config hw_config;
238 hw_config.bus_num =
static_cast<u8>(hw->getBusId());
239 hw_config.clock_speed_hz =
pImpl->config.clock_speed_hz;
240 hw_config.clock_pin =
pImpl->config.clock_pin;
241 hw_config.data0_pin =
pImpl->config.data_pins[0];
242 hw_config.data1_pin = (num_lanes > 1) ?
pImpl->config.data_pins[1] : -1;
243 hw_config.data2_pin = (num_lanes > 2) ?
pImpl->config.data_pins[2] : -1;
244 hw_config.data3_pin = (num_lanes > 3) ?
pImpl->config.data_pins[3] : -1;
245 hw_config.data4_pin = (num_lanes > 4) ?
pImpl->config.data_pins[4] : -1;
246 hw_config.data5_pin = (num_lanes > 5) ?
pImpl->config.data_pins[5] : -1;
247 hw_config.data6_pin = (num_lanes > 6) ?
pImpl->config.data_pins[6] : -1;
248 hw_config.data7_pin = (num_lanes > 7) ?
pImpl->config.data_pins[7] : -1;
250 if (!hw->begin(hw_config)) {
251 FL_WARN(
"MultiLaneDevice: Failed to initialize Octal-SPI hardware");
256 pImpl->backend_type = 8;
257 FL_DBG(
"MultiLaneDevice: Initialized Octal-SPI (" << hw->getName() <<
")");
260 pImpl->initialized =
true;
273 pImpl->releaseBackend();
280 FL_DBG(
"MultiLaneDevice: Shutdown complete");
288 if (!
pImpl || lane_id >=
pImpl->lanes.size()) {
289 FL_WARN(
"MultiLaneDevice: Invalid lane ID " << lane_id);
291 static Lane dummy_lane(0,
nullptr);
294 return pImpl->lanes[lane_id];
304 "Device not initialized");
308 size_t expected_size = 0;
309 bool found_first =
false;
311 for (
size_t i = 0; i <
pImpl->lanes.size(); i++) {
312 size_t lane_size =
pImpl->lanes[i].bufferSize();
317 expected_size = lane_size;
319 }
else if (lane_size != expected_size) {
321 FL_WARN(
"MultiLaneDevice: Lane size mismatch - expected " << expected_size
322 <<
" bytes (lane 0), but lane " << i <<
" has " << lane_size <<
" bytes");
324 "Lane size mismatch: all lanes must have identical sizes");
329 if (expected_size == 0) {
330 FL_WARN(
"MultiLaneDevice: No data to flush (all lanes empty)");
332 "No data to transmit");
339 DMABuffer dma_buffer =
pImpl->backend->acquireDMABuffer(
max_size);
341 if (!dma_buffer.ok()) {
342 FL_WARN(
"MultiLaneDevice: Failed to acquire DMA buffer");
344 "Failed to acquire DMA buffer");
348 const char* error =
nullptr;
349 bool transpose_ok =
false;
351 if (
pImpl->backend_type == 1) {
353 if (
pImpl->lanes.size() > 0) {
358 if (lane_data.
size() != dma_data.
size()) {
359 FL_WARN(
"MultiLaneDevice: DMA buffer size mismatch - expected " << lane_data.
size()
360 <<
" bytes, got " << dma_data.
size() <<
" bytes");
361 error =
"DMA buffer size mismatch";
362 transpose_ok =
false;
365 for (
size_t i = 0; i < lane_data.
size(); i++) {
366 dma_data[i] = lane_data[i];
371 error =
"No lanes configured";
372 transpose_ok =
false;
375 }
else if (
pImpl->backend_type == 2) {
378 if (
pImpl->lanes.size() > 0) {
380 pImpl->lanes[0].data(),
384 if (
pImpl->lanes.size() > 1) {
386 pImpl->lanes[1].data(),
393 }
else if (
pImpl->backend_type == 4) {
396 for (
size_t i = 0; i <
pImpl->lanes.size() && i < 4; i++) {
398 pImpl->lanes[i].data(),
404 dma_buffer.data(), &error);
406 }
else if (
pImpl->backend_type == 8) {
409 for (
size_t i = 0; i <
pImpl->lanes.size() && i < 8; i++) {
411 pImpl->lanes[i].data(),
420 FL_WARN(
"MultiLaneDevice: Transposition failed - " << (error ? error :
"unknown error"));
422 error ? error :
"Transposition failed");
426 bool transmit_ok =
pImpl->backend->transmit(TransmitMode::ASYNC);
429 FL_WARN(
"MultiLaneDevice: Hardware transmit failed");
431 "Hardware transmit failed");
439 FL_DBG(
"MultiLaneDevice: Flushed " <<
pImpl->lanes.size() <<
" lanes ("
453 return pImpl->backend->waitComplete(timeout_ms);
462 return pImpl->backend->isBusy();
467 FL_WARN(
"MultiLaneDevice: Not ready for write");
471 if (lane_data.size() >
pImpl->lanes.size()) {
472 FL_WARN(
"MultiLaneDevice: Too many lanes provided (" << lane_data.size()
473 <<
" > " <<
pImpl->lanes.size() <<
")");
478 if (lane_data.size() > 1) {
479 size_t first_size = lane_data[0].size();
480 for (
size_t i = 1; i < lane_data.size(); i++) {
481 if (lane_data[i].size() != first_size) {
482 FL_WARN(
"MultiLaneDevice: Lane size mismatch - lane 0 has " << first_size
483 <<
" bytes, lane " << i <<
" has " << lane_data[i].size() <<
" bytes");
484 return WriteResult(
"Lane size mismatch: all lanes must have identical sizes");
493 for (
size_t i = 0; i < lane_data.size(); i++) {
494 pImpl->lanes[i].write(lane_data[i].data(), lane_data[i].size());
498 auto flush_result =
flush();
499 if (!flush_result.ok()) {
500 FL_WARN(
"MultiLaneDevice: Flush failed");
504 FL_DBG(
"MultiLaneDevice: Wrote " << lane_data.size() <<
" lanes atomically (async)");
511 static const Config empty_config;
static bool transpose8(const fl::optional< LaneData > lanes[8], fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 8 lanes of data into interleaved octal-SPI format.
static bool transpose4(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, const fl::optional< LaneData > &lane2, const fl::optional< LaneData > &lane3, fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 4 lanes of data into interleaved quad-SPI format.
static bool transpose2(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 2 lanes of data into interleaved dual-SPI format.
Lane data structure: payload + padding frame.
static expected failure(E err, const char *msg=nullptr) FL_NOEXCEPT
Create error result.
static expected success(T value) FL_NOEXCEPT
Create successful result.
const T * data() const FL_NOEXCEPT
constexpr fl::size size() const FL_NOEXCEPT
Single lane in a multi-lane SPI device.
~MultiLaneDevice() FL_NOEXCEPT
Destructor - releases hardware resources.
bool waitComplete(u32 timeout_ms=(fl::numeric_limits< u32 >::max)())
Wait for pending transmission to complete.
bool isReady() const
Check if device is initialized.
WriteResult writeImpl(fl::span< const fl::span< const u8 > > lane_data)
Internal implementation - write all lanes atomically.
Lane & lane(size_t lane_id)
Get access to a specific lane.
void end()
Shutdown hardware and release resources.
const Config & getConfig() const
Get current configuration.
fl::optional< fl::task::Error > begin()
Initialize hardware.
fl::unique_ptr< Impl > pImpl
Result< void > flush()
Flush all lanes (transpose and transmit)
size_t numLanes() const
Get number of lanes.
MultiLaneDevice(const Config &config)
Construct multi-lane device.
bool isBusy() const
Check if transmission is in progress.
fl::size size() const FL_NOEXCEPT
Shared implementation helpers for SPI device classes.
Centralized logging categories for FastLED hardware interfaces and subsystems.
Multi-lane SPI device for 2-8 independent LED strips.
fl::result< T, SPIError > Result
fl::enable_if<!fl::is_array< T >::value, unique_ptr< T > >::type make_unique(Args &&... args) FL_NOEXCEPT
constexpr nullopt_t nullopt
Base definition for an LED controller.
Result of a write operation.
DeviceImplBase() FL_NOEXCEPT
Constructor.
bool initialized
Whether hardware is initialized.
void clearBackend()
Clear backend state.
fl::shared_ptr< SpiHwBase > backend
Polymorphic SPI hardware backend (SpiHw1/2/4/8)
fl::vector< u8 > data_pins
Data pins (1-8 pins)
Configuration for multi-lane SPI.