1010#include <sys/param.h>
1111#include "sdkconfig.h"
1212#include "freertos/FreeRTOS.h"
13- #include "soc/soc_caps.h"
1413#include "soc/interrupts.h"
1514#include "esp_log.h"
1615#include "esp_check.h"
2019#include "esp_memory_utils.h"
2120#include "esp_async_memcpy.h"
2221#include "esp_async_memcpy_priv.h"
22+ #include "esp_private/gdma_link.h"
2323#include "hal/cp_dma_hal.h"
2424#include "hal/cp_dma_ll.h"
25- #include "hal/dma_types.h"
2625
2726static const char * TAG = "async_mcp.cpdma" ;
2827
28+ #define MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE 4095
29+
2930/// @brief Transaction object for async memcpy
30- /// @note - the DMA descriptors to be 4-byte aligned
31- /// @note - The DMA descriptor link list is allocated dynamically from DMA-able memory
32- /// @note - Because of the eof_node, the transaction object should also be allocated from DMA-able memory
3331typedef struct async_memcpy_transaction_t {
34- dma_descriptor_align4_t eof_node ; // this is the DMA node which act as the EOF descriptor (RX path only)
35- dma_descriptor_align4_t * tx_desc_link ; // descriptor link list, the length of the link is determined by the copy buffer size
36- dma_descriptor_align4_t * rx_desc_link ; // descriptor link list, the length of the link is determined by the copy buffer size
37- intptr_t tx_start_desc_addr ; // TX start descriptor address
38- intptr_t rx_start_desc_addr ; // RX start descriptor address
39- async_memcpy_isr_cb_t cb ; // user callback
40- void * cb_args ; // user callback args
32+ gdma_link_list_handle_t tx_link_list ; // DMA link list for TX direction
33+ gdma_link_list_handle_t rx_link_list ; // DMA link list for RX direction
34+ async_memcpy_isr_cb_t cb ; // user callback
35+ void * cb_args ; // user callback args
4136 STAILQ_ENTRY (async_memcpy_transaction_t ) idle_queue_entry ; // Entry for the idle queue
4237 STAILQ_ENTRY (async_memcpy_transaction_t ) ready_queue_entry ; // Entry for the ready queue
4338} async_memcpy_transaction_t ;
4439
4540/// @brief Context of async memcpy driver
4641/// @note - It saves two queues, one for idle transaction objects, one for ready transaction objects
47- /// @note - Transaction objects are allocated from DMA-able memory
4842/// @note - Number of transaction objects are determined by the backlog parameter
4943typedef struct {
5044 async_memcpy_context_t parent ; // Parent IO interface
51- size_t max_single_dma_buffer ; // max DMA buffer size by a single descriptor
5245 cp_dma_hal_context_t hal ; // CPDMA hal
5346 intr_handle_t intr ; // CPDMA interrupt handle
54- portMUX_TYPE spin_lock ; // spin lock to avoid threads and isr from accessing the same resource simultaneously
55- _Atomic async_memcpy_fsm_t fsm ; // driver state machine, changing state should be atomic
56- async_memcpy_transaction_t * transaction_pool ; // transaction object pool
47+ portMUX_TYPE spin_lock ; // spin lock to avoid threads and isr from accessing the same resource simultaneously
48+ _Atomic async_memcpy_fsm_t fsm ;// driver state machine, changing state should be atomic
49+ size_t num_trans_objs ; // number of transaction objects
50+ async_memcpy_transaction_t * transaction_pool ; // transaction object pool
51+ async_memcpy_transaction_t * current_transaction ; // current transaction object
5752 STAILQ_HEAD (, async_memcpy_transaction_t ) idle_queue_head ; // Head of the idle queue
5853 STAILQ_HEAD (, async_memcpy_transaction_t ) ready_queue_head ; // Head of the ready queue
5954} async_memcpy_cpdma_context_t ;
@@ -65,6 +60,15 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
6560static esp_err_t mcp_cpdma_destroy (async_memcpy_cpdma_context_t * mcp_dma )
6661{
6762 if (mcp_dma -> transaction_pool ) {
63+ for (size_t i = 0 ; i < mcp_dma -> num_trans_objs ; i ++ ) {
64+ async_memcpy_transaction_t * trans = & mcp_dma -> transaction_pool [i ];
65+ if (trans -> tx_link_list ) {
66+ gdma_del_link_list (trans -> tx_link_list );
67+ }
68+ if (trans -> rx_link_list ) {
69+ gdma_del_link_list (trans -> rx_link_list );
70+ }
71+ }
6872 free (mcp_dma -> transaction_pool );
6973 }
7074 if (mcp_dma -> intr ) {
@@ -83,13 +87,12 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
8387 esp_err_t ret = ESP_OK ;
8488 async_memcpy_cpdma_context_t * mcp_dma = NULL ;
8589 ESP_RETURN_ON_FALSE (config && mcp , ESP_ERR_INVALID_ARG , TAG , "invalid argument" );
86- // allocate memory of driver context from internal memory
90+ // allocate memory of driver context from internal memory (because it contains atomic variable)
8791 mcp_dma = heap_caps_calloc (1 , sizeof (async_memcpy_cpdma_context_t ), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT );
8892 ESP_GOTO_ON_FALSE (mcp_dma , ESP_ERR_NO_MEM , err , TAG , "no mem for driver context" );
8993 uint32_t trans_queue_len = config -> backlog ? config -> backlog : DEFAULT_TRANSACTION_QUEUE_LENGTH ;
90- // allocate memory for transaction pool, aligned to 4 because the trans->eof_node requires that alignment
91- mcp_dma -> transaction_pool = heap_caps_aligned_calloc (4 , trans_queue_len , sizeof (async_memcpy_transaction_t ),
92- MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA );
94+ // allocate memory for transaction pool from internal memory
95+ mcp_dma -> transaction_pool = heap_caps_calloc (trans_queue_len , sizeof (async_memcpy_transaction_t ), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT );
9396 ESP_GOTO_ON_FALSE (mcp_dma -> transaction_pool , ESP_ERR_NO_MEM , err , TAG , "no mem for transaction pool" );
9497
9598 // Init hal context
@@ -110,8 +113,8 @@ esp_err_t esp_async_memcpy_install_cpdma(const async_memcpy_config_t *config, as
110113 // initialize other members
111114 portMUX_INITIALIZE (& mcp_dma -> spin_lock );
112115 atomic_init (& mcp_dma -> fsm , MCP_FSM_IDLE );
113- size_t trans_align = config -> dma_burst_size ;
114- mcp_dma -> max_single_dma_buffer = trans_align ? ALIGN_DOWN ( DMA_DESCRIPTOR_BUFFER_MAX_SIZE , trans_align ) : DMA_DESCRIPTOR_BUFFER_MAX_SIZE ;
116+ mcp_dma -> num_trans_objs = trans_queue_len ;
117+
115118 mcp_dma -> parent .del = mcp_cpdma_del ;
116119 mcp_dma -> parent .memcpy = mcp_cpdma_memcpy ;
117120 // return driver object
@@ -138,55 +141,6 @@ static esp_err_t mcp_cpdma_del(async_memcpy_context_t *ctx)
138141 return mcp_cpdma_destroy (mcp_dma );
139142}
140143
141- static void mount_tx_buffer_to_dma (dma_descriptor_align4_t * desc_array , int num_desc ,
142- uint8_t * buf , size_t buf_sz , size_t max_single_dma_buffer )
143- {
144- uint32_t prepared_length = 0 ;
145- size_t len = buf_sz ;
146- for (int i = 0 ; i < num_desc - 1 ; i ++ ) {
147- desc_array [i ].buffer = & buf [prepared_length ];
148- desc_array [i ].dw0 .owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA ;
149- desc_array [i ].dw0 .suc_eof = 0 ;
150- desc_array [i ].dw0 .size = max_single_dma_buffer ;
151- desc_array [i ].dw0 .length = max_single_dma_buffer ;
152- desc_array [i ].next = & desc_array [i + 1 ];
153- prepared_length += max_single_dma_buffer ;
154- len -= max_single_dma_buffer ;
155- }
156- // take special care to the EOF descriptor
157- desc_array [num_desc - 1 ].buffer = & buf [prepared_length ];
158- desc_array [num_desc - 1 ].next = NULL ;
159- desc_array [num_desc - 1 ].dw0 .owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA ;
160- desc_array [num_desc - 1 ].dw0 .suc_eof = 1 ;
161- desc_array [num_desc - 1 ].dw0 .size = len ;
162- desc_array [num_desc - 1 ].dw0 .length = len ;
163- }
164-
165- static void mount_rx_buffer_to_dma (dma_descriptor_align4_t * desc_array , int num_desc , dma_descriptor_align4_t * eof_desc ,
166- uint8_t * buf , size_t buf_sz , size_t max_single_dma_buffer )
167- {
168- uint32_t prepared_length = 0 ;
169- size_t len = buf_sz ;
170- if (desc_array ) {
171- assert (num_desc > 0 );
172- for (int i = 0 ; i < num_desc ; i ++ ) {
173- desc_array [i ].buffer = & buf [prepared_length ];
174- desc_array [i ].dw0 .owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA ;
175- desc_array [i ].dw0 .size = max_single_dma_buffer ;
176- desc_array [i ].dw0 .length = max_single_dma_buffer ;
177- desc_array [i ].next = & desc_array [i + 1 ];
178- prepared_length += max_single_dma_buffer ;
179- len -= max_single_dma_buffer ;
180- }
181- desc_array [num_desc - 1 ].next = eof_desc ;
182- }
183- eof_desc -> buffer = & buf [prepared_length ];
184- eof_desc -> next = NULL ;
185- eof_desc -> dw0 .owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA ;
186- eof_desc -> dw0 .size = len ;
187- eof_desc -> dw0 .length = len ;
188- }
189-
190144/// @brief help function to get one transaction from the ready queue
191145/// @note this function is allowed to be called in ISR
192146static async_memcpy_transaction_t * try_pop_trans_from_ready_queue (async_memcpy_cpdma_context_t * mcp_dma )
@@ -211,7 +165,10 @@ static void try_start_pending_transaction(async_memcpy_cpdma_context_t *mcp_dma)
211165 trans = try_pop_trans_from_ready_queue (mcp_dma );
212166 if (trans ) {
213167 atomic_store (& mcp_dma -> fsm , MCP_FSM_RUN );
214- cp_dma_hal_set_desc_base_addr (& mcp_dma -> hal , trans -> tx_start_desc_addr , trans -> rx_start_desc_addr );
168+ mcp_dma -> current_transaction = trans ;
169+ cp_dma_hal_set_desc_base_addr (& mcp_dma -> hal ,
170+ gdma_link_get_head_addr (trans -> tx_link_list ),
171+ gdma_link_get_head_addr (trans -> rx_link_list ));
215172 cp_dma_hal_start (& mcp_dma -> hal ); // enable DMA and interrupt
216173 } else {
217174 atomic_store (& mcp_dma -> fsm , MCP_FSM_IDLE );
@@ -244,33 +201,67 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
244201 // check if we get the transaction object successfully
245202 ESP_RETURN_ON_FALSE (trans , ESP_ERR_INVALID_STATE , TAG , "no free node in the idle queue" );
246203
247- // calculate how many descriptors we want
248- size_t max_single_dma_buffer = mcp_dma -> max_single_dma_buffer ;
249- uint32_t num_desc_per_path = (n + max_single_dma_buffer - 1 ) / max_single_dma_buffer ;
250- // allocate DMA descriptors, descriptors need a strict alignment
251- trans -> tx_desc_link = heap_caps_aligned_calloc (4 , num_desc_per_path , sizeof (dma_descriptor_align4_t ),
252- MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA );
253- ESP_GOTO_ON_FALSE (trans -> tx_desc_link , ESP_ERR_NO_MEM , err , TAG , "no mem for DMA descriptors" );
254- // don't have to allocate the EOF descriptor, we will use trans->eof_node as the RX EOF descriptor
255- if (num_desc_per_path > 1 ) {
256- trans -> rx_desc_link = heap_caps_aligned_calloc (4 , num_desc_per_path - 1 , sizeof (dma_descriptor_align4_t ),
257- MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA );
258- ESP_GOTO_ON_FALSE (trans -> rx_desc_link , ESP_ERR_NO_MEM , err , TAG , "no mem for DMA descriptors" );
259- } else {
260- // small copy buffer, use the trans->eof_node is sufficient
261- trans -> rx_desc_link = NULL ;
204+ // clean up the transaction configuration comes from the last one
205+ if (trans -> tx_link_list ) {
206+ gdma_del_link_list (trans -> tx_link_list );
207+ trans -> tx_link_list = NULL ;
208+ }
209+ if (trans -> rx_link_list ) {
210+ gdma_del_link_list (trans -> rx_link_list );
211+ trans -> rx_link_list = NULL ;
262212 }
263213
264- // (preload) mount src data to the TX descriptor
265- mount_tx_buffer_to_dma (trans -> tx_desc_link , num_desc_per_path , src , n , max_single_dma_buffer );
266- // (preload) mount dst data to the RX descriptor
267- mount_rx_buffer_to_dma (trans -> rx_desc_link , num_desc_per_path - 1 , & trans -> eof_node , dst , n , max_single_dma_buffer );
214+ // allocate gdma TX link
215+ gdma_link_list_config_t tx_link_cfg = {
216+ .buffer_alignment = 1 , // CP_DMA doesn't have alignment requirement for internal memory
217+ .item_alignment = 4 , // CP_DMA requires 4 bytes alignment for each descriptor
218+ .num_items = n / MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1 ,
219+ .flags = {
220+ .check_owner = true,
221+ .items_in_ext_mem = false,
222+ },
223+ };
224+ ESP_GOTO_ON_ERROR (gdma_new_link_list (& tx_link_cfg , & trans -> tx_link_list ), err , TAG , "failed to create TX link list" );
225+ // mount the source buffer to the TX link list
226+ gdma_buffer_mount_config_t tx_buf_mount_config [1 ] = {
227+ [0 ] = {
228+ .buffer = src ,
229+ .length = n ,
230+ .flags = {
231+ .mark_eof = true, // mark the last item as EOF, so the RX channel can also received an EOF list item
232+ .mark_final = true, // using singly list, so terminate the link here
233+ }
234+ }
235+ };
236+ gdma_link_mount_buffers (trans -> tx_link_list , 0 , tx_buf_mount_config , 1 , NULL );
237+
238+ // allocate gdma RX link
239+ gdma_link_list_config_t rx_link_cfg = {
240+ .buffer_alignment = 1 , // CP_DMA doesn't have alignment requirement for internal memory
241+ .item_alignment = 4 , // CP_DMA requires 4 bytes alignment for each descriptor
242+ .num_items = n / MCP_DMA_DESCRIPTOR_BUFFER_MAX_SIZE + 1 ,
243+ .flags = {
244+ .check_owner = true,
245+ .items_in_ext_mem = false,
246+ },
247+ };
248+ ESP_GOTO_ON_ERROR (gdma_new_link_list (& rx_link_cfg , & trans -> rx_link_list ), err , TAG , "failed to create RX link list" );
249+ // mount the destination buffer to the RX link list
250+ gdma_buffer_mount_config_t rx_buf_mount_config [1 ] = {
251+ [0 ] = {
252+ .buffer = dst ,
253+ .length = n ,
254+ .flags = {
255+ .mark_eof = false, // EOF is set by TX side
256+ .mark_final = true, // using singly list, so terminate the link here
257+ }
258+ }
259+ };
260+ gdma_link_mount_buffers (trans -> rx_link_list , 0 , rx_buf_mount_config , 1 , NULL );
268261
269262 // save other transaction context
270263 trans -> cb = cb_isr ;
271264 trans -> cb_args = cb_args ;
272- trans -> tx_start_desc_addr = (intptr_t )trans -> tx_desc_link ;
273- trans -> rx_start_desc_addr = trans -> rx_desc_link ? (intptr_t )trans -> rx_desc_link : (intptr_t )& trans -> eof_node ;
274265
275266 portENTER_CRITICAL (& mcp_dma -> spin_lock );
276267 // insert the trans to ready queue
@@ -284,14 +275,6 @@ static esp_err_t mcp_cpdma_memcpy(async_memcpy_context_t *ctx, void *dst, void *
284275
285276err :
286277 if (trans ) {
287- if (trans -> tx_desc_link ) {
288- free (trans -> tx_desc_link );
289- trans -> tx_desc_link = NULL ;
290- }
291- if (trans -> rx_desc_link ) {
292- free (trans -> rx_desc_link );
293- trans -> rx_desc_link = NULL ;
294- }
295278 // return back the trans to idle queue
296279 portENTER_CRITICAL (& mcp_dma -> spin_lock );
297280 STAILQ_INSERT_TAIL (& mcp_dma -> idle_queue_head , trans , idle_queue_entry );
@@ -310,9 +293,7 @@ static void mcp_default_isr_handler(void *args)
310293
311294 // End-Of-Frame on RX side
312295 if (status & CP_DMA_LL_EVENT_RX_EOF ) {
313- dma_descriptor_align4_t * eof_desc = (dma_descriptor_align4_t * )cp_dma_ll_get_rx_eof_descriptor_address (mcp_dma -> hal .dev );
314- // get the transaction object address by the EOF descriptor address
315- async_memcpy_transaction_t * trans = __containerof (eof_desc , async_memcpy_transaction_t , eof_node );
296+ async_memcpy_transaction_t * trans = mcp_dma -> current_transaction ;
316297
317298 // switch driver state from RUN to IDLE
318299 async_memcpy_fsm_t expected_fsm = MCP_FSM_RUN ;
@@ -325,11 +306,6 @@ static void mcp_default_isr_handler(void *args)
325306 };
326307 need_yield = cb (& mcp_dma -> parent , & e , trans -> cb_args );
327308 }
328- // recycle descriptor memory
329- free (trans -> tx_desc_link );
330- free (trans -> rx_desc_link );
331- trans -> tx_desc_link = NULL ;
332- trans -> rx_desc_link = NULL ;
333309 trans -> cb = NULL ;
334310
335311 portENTER_CRITICAL_ISR (& mcp_dma -> spin_lock );
0 commit comments