roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
spi_dma.cpp
Go to the documentation of this file.
2
3#include "esp_attr.h"
4#include "esp_err.h"
5#include "esp_intr_alloc.h"
6#include "freertos/FreeRTOS.h"
7#include "freertos/task.h"
9#include "roo_logging.h"
10
11#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA && SOC_GDMA_SUPPORTED
12#include "esp_private/gdma.h"
13#endif
14
15namespace roo_display {
16namespace esp32 {
17
18namespace {
19
20inline uint8_t hostToSpiPort(spi_host_device_t host_id) {
21 return static_cast<uint8_t>(host_id) + 1;
22}
23
24} // namespace
25
27#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
28 auto* controller = static_cast<DmaController*>(arg);
29 if (controller == nullptr) return;
30
33 // The ISR must have been invoked due to another event.
34 return;
35 }
36
38 controller->transferCompleteISR();
39#else
40 (void)arg;
41#endif
42}
43
59
61#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
62 if (dma_ctx_ != nullptr && dma_intr_handle_ != nullptr) return;
63
65 waiter_task_ = nullptr;
66 current_op_ = {nullptr, 0};
67 pending_ops_.clear();
68
69 // Try to acquire the DMA context from the SPI bus.
70 dma_ctx_ = spi_bus_get_dma_ctx(host_id_);
71 owned_dma_ctx_ = nullptr;
72 if (dma_ctx_ != nullptr) {
73 // Continue to interrupt setup below.
74 } else {
75 spi_dma_ctx_t* tmp_ctx = nullptr;
78 if (err != ESP_OK || tmp_ctx == nullptr) {
79 dma_ctx_ = nullptr;
80 return;
81 }
82 int actual_max_transfer = 0;
87 dma_ctx_ = nullptr;
88 return;
89 }
92 }
93
98 if (intr_err != ESP_OK || dma_intr_handle_ == nullptr) {
99 if (owned_dma_ctx_ != nullptr) {
101 owned_dma_ctx_ = nullptr;
102 }
103 dma_ctx_ = nullptr;
104 return;
105 }
106
107 uint8_t spi_port = hostToSpiPort(host_id_);
111#else
112 pending_ops_.clear();
113#endif
114}
115
117#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
119
120 uint8_t spi_port = hostToSpiPort(host_id_);
124
125 if (dma_intr_handle_ != nullptr) {
127 dma_intr_handle_ = nullptr;
128 }
129
130 if (owned_dma_ctx_ != nullptr) {
132 owned_dma_ctx_ = nullptr;
133 }
134
135 dma_ctx_ = nullptr;
136 transfer_in_progress_ = false;
137 waiter_task_ = nullptr;
138 current_op_ = {nullptr, 0};
139 pending_ops_.clear();
140#else
141 pending_ops_.clear();
142#endif
143}
144
147 CHECK_GT(op.out_len, 0u);
148 CHECK_EQ((op.out_len & 0x3u), 0u);
149
150#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
151 if (dma_ctx_ == nullptr || dma_intr_handle_ == nullptr) return false;
152
153 bool ok;
154
155 portENTER_CRITICAL(&mux_);
157 ok = startOperationCritical(op);
158 } else {
159 ok = pending_ops_.write(op);
160 }
161 portEXIT_CRITICAL(&mux_);
162
163 return ok;
164#else
165 (void)op;
166 return false;
167#endif
168}
169
171#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
172 if (dma_ctx_ == nullptr || dma_intr_handle_ == nullptr) return;
173
175 while (true) {
176 portENTER_CRITICAL(&mux_);
177 bool done = !transfer_in_progress_ && pending_ops_.empty();
178 if (done) {
179 if (waiter_task_ == self) {
180 waiter_task_ = nullptr;
181 }
182 portEXIT_CRITICAL(&mux_);
183 return;
184 }
186 portEXIT_CRITICAL(&mux_);
188 }
189#endif
190}
191
192void DmaController::transferCompleteISR() {
193#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
194 Operation completed = {nullptr, 0};
195 Operation next = {nullptr, 0};
196 bool has_next = false;
197 bool notify_waiter = false;
198 TaskHandle_t waiter = nullptr;
199
200 portENTER_CRITICAL_ISR(&mux_);
201 if (!transfer_in_progress_) {
202 // Can happen on a stale/late IRQ after software already transitioned the
203 // controller to idle (e.g. teardown or IRQ/status timing race).
204 portEXIT_CRITICAL_ISR(&mux_);
205 return;
206 }
207
208 completed = current_op_;
209 current_op_ = {nullptr, 0};
210
211 has_next = pending_ops_.read(next);
212 if (has_next) {
213 startOperationCritical(next);
214 } else {
215 transfer_in_progress_ = false;
216 uint8_t spi_port = hostToSpiPort(host_id_);
217 SpiDmaTxDisable(spi_port);
219 notify_waiter = true;
220 }
221
222 if (notify_waiter && waiter_task_ != nullptr) {
223 waiter = waiter_task_;
224 waiter_task_ = nullptr;
225 }
226 portEXIT_CRITICAL_ISR(&mux_);
227
228 if (completed.out_data != nullptr) {
229 dma_buffer_pool_.release(
230 DmaBufferPool::Buffer{const_cast<roo::byte*>(completed.out_data)});
231 }
232
233 if (waiter != nullptr) {
234 BaseType_t high_wakeup = pdFALSE;
235 vTaskNotifyGiveFromISR(waiter, &high_wakeup);
236 portYIELD_FROM_ISR(high_wakeup);
237 }
238#endif
239}
240
241#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
242bool DmaController::startOperationCritical(Operation op) {
243 CHECK_NOTNULL(dma_ctx_);
244 CHECK_NOTNULL(op.out_data);
245 CHECK_GT(op.out_len, 0);
246
247 spicommon_dma_desc_setup_link(dma_ctx_->dmadesc_tx, op.out_data,
248 static_cast<int>(op.out_len), false);
249
250 uint8_t spi_port = hostToSpiPort(host_id_);
251 SpiSetOutBufferSize(spi_port, op.out_len);
254 SpiDmaTxEnable(spi_port);
255
256#if SOC_GDMA_SUPPORTED
257 gdma_reset(dma_ctx_->tx_dma_chan);
258 gdma_start(dma_ctx_->tx_dma_chan,
259 reinterpret_cast<intptr_t>(
260 const_cast<spi_dma_desc_t*>(dma_ctx_->dmadesc_tx)));
261#else
262 spi_dma_reset(dma_ctx_->tx_dma_chan);
263 spi_dma_start(dma_ctx_->tx_dma_chan,
264 const_cast<spi_dma_desc_t*>(dma_ctx_->dmadesc_tx));
265#endif
266
267 SpiTxStart(spi_port);
268 current_op_ = op;
269 transfer_in_progress_ = true;
270 return true;
271}
272#endif
273
274} // namespace esp32
275} // namespace roo_display
DmaController(spi_host_device_t host_id, DmaBufferPool &dma_buffer_pool)
Definition spi_dma.cpp:44
friend void IRAM_ATTR DmaTransferCompleteISR(void *)
Definition spi_dma.cpp:26
void SpiDmaTransferDoneIntDisable(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:165
void SpiDmaTransferDoneIntClear(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:157
void SpiDmaTxDisable(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:173
void IRAM_ATTR DmaTransferCompleteISR(void *arg)
Definition spi_dma.cpp:26
void SpiSetOutBufferSize(uint8_t spi_port, int len) __attribute__((always_inline))
Definition spi_reg.h:194
static constexpr size_t kDmaBufferCapacity
void SpiTxStart(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:177
void SpiDmaTransferDoneIntEnable(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:161
bool SpiDmaTransferDoneIntPending(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:152
void SpiDmaTxEnable(uint8_t spi_port) __attribute__((always_inline))
Definition spi_reg.h:169
Defines 140 opaque HTML named colors.
#define ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
Definition spi_dma.h:14