1 /*
2  * Copyright 2019-2021 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     return kStatus_Success;
199 }
200 
201 /*!
202  * brief Performs a non-blocking FlexIO MCULCD transfer using SMARTDMA.
203  *
204  * This function returns immediately after transfer initiates. Use the callback
205  * function to check whether the transfer is completed.
206  *
207  * param base pointer to FLEXIO_MCULCD_Type structure.
208  * param handle pointer to flexio_mculcd_smartdma_handle_t structure to store the
209  * transfer state.
210  * param xfer Pointer to FlexIO MCULCD transfer structure.
211  * retval kStatus_Success Successfully start a transfer.
212  * retval kStatus_InvalidArgument Input argument is invalid.
213  * retval kStatus_FLEXIO_MCULCD_Busy FlexIO MCULCD is not idle, it is running another
214  * transfer.
215  */
FLEXIO_MCULCD_TransferSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle,flexio_mculcd_transfer_t * xfer)216 status_t FLEXIO_MCULCD_TransferSMARTDMA(FLEXIO_MCULCD_Type *base,
217                                         flexio_mculcd_smartdma_handle_t *handle,
218                                         flexio_mculcd_transfer_t *xfer)
219 {
220     assert(handle != NULL);
221     assert(xfer != NULL);
222 
223     uint32_t part1Len, part2Len, part3Len;
224 
225     /* Check if the device is busy. */
226     if ((uint32_t)kFLEXIO_MCULCD_StateIdle != handle->state)
227     {
228         return kStatus_FLEXIO_MCULCD_Busy;
229     }
230 
231     /* Only support write array. */
232     if (kFLEXIO_MCULCD_WriteArray != xfer->mode)
233     {
234         return kStatus_InvalidArgument;
235     }
236 
237     FLEXIO_MCULCD_SMARTDMA_GetTxChunkLen(xfer->dataSize, xfer->dataAddrOrSameValue, &part1Len, &part2Len, &part3Len);
238 
239     handle->state = (uint32_t)kFLEXIO_MCULCD_StateWriteArray;
240 
241     /* Start transfer. */
242     handle->remainingCount      = xfer->dataSize;
243     handle->dataCount           = xfer->dataSize;
244     handle->dataAddrOrSameValue = xfer->dataAddrOrSameValue;
245 
246     /* Assert the nCS. */
247     FLEXIO_MCULCD_StartTransfer(base);
248     /* Send the command. */
249     FLEXIO_MCULCD_WriteCommandBlocking(base, xfer->command);
250 
251     if (part1Len > 0U)
252     {
253         if (handle->needColorConvert)
254         {
255             FLEXIO_MCULCD_RGB656ToRGB888((uint16_t *)xfer->dataAddrOrSameValue, part1Len >> 1U,
256                                          handle->blockingXferBuffer);
257             FLEXIO_MCULCD_WriteDataArrayBlocking(base, handle->blockingXferBuffer, (part1Len >> 1U) * 3U);
258         }
259         else
260         {
261             FLEXIO_MCULCD_WriteDataArrayBlocking(base, (void *)(uint8_t *)xfer->dataAddrOrSameValue, (size_t)part1Len);
262         }
263         handle->remainingCount -= part1Len;
264         handle->dataAddrOrSameValue += part1Len;
265     }
266 
267     if (0U == part2Len)
268     {
269         /* In this case, all data are sent out as part 1. Only notify upper layer here. */
270         FLEXIO_MCULCD_StopTransfer(base);
271         handle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
272 
273         /* Callback to inform upper layer. */
274         if (NULL != handle->completionCallback)
275         {
276             handle->completionCallback(base, handle, kStatus_FLEXIO_MCULCD_Idle, handle->userData);
277         }
278     }
279     else
280     {
281         /* For 6800, de-assert the RDWR pin. */
282         if (kFLEXIO_MCULCD_6800 == base->busType)
283         {
284             base->setRDWRPin(false);
285         }
286 
287         FLEXIO_MCULCD_SetMultiBeatsWriteConfig(base);
288 
289         /* Save the part 3 information. */
290         handle->dataCountUsingEzh = part2Len;
291         handle->dataAddrOrSameValue += part2Len;
292 
293         /* The part 3 is transfered using blocking method in ISR, convert the color
294            to save time in ISR. */
295         if ((0U != part3Len) && (handle->needColorConvert))
296         {
297             FLEXIO_MCULCD_RGB656ToRGB888((uint16_t *)xfer->dataAddrOrSameValue, part3Len >> 1U,
298                                          handle->blockingXferBuffer);
299         }
300 
301         handle->smartdmaParam.p_buffer       = (uint32_t *)(xfer->dataAddrOrSameValue + part1Len);
302         handle->smartdmaParam.buffersize     = part2Len;
303         handle->smartdmaParam.smartdma_stack = handle->smartdmaStack;
304 
305         SMARTDMA_Reset();
306         SMARTDMA_Boot(handle->smartdmaApi, &(handle->smartdmaParam), 0);
307     }
308 
309     return kStatus_Success;
310 }
311 
312 /*!
313  * brief Aborts a FlexIO MCULCD transfer using SMARTDMA.
314  *
315  * param base pointer to FLEXIO_MCULCD_Type structure.
316  * param handle FlexIO MCULCD SMARTDMA handle pointer.
317  */
FLEXIO_MCULCD_TransferAbortSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle)318 void FLEXIO_MCULCD_TransferAbortSMARTDMA(FLEXIO_MCULCD_Type *base, flexio_mculcd_smartdma_handle_t *handle)
319 {
320     assert(handle != NULL);
321 
322     SMARTDMA_Reset();
323 
324     /* Set the handle state. */
325     handle->state     = (uint32_t)kFLEXIO_MCULCD_StateIdle;
326     handle->dataCount = 0;
327 }
328 
329 /*!
330  * brief Gets the remaining bytes for FlexIO MCULCD SMARTDMA transfer.
331  *
332  * param base pointer to FLEXIO_MCULCD_Type structure.
333  * param handle FlexIO MCULCD SMARTDMA handle pointer.
334  * param count Number of count transferred so far by the SMARTDMA transaction.
335  * retval kStatus_Success Get the transferred count Successfully.
336  * retval kStatus_NoTransferInProgress No transfer in process.
337  */
FLEXIO_MCULCD_TransferGetCountSMARTDMA(FLEXIO_MCULCD_Type * base,flexio_mculcd_smartdma_handle_t * handle,size_t * count)338 status_t FLEXIO_MCULCD_TransferGetCountSMARTDMA(FLEXIO_MCULCD_Type *base,
339                                                 flexio_mculcd_smartdma_handle_t *handle,
340                                                 size_t *count)
341 {
342     assert(handle != NULL);
343     assert(count != NULL);
344 
345     uint32_t state = handle->state;
346 
347     if ((uint32_t)kFLEXIO_MCULCD_StateIdle == state)
348     {
349         return kStatus_NoTransferInProgress;
350     }
351     else
352     {
353         *count = handle->dataCount - handle->remainingCount;
354     }
355 
356     return kStatus_Success;
357 }
358 
FLEXIO_MCULCD_SMARTDMA_Callback(void * param)359 static void FLEXIO_MCULCD_SMARTDMA_Callback(void *param)
360 {
361     flexio_mculcd_smartdma_handle_t *flexioMculcdSmartDmaHandle = (flexio_mculcd_smartdma_handle_t *)param;
362 
363     FLEXIO_MCULCD_Type *flexioLcdMcuBase = flexioMculcdSmartDmaHandle->base;
364 
365     FLEXIO_MCULCD_WaitTransmitComplete();
366 
367     /* Disable the TX shifter and the timer. */
368     FLEXIO_MCULCD_ClearMultiBeatsWriteConfig(flexioLcdMcuBase);
369 
370     flexioMculcdSmartDmaHandle->remainingCount -= flexioMculcdSmartDmaHandle->dataCountUsingEzh;
371 
372     /* Send the part 3 */
373     if (0U != flexioMculcdSmartDmaHandle->remainingCount)
374     {
375         if (flexioMculcdSmartDmaHandle->needColorConvert)
376         {
377             FLEXIO_MCULCD_WriteDataArrayBlocking(flexioLcdMcuBase, flexioMculcdSmartDmaHandle->blockingXferBuffer,
378                                                  (flexioMculcdSmartDmaHandle->remainingCount >> 1U) * 3U);
379         }
380         else
381         {
382             FLEXIO_MCULCD_WriteDataArrayBlocking(flexioLcdMcuBase,
383                                                  (void *)(uint8_t *)flexioMculcdSmartDmaHandle->dataAddrOrSameValue,
384                                                  flexioMculcdSmartDmaHandle->remainingCount);
385         }
386     }
387 
388     flexioMculcdSmartDmaHandle->remainingCount = 0;
389     FLEXIO_MCULCD_StopTransfer(flexioLcdMcuBase);
390     flexioMculcdSmartDmaHandle->state = (uint32_t)kFLEXIO_MCULCD_StateIdle;
391 
392     /* Callback to inform upper layer. */
393     if (NULL != flexioMculcdSmartDmaHandle->completionCallback)
394     {
395         flexioMculcdSmartDmaHandle->completionCallback(flexioLcdMcuBase, flexioMculcdSmartDmaHandle,
396                                                        kStatus_FLEXIO_MCULCD_Idle,
397                                                        flexioMculcdSmartDmaHandle->userData);
398     }
399 }
400