1 /*
2  * Copyright 2021-2022, 2024 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_lcdic_dma.h"
9 
10 /*******************************************************************************
11  * Definitions
12  ******************************************************************************/
13 
14 /* Component ID definition, used by tools. */
15 #ifndef FSL_COMPONENT_ID
16 #define FSL_COMPONENT_ID "platform.drivers.lcdic_dma"
17 #endif
18 
19 /* Max transfer byte count in each DMA transfer. */
20 #define LCDIC_MAX_DMA_XFER_COUNT (DMA_MAX_TRANSFER_COUNT * 4U)
21 
22 /*******************************************************************************
23  * Prototypes
24  ******************************************************************************/
25 /*!
26  * @brief LCDIC TX DMA callback.
27  *
28  * @param dmaHandle DMA handle.
29  * @param param Callback parameter.
30  * @param dmaXferDone DMA transfer done or not.
31  * @param intmode Interrupt mode, kDMA_IntA, kDMA_IntB, or kDMA_IntError.
32  */
33 static void LCDIC_TransferSendDMACallback(dma_handle_t *dmaHandle, void *param, bool dmaXferDone, uint32_t intmode);
34 
35 /*!
36  * @brief LCDIC RX DMA callback.
37  *
38  * @param dmaHandle DMA handle.
39  * @param param Callback parameter.
40  * @param dmaXferDone DMA transfer done or not.
41  * @param intmode Interrupt mode, kDMA_IntA, kDMA_IntB, or kDMA_IntError.
42  */
43 static void LCDIC_TransferReceiveDMACallback(dma_handle_t *dmaHandle, void *param, bool dmaXferDone, uint32_t intmode);
44 
45 /*!
46  * @brief Handle the LCDIC DMA transfer done.
47  *
48  * Disables the related interrupts, disable DMA, calling user callback.
49  *
50  * @param base LCDIC peripheral base address.
51  * @param handle LCDIC DMA driver handle.
52  * @param status The status passing to user callback.
53  */
54 static void LCDIC_TransferDoneDMA(lcdic_dma_handle_t *handle, status_t status);
55 
56 /*!
57  * @brief Send data array using DMA way.
58  *
59  * @param base LCDIC peripheral base address.
60  * @param handle LCDIC DMA driver handle.
61  * @param xfer Pointer to the transfer structure.
62  * @retval kStatus_Success Successfully start a transfer.
63  * @retval kStatus_InvalidArgument Input argument is invalid.
64  */
65 static status_t LCDIC_TransferSendDataArrayDMA(LCDIC_Type *base,
66                                                lcdic_dma_handle_t *handle,
67                                                const lcdic_tx_xfer_t *xfer);
68 
69 /*!
70  * @brief Read data array using DMA way.
71  *
72  * @param base LCDIC peripheral base address.
73  * @param handle LCDIC DMA driver handle.
74  * @param xfer Pointer to the transfer structure.
75  * @retval kStatus_Success Successfully start a transfer.
76  * @retval kStatus_InvalidArgument Input argument is invalid.
77  */
78 static status_t LCDIC_TransferReceiveDataArrayDMA(LCDIC_Type *base,
79                                                   lcdic_dma_handle_t *handle,
80                                                   const lcdic_rx_xfer_t *xfer);
81 
82 /*!
83  * @brief Submit RX transfer request to DMA.
84  *
85  * Submit the data transfer request saved in LCDIC DMA driver handle to DMA.
86  *
87  * @param handle LCDIC DMA driver handle.
88  */
89 static void LCDIC_TransferSubmitRxDataDMA(lcdic_dma_handle_t *handle);
90 
91 /*!
92  * @brief Submit TX transfer request to DMA.
93  *
94  * Submit the data transfer request saved in LCDIC DMA driver handle to DMA.
95  *
96  * @param handle LCDIC DMA driver handle.
97  */
98 static void LCDIC_TransferSubmitTxDataDMA(lcdic_dma_handle_t *handle);
99 
100 /*******************************************************************************
101  * Variables
102  ******************************************************************************/
103 
104 /*******************************************************************************
105  * Code
106  ******************************************************************************/
107 
108 /*
109  * brief LCDIC TX DMA callback.
110  *
111  * param handle DMA handle.
112  * param param Callback parameter.
113  * param dmaXferDone DMA transfer done or not.
114  * param intmode Interrupt mode, kDMA_IntA, kDMA_IntB, or kDMA_IntError.
115  */
LCDIC_TransferSendDMACallback(dma_handle_t * dmaHandle,void * param,bool dmaXferDone,uint32_t intmode)116 static void LCDIC_TransferSendDMACallback(dma_handle_t *dmaHandle, void *param, bool dmaXferDone, uint32_t intmode)
117 {
118     assert(dmaHandle != NULL);
119     assert(param != NULL);
120 
121     lcdic_dma_handle_t *lcdicDmaHandle = (lcdic_dma_handle_t *)param;
122 
123     if (dmaXferDone)
124     {
125         LCDIC_TransferSubmitTxDataDMA(lcdicDmaHandle);
126     }
127 }
128 
129 /*
130  * brief LCDIC RX DMA callback.
131  *
132  * param handle DMA handle.
133  * param param Callback parameter.
134  * param dmaXferDone DMA transfer done or not.
135  * param intmode Interrupt mode, kDMA_IntA, kDMA_IntB, or kDMA_IntError.
136  */
LCDIC_TransferReceiveDMACallback(dma_handle_t * dmaHandle,void * param,bool dmaXferDone,uint32_t intmode)137 static void LCDIC_TransferReceiveDMACallback(dma_handle_t *dmaHandle, void *param, bool dmaXferDone, uint32_t intmode)
138 {
139     assert(dmaHandle != NULL);
140     assert(param != NULL);
141 
142     lcdic_dma_handle_t *lcdicDmaHandle = (lcdic_dma_handle_t *)param;
143 
144     if (dmaXferDone)
145     {
146         LCDIC_TransferSubmitRxDataDMA(lcdicDmaHandle);
147     }
148 }
149 
150 /*
151  * brief Submit RX transfer request to DMA.
152  *
153  * Submit the data transfer request saved in LCDIC DMA driver handle to DMA.
154  *
155  * param handle LCDIC DMA driver handle.
156  */
LCDIC_TransferSubmitRxDataDMA(lcdic_dma_handle_t * handle)157 static void LCDIC_TransferSubmitRxDataDMA(lcdic_dma_handle_t *handle)
158 {
159     dma_handle_t *dmaHandle = handle->rxDmaHandle;
160     dma_descriptor_t *dmaDesc, *nextDmaDesc;
161     bool startDMA = true;
162     bool reloadDmaXferCfg;
163     uint8_t xferSizeWordUnaligned = handle->xferSizeWordUnaligned;
164     uint32_t xferSizeWordAligned  = handle->xferSizeWordAligned;
165 
166     LCDIC_EnableDMA(handle->lcdic, false);
167     /*
168      * If left word-size aligned data is more than LCDIC_MAX_DMA_XFER_COUNT,
169      * start DMA to handle the first LCDIC_MAX_DMA_XFER_COUNT part.
170      */
171     if (xferSizeWordAligned > LCDIC_MAX_DMA_XFER_COUNT)
172     {
173         dmaDesc = &handle->dmaDesc[0];
174 
175         dmaDesc->xfercfg    = DMA_SetChannelXferConfig(false,                                  /* Reload */
176                                                     true,                                   /* Clear Trigger. */
177                                                     true,                                   /* intA */
178                                                     false,                                  /* intB */
179                                                     4U,                                     /* Width */
180                                                     (uint8_t)kDMA_AddressInterleave0xWidth, /* Source increase. */
181                                                     (uint8_t)kDMA_AddressInterleave1xWidth, /* Destination increase. */
182                                                     LCDIC_MAX_DMA_XFER_COUNT);
183         dmaDesc->srcEndAddr = (uint32_t *)(uintptr_t) & (handle->lcdic->RFIFO_RDATA);
184         dmaDesc->dstEndAddr = DMA_DESCRIPTOR_END_ADDRESS(handle->rxData, 1U, LCDIC_MAX_DMA_XFER_COUNT, 4U);
185 
186         dmaDesc->linkToNextDesc = NULL;
187 
188         handle->xferSizeWordAligned = xferSizeWordAligned - LCDIC_MAX_DMA_XFER_COUNT;
189         handle->txData += LCDIC_MAX_DMA_XFER_COUNT;
190     }
191     /*
192      * If left word-size aligned data is equal or less than LCDIC_MAX_DMA_XFER_COUNT,
193      * besides the word-size aligned part, a descriptor is setup to handle the
194      * word-size unaligned part.
195      */
196     else if (xferSizeWordAligned > 0U)
197     {
198         if (xferSizeWordUnaligned > 0U)
199         {
200             /* If there is unaligned part, using transfer link. */
201             reloadDmaXferCfg = true;
202             nextDmaDesc      = &handle->dmaDesc[1];
203         }
204         else
205         {
206             reloadDmaXferCfg = false;
207             nextDmaDesc      = NULL;
208         }
209 
210         dmaDesc = &handle->dmaDesc[0];
211 
212         dmaDesc->xfercfg = DMA_SetChannelXferConfig(reloadDmaXferCfg,                       /* Reload */
213                                                     true,                                   /* Clear Trigger. */
214                                                     true,                                   /* intA */
215                                                     false,                                  /* intB */
216                                                     4U,                                     /* Width */
217                                                     (uint8_t)kDMA_AddressInterleave0xWidth, /* Source increase. */
218                                                     (uint8_t)kDMA_AddressInterleave1xWidth, /* Destination increase. */
219                                                     xferSizeWordAligned);
220 
221         dmaDesc->srcEndAddr = (uint32_t *)(uintptr_t) & (handle->lcdic->RFIFO_RDATA);
222         dmaDesc->dstEndAddr = DMA_DESCRIPTOR_END_ADDRESS(handle->rxData, 1U, xferSizeWordAligned, 4U);
223 
224         dmaDesc->linkToNextDesc = nextDmaDesc;
225 
226         /* All data will be sent out after this DMA transfer. */
227         handle->xferSizeWordAligned   = 0UL;
228         handle->xferSizeWordUnaligned = 0U;
229     }
230     else
231     {
232         if (xferSizeWordUnaligned > 0U)
233         {
234             /* Handle the unaligned part. */
235             dmaDesc = &handle->dmaDesc[1];
236 
237             /* All data will be sent out after this DMA transfer. */
238             handle->xferSizeWordUnaligned = 0U;
239         }
240         else
241         {
242             /*
243              * All data received, fill the unaligned part to user array if necessary,
244              * then call the callback to notify user.
245              */
246             startDMA = false;
247 
248             if (handle->rxSizeWordUnaligned > 0U)
249             {
250                 LCDIC_ExtractByteFromWord(handle->tmpData, handle->rxData, handle->rxSizeWordUnaligned);
251                 handle->rxSizeWordUnaligned = 0U;
252             }
253 
254             LCDIC_TransferDoneDMA(handle, kStatus_Success);
255         }
256     }
257 
258     if (startDMA)
259     {
260         DMA_SubmitChannelDescriptor(dmaHandle, dmaDesc);
261         LCDIC_EnableDMA(handle->lcdic, true);
262         DMA_StartTransfer(dmaHandle);
263     }
264 }
265 
266 /*
267  * brief Submit TX transfer request to DMA.
268  *
269  * Submit the data transfer request saved in LCDIC DMA driver handle to DMA.
270  *
271  * param handle LCDIC DMA driver handle.
272  */
LCDIC_TransferSubmitTxDataDMA(lcdic_dma_handle_t * handle)273 static void LCDIC_TransferSubmitTxDataDMA(lcdic_dma_handle_t *handle)
274 {
275     dma_handle_t *dmaHandle = handle->txDmaHandle;
276     dma_descriptor_t *dmaDesc, *nextDmaDesc;
277     bool startDMA = true;
278     bool reloadDmaXferCfg;
279     uint8_t xferSizeWordUnaligned = handle->xferSizeWordUnaligned;
280     uint32_t xferSizeWordAligned  = handle->xferSizeWordAligned;
281 
282     LCDIC_EnableDMA(handle->lcdic, false);
283     /*
284      * If left word-size aligned data is more than LCDIC_MAX_DMA_XFER_COUNT,
285      * start DMA to send the first LCDIC_MAX_DMA_XFER_COUNT part.
286      */
287     if (xferSizeWordAligned > LCDIC_MAX_DMA_XFER_COUNT)
288     {
289         dmaDesc = &handle->dmaDesc[0];
290 
291         dmaDesc->xfercfg        = DMA_SetChannelXferConfig(false,                                  /* Reload */
292                                                     true,                                   /* Clear Trigger. */
293                                                     true,                                   /* intA */
294                                                     false,                                  /* intB */
295                                                     4U,                                     /* Width */
296                                                     (uint8_t)kDMA_AddressInterleave1xWidth, /* Source increase. */
297                                                     (uint8_t)kDMA_AddressInterleave0xWidth, /* Destination increase. */
298                                                     LCDIC_MAX_DMA_XFER_COUNT);
299         dmaDesc->srcEndAddr     = DMA_DESCRIPTOR_END_ADDRESS(handle->txData, 1U, LCDIC_MAX_DMA_XFER_COUNT, 4U);
300         dmaDesc->dstEndAddr     = (uint32_t *)(uintptr_t) & (handle->lcdic->TFIFO_WDATA);
301         dmaDesc->linkToNextDesc = NULL;
302 
303         handle->xferSizeWordAligned = xferSizeWordAligned - LCDIC_MAX_DMA_XFER_COUNT;
304         handle->txData += LCDIC_MAX_DMA_XFER_COUNT;
305     }
306     /*
307      * If left word-size aligned data is equal or less than LCDIC_MAX_DMA_XFER_COUNT,
308      * besides the word-size aligned part, a descriptor is setup to send the
309      * word-size unaligned part.
310      */
311     else if (xferSizeWordAligned > 0U)
312     {
313         /* If there is unaligned part, using transfer link. */
314         if (xferSizeWordUnaligned > 0U)
315         {
316             reloadDmaXferCfg = true;
317             nextDmaDesc      = &handle->dmaDesc[1];
318         }
319         else
320         {
321             reloadDmaXferCfg = false;
322             nextDmaDesc      = NULL;
323         }
324 
325         dmaDesc = &handle->dmaDesc[0];
326 
327         dmaDesc->xfercfg = DMA_SetChannelXferConfig(reloadDmaXferCfg,                       /* Reload */
328                                                     true,                                   /* Clear Trigger. */
329                                                     false,                                  /* intA */
330                                                     false,                                  /* intB */
331                                                     4U,                                     /* Width */
332                                                     (uint8_t)kDMA_AddressInterleave1xWidth, /* Source increase. */
333                                                     (uint8_t)kDMA_AddressInterleave0xWidth, /* Destination increase. */
334                                                     xferSizeWordAligned);
335 
336         dmaDesc->srcEndAddr = DMA_DESCRIPTOR_END_ADDRESS(handle->txData, 1U, xferSizeWordAligned, 4U);
337         dmaDesc->dstEndAddr = (uint32_t *)(uintptr_t) & (handle->lcdic->TFIFO_WDATA);
338 
339         dmaDesc->linkToNextDesc = nextDmaDesc;
340 
341         /* All data will be sent out after this DMA transfer. */
342         handle->xferSizeWordAligned   = 0UL;
343         handle->xferSizeWordUnaligned = 0U;
344     }
345     else
346     {
347         if (xferSizeWordUnaligned > 0U)
348         {
349             dmaDesc = &handle->dmaDesc[1];
350 
351             /* All data will be sent out after this DMA transfer. */
352             handle->xferSizeWordUnaligned = 0U;
353         }
354         else
355         {
356             startDMA = false;
357         }
358     }
359 
360     if (startDMA)
361     {
362         DMA_SubmitChannelDescriptor(dmaHandle, dmaDesc);
363         LCDIC_EnableDMA(handle->lcdic, true);
364         DMA_StartTransfer(dmaHandle);
365     }
366 }
367 
368 /*
369  * brief Send data array using DMA way.
370  *
371  * param base LCDIC peripheral base address.
372  * param handle LCDIC DMA driver handle.
373  * param xfer Pointer to the transfer structure.
374  * retval kStatus_Success Successfully start a transfer.
375  * retval kStatus_InvalidArgument Input argument is invalid.
376  */
LCDIC_TransferSendDataArrayDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,const lcdic_tx_xfer_t * xfer)377 static status_t LCDIC_TransferSendDataArrayDMA(LCDIC_Type *base,
378                                                lcdic_dma_handle_t *handle,
379                                                const lcdic_tx_xfer_t *xfer)
380 {
381     status_t status;
382     uint32_t xferSizeWordAligned;
383     uint8_t xferSizeWordUnaligned;
384     uint32_t wordUnalignedData;
385     dma_descriptor_t *dmaDesc;
386 
387     /* Data must be 4-byte aligned. */
388     if (0U != (0x03U & (uint32_t)xfer->txData))
389     {
390         return kStatus_InvalidArgument;
391     }
392 
393     status = LCDIC_PrepareSendDataArray(base, xfer, &xferSizeWordAligned, &xferSizeWordUnaligned, &wordUnalignedData);
394 
395     if (kStatus_Success == status)
396     {
397         handle->xferSizeWordUnaligned = xferSizeWordUnaligned;
398         handle->xferSizeWordAligned   = xferSizeWordAligned;
399         handle->tmpData               = wordUnalignedData;
400         handle->txData                = xfer->txData;
401 
402         /*
403          * There are two DMA descriptors in LCDIC DMA driver handle, the first is
404          * used for 4-byte size aligned part, the second is for 4-byte size unaligned
405          * part. For the second part, the descriptor is prepared during transfer started,
406          * then used directly in DMA callback function.
407          */
408         /* If the data size is not word aligned, link descriptor is used. */
409         if (xferSizeWordUnaligned > 0U)
410         {
411             dmaDesc = &handle->dmaDesc[1];
412 
413             dmaDesc->xfercfg =
414                 DMA_SetChannelXferConfig(false,                                  /* Reload */
415                                          true,                                   /* Clear Trigger. */
416                                          true,                                   /* intA */
417                                          false,                                  /* intB */
418                                          4U,                                     /* Width */
419                                          (uint8_t)kDMA_AddressInterleave0xWidth, /* Source increase. */
420                                          (uint8_t)kDMA_AddressInterleave0xWidth, /* Destination increase. */
421                                          4U);
422             dmaDesc->srcEndAddr     = &handle->tmpData;
423             dmaDesc->dstEndAddr     = (uint32_t *)(uintptr_t) & (base->TFIFO_WDATA);
424             dmaDesc->linkToNextDesc = NULL;
425         }
426 
427         LCDIC_TransferSubmitTxDataDMA(handle);
428 
429         LCDIC_EnableInterrupts(base, (uint32_t)kLCDIC_CmdDoneInterrupt | (uint32_t)kLCDIC_TeTimeoutInterrupt |
430                                          (uint32_t)kLCDIC_CmdTimeoutInterrupt);
431     }
432 
433     return status;
434 }
435 
436 /*
437  * brief Read data array using DMA way.
438  *
439  * param base LCDIC peripheral base address.
440  * param handle LCDIC DMA driver handle.
441  * param xfer Pointer to the transfer structure.
442  * retval kStatus_Success Successfully start a transfer.
443  * retval kStatus_InvalidArgument Input argument is invalid.
444  */
LCDIC_TransferReceiveDataArrayDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,const lcdic_rx_xfer_t * xfer)445 static status_t LCDIC_TransferReceiveDataArrayDMA(LCDIC_Type *base,
446                                                   lcdic_dma_handle_t *handle,
447                                                   const lcdic_rx_xfer_t *xfer)
448 {
449     status_t status;
450     uint32_t xferSizeWordAligned;
451     uint8_t xferSizeWordUnaligned;
452     dma_descriptor_t *dmaDesc;
453 
454     /* Data must be 4-byte aligned. */
455     if (0U != (0x03U & (uint32_t)xfer->rxData))
456     {
457         return kStatus_InvalidArgument;
458     }
459 
460     status = LCDIC_PrepareReadDataArray(base, xfer, &xferSizeWordAligned, &xferSizeWordUnaligned);
461 
462     if (kStatus_Success == status)
463     {
464         handle->xferSizeWordUnaligned = xferSizeWordUnaligned;
465         handle->xferSizeWordAligned   = xferSizeWordAligned;
466         handle->rxData                = xfer->rxData;
467 
468         /*
469          * There are two DMA descriptors in LCDIC DMA driver handle, the first is
470          * used for 4-byte size aligned part, the second is for 4-byte size unaligned
471          * part. For the second part, the descriptor is prepared during transfer started,
472          * then used directly in DMA callback function.
473          */
474 
475         /* If the data size is not word aligned, link descriptor is used. */
476         if (xferSizeWordUnaligned > 0U)
477         {
478             dmaDesc = &handle->dmaDesc[1];
479 
480             dmaDesc->xfercfg =
481                 DMA_SetChannelXferConfig(false,                                  /* Reload */
482                                          true,                                   /* Clear Trigger. */
483                                          true,                                   /* intA */
484                                          false,                                  /* intB */
485                                          4U,                                     /* Width */
486                                          (uint8_t)kDMA_AddressInterleave0xWidth, /* Source increase. */
487                                          (uint8_t)kDMA_AddressInterleave0xWidth, /* Destination increase. */
488                                          4U);
489             dmaDesc->srcEndAddr     = (uint32_t *)(uintptr_t) & (base->RFIFO_RDATA);
490             dmaDesc->dstEndAddr     = &handle->tmpData;
491             dmaDesc->linkToNextDesc = NULL;
492         }
493 
494         LCDIC_TransferSubmitRxDataDMA(handle);
495 
496         LCDIC_EnableInterrupts(base, (uint32_t)kLCDIC_TeTimeoutInterrupt | (uint32_t)kLCDIC_CmdTimeoutInterrupt);
497     }
498 
499     return status;
500 }
501 
502 /*
503  * brief Handle the LCDIC DMA transfer done.
504  *
505  * Disables the related interrupts, disable DMA, calling user callback.
506  *
507  * param base LCDIC peripheral base address.
508  * param handle LCDIC DMA driver handle.
509  * param status The status passing to user callback.
510  */
LCDIC_TransferDoneDMA(lcdic_dma_handle_t * handle,status_t status)511 static void LCDIC_TransferDoneDMA(lcdic_dma_handle_t *handle, status_t status)
512 {
513     LCDIC_Type *lcdic = handle->lcdic;
514 
515     LCDIC_DisableInterrupts(lcdic, (uint32_t)kLCDIC_CmdDoneInterrupt | (uint32_t)kLCDIC_TeTimeoutInterrupt |
516                                        (uint32_t)kLCDIC_CmdTimeoutInterrupt);
517 
518     LCDIC_EnableDMA(lcdic, false);
519 
520     handle->xferInProgress = false;
521 
522     if (NULL != handle->callback)
523     {
524         handle->callback(lcdic, handle, status, handle->userData);
525     }
526 }
527 
528 /*
529  * brief Initialize the LCDIC DMA handle.
530  *
531  * This function initializes the LCDIC DMA handle which can be used for other LCDIC transactional APIs.
532  * Usually, for a specified LCDIC instance, user need only call this API once to get the initialized handle.
533  *
534  * param base LCDIC peripheral base address.
535  * param handle LCDIC handle pointer.
536  * param callback User callback function called at the end of a transfer.
537  * param userData User data for callback.
538  * param txDmaHandle DMA handle pointer for LCDIC Tx, the handle shall be static allocated by users.
539  * param rxDmaHandle DMA handle pointer for LCDIC Rx, the handle shall be static allocated by users.
540  * param dmaDesc User allocated dma descriptor, it should be in non-cacheable region and 16-byte aligned.
541  */
LCDIC_TransferCreateHandleDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,lcdic_dma_callback_t callback,void * userData,dma_handle_t * txDmaHandle,dma_handle_t * rxDmaHandle,dma_descriptor_t dmaDesc[2])542 status_t LCDIC_TransferCreateHandleDMA(LCDIC_Type *base,
543                                        lcdic_dma_handle_t *handle,
544                                        lcdic_dma_callback_t callback,
545                                        void *userData,
546                                        dma_handle_t *txDmaHandle,
547                                        dma_handle_t *rxDmaHandle,
548                                        dma_descriptor_t dmaDesc[2])
549 {
550     assert(NULL != handle);
551     assert(!((NULL == txDmaHandle) && (NULL == rxDmaHandle)));
552 
553     uint32_t instance;
554 
555     dma_channel_trigger_t trigger = {
556         .type = kDMA_RisingEdgeTrigger, .burst = kDMA_EdgeBurstTransfer1, .wrap = kDMA_NoWrap};
557 
558     if (((uint32_t)dmaDesc & ((uint32_t)FSL_FEATURE_DMA_LINK_DESCRIPTOR_ALIGN_SIZE - 1UL)) != 0UL)
559     {
560         return kStatus_InvalidArgument;
561     }
562 
563     (void)memset(handle, 0, sizeof(lcdic_dma_handle_t));
564 
565     instance = LCDIC_GetInstance(base);
566 
567     handle->lcdic       = base;
568     handle->txDmaHandle = txDmaHandle;
569     handle->rxDmaHandle = rxDmaHandle;
570     handle->callback    = callback;
571     handle->userData    = userData;
572     handle->dmaDesc     = dmaDesc;
573 
574     /* Configure RX callback. */
575     if (rxDmaHandle != NULL)
576     {
577         DMA_SetChannelConfig(rxDmaHandle->base, rxDmaHandle->channel, &trigger, false);
578         DMA_SetCallback(rxDmaHandle, LCDIC_TransferReceiveDMACallback, handle);
579 
580         LCDIC_SetRxThreshold(base, kLCDIC_RxThreshold0Word);
581     }
582 
583     /* Configure TX callback. */
584     if (txDmaHandle != NULL)
585     {
586         DMA_SetChannelConfig(txDmaHandle->base, txDmaHandle->channel, &trigger, false);
587         DMA_SetCallback(txDmaHandle, LCDIC_TransferSendDMACallback, handle);
588 
589         LCDIC_SetTxThreshold(base, kLCDIC_TxThreshold0Word);
590     }
591 
592     LCDIC_TransferInstallIRQHandler(instance, handle, LCDIC_TransferHandleIRQDMA);
593 
594     (void)EnableIRQ(LCDIC_GetIRQn(instance));
595 
596     return kStatus_Success;
597 }
598 
599 /*
600  * brief Perform a non-blocking LCDIC transfer using DMA.
601  *
602  * This function returned immediately after transfer initiates, monitor the transfer
603  * done by callback.
604  *
605  * param base LCDIC peripheral base address.
606  * param handle LCDIC DMA handle pointer.
607  * param xfer Pointer to dma transfer structure.
608  * retval kStatus_Success Successfully start a transfer.
609  * retval kStatus_InvalidArgument Input argument is invalid.
610  * retval kStatus_Busy LCDIC is not idle, is running another transfer.
611  */
LCDIC_TransferDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,const lcdic_xfer_t * xfer)612 status_t LCDIC_TransferDMA(LCDIC_Type *base, lcdic_dma_handle_t *handle, const lcdic_xfer_t *xfer)
613 {
614     status_t status = kStatus_InvalidArgument;
615 
616     if (((kLCDIC_XferSendDataArray == xfer->mode) &&
617          (NULL == handle->txDmaHandle)) || /* send mode but no TX DMA handle. */
618         ((kLCDIC_XferReceiveDataArray == xfer->mode) &&
619          (NULL == handle->rxDmaHandle))) /* read mode but no RX DMA handle. */
620     {
621         return kStatus_InvalidArgument;
622     }
623 
624     if (handle->xferInProgress)
625     {
626         return kStatus_Busy;
627     }
628 
629     handle->xferInProgress = true;
630     handle->xferMode       = xfer->mode;
631 
632     switch (xfer->mode)
633     {
634         case kLCDIC_XferCmdOnly:
635             status = LCDIC_PrepareSendCommand(base, xfer->cmdToSendOnly);
636             LCDIC_EnableInterrupts(base, (uint32_t)kLCDIC_CmdDoneInterrupt);
637             break;
638 
639         case kLCDIC_XferSendRepeatData:
640             status = LCDIC_PrepareSendRepeatData(base, &xfer->repeatTxXfer);
641             LCDIC_EnableInterrupts(base, (uint32_t)kLCDIC_CmdDoneInterrupt | (uint32_t)kLCDIC_TeTimeoutInterrupt |
642                                              (uint32_t)kLCDIC_CmdTimeoutInterrupt);
643             break;
644 
645         case kLCDIC_XferSendDataArray:
646             status = LCDIC_TransferSendDataArrayDMA(base, handle, &xfer->txXfer);
647             break;
648 
649         case kLCDIC_XferReceiveDataArray:
650             status = LCDIC_TransferReceiveDataArrayDMA(base, handle, &xfer->rxXfer);
651             break;
652 
653         default:
654             /* Should not reach here. */
655             status = kStatus_InvalidArgument;
656             break;
657     }
658 
659     if (status != kStatus_Success)
660     {
661         handle->xferInProgress = false;
662     }
663 
664     return status;
665 }
666 
667 /*
668  * brief Send data array using DMA.
669  *
670  * param base LCDIC peripheral base address.
671  * param handle Pointer to the lcdic_handle_t structure to store the transfer state.
672  * param xfer LCDIC transfer structure.
673  * retval kStatus_Success Successfully start a transfer.
674  * retval kStatus_InvalidArgument Input argument is invalid.
675  * retval kStatus_Busy LCDIC driver is busy with another transfer.
676  */
LCDIC_SendDataArrayDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,const lcdic_tx_xfer_t * xfer)677 status_t LCDIC_SendDataArrayDMA(LCDIC_Type *base, lcdic_dma_handle_t *handle, const lcdic_tx_xfer_t *xfer)
678 {
679     status_t status = kStatus_InvalidArgument;
680 
681     if (NULL == handle->txDmaHandle)
682     {
683         return kStatus_InvalidArgument;
684     }
685 
686     if (handle->xferInProgress)
687     {
688         return kStatus_Busy;
689     }
690 
691     handle->xferInProgress = true;
692     handle->xferMode       = kLCDIC_XferSendDataArray;
693 
694     status = LCDIC_TransferSendDataArrayDMA(base, handle, xfer);
695 
696     if (status != kStatus_Success)
697     {
698         handle->xferInProgress = false;
699     }
700 
701     return status;
702 }
703 
704 /*
705  * brief Read data array using DMA.
706  *
707  * param base LCDIC peripheral base address.
708  * param handle Pointer to the lcdic_handle_t structure to store the transfer state.
709  * param xfer LCDIC transfer structure.
710  * retval kStatus_Success Successfully start a transfer.
711  * retval kStatus_InvalidArgument Input argument is invalid.
712  * retval kStatus_Busy LCDIC driver is busy with another transfer.
713  */
LCDIC_ReadDataArrayDMA(LCDIC_Type * base,lcdic_dma_handle_t * handle,const lcdic_rx_xfer_t * xfer)714 status_t LCDIC_ReadDataArrayDMA(LCDIC_Type *base, lcdic_dma_handle_t *handle, const lcdic_rx_xfer_t *xfer)
715 {
716     status_t status = kStatus_InvalidArgument;
717 
718     if (NULL == handle->rxDmaHandle) /* read mode but no RX DMA handle. */
719     {
720         return kStatus_InvalidArgument;
721     }
722 
723     if (handle->xferInProgress)
724     {
725         return kStatus_Busy;
726     }
727 
728     handle->xferInProgress = true;
729     handle->xferMode       = kLCDIC_XferReceiveDataArray;
730 
731     status = LCDIC_TransferReceiveDataArrayDMA(base, handle, xfer);
732 
733     if (status != kStatus_Success)
734     {
735         handle->xferInProgress = false;
736     }
737 
738     return status;
739 }
740 
741 /*
742  * brief LCDIC IRQ handler function work with DMA transactional APIs.
743  *
744  * IRQ handler to work with @ref LCDIC_TransferDMA.
745  *
746  * param base LCDIC peripheral base address.
747  * param handle Pointer to the lcdic_dma_handle_t structure to store the transfer state.
748  */
LCDIC_TransferHandleIRQDMA(LCDIC_Type * base,void * handle)749 void LCDIC_TransferHandleIRQDMA(LCDIC_Type *base, void *handle)
750 {
751     assert(NULL != handle);
752 
753     uint32_t intStat;
754     status_t status;
755     bool xferDone = false;
756 
757     lcdic_dma_handle_t *lcdicDmaHandle = (lcdic_dma_handle_t *)handle;
758 
759     if (lcdicDmaHandle->xferInProgress)
760     {
761         intStat = LCDIC_GetInterruptStatus(base);
762         LCDIC_ClearInterruptStatus(base, intStat);
763 
764         /* Timeout. */
765         if (0U != (intStat & ((uint32_t)kLCDIC_CmdTimeoutInterrupt | (uint32_t)kLCDIC_TeTimeoutInterrupt)))
766         {
767             if (kLCDIC_XferReceiveDataArray == lcdicDmaHandle->xferMode)
768             {
769                 DMA_AbortTransfer(lcdicDmaHandle->rxDmaHandle);
770             }
771             else if (kLCDIC_XferSendDataArray == lcdicDmaHandle->xferMode)
772             {
773                 DMA_AbortTransfer(lcdicDmaHandle->txDmaHandle);
774             }
775             else
776             {
777                 /* Empty */
778             }
779 
780             xferDone = true;
781             status   = kStatus_Timeout;
782         }
783         else if (0U != (intStat & ((uint32_t)kLCDIC_CmdDoneInterrupt)))
784         {
785             if (lcdicDmaHandle->xferMode != kLCDIC_XferReceiveDataArray)
786             {
787                 xferDone = true;
788                 status   = kStatus_Success;
789             }
790         }
791         else
792         {
793             /* Empty. */
794         }
795 
796         if (xferDone)
797         {
798             LCDIC_TransferDoneDMA(lcdicDmaHandle, status);
799         }
800     }
801 }
802