1 /*
2  * Copyright 2019-2021,2023 NXP
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "fsl_flexio_mculcd_smartdma.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.flexio_mculcd_smartdma"
17 #endif
18 
19 #define FLEXIO_MCULCD_SMARTDMA_TX_START_SHIFTER 0U
20 #define FLEXIO_MCULCD_SMARTDMA_TX_END_SHIFTER   7U
21 #define FLEXIO_MCULCD_SMARTDMA_TX_SHIFTER_NUM \
22     (FLEXIO_MCULCD_SMARTDMA_TX_END_SHIFTER - FLEXIO_MCULCD_SMARTDMA_TX_START_SHIFTER + 1)
23 
24 enum _MCULCD_transfer_state
25 {
26     kFLEXIO_MCULCD_StateIdle,           /*!< No transfer in progress. */
27     kFLEXIO_MCULCD_StateReadArray,      /*!< Reading array in progress. */
28     kFLEXIO_MCULCD_StateWriteArray,     /*!< Writing array in progress. */
29     kFLEXIO_MCULCD_StateWriteSameValue, /*!< Writing the same value in progress.
30                                          */
31 };
32 
33 /*******************************************************************************
34  * Prototypes
35  ******************************************************************************/
36 
37 /*!
38  * @brief Get the TX chunk size.
39  *
40  * The SMARTDMA TX transfer memory must be 4Byte aligned, the transfer size must
41  * be multiple of 64Byte. So the transfer data is devided in to three part:
42  * part1 + part2 + part3.
43  * The part2 is transfered using SMARTDMA, it should be 4Byte aligned, multiple
44  * of 64Byte.
45  * The part1 and part3 are transfered using blocking method, each of them is
46  * less than 64Byte, and total of them is less than (64 + 4) bytes.
47  *
48  * This function gets the size of each part.
49  *
50  * @param totalLen The total TX size in byte.
51  * @param startAddr The start address of the TX data.
52  * @param part1Len Length of the part 1 in byte.
53  * @param part2Len Length of the part 2 in byte.
54  * @param part3Len Length of the part 3 in byte.
55  */
56 static void FLEXIO_MCULCD_SMARTDMA_GetTxChunkLen(
57     uint32_t totalLen, uint32_t startAddr, uint32_t *part1Len, uint32_t *part2Len, uint32_t *part3Len);
58 
59 /*!
60  * @brief Convert RGB565 to RGB888.
61  *
62  * @param rgb565 Input RGB565.
63  * @param pixelCount Pixel count.
64  * @param rgb888 Output RGB888.
65  */
66 static void FLEXIO_MCULCD_RGB656ToRGB888(const uint16_t *rgb565, uint32_t pixelCount, uint8_t *rgb888);
67 
68 /*!
69  * @brief Callback function registered to SMARTDMA driver.
70  */
71 static void FLEXIO_MCULCD_SMARTDMA_Callback(void *param);
72 
73 /*******************************************************************************
74  * Variables
75  ******************************************************************************/
76 
77 /*******************************************************************************
78  * Code
79  ******************************************************************************/
FLEXIO_MCULCD_SMARTDMA_GetTxChunkLen(uint32_t totalLen,uint32_t startAddr,uint32_t * part1Len,uint32_t * part2Len,uint32_t * part3Len)80 static void FLEXIO_MCULCD_SMARTDMA_GetTxChunkLen(
81     uint32_t totalLen, uint32_t startAddr, uint32_t *part1Len, uint32_t *part2Len, uint32_t *part3Len)
82 {
83     if (totalLen < FLEXIO_MCULCD_SMARTDMA_TX_LEN_ALIGN)
84     {
85         *part1Len = totalLen;
86         *part2Len = 0;
87         *part3Len = 0;
88     }
89     else
90     {
91         *part3Len = (startAddr + totalLen) & (FLEXIO_MCULCD_SMARTDMA_TX_ADDR_ALIGN - 1U);
92         *part2Len = ((uint32_t)(totalLen - *part3Len)) & (~(FLEXIO_MCULCD_SMARTDMA_TX_LEN_ALIGN - 1U));
93 
94         if (FLEXIO_MCULCD_SMARTDMA_TX_LEN_ALIGN > *part2Len)
95         {
96             *part1Len = totalLen;
97             *part2Len = 0;
98             *part3Len = 0;
99         }
100         else
101         {
102             *part1Len = totalLen - *part2Len - *part3Len;
103         }
104     }
105 }
106 
FLEXIO_MCULCD_RGB656ToRGB888(const uint16_t * rgb565,uint32_t pixelCount,uint8_t * rgb888)107 static void FLEXIO_MCULCD_RGB656ToRGB888(const uint16_t *rgb565, uint32_t pixelCount, uint8_t *rgb888)
108 {
109     while ((pixelCount--) != 0U)
110     {
111         *rgb888 = (uint8_t)(((*rgb565) & 0x001FU) << 3U);
112         rgb888++;
113         *rgb888 = (uint8_t)(((*rgb565) & 0x07E0U) >> 3U);
114         rgb888++;
115         *rgb888 = (uint8_t)(((*rgb565) & 0xF800U) >> 8U);
116         rgb888++;
117 
118         rgb565++;
119     }
120 }
121 
122 /*!
123  * brief Initializes the FLEXO MCULCD master SMARTDMA handle.
124  *
125  * This function initializes the FLEXO MCULCD master SMARTDMA handle which can be
126  * used for other FLEXO MCULCD transactional APIs. For a specified FLEXO MCULCD
127  * instance, call this API once to get the initialized handle.
128  *
129  * param base Pointer to FLEXIO_MCULCD_Type structure.
130  * param handle Pointer to flexio_mculcd_smartdma_handle_t structure to store the
131  * transfer state.
132  * param config Pointer to the configuration.
133  * param callback MCULCD transfer complete callback, NULL means no callback.
134  * param userData callback function parameter.
135  * retval kStatus_Success Successfully create the handle.
136  */
FLEXIO_MCULCD_TransferCreateHandleSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle,const flexio_mculcd_smartdma_config_t * config,flexio_mculcd_smartdma_transfer_callback_t callback,void * userData)137 status_t FLEXIO_MCULCD_TransferCreateHandleSMARTDMA(FLEXIO_MCULCD_Type *base,
138                                                     flexio_mculcd_smartdma_handle_t *handle,
139                                                     const flexio_mculcd_smartdma_config_t *config,
140                                                     flexio_mculcd_smartdma_transfer_callback_t callback,
141                                                     void *userData)
142 {
143     assert(handle != NULL);
144 
145     /* The SMARTDMA firmware only support TX using shifter 0 to shifter 7 */
146     if (base->txShifterStartIndex != FLEXIO_MCULCD_SMARTDMA_TX_START_SHIFTER)
147     {
148         return kStatus_InvalidArgument;
149     }
150 
151     if (base->txShifterEndIndex != FLEXIO_MCULCD_SMARTDMA_TX_END_SHIFTER)
152     {
153         return kStatus_InvalidArgument;
154     }
155 
156     /* Zero the handle. */
157     (void)memset(handle, 0, sizeof(*handle));
158 
159     if (NULL == config)
160     {
161         handle->smartdmaApi = (uint8_t)kSMARTDMA_FlexIO_DMA;
162     }
163     else
164     {
165         if (config->inputPixelFormat == config->outputPixelFormat)
166         {
167             handle->smartdmaApi = (uint8_t)kSMARTDMA_FlexIO_DMA;
168         }
169         else if (((config->inputPixelFormat == kFLEXIO_MCULCD_RGB565) &&
170                   (config->outputPixelFormat == kFLEXIO_MCULCD_RGB888)) ||
171                  ((config->inputPixelFormat == kFLEXIO_MCULCD_BGR565) &&
172                   (config->outputPixelFormat == kFLEXIO_MCULCD_BGR888)))
173         {
174             handle->smartdmaApi      = (uint8_t)kSMARTDMA_FlexIO_DMA_RGB565To888;
175             handle->needColorConvert = true;
176         }
177         else
178         {
179             return kStatus_InvalidArgument;
180         }
181     }
182 
183     /* Initialize the state. */
184     handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
185 
186     /* Register callback and userData. */
187     handle->completionCallback = callback;
188     handle->userData           = userData;
189     handle->base               = base;
190 
191     SMARTDMA_InstallFirmware(SMARTDMA_DISPLAY_MEM_ADDR, s_smartdmaDisplayFirmware, SMARTDMA_DISPLAY_FIRMWARE_SIZE);
192 
193     SMARTDMA_InstallCallback(FLEXIO_MCULCD_SMARTDMA_Callback, handle);
194 
195     /* The shifter interrupt is used by the SMARTDMA. */
196     FLEXIO_EnableShifterStatusInterrupts(base->flexioBase, (1UL << FLEXIO_MCULCD_SMARTDMA_TX_END_SHIFTER));
197 
198 #if (defined(SMARTDMA_USE_FLEXIO_SHIFTER_DMA) && SMARTDMA_USE_FLEXIO_SHIFTER_DMA)
199     FLEXIO_EnableShifterStatusDMA(base->flexioBase, 1UL, true);
200 #endif
201 
202     return kStatus_Success;
203 }
204 
205 /*!
206  * brief Performs a non-blocking FlexIO MCULCD transfer using SMARTDMA.
207  *
208  * This function returns immediately after transfer initiates. Use the callback
209  * function to check whether the transfer is completed.
210  *
211  * param base pointer to FLEXIO_MCULCD_Type structure.
212  * param handle pointer to flexio_mculcd_smartdma_handle_t structure to store the
213  * transfer state.
214  * param xfer Pointer to FlexIO MCULCD transfer structure.
215  * retval kStatus_Success Successfully start a transfer.
216  * retval kStatus_InvalidArgument Input argument is invalid.
217  * retval kStatus_FLEXIO_MCULCD_Busy FlexIO MCULCD is not idle, it is running another
218  * transfer.
219  */
FLEXIO_MCULCD_TransferSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle,flexio_mculcd_transfer_t * xfer)220 status_t FLEXIO_MCULCD_TransferSMARTDMA(FLEXIO_MCULCD_Type *base,
221                                         flexio_mculcd_smartdma_handle_t *handle,
222                                         flexio_mculcd_transfer_t *xfer)
223 {
224     assert(handle != NULL);
225     assert(xfer != NULL);
226 
227     uint32_t part1Len, part2Len, part3Len;
228 
229     /* Check if the device is busy. */
230     if ((uint32_t)kFLEXIO_MCULCD_StateIdle != handle->state)
231     {
232         return kStatus_FLEXIO_MCULCD_Busy;
233     }
234 
235     /* Only support write array. */
236     if (kFLEXIO_MCULCD_WriteArray != xfer->mode)
237     {
238         return kStatus_InvalidArgument;
239     }
240 
241     FLEXIO_MCULCD_SMARTDMA_GetTxChunkLen(xfer->dataSize, xfer->dataAddrOrSameValue, &part1Len, &part2Len, &part3Len);
242 
243     handle->state = (uint32_t)kFLEXIO_MCULCD_StateWriteArray;
244 
245     /* Start transfer. */
246     handle->remainingCount      = xfer->dataSize;
247     handle->dataCount           = xfer->dataSize;
248     handle->dataAddrOrSameValue = xfer->dataAddrOrSameValue;
249 
250     /* Assert the nCS. */
251     FLEXIO_MCULCD_StartTransfer(base);
252 
253     if (!xfer->dataOnly)
254     {
255         /* Send the command. */
256         FLEXIO_MCULCD_WriteCommandBlocking(base, xfer->command);
257     }
258 
259     if (part1Len > 0U)
260     {
261         if (handle->needColorConvert)
262         {
263             FLEXIO_MCULCD_RGB656ToRGB888((uint16_t *)xfer->dataAddrOrSameValue, part1Len >> 1U,
264                                          handle->blockingXferBuffer);
265             FLEXIO_MCULCD_WriteDataArrayBlocking(base, handle->blockingXferBuffer, (part1Len >> 1U) * 3U);
266         }
267         else
268         {
269             FLEXIO_MCULCD_WriteDataArrayBlocking(base, (void *)(uint8_t *)xfer->dataAddrOrSameValue, (size_t)part1Len);
270         }
271         handle->remainingCount -= part1Len;
272         handle->dataAddrOrSameValue += part1Len;
273     }
274 
275     if (0U == part2Len)
276     {
277         /* In this case, all data are sent out as part 1. Only notify upper layer here. */
278         FLEXIO_MCULCD_StopTransfer(base);
279         handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
280 
281         /* Callback to inform upper layer. */
282         if (NULL != handle->completionCallback)
283         {
284             handle->completionCallback(base, handle, kStatus_FLEXIO_MCULCD_Idle, handle->userData);
285         }
286     }
287     else
288     {
289         /* For 6800, de-assert the RDWR pin. */
290         if (kFLEXIO_MCULCD_6800 == base->busType)
291         {
292             base->setRDWRPin(false);
293         }
294 
295         FLEXIO_MCULCD_SetMultiBeatsWriteConfig(base);
296 
297         /* Save the part 3 information. */
298         handle->dataCountUsingEzh = part2Len;
299         handle->dataAddrOrSameValue += part2Len;
300 
301         /* The part 3 is transfered using blocking method in ISR, convert the color
302            to save time in ISR. */
303         if ((0U != part3Len) && (handle->needColorConvert))
304         {
305             FLEXIO_MCULCD_RGB656ToRGB888((uint16_t *)xfer->dataAddrOrSameValue, part3Len >> 1U,
306                                          handle->blockingXferBuffer);
307         }
308 
309         handle->smartdmaParam.p_buffer       = (uint32_t *)(xfer->dataAddrOrSameValue + part1Len);
310         handle->smartdmaParam.buffersize     = part2Len;
311         handle->smartdmaParam.smartdma_stack = handle->smartdmaStack;
312 
313         SMARTDMA_Reset();
314         SMARTDMA_Boot(handle->smartdmaApi, &(handle->smartdmaParam), 0);
315     }
316 
317     return kStatus_Success;
318 }
319 
320 /*!
321  * brief Aborts a FlexIO MCULCD transfer using SMARTDMA.
322  *
323  * param base pointer to FLEXIO_MCULCD_Type structure.
324  * param handle FlexIO MCULCD SMARTDMA handle pointer.
325  */
FLEXIO_MCULCD_TransferAbortSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle)326 void FLEXIO_MCULCD_TransferAbortSMARTDMA(FLEXIO_MCULCD_Type *base, flexio_mculcd_smartdma_handle_t *handle)
327 {
328     assert(handle != NULL);
329 
330     SMARTDMA_Reset();
331 
332     /* Set the handle state. */
333     handle->state     = (uint32_t)kFLEXIO_MCULCD_StateIdle;
334     handle->dataCount = 0;
335 }
336 
337 /*!
338  * brief Gets the remaining bytes for FlexIO MCULCD SMARTDMA transfer.
339  *
340  * param base pointer to FLEXIO_MCULCD_Type structure.
341  * param handle FlexIO MCULCD SMARTDMA handle pointer.
342  * param count Number of count transferred so far by the SMARTDMA transaction.
343  * retval kStatus_Success Get the transferred count Successfully.
344  * retval kStatus_NoTransferInProgress No transfer in process.
345  */
FLEXIO_MCULCD_TransferGetCountSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle,size_t * count)346 status_t FLEXIO_MCULCD_TransferGetCountSMARTDMA(FLEXIO_MCULCD_Type *base,
347                                                 flexio_mculcd_smartdma_handle_t *handle,
348                                                 size_t *count)
349 {
350     assert(handle != NULL);
351     assert(count != NULL);
352 
353     uint32_t state = handle->state;
354 
355     if ((uint32_t)kFLEXIO_MCULCD_StateIdle == state)
356     {
357         return kStatus_NoTransferInProgress;
358     }
359     else
360     {
361         *count = handle->dataCount - handle->remainingCount;
362     }
363 
364     return kStatus_Success;
365 }
366 
FLEXIO_MCULCD_SMARTDMA_Callback(void * param)367 static void FLEXIO_MCULCD_SMARTDMA_Callback(void *param)
368 {
369     flexio_mculcd_smartdma_handle_t *flexioMculcdSmartDmaHandle = (flexio_mculcd_smartdma_handle_t *)param;
370 
371     FLEXIO_MCULCD_Type *flexioLcdMcuBase = flexioMculcdSmartDmaHandle->base;
372 
373     FLEXIO_MCULCD_WaitTransmitComplete();
374 
375     /* Disable the TX shifter and the timer. */
376     FLEXIO_MCULCD_ClearMultiBeatsWriteConfig(flexioLcdMcuBase);
377 
378     flexioMculcdSmartDmaHandle->remainingCount -= flexioMculcdSmartDmaHandle->dataCountUsingEzh;
379 
380     /* Send the part 3 */
381     if (0U != flexioMculcdSmartDmaHandle->remainingCount)
382     {
383         if (flexioMculcdSmartDmaHandle->needColorConvert)
384         {
385             FLEXIO_MCULCD_WriteDataArrayBlocking(flexioLcdMcuBase, flexioMculcdSmartDmaHandle->blockingXferBuffer,
386                                                  (flexioMculcdSmartDmaHandle->remainingCount >> 1U) * 3U);
387         }
388         else
389         {
390             FLEXIO_MCULCD_WriteDataArrayBlocking(flexioLcdMcuBase,
391                                                  (void *)(uint8_t *)flexioMculcdSmartDmaHandle->dataAddrOrSameValue,
392                                                  flexioMculcdSmartDmaHandle->remainingCount);
393         }
394     }
395 
396     flexioMculcdSmartDmaHandle->remainingCount = 0;
397     FLEXIO_MCULCD_StopTransfer(flexioLcdMcuBase);
398     flexioMculcdSmartDmaHandle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
399 
400     /* Callback to inform upper layer. */
401     if (NULL != flexioMculcdSmartDmaHandle->completionCallback)
402     {
403         flexioMculcdSmartDmaHandle->completionCallback(flexioLcdMcuBase, flexioMculcdSmartDmaHandle,
404                                                        kStatus_FLEXIO_MCULCD_Idle,
405                                                        flexioMculcdSmartDmaHandle->userData);
406     }
407 }
408