1 /*
2  * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 // DO NOT USE THESE APIS IN ANY APPLICATIONS
8 // GDMA driver is not public for end users, but for ESP-IDF developpers.
9 
10 #pragma once
11 
12 #include <stdbool.h>
13 #include "soc/gdma_channel.h"
14 #include "esp_err.h"
15 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 /**
21  * @brief Type of GDMA channel handle
22  *
23  */
24 typedef struct gdma_channel_t *gdma_channel_handle_t;
25 
26 /**
27  * @brief Enumeration of peripherals which have the DMA capability
28  * @note Some peripheral might not be available on certain chip, please refer to `soc_caps.h` for detail.
29  *
30  */
31 typedef enum {
32     GDMA_TRIG_PERIPH_M2M,  /*!< GDMA trigger peripheral: M2M */
33     GDMA_TRIG_PERIPH_UART, /*!< GDMA trigger peripheral: UART */
34     GDMA_TRIG_PERIPH_SPI,  /*!< GDMA trigger peripheral: SPI */
35     GDMA_TRIG_PERIPH_I2S,  /*!< GDMA trigger peripheral: I2S */
36     GDMA_TRIG_PERIPH_AES,  /*!< GDMA trigger peripheral: AES */
37     GDMA_TRIG_PERIPH_SHA,  /*!< GDMA trigger peripheral: SHA */
38     GDMA_TRIG_PERIPH_ADC,  /*!< GDMA trigger peripheral: ADC */
39     GDMA_TRIG_PERIPH_DAC,  /*!< GDMA trigger peripheral: DAC */
40     GDMA_TRIG_PERIPH_LCD,  /*!< GDMA trigger peripheral: LCD */
41     GDMA_TRIG_PERIPH_CAM,  /*!< GDMA trigger peripheral: CAM */
42     GDMA_TRIG_PERIPH_RMT,  /*!< GDMA trigger peripheral: RMT */
43 } gdma_trigger_peripheral_t;
44 
45 /**
46  * @brief Enumeration of GDMA channel direction
47  *
48  */
49 typedef enum {
50     GDMA_CHANNEL_DIRECTION_TX, /*!< GDMA channel direction: TX */
51     GDMA_CHANNEL_DIRECTION_RX, /*!< GDMA channel direction: RX */
52 } gdma_channel_direction_t;
53 
54 /**
55  * @brief Collection of configuration items that used for allocating GDMA channel
56  *
57  */
58 typedef struct {
59     gdma_channel_handle_t sibling_chan; /*!< DMA sibling channel handle (NULL means having sibling is not necessary) */
60     gdma_channel_direction_t direction; /*!< DMA channel direction */
61     struct {
62         int reserve_sibling: 1; /*!< If set, DMA channel allocator would prefer to allocate new channel in a new pair, and reserve sibling channel for future use */
63     } flags;
64 } gdma_channel_alloc_config_t;
65 
66 /**
67  * @brief GDMA transfer ability
68  *
69  * @note The alignment set in this structure is **not** a guarantee that gdma driver will take care of the nonalignment cases.
70  *       Actually the GDMA driver has no knowledge about the DMA buffer (address and size) used by upper layer.
71  *       So it's the responsibility of the **upper layer** to take care of the buffer address and size.
72  *
73  */
74 typedef struct {
75     size_t sram_trans_align;  /*!< DMA transfer alignment for memory in SRAM, in bytes. The driver enables/disables burst mode based on this value. 0 means no alignment is required */
76     size_t psram_trans_align; /*!< DMA transfer alignment for memory in PSRAM, in bytes. The driver sets proper burst block size based on the alignment value. 0 means no alignment is required */
77 } gdma_transfer_ability_t;
78 
79 /**
80  * @brief Type of GDMA event data
81  *
82  */
83 typedef struct {
84     union {
85         intptr_t rx_eof_desc_addr; /*!< EOF descriptor address of RX channel */
86         intptr_t tx_eof_desc_addr; /*!< EOF descriptor address of TX channel */
87     };
88 } gdma_event_data_t;
89 
90 /**
91  * @brief Type of GDMA event callback
92  * @param dma_chan GDMA channel handle, created from `gdma_new_channel`
93  * @param event_data GDMA event data
94  * @param user_data User registered data from `gdma_register_tx_event_callbacks` or `gdma_register_rx_event_callbacks`
95  *
96  * @return Whether a task switch is needed after the callback function returns,
97  *         this is usually due to the callback wakes up some high priority task.
98  *
99  */
100 typedef bool (*gdma_event_callback_t)(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
101 
102 /**
103  * @brief Group of supported GDMA TX callbacks
104  * @note The callbacks are all running under ISR environment
105  *
106  */
107 typedef struct {
108     gdma_event_callback_t on_trans_eof; /*!< Invoked when TX engine meets EOF descriptor */
109 } gdma_tx_event_callbacks_t;
110 
111 /**
112  * @brief Group of supported GDMA RX callbacks
113  * @note The callbacks are all running under ISR environment
114  *
115  */
116 typedef struct {
117     gdma_event_callback_t on_recv_eof; /*!< Invoked when RX engine meets EOF descriptor */
118 } gdma_rx_event_callbacks_t;
119 
120 /**
121  * @brief Type of GDMA engine trigger
122  * @note It's recommended to initialize this structure with `GDMA_MAKE_TRIGGER`.
123  *
124  */
125 typedef struct {
126     gdma_trigger_peripheral_t periph; /*!< Target peripheral which will trigger DMA operations */
127     int instance_id;                  /*!< Peripheral instance ID. Supported IDs are listed in `soc/gdma_channel.h`, e.g. SOC_GDMA_TRIG_PERIPH_UART0 */
128 } gdma_trigger_t;
129 
130 /**
131  * @brief Helper macro to initialize GDMA trigger
132  * @note value of `peri` must be selected from `gdma_trigger_peripheral_t` enum.
133  *       e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_UART,0)
134  *
135  */
136 #define GDMA_MAKE_TRIGGER(peri, id) \
137     (gdma_trigger_t) { .periph = peri, .instance_id = SOC_##peri##id }
138 
139 /**
140  * @brief A collection of strategy item that each GDMA channel could apply
141  *
142  */
143 typedef struct {
144     bool owner_check;      /*!< If set / clear, DMA channel enables / disables checking owner validity */
145     bool auto_update_desc; /*!< If set / clear, DMA channel enables / disables hardware to update descriptor automatically (TX channel only) */
146 } gdma_strategy_config_t;
147 
148 /**
149  * @brief Create GDMA channel
150  * @note This API won't install interrupt service for the allocated channel.
151  *       If interrupt service is needed, user has to register GDMA event callback by `gdma_register_tx_event_callbacks` or `gdma_register_rx_event_callbacks`.
152  *
153  * @param[in] config Pointer to a collection of configurations for allocating GDMA channel
154  * @param[out] ret_chan Returnned channel handle
155  * @return
156  *      - ESP_OK: Create DMA channel successfully
157  *      - ESP_ERR_INVALID_ARG: Create DMA channel failed because of invalid argument
158  *      - ESP_ERR_NO_MEM: Create DMA channel failed because out of memory
159  *      - ESP_FAIL: Create DMA channel failed because of other error
160  */
161 esp_err_t gdma_new_channel(const gdma_channel_alloc_config_t *config, gdma_channel_handle_t *ret_chan);
162 
163 /**
164  * @brief Connect GDMA channel to trigger peripheral
165  *
166  * @note Suggest to use helper macro `GDMA_MAKE_TRIGGER` to construct parameter `trig_periph`. e.g. GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_SHA,0)
167  * @note Connecting to a peripheral will also reset the DMA FIFO and FSM automatically
168  *
169  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
170  * @param[in] trig_periph GDMA trigger peripheral
171  * @return
172  *      - ESP_OK: Connect GDMA channel successfully
173  *      - ESP_ERR_INVALID_ARG: Connect GDMA channel failed because of invalid argument
174  *      - ESP_ERR_INVALID_STATE: Connect GDMA channel failed because DMA channel is working with another peripheral
175  *      - ESP_FAIL: Connect GDMA channel failed because of other error
176  */
177 esp_err_t gdma_connect(gdma_channel_handle_t dma_chan, gdma_trigger_t trig_periph);
178 
179 /**
180  * @brief Disconnect GMA channel from peripheral
181  *
182  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
183  * @return
184  *      - ESP_OK: Disconnect GDMA channel successfully
185  *      - ESP_ERR_INVALID_ARG: Disconnect GDMA channel failed because of invalid argument
186  *      - ESP_ERR_INVALID_STATE: Disconnect GDMA channel failed because DMA channel is not connected to any peripheral
187  *      - ESP_FAIL: Disconnect DMA channel failed because of other error
188  */
189 esp_err_t gdma_disconnect(gdma_channel_handle_t dma_chan);
190 
191 /**
192  * @brief Set DMA channel transfer ability
193  *
194  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
195  * @param[in] ability Transfer ability, e.g. alignment
196  * @return
197  *      - ESP_OK: Set DMA channel transfer ability successfully
198  *      - ESP_ERR_INVALID_ARG: Set DMA channel transfer ability failed because of invalid argument
199  *      - ESP_FAIL: Set DMA channel transfer ability failed because of other error
200  */
201 esp_err_t gdma_set_transfer_ability(gdma_channel_handle_t dma_chan, const gdma_transfer_ability_t *ability);
202 
203 /**
204  * @brief Apply channel strategy for GDMA channel
205  *
206  * @param dma_chan GDMA channel handle, allocated by `gdma_new_channel`
207  * @param config Configuration of GDMA channel strategy
208  *      - ESP_OK: Apply channel strategy successfully
209  *      - ESP_ERR_INVALID_ARG: Apply channel strategy failed because of invalid argument
210  *      - ESP_FAIL: Apply channel strategy failed because of other error
211  */
212 esp_err_t gdma_apply_strategy(gdma_channel_handle_t dma_chan, const gdma_strategy_config_t *config);
213 
214 /**
215  * @brief Delete GDMA channel
216  * @note If you call `gdma_new_channel` several times for a same peripheral, make sure you call this API the same times.
217  *
218  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
219  * @return
220  *      - ESP_OK: Delete GDMA channel successfully
221  *      - ESP_ERR_INVALID_ARG: Delete GDMA channel failed because of invalid argument
222  *      - ESP_FAIL: Delete GDMA channel failed because of other error
223  */
224 esp_err_t gdma_del_channel(gdma_channel_handle_t dma_chan);
225 
226 /**
227  * @brief Get the channel ID
228  *
229  * @note This API breaks the encapsulation of GDMA Channel Object.
230  *       With the returned channel ID, you can even bypass all other GDMA driver API and access Low Level API directly.
231  *
232  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
233  * @param[out] channel_id Returned channel ID
234  * @return
235  *      - ESP_OK: Get GDMA channel ID successfully
236  *      - ESP_ERR_INVALID_ARG: Get GDMA channel ID failed because of invalid argument
237  *      - ESP_FAIL: Get GDMA channel ID failed because of other error
238  */
239 esp_err_t gdma_get_channel_id(gdma_channel_handle_t dma_chan, int *channel_id);
240 
241 /**
242  * @brief Set GDMA event callbacks for TX channel
243  * @note This API will install GDMA interrupt service for the channel internally
244  *
245  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
246  * @param[in] cbs Group of callback functions
247  * @param[in] user_data User data, which will be passed to callback functions directly
248  * @return
249  *      - ESP_OK: Set event callbacks successfully
250  *      - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
251  *      - ESP_FAIL: Set event callbacks failed because of other error
252  */
253 esp_err_t gdma_register_tx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_tx_event_callbacks_t *cbs, void *user_data);
254 
255 /**
256  * @brief Set GDMA event callbacks for RX channel
257  * @note This API will install GDMA interrupt service for the channel internally
258  *
259  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
260  * @param[in] cbs Group of callback functions
261  * @param[in] user_data User data, which will be passed to callback functions directly
262  * @return
263  *      - ESP_OK: Set event callbacks successfully
264  *      - ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
265  *      - ESP_FAIL: Set event callbacks failed because of other error
266  */
267 esp_err_t gdma_register_rx_event_callbacks(gdma_channel_handle_t dma_chan, gdma_rx_event_callbacks_t *cbs, void *user_data);
268 
269 /**
270  * @brief Set DMA descriptor address and start engine
271  *
272  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
273  * @param[in] desc_base_addr Base address of descriptors (usually the descriptors are chained into a link or ring)
274  * @return
275  *      - ESP_OK: Start DMA engine successfully
276  *      - ESP_ERR_INVALID_ARG: Start DMA engine failed because of invalid argument
277  *      - ESP_FAIL: Start DMA engine failed because of other error
278  */
279 esp_err_t gdma_start(gdma_channel_handle_t dma_chan, intptr_t desc_base_addr);
280 
281 /**
282  * @brief Stop DMA engine
283  *
284  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
285  * @return
286  *      - ESP_OK: Stop DMA engine successfully
287  *      - ESP_ERR_INVALID_ARG: Stop DMA engine failed because of invalid argument
288  *      - ESP_FAIL: Stop DMA engine failed because of other error
289  */
290 esp_err_t gdma_stop(gdma_channel_handle_t dma_chan);
291 
292 /**
293  * @brief Make the appended descriptors be aware to the DMA engine
294  * @note This API could also resume a paused DMA engine, make sure new descriptors have been appended to the descriptor chain before calling it.
295  *
296  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
297  * @return
298  *      - ESP_OK: Send append command to DMA engine successfully
299  *      - ESP_ERR_INVALID_ARG: Send append command to DMA engine failed because of invalid argument
300  *      - ESP_FAIL: Send append command to DMA engine failed because of other error
301  */
302 esp_err_t gdma_append(gdma_channel_handle_t dma_chan);
303 
304 /**
305  * @brief Reset DMA channel FIFO and internal finite state machine
306  * @note Resetting a DMA channel won't break the connection with the target peripheral
307  *
308  * @param[in] dma_chan GDMA channel handle, allocated by `gdma_new_channel`
309  * @return
310  *      - ESP_OK: DMA channel reset successfully
311  *      - ESP_ERR_INVALID_ARG: DMA channel reset failed due to invalid arguments
312  *      - ESP_FAIL: DMA channel reset failed due to other errors
313  */
314 esp_err_t gdma_reset(gdma_channel_handle_t dma_chan);
315 
316 #ifdef __cplusplus
317 }
318 #endif
319