2727
2828#include <string.h>
2929
30- #include "mphalport.h"
3130#include "py/gc.h"
31+ #include "py/mphal.h"
3232#include "py/mpstate.h"
3333
3434#include "hal/utils/include/utils.h"
@@ -51,19 +51,31 @@ COMPILER_ALIGNED(16) static DmacDescriptor write_back_descriptors[DMA_CHANNEL_CO
5151
5252static bool dma_allocated [DMA_CHANNEL_COUNT ];
5353
54- uint8_t dma_allocate_channel (bool audio_channel ) {
55- uint8_t channel ;
56- uint8_t lim = audio_channel ? AUDIO_DMA_CHANNEL_COUNT : DMA_CHANNEL_COUNT ;
57- for (channel = (audio_channel ? 0 : AUDIO_DMA_CHANNEL_COUNT ); channel < lim ; channel ++ ) {
54+ uint8_t dma_allocate_audio_channel (void ) {
55+ for (uint8_t channel = 0 ; channel < AUDIO_DMA_CHANNEL_COUNT ; channel ++ ) {
5856 if (!dma_allocated [channel ]) {
5957 dma_allocated [channel ] = true;
6058 return channel ;
6159 }
6260 }
63- return channel ; // i.e., return failure
61+ return NO_DMA_CHANNEL ;
62+ }
63+
64+ uint8_t dma_allocate_non_audio_channel (void ) {
65+ // Start allocating above audio channels.
66+ for (uint8_t channel = AUDIO_DMA_CHANNEL_COUNT ; channel < DMA_CHANNEL_COUNT ; channel ++ ) {
67+ if (!dma_allocated [channel ]) {
68+ dma_allocated [channel ] = true;
69+ return channel ;
70+ }
71+ }
72+ return NO_DMA_CHANNEL ;
6473}
6574
6675void dma_free_channel (uint8_t channel ) {
76+ if (channel == NO_DMA_CHANNEL ) {
77+ return ;
78+ }
6779 assert (dma_allocated [channel ]);
6880 dma_disable_channel (channel );
6981 dma_allocated [channel ] = false;
@@ -87,6 +99,7 @@ void init_shared_dma(void) {
8799
88100 DMAC -> CTRL .reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN0 ;
89101
102+ // Configure audio channels in advance.
90103 // Non-audio channels will be configured on demand.
91104 for (uint8_t i = 0 ; i < AUDIO_DMA_CHANNEL_COUNT ; i ++ ) {
92105 dma_configure (i , 0 , true);
@@ -97,21 +110,24 @@ void init_shared_dma(void) {
97110// If buffer_out is a real buffer, ignore tx.
98111// DMAs buffer_out -> dest
99112// DMAs src -> buffer_in
100- dma_descr_t shared_dma_transfer_start (void * peripheral ,
101- const uint8_t * buffer_out , volatile uint32_t * dest ,
102- volatile uint32_t * src , uint8_t * buffer_in ,
103- uint32_t length , uint8_t tx ) {
104- dma_descr_t res ;
105- res . progress = 0 ;
106-
107- uint8_t tx_channel = dma_allocate_channel (false) ;
108- uint8_t rx_channel = dma_allocate_channel (false);
113+ void shared_dma_transfer_start (dma_transfer_t * transfer , void * peripheral , const uint8_t * buffer_out , volatile uint32_t * dest , volatile uint32_t * src , uint8_t * buffer_in , uint32_t length , uint8_t tx ) {
114+ transfer -> progress = 0 ;
115+
116+ // There's always a tx DMA channel, though it may be sending only the tx byte if buffer_out is NULL.
117+ uint8_t tx_channel = dma_allocate_non_audio_channel () ;
118+ if ( tx_channel == NO_DMA_CHANNEL ) {
119+ transfer -> failure = DMA_FAILURE_NO_CHANNEL_AVAILABLE ;
120+ return ;
121+ }
109122
110- if ((tx_channel >= DMA_CHANNEL_COUNT ) || (rx_channel >= DMA_CHANNEL_COUNT ) ||
111- !dma_channel_free (tx_channel ) ||
112- (buffer_in != NULL && !dma_channel_free (rx_channel ))) {
113- res .failure = -1 ;
114- return res ;
123+ // Allocate an rx dma channel only if we're going to read.
124+ uint8_t rx_channel = NO_DMA_CHANNEL ;
125+ if (buffer_in != NULL ) {
126+ rx_channel = dma_allocate_non_audio_channel ();
127+ if (rx_channel == NO_DMA_CHANNEL ) {
128+ transfer -> failure = DMA_FAILURE_NO_CHANNEL_AVAILABLE ;
129+ return ;
130+ }
115131 }
116132
117133 uint32_t beat_size = DMAC_BTCTRL_BEATSIZE_BYTE ;
@@ -122,10 +138,11 @@ dma_descr_t shared_dma_transfer_start(void* peripheral,
122138 #ifdef SAM_D5X_E5X
123139 if (peripheral == QSPI ) {
124140 // Check input alignment on word boundaries.
141+ // NULLs will pass this test, so no need to check for them separately.
125142 if ((((uint32_t ) buffer_in ) & 0x3 ) != 0 ||
126143 (((uint32_t ) buffer_out ) & 0x3 ) != 0 ) {
127- res . failure = -3 ;
128- return res ;
144+ transfer -> failure = DMA_FAILURE_ALIGNMENT ;
145+ return ;
129146 }
130147 beat_size = DMAC_BTCTRL_BEATSIZE_WORD | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_DSTINC ;
131148 beat_length /= 4 ;
@@ -141,9 +158,10 @@ dma_descr_t shared_dma_transfer_start(void* peripheral,
141158 } else {
142159 #endif
143160
161+ // There is always a TX channel.
144162 dma_configure (tx_channel , sercom_index (peripheral ) * 2 + FIRST_SERCOM_TX_TRIGSRC , false);
145163 tx_active = true;
146- if (buffer_in != NULL ) {
164+ if (rx_channel != NO_DMA_CHANNEL ) {
147165 dma_configure (rx_channel , sercom_index (peripheral ) * 2 + FIRST_SERCOM_RX_TRIGSRC , false);
148166 rx_active = true;
149167 }
@@ -225,54 +243,58 @@ dma_descr_t shared_dma_transfer_start(void* peripheral,
225243 is_okay = is_okay || (DMAC -> ACTIVE .bit .ABUSY || complete );
226244 }
227245 if (!is_okay ) {
228- for (int i = 0 ; i < AUDIO_DMA_CHANNEL_COUNT ; i ++ ) {
246+ for (int i = 0 ; i < DMA_CHANNEL_COUNT ; i ++ ) {
229247 if (DMAC -> Channel [i ].CHCTRLA .bit .ENABLE ) {
230248 DMAC -> Channel [i ].CHCTRLA .bit .ENABLE = 0 ;
231249 DMAC -> Channel [i ].CHCTRLA .bit .ENABLE = 1 ;
232250 }
233251 }
234252 }
235253 #endif
236- res . peripheral = peripheral ;
237- res . length = length ;
238- res . rx_channel = rx_channel ;
239- res . tx_channel = tx_channel ;
240- res . rx_active = rx_active ;
241- res . tx_active = tx_active ;
242- res . sercom = sercom ;
243- res . failure = 0 ;
244- return res ;
254+ transfer -> peripheral = peripheral ;
255+ transfer -> length = length ;
256+ transfer -> rx_channel = rx_channel ;
257+ transfer -> tx_channel = tx_channel ;
258+ transfer -> rx_active = rx_active ;
259+ transfer -> tx_active = tx_active ;
260+ transfer -> sercom = sercom ;
261+ transfer -> failure = 0 ;
262+ transfer -> progress = 0 ;
245263}
246264
247- bool shared_dma_transfer_finished (dma_descr_t descr ) {
248- if (descr . failure != 0 ) {
265+ bool shared_dma_transfer_finished (dma_transfer_t * transfer ) {
266+ if (transfer -> failure != 0 ) {
249267 return true;
250268 }
251269
252- if (descr .progress < 1 && descr .rx_active ) {
253- if ((dma_transfer_status (descr .rx_channel ) & 0x3 ) == 0 ) {
270+ if (transfer -> progress < 1 && transfer -> rx_active ) {
271+ if ((dma_transfer_status (transfer -> rx_channel ) & 0x3 ) == 0 ) {
272+ // RX hasn't finished.
254273 return false;
255274 }
256- descr .progress = 1 ;
275+ // RX has finished
276+ transfer -> progress = 1 ;
257277 }
258- if (descr .progress < 2 && descr .tx_active ) {
259- if ((dma_transfer_status (descr .tx_channel ) & 0x3 ) == 0 ) {
278+ if (transfer -> progress < 2 && transfer -> tx_active ) {
279+ if ((dma_transfer_status (transfer -> tx_channel ) & 0x3 ) == 0 ) {
280+ // TX hasn't finished.
260281 return false;
261282 }
262- descr .progress = 2 ;
283+ // TX has finished. RX has also finished, or there is no RX.
284+ transfer -> progress = 2 ;
263285 }
264286
265- if (descr . progress < 3 && descr . sercom ) {
266- Sercom * s = (Sercom * ) descr . peripheral ;
287+ if (transfer -> progress < 3 && transfer -> sercom ) {
288+ Sercom * s = (Sercom * ) transfer -> peripheral ;
267289 // Wait for the SPI transfer to complete.
268290 if (s -> SPI .INTFLAG .bit .TXC == 0 ) {
269291 return false;
270292 }
271- descr . progress = 3 ;
293+ transfer -> progress = 3 ;
272294
273295 // This transmit will cause the RX buffer overflow but we're OK with that.
274296 // So, read the garbage and clear the overflow flag.
275- if (!descr . rx_active ) {
297+ if (!transfer -> rx_active ) {
276298 while (s -> SPI .INTFLAG .bit .RXC == 1 ) {
277299 s -> SPI .DATA .reg ;
278300 }
@@ -284,19 +306,20 @@ bool shared_dma_transfer_finished(dma_descr_t descr) {
284306 return true;
285307}
286308
287- int shared_dma_transfer_close (dma_descr_t descr ) {
288- dma_free_channel (descr .tx_channel );
289- dma_free_channel (descr .rx_channel );
309+ int shared_dma_transfer_close (dma_transfer_t * transfer ) {
310+ // It is not an error if either is NO_DMA_CHANNEL.
311+ dma_free_channel (transfer -> tx_channel );
312+ dma_free_channel (transfer -> rx_channel );
290313
291- if (descr . failure != 0 ) {
292- return descr . failure ;
314+ if (transfer -> failure != 0 ) {
315+ return transfer -> failure ;
293316 }
294317
295- if ((!descr . rx_active || dma_transfer_status (descr . rx_channel ) == DMAC_CHINTFLAG_TCMPL ) &&
296- (!descr . tx_active || dma_transfer_status (descr . tx_channel ) == DMAC_CHINTFLAG_TCMPL )) {
297- return descr . length ;
318+ if ((!transfer -> rx_active || dma_transfer_status (transfer -> rx_channel ) == DMAC_CHINTFLAG_TCMPL ) &&
319+ (!transfer -> tx_active || dma_transfer_status (transfer -> tx_channel ) == DMAC_CHINTFLAG_TCMPL )) {
320+ return transfer -> length ;
298321 }
299- return -2 ;
322+ return DMA_FAILURE_INCOMPLETE ;
300323}
301324
302325// Do write and read simultaneously. If buffer_out is NULL, write the tx byte over and over.
@@ -307,12 +330,14 @@ static int32_t shared_dma_transfer(void* peripheral,
307330 const uint8_t * buffer_out , volatile uint32_t * dest ,
308331 volatile uint32_t * src , uint8_t * buffer_in ,
309332 uint32_t length , uint8_t tx ) {
310- dma_descr_t descr = shared_dma_transfer_start (peripheral , buffer_out , dest , src , buffer_in , length , tx );
311- if (descr .failure != 0 ) {
312- return descr .failure ;
333+ dma_transfer_t transfer ;
334+ shared_dma_transfer_start (& transfer , peripheral , buffer_out , dest , src , buffer_in , length , tx );
335+ if (transfer .failure != 0 ) {
336+ return transfer .failure ;
313337 }
314- while (!shared_dma_transfer_finished (descr )) {}
315- return shared_dma_transfer_close (descr );
338+ while (!shared_dma_transfer_finished (& transfer )) {}
339+
340+ return shared_dma_transfer_close (& transfer );
316341}
317342
318343int32_t sercom_dma_transfer (Sercom * sercom , const uint8_t * buffer_out , uint8_t * buffer_in ,
0 commit comments