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