@@ -49,6 +49,26 @@ COMPILER_ALIGNED(16) static DmacDescriptor write_back_descriptors[DMA_CHANNEL_CO
4949#define FIRST_SERCOM_TX_TRIGSRC 0x05
5050#endif
5151
52+ static bool dma_allocated [DMA_CHANNEL_COUNT ];
53+
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 ++ ) {
58+ if (!dma_allocated [channel ]) {
59+ dma_allocated [channel ] = true;
60+ return channel ;
61+ }
62+ }
63+ return channel ; // i.e., return failure
64+ }
65+
66+ void dma_free_channel (uint8_t channel ) {
67+ assert (dma_allocated [channel ]);
68+ dma_disable_channel (channel );
69+ dma_allocated [channel ] = false;
70+ }
71+
5272void init_shared_dma (void ) {
5373 // Turn on the clocks
5474 #ifdef SAM_D5X_E5X
@@ -77,13 +97,21 @@ void init_shared_dma(void) {
7797// If buffer_out is a real buffer, ignore tx.
7898// DMAs buffer_out -> dest
7999// DMAs src -> buffer_in
80- static int32_t shared_dma_transfer (void * peripheral ,
100+ dma_descr_t shared_dma_transfer_start (void * peripheral ,
81101 const uint8_t * buffer_out , volatile uint32_t * dest ,
82102 volatile uint32_t * src , uint8_t * buffer_in ,
83103 uint32_t length , uint8_t tx ) {
84- if (!dma_channel_free (SHARED_TX_CHANNEL ) ||
85- (buffer_in != NULL && !dma_channel_free (SHARED_RX_CHANNEL ))) {
86- return -1 ;
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);
109+
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 ;
87115 }
88116
89117 uint32_t beat_size = DMAC_BTCTRL_BEATSIZE_BYTE ;
@@ -96,26 +124,27 @@ static int32_t shared_dma_transfer(void* peripheral,
96124 // Check input alignment on word boundaries.
97125 if ((((uint32_t ) buffer_in ) & 0x3 ) != 0 ||
98126 (((uint32_t ) buffer_out ) & 0x3 ) != 0 ) {
99- return -3 ;
127+ res .failure = -3 ;
128+ return res ;
100129 }
101130 beat_size = DMAC_BTCTRL_BEATSIZE_WORD | DMAC_BTCTRL_SRCINC | DMAC_BTCTRL_DSTINC ;
102131 beat_length /= 4 ;
103132 sercom = false;
104133 if (buffer_out != NULL ) {
105- dma_configure (SHARED_TX_CHANNEL , QSPI_DMAC_ID_TX , false);
134+ dma_configure (tx_channel , QSPI_DMAC_ID_TX , false);
106135 tx_active = true;
107136 } else {
108- dma_configure (SHARED_RX_CHANNEL , QSPI_DMAC_ID_RX , false);
137+ dma_configure (rx_channel , QSPI_DMAC_ID_RX , false);
109138 rx_active = true;
110139 }
111140
112141 } else {
113142 #endif
114143
115- dma_configure (SHARED_TX_CHANNEL , sercom_index (peripheral ) * 2 + FIRST_SERCOM_TX_TRIGSRC , false);
144+ dma_configure (tx_channel , sercom_index (peripheral ) * 2 + FIRST_SERCOM_TX_TRIGSRC , false);
116145 tx_active = true;
117146 if (buffer_in != NULL ) {
118- dma_configure (SHARED_RX_CHANNEL , sercom_index (peripheral ) * 2 + FIRST_SERCOM_RX_TRIGSRC , false);
147+ dma_configure (rx_channel , sercom_index (peripheral ) * 2 + FIRST_SERCOM_RX_TRIGSRC , false);
119148 rx_active = true;
120149 }
121150
@@ -125,7 +154,7 @@ static int32_t shared_dma_transfer(void* peripheral,
125154
126155 // Set up RX first.
127156 if (rx_active ) {
128- DmacDescriptor * rx_descriptor = & dma_descriptors [SHARED_RX_CHANNEL ];
157+ DmacDescriptor * rx_descriptor = & dma_descriptors [rx_channel ];
129158 rx_descriptor -> BTCTRL .reg = beat_size | DMAC_BTCTRL_DSTINC ;
130159 rx_descriptor -> BTCNT .reg = beat_length ;
131160 rx_descriptor -> SRCADDR .reg = ((uint32_t ) src );
@@ -140,7 +169,7 @@ static int32_t shared_dma_transfer(void* peripheral,
140169
141170 // Set up TX second.
142171 if (tx_active ) {
143- DmacDescriptor * tx_descriptor = & dma_descriptors [SHARED_TX_CHANNEL ];
172+ DmacDescriptor * tx_descriptor = & dma_descriptors [tx_channel ];
144173 tx_descriptor -> BTCTRL .reg = beat_size ;
145174 tx_descriptor -> BTCNT .reg = beat_length ;
146175
@@ -155,23 +184,25 @@ static int32_t shared_dma_transfer(void* peripheral,
155184 }
156185 if (sercom ) {
157186 SercomSpi * s = & ((Sercom * ) peripheral )-> SPI ;
187+ // TODO: test if this operation is necessary or if it's just a waste of time and space
188+ // Section 35.8.7 of the datasheet lists both of these bits as read-only, so this shouldn't do anything
158189 s -> INTFLAG .reg = SERCOM_SPI_INTFLAG_RXC | SERCOM_SPI_INTFLAG_DRE ;
159190 }
160191
161192 // Start the RX job first so we don't miss the first byte. The TX job clocks the output.
162193 // Disable interrupts during startup to make sure both RX and TX start at just about the same time.
163194 mp_hal_disable_all_interrupts ();
164195 if (rx_active ) {
165- dma_enable_channel (SHARED_RX_CHANNEL );
196+ dma_enable_channel (rx_channel );
166197 }
167198 if (tx_active ) {
168- dma_enable_channel (SHARED_TX_CHANNEL );
199+ dma_enable_channel (tx_channel );
169200 }
170201 mp_hal_enable_all_interrupts ();
171202
172203 if (!sercom ) {
173204 if (rx_active ) {
174- DMAC -> SWTRIGCTRL .reg |= (1 << SHARED_RX_CHANNEL );
205+ DMAC -> SWTRIGCTRL .reg |= (1 << rx_channel );
175206 }
176207 }
177208
@@ -184,11 +215,11 @@ static int32_t shared_dma_transfer(void* peripheral,
184215 for (int i = 0 ; i < 10 && !is_okay ; i ++ ) {
185216 bool complete = true;
186217 if (rx_active ) {
187- if (DMAC -> Channel [SHARED_RX_CHANNEL ].CHSTATUS .reg & 0x3 )
218+ if (DMAC -> Channel [rx_channel ].CHSTATUS .reg & 0x3 )
188219 complete = false;
189220 }
190221 if (tx_active ) {
191- if (DMAC -> Channel [SHARED_TX_CHANNEL ].CHSTATUS .reg & 0x3 )
222+ if (DMAC -> Channel [tx_channel ].CHSTATUS .reg & 0x3 )
192223 complete = false;
193224 }
194225 is_okay = is_okay || (DMAC -> ACTIVE .bit .ABUSY || complete );
@@ -202,23 +233,46 @@ static int32_t shared_dma_transfer(void* peripheral,
202233 }
203234 }
204235 #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 ;
245+ }
205246
206- // busy-wait for the RX and TX DMAs to either complete or encounter an error
207- if (rx_active ) {
208- while (( dma_transfer_status ( SHARED_RX_CHANNEL ) & 0x3 ) == 0 ) {}
247+ bool shared_dma_transfer_finished ( dma_descr_t descr ) {
248+ if (descr . failure != 0 ) {
249+ return true;
209250 }
210- if (tx_active ) {
211- while ((dma_transfer_status (SHARED_TX_CHANNEL ) & 0x3 ) == 0 ) {}
251+
252+ if (descr .progress < 1 && descr .rx_active ) {
253+ if ((dma_transfer_status (descr .rx_channel ) & 0x3 ) == 0 ) {
254+ return false;
255+ }
256+ descr .progress = 1 ;
257+ }
258+ if (descr .progress < 2 && descr .tx_active ) {
259+ if ((dma_transfer_status (descr .tx_channel ) & 0x3 ) == 0 ) {
260+ return false;
261+ }
262+ descr .progress = 2 ;
212263 }
213264
214- if (sercom ) {
215- Sercom * s = (Sercom * ) peripheral ;
265+ if (descr . progress < 3 && descr . sercom ) {
266+ Sercom * s = (Sercom * ) descr . peripheral ;
216267 // Wait for the SPI transfer to complete.
217- while (s -> SPI .INTFLAG .bit .TXC == 0 ) {}
268+ if (s -> SPI .INTFLAG .bit .TXC == 0 ) {
269+ return false;
270+ }
271+ descr .progress = 3 ;
218272
219273 // This transmit will cause the RX buffer overflow but we're OK with that.
220274 // So, read the garbage and clear the overflow flag.
221- if (!rx_active ) {
275+ if (!descr . rx_active ) {
222276 while (s -> SPI .INTFLAG .bit .RXC == 1 ) {
223277 s -> SPI .DATA .reg ;
224278 }
@@ -227,13 +281,39 @@ static int32_t shared_dma_transfer(void* peripheral,
227281 }
228282 }
229283
230- if ((!rx_active || dma_transfer_status (SHARED_RX_CHANNEL ) == DMAC_CHINTFLAG_TCMPL ) &&
231- (!tx_active || dma_transfer_status (SHARED_TX_CHANNEL ) == DMAC_CHINTFLAG_TCMPL )) {
232- return length ;
284+ return true;
285+ }
286+
287+ int shared_dma_transfer_close (dma_descr_t descr ) {
288+ dma_free_channel (descr .tx_channel );
289+ dma_free_channel (descr .rx_channel );
290+
291+ if (descr .failure != 0 ) {
292+ return descr .failure ;
293+ }
294+
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 ;
233298 }
234299 return -2 ;
235300}
236301
302+ // Do write and read simultaneously. If buffer_out is NULL, write the tx byte over and over.
303+ // If buffer_out is a real buffer, ignore tx.
304+ // DMAs buffer_out -> dest
305+ // DMAs src -> buffer_in
306+ static int32_t shared_dma_transfer (void * peripheral ,
307+ const uint8_t * buffer_out , volatile uint32_t * dest ,
308+ volatile uint32_t * src , uint8_t * buffer_in ,
309+ 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 ;
313+ }
314+ while (!shared_dma_transfer_finished (descr )) {}
315+ return shared_dma_transfer_close (descr );
316+ }
237317
238318int32_t sercom_dma_transfer (Sercom * sercom , const uint8_t * buffer_out , uint8_t * buffer_in ,
239319 uint32_t length ) {
0 commit comments