1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2021 NXP
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include "fsl_sai_edma.h"
10 
11 /* Component ID definition, used by tools. */
12 #ifndef FSL_COMPONENT_ID
13 #define FSL_COMPONENT_ID "platform.drivers.sai_edma"
14 #endif
15 
16 /*******************************************************************************
17  * Definitions
18  ******************************************************************************/
19 /* Used for 32byte aligned */
20 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32UL) & ~0x1FU)
21 
22 static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
23 /* Only support 2 and 4 channel */
24 #define SAI_CHANNEL_MAP_MODULO(channel) ((channel) == 2U ? kEDMA_Modulo8bytes : kEDMA_Modulo16bytes)
25 
26 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
27 typedef struct sai_edma_private_handle
28 {
29     I2S_Type *base;
30     sai_edma_handle_t *handle;
31 } sai_edma_private_handle_t;
32 
33 /*! @brief sai_edma_transfer_state, sai edma transfer state.*/
34 enum
35 {
36     kSAI_Busy = 0x0U,      /*!< SAI is busy */
37     kSAI_BusyLoopTransfer, /*!< SAI is busy for Loop transfer */
38     kSAI_Idle,             /*!< Transfer is done. */
39 };
40 
41 /*<! Private handle only used for internally. */
42 static sai_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2];
43 
44 /*******************************************************************************
45  * Prototypes
46  ******************************************************************************/
47 /*!
48  * @brief Get the instance number for SAI.
49  *
50  * @param base SAI base pointer.
51  */
52 static uint32_t SAI_GetInstance(I2S_Type *base);
53 
54 /*!
55  * @brief SAI EDMA callback for send.
56  *
57  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
58  * @param userData Parameter for user callback.
59  * @param done If the DMA transfer finished.
60  * @param tcds The TCD index.
61  */
62 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
63 
64 /*!
65  * @brief SAI EDMA callback for receive.
66  *
67  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
68  * @param userData Parameter for user callback.
69  * @param done If the DMA transfer finished.
70  * @param tcds The TCD index.
71  */
72 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
73 
74 /*******************************************************************************
75  * Code
76  ******************************************************************************/
SAI_GetInstance(I2S_Type * base)77 static uint32_t SAI_GetInstance(I2S_Type *base)
78 {
79     uint32_t instance;
80 
81     /* Find the instance index from base address mappings. */
82     for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++)
83     {
84         if (s_saiBases[instance] == base)
85         {
86             break;
87         }
88     }
89 
90     assert(instance < ARRAY_SIZE(s_saiBases));
91 
92     return instance;
93 }
94 
SAI_TxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)95 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
96 {
97     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
98     sai_edma_handle_t *saiHandle          = privHandle->handle;
99     status_t status                       = kStatus_SAI_TxBusy;
100 
101     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
102     {
103         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
104         {
105             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
106                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
107             (void)memset(&saiHandle->saiQueue[0U], 0,
108                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
109         }
110         else
111         {
112             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
113         }
114         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
115 
116         /* If all data finished, just stop the transfer */
117         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
118         {
119             /* Disable DMA enable bit */
120             SAI_TxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
121             EDMA_AbortTransfer(handle);
122             status = kStatus_SAI_TxIdle;
123         }
124     }
125 
126     /* If finished a block, call the callback function */
127     if (saiHandle->callback != NULL)
128     {
129         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
130     }
131 }
132 
SAI_RxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)133 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
134 {
135     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
136     sai_edma_handle_t *saiHandle          = privHandle->handle;
137     status_t status                       = kStatus_SAI_RxBusy;
138 
139     if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer)
140     {
141         if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE)
142         {
143             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0,
144                          sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver));
145             (void)memset(&saiHandle->saiQueue[0U], 0,
146                          sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE));
147         }
148         else
149         {
150             (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds);
151         }
152         saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE);
153 
154         /* If all data finished, just stop the transfer */
155         if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
156         {
157             /* Disable DMA enable bit */
158             SAI_RxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false);
159             EDMA_AbortTransfer(handle);
160             status = kStatus_SAI_RxIdle;
161         }
162     }
163 
164     /* If finished a block, call the callback function */
165     if (saiHandle->callback != NULL)
166     {
167         (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData);
168     }
169 }
170 
171 /*!
172  * brief Initializes the SAI eDMA handle.
173  *
174  * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs.
175  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
176  *
177  * param base SAI base pointer.
178  * param handle SAI eDMA handle pointer.
179  * param base SAI peripheral base address.
180  * param callback Pointer to user callback function.
181  * param userData User parameter passed to the callback function.
182  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
183  */
SAI_TransferTxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * txDmaHandle)184 void SAI_TransferTxCreateHandleEDMA(
185     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *txDmaHandle)
186 {
187     assert((handle != NULL) && (txDmaHandle != NULL));
188 
189     uint32_t instance = SAI_GetInstance(base);
190 
191     /* Zero the handle */
192     (void)memset(handle, 0, sizeof(*handle));
193 
194     /* Set sai base to handle */
195     handle->dmaHandle      = txDmaHandle;
196     handle->callback       = callback;
197     handle->userData       = userData;
198     handle->interleaveType = kSAI_EDMAInterleavePerChannelSample;
199 
200     /* Set SAI state to idle */
201     handle->state = (uint32_t)kSAI_Idle;
202 
203     s_edmaPrivateHandle[instance][0].base   = base;
204     s_edmaPrivateHandle[instance][0].handle = handle;
205 
206     /* Need to use scatter gather */
207     EDMA_InstallTCDMemory(txDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->tcd)), SAI_XFER_QUEUE_SIZE);
208 
209     /* Install callback for Tx dma channel */
210     EDMA_SetCallback(txDmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
211 }
212 
213 /*!
214  * brief Initializes the SAI Rx eDMA handle.
215  *
216  * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs.
217  * Usually, for a specified SAI instance, call this API once to get the initialized handle.
218  *
219  * param base SAI base pointer.
220  * param handle SAI eDMA handle pointer.
221  * param base SAI peripheral base address.
222  * param callback Pointer to user callback function.
223  * param userData User parameter passed to the callback function.
224  * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users.
225  */
SAI_TransferRxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * rxDmaHandle)226 void SAI_TransferRxCreateHandleEDMA(
227     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *rxDmaHandle)
228 {
229     assert((handle != NULL) && (rxDmaHandle != NULL));
230 
231     uint32_t instance = SAI_GetInstance(base);
232 
233     /* Zero the handle */
234     (void)memset(handle, 0, sizeof(*handle));
235 
236     /* Set sai base to handle */
237     handle->dmaHandle      = rxDmaHandle;
238     handle->callback       = callback;
239     handle->userData       = userData;
240     handle->interleaveType = kSAI_EDMAInterleavePerChannelSample;
241 
242     /* Set SAI state to idle */
243     handle->state = (uint32_t)kSAI_Idle;
244 
245     s_edmaPrivateHandle[instance][1].base   = base;
246     s_edmaPrivateHandle[instance][1].handle = handle;
247 
248     /* Need to use scatter gather */
249     EDMA_InstallTCDMemory(rxDmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
250 
251     /* Install callback for Tx dma channel */
252     EDMA_SetCallback(rxDmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
253 }
254 
255 /*!
256  * brief Initializes the SAI interleave type.
257  *
258  * This function initializes the SAI DMA handle member interleaveType, it shall be called only when application would
259  * like to use type kSAI_EDMAInterleavePerChannelBlock, since the default interleaveType is
260  * kSAI_EDMAInterleavePerChannelSample always
261  *
262  * param handle SAI eDMA handle pointer.
263  * param interleaveType SAI interleave type.
264  */
SAI_TransferSetInterleaveType(sai_edma_handle_t * handle,sai_edma_interleave_t interleaveType)265 void SAI_TransferSetInterleaveType(sai_edma_handle_t *handle, sai_edma_interleave_t interleaveType)
266 {
267     handle->interleaveType = interleaveType;
268 }
269 
270 /*!
271  * brief Configures the SAI Tx.
272  *
273  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
274  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
275  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
276  * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t
277  * which is a member of sai_transceiver_t.
278  * This is an example of multi-channel data transfer configuration step.
279  *  code
280  *   sai_transceiver_t config;
281  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
282  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite;
283  *   SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config);
284  *  endcode
285  * param base SAI base pointer.
286  * param handle SAI eDMA handle pointer.
287  * param saiConfig sai configurations.
288  */
SAI_TransferTxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)289 void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
290 {
291     assert((handle != NULL) && (saiConfig != NULL));
292 
293     /* Configure the audio format to SAI registers */
294     SAI_TxSetConfig(base, saiConfig);
295 
296 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
297     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
298     assert(
299         (saiConfig->channelNums <= 1U) ||
300         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnWrite) ||
301                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
302 #endif
303 
304     /* Get the transfer size from format, this should be used in EDMA configuration */
305     if (saiConfig->serialData.dataWordLength == 24U)
306     {
307         handle->bytesPerFrame = 4U;
308     }
309     else
310     {
311         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
312     }
313     /* Update the data channel SAI used */
314     handle->channel     = saiConfig->startChannel;
315     handle->channelMask = saiConfig->channelMask;
316     handle->channelNums = saiConfig->channelNums;
317 
318     /* Clear the channel enable bits until do a send/receive */
319     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
320 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
321     handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - saiConfig->fifo.fifoWatermark);
322 #else
323     handle->count = 1U;
324 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
325 }
326 
327 /*!
328  * brief Configures the SAI Rx.
329  *
330  * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported.
331  * To activate the multi-channel transfer enable SAI channels by filling the channelMask
332  * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine
333  * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t
334  * which is a member of sai_transceiver_t.
335  * This is an example of multi-channel data transfer configuration step.
336  *  code
337  *   sai_transceiver_t config;
338  *   SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask);
339  *   config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead;
340  *   SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config);
341  *  endcode
342  * param base SAI base pointer.
343  * param handle SAI eDMA handle pointer.
344  * param saiConfig sai configurations.
345  */
SAI_TransferRxSetConfigEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transceiver_t * saiConfig)346 void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig)
347 {
348     assert((handle != NULL) && (saiConfig != NULL));
349 
350     /* Configure the audio format to SAI registers */
351     SAI_RxSetConfig(base, saiConfig);
352 
353 #if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE
354     /* Allow multi-channel transfer only if FIFO Combine mode is enabled */
355     assert(
356         (saiConfig->channelNums <= 1U) ||
357         ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnRead) ||
358                                            (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite))));
359 #endif
360 
361     /* Get the transfer size from format, this should be used in EDMA configuration */
362     if (saiConfig->serialData.dataWordLength == 24U)
363     {
364         handle->bytesPerFrame = 4U;
365     }
366     else
367     {
368         handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U;
369     }
370 
371     /* Update the data channel SAI used */
372     handle->channel     = saiConfig->startChannel;
373     handle->channelMask = saiConfig->channelMask;
374     handle->channelNums = saiConfig->channelNums;
375     /* Clear the channel enable bits until do a send/receive */
376     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
377 #if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)
378     handle->count = saiConfig->fifo.fifoWatermark;
379 #else
380     handle->count = 1U;
381 #endif /* FSL_FEATURE_SAI_HAS_FIFO */
382 }
383 
384 /*!
385  * brief Performs a non-blocking SAI transfer using DMA.
386  *
387  * note This interface returns immediately after the transfer initiates. Call
388  * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished.
389  *
390  * In classic I2S mode configuration.
391  * 1. The data source sent should be formatted as below if handle->interleaveType =
392  *    kSAI_EDMAInterleavePerChannelSample :
393  *    --------------------------------------------------------------------------------------------------
394  *    |LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | ...|
395  *    --------------------------------------------------------------------------------------------------
396  * 2. The data source sent should be formatted as below if handle->interleaveType =
397  *    kSAI_EDMAInterleavePerChannelBlock :
398  *    -------------------------------------------------------------------------------------------------------
399  *    |LEFT CHANNEL | LEFT CHANNEL | LEFT CHANNEL | ...| RIGHT CHANNEL | RIGHT CHANNEL | RIGHT CHANNEL | ...|
400  *    -------------------------------------------------------------------------------------------------------
401  *
402  * This function support multi channel transfer,
403  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
404  *    on channel numbers
405  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
406  *    EDMA modulo feature, but support 2 or 4 channels only.
407  *
408  * param base SAI base pointer.
409  * param handle SAI eDMA handle pointer.
410  * param xfer Pointer to the DMA transfer structure.
411  * retval kStatus_Success Start a SAI eDMA send successfully.
412  * retval kStatus_InvalidArgument The input argument is invalid.
413  * retval kStatus_TxBusy SAI is busy sending data.
414  */
SAI_TransferSendEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)415 status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
416 {
417     assert((handle != NULL) && (xfer != NULL));
418 
419     edma_transfer_config_t config          = {0};
420     uint32_t destAddr                      = SAI_TxGetDataRegisterAddress(base, handle->channel);
421     uint32_t destOffset                    = 0U;
422     uint32_t srcOffset                     = xfer->dataSize / 2U;
423     edma_tcd_t *currentTCD                 = STCD_ADDR(handle->tcd);
424     edma_minor_offset_config_t minorOffset = {.enableSrcMinorOffset  = true,
425                                               .enableDestMinorOffset = false,
426                                               .minorOffset = 0xFFFFFU - 2U * srcOffset + 1U + handle->bytesPerFrame};
427 
428     /* Check if input parameter invalid */
429     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
430     {
431         return kStatus_InvalidArgument;
432     }
433 
434     if (handle->saiQueue[handle->queueUser].data != NULL)
435     {
436         return kStatus_SAI_QueueFull;
437     }
438 
439     /* Change the state of handle */
440     handle->state = (uint32_t)kSAI_Busy;
441 
442     /* Update the queue state */
443     handle->transferSize[handle->queueUser]      = xfer->dataSize;
444     handle->saiQueue[handle->queueUser].data     = xfer->data;
445     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
446     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
447 
448 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
449     if (handle->channelNums > 1U)
450     {
451         destOffset = sizeof(uint32_t);
452     }
453 #endif
454 
455     if (handle->interleaveType == kSAI_EDMAInterleavePerChannelSample)
456     {
457         /* Prepare edma configure */
458         EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
459                                    (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
460                                    (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
461     }
462     else
463     {
464         EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
465                                    (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
466                                    (uint32_t)2U * handle->bytesPerFrame, xfer->dataSize);
467         EDMA_TcdSetTransferConfig(currentTCD, &config, NULL);
468         EDMA_TcdSetMinorOffsetConfig(currentTCD, &minorOffset);
469         EDMA_TcdEnableInterrupts(currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
470         EDMA_TcdEnableAutoStopRequest(currentTCD, true);
471         EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, currentTCD);
472     }
473     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
474     handle->nbytes = handle->count * handle->bytesPerFrame;
475 
476     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
477     {
478         return kStatus_SAI_QueueFull;
479     }
480 
481 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
482     if (handle->channelNums > 1U)
483     {
484         if ((handle->channelNums % 2U) != 0U)
485         {
486             return kStatus_InvalidArgument;
487         }
488 
489         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, kEDMA_ModuloDisable,
490                        SAI_CHANNEL_MAP_MODULO(handle->channelNums));
491     }
492 #endif
493     /* Start DMA transfer */
494     EDMA_StartTransfer(handle->dmaHandle);
495 
496     /* Enable DMA enable bit */
497     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
498 
499     /* Enable SAI Tx clock */
500     SAI_TxEnable(base, true);
501 
502     /* Enable the channel FIFO */
503     base->TCR3 |= I2S_TCR3_TCE(handle->channelMask);
504 
505     return kStatus_Success;
506 }
507 
508 /*!
509  * brief Performs a non-blocking SAI receive using eDMA.
510  *
511  * note This interface returns immediately after the transfer initiates. Call
512  * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished.
513  *
514  * In classic I2S mode configuration.
515  * 1. The output data will be formatted as below if handle->interleaveType =
516  *    kSAI_EDMAInterleavePerChannelSample :
517  *    --------------------------------------------------------------------------------------------------
518  *    |LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | LEFT CHANNEL | RIGHT CHANNEL | ...|
519  *    --------------------------------------------------------------------------------------------------
520  * 2. The output data will be formatted as below if handle->interleaveType =
521  *    kSAI_EDMAInterleavePerChannelBlock :
522  *    -------------------------------------------------------------------------------------------------------
523  *    |LEFT CHANNEL | LEFT CHANNEL | LEFT CHANNEL | ...| RIGHT CHANNEL | RIGHT CHANNEL | RIGHT CHANNEL | ...|
524  *    -------------------------------------------------------------------------------------------------------
525  *
526  * This function support multi channel transfer,
527  * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation
528  *    on channel numbers
529  * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using
530  *    EDMA modulo feature, but support 2 or 4 channels only.
531  *
532  * param base SAI base pointer
533  * param handle SAI eDMA handle pointer.
534  * param xfer Pointer to DMA transfer structure.
535  * retval kStatus_Success Start a SAI eDMA receive successfully.
536  * retval kStatus_InvalidArgument The input argument is invalid.
537  * retval kStatus_RxBusy SAI is busy receiving data.
538  */
SAI_TransferReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)539 status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
540 {
541     assert((handle != NULL) && (xfer != NULL));
542 
543     edma_transfer_config_t config          = {0};
544     uint32_t srcAddr                       = SAI_RxGetDataRegisterAddress(base, handle->channel);
545     uint32_t srcOffset                     = 0U;
546     uint32_t destOffset                    = xfer->dataSize / 2U;
547     edma_tcd_t *currentTCD                 = STCD_ADDR(handle->tcd);
548     edma_minor_offset_config_t minorOffset = {
549         .enableSrcMinorOffset  = false,
550         .enableDestMinorOffset = true,
551         .minorOffset           = 0xFFFFFU - 2U * destOffset + 1U + (uint32_t)handle->bytesPerFrame};
552 
553     /* Check if input parameter invalid */
554     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
555     {
556         return kStatus_InvalidArgument;
557     }
558 
559     if (handle->saiQueue[handle->queueUser].data != NULL)
560     {
561         return kStatus_SAI_QueueFull;
562     }
563 
564     /* Change the state of handle */
565     handle->state = (uint32_t)kSAI_Busy;
566 
567     /* Update queue state  */
568     handle->transferSize[handle->queueUser]      = xfer->dataSize;
569     handle->saiQueue[handle->queueUser].data     = xfer->data;
570     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
571     handle->queueUser                            = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
572 
573 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
574     if (handle->channelNums > 1U)
575     {
576         srcOffset = sizeof(uint32_t);
577     }
578 #endif
579 
580     if (handle->interleaveType == kSAI_EDMAInterleavePerChannelSample)
581     {
582         /* Prepare edma configure */
583         EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
584                                    xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame,
585                                    (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize);
586     }
587     else
588     {
589         EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset,
590                                    xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset,
591                                    (uint32_t)2U * handle->bytesPerFrame, xfer->dataSize);
592         EDMA_TcdSetTransferConfig(currentTCD, &config, NULL);
593         EDMA_TcdSetMinorOffsetConfig(currentTCD, &minorOffset);
594         EDMA_TcdEnableInterrupts(currentTCD, (uint32_t)kEDMA_MajorInterruptEnable);
595         EDMA_TcdEnableAutoStopRequest(currentTCD, true);
596         EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, currentTCD);
597     }
598 
599     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
600     handle->nbytes = handle->count * handle->bytesPerFrame;
601 
602     if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success)
603     {
604         return kStatus_SAI_QueueFull;
605     }
606 
607 #if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE)
608     if (handle->channelNums > 1U)
609     {
610         if ((handle->channelNums % 2U) != 0U)
611         {
612             return kStatus_InvalidArgument;
613         }
614 
615         EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, SAI_CHANNEL_MAP_MODULO(handle->channelNums),
616                        kEDMA_ModuloDisable);
617     }
618 #endif
619     /* Start DMA transfer */
620     EDMA_StartTransfer(handle->dmaHandle);
621 
622     /* Enable DMA enable bit */
623     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
624 
625     /* Enable the channel FIFO */
626     base->RCR3 |= I2S_RCR3_RCE(handle->channelMask);
627 
628     /* Enable SAI Rx clock */
629     SAI_RxEnable(base, true);
630 
631     return kStatus_Success;
632 }
633 
634 /*!
635  * brief Performs a non-blocking SAI loop transfer using eDMA.
636  *
637  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
638  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
639  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
640  * This function support one sai channel only.
641  *
642  * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer.
643  *
644  * param base SAI base pointer.
645  * param handle SAI eDMA handle pointer.
646  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
647  * param loopTransferCount the counts of xfer array.
648  * retval kStatus_Success Start a SAI eDMA send successfully.
649  * retval kStatus_InvalidArgument The input argument is invalid.
650  */
SAI_TransferSendLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)651 status_t SAI_TransferSendLoopEDMA(I2S_Type *base,
652                                   sai_edma_handle_t *handle,
653                                   sai_transfer_t *xfer,
654                                   uint32_t loopTransferCount)
655 {
656     assert((handle != NULL) && (xfer != NULL));
657 
658     edma_transfer_config_t config = {0};
659     uint32_t destAddr             = SAI_TxGetDataRegisterAddress(base, handle->channel);
660     sai_transfer_t *transfer      = xfer;
661     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
662     uint32_t tcdIndex             = 0U;
663 
664     /* Change the state of handle */
665     handle->state = (uint32_t)kSAI_Busy;
666 
667     for (uint32_t i = 0U; i < loopTransferCount; i++)
668     {
669         transfer = &xfer[i];
670 
671         if ((transfer->data == NULL) || (transfer->dataSize == 0U) || (tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE))
672         {
673             return kStatus_InvalidArgument;
674         }
675 
676         /* Update the queue state */
677         handle->transferSize[tcdIndex]      = transfer->dataSize;
678         handle->saiQueue[tcdIndex].data     = transfer->data;
679         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
680 
681         /* Prepare edma configure */
682         EDMA_PrepareTransfer(&config, transfer->data, handle->bytesPerFrame, (uint32_t *)destAddr,
683                              handle->bytesPerFrame, (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
684                              kEDMA_MemoryToPeripheral);
685 
686         if (i == (loopTransferCount - 1U))
687         {
688             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
689             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
690             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
691             break;
692         }
693         else
694         {
695             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
696             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
697         }
698 
699         tcdIndex = tcdIndex + 1U;
700     }
701 
702     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
703     /* Start DMA transfer */
704     EDMA_StartTransfer(handle->dmaHandle);
705 
706     /* Enable DMA enable bit */
707     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
708 
709     /* Enable SAI Tx clock */
710     SAI_TxEnable(base, true);
711 
712     /* Enable the channel FIFO */
713     base->TCR3 |= I2S_TCR3_TCE(1UL << handle->channel);
714 
715     return kStatus_Success;
716 }
717 
718 /*!
719  * brief Performs a non-blocking SAI loop transfer using eDMA.
720  *
721  * note This function support loop transfer only,such as A->B->...->A, application must be aware of
722  * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in
723  * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size.
724  * This function support one sai channel only.
725  *
726  * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer.
727  *
728  * param base SAI base pointer.
729  * param handle SAI eDMA handle pointer.
730  * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount).
731  * param loopTransferCount the counts of xfer array.
732  * retval kStatus_Success Start a SAI eDMA receive successfully.
733  * retval kStatus_InvalidArgument The input argument is invalid.
734  */
SAI_TransferReceiveLoopEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer,uint32_t loopTransferCount)735 status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base,
736                                      sai_edma_handle_t *handle,
737                                      sai_transfer_t *xfer,
738                                      uint32_t loopTransferCount)
739 {
740     assert((handle != NULL) && (xfer != NULL));
741 
742     edma_transfer_config_t config = {0};
743     uint32_t srcAddr              = SAI_RxGetDataRegisterAddress(base, handle->channel);
744     sai_transfer_t *transfer      = xfer;
745     edma_tcd_t *currentTCD        = STCD_ADDR(handle->tcd);
746     uint32_t tcdIndex             = 0U;
747 
748     /* Change the state of handle */
749     handle->state = (uint32_t)kSAI_Busy;
750 
751     for (uint32_t i = 0U; i < loopTransferCount; i++)
752     {
753         transfer = &xfer[i];
754 
755         if ((tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE) || (xfer->data == NULL) || (xfer->dataSize == 0U))
756         {
757             return kStatus_InvalidArgument;
758         }
759 
760         /* Update the queue state */
761         handle->transferSize[tcdIndex]      = transfer->dataSize;
762         handle->saiQueue[tcdIndex].data     = transfer->data;
763         handle->saiQueue[tcdIndex].dataSize = transfer->dataSize;
764 
765         /* Prepare edma configure */
766         EDMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, transfer->data, handle->bytesPerFrame,
767                              (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize,
768                              kEDMA_PeripheralToMemory);
769 
770         if (i == (loopTransferCount - 1U))
771         {
772             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[0U]);
773             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
774             handle->state = (uint32_t)kSAI_BusyLoopTransfer;
775             break;
776         }
777         else
778         {
779             EDMA_TcdSetTransferConfig(&currentTCD[tcdIndex], &config, &currentTCD[tcdIndex + 1U]);
780             EDMA_TcdEnableInterrupts(&currentTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable);
781         }
782 
783         tcdIndex = tcdIndex + 1U;
784     }
785 
786     EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, &currentTCD[0]);
787     /* Start DMA transfer */
788     EDMA_StartTransfer(handle->dmaHandle);
789     /* Enable DMA enable bit */
790     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
791 
792     /* Enable the channel FIFO */
793     base->RCR3 |= I2S_RCR3_RCE(1UL << handle->channel);
794 
795     /* Enable SAI Rx clock */
796     SAI_RxEnable(base, true);
797 
798     return kStatus_Success;
799 }
800 
801 /*!
802  * brief Aborts a SAI transfer using eDMA.
803  *
804  * This function only aborts the current transfer slots, the other transfer slots' information still kept
805  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA.
806  *
807  * param base SAI base pointer.
808  * param handle SAI eDMA handle pointer.
809  */
SAI_TransferAbortSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)810 void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
811 {
812     assert(handle != NULL);
813 
814     /* Disable dma */
815     EDMA_AbortTransfer(handle->dmaHandle);
816 
817     /* Disable the channel FIFO */
818     base->TCR3 &= ~I2S_TCR3_TCE_MASK;
819 
820     /* Disable DMA enable bit */
821     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
822 
823     /* Disable Tx */
824     SAI_TxEnable(base, false);
825 
826     /* If Tx is disabled, reset the FIFO pointer and clear error flags */
827     if ((base->TCSR & I2S_TCSR_TE_MASK) == 0UL)
828     {
829         base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK);
830         base->TCSR &= ~I2S_TCSR_SR_MASK;
831     }
832 
833     /* Handle the queue index */
834     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
835     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
836 
837     /* Set the handle state */
838     handle->state = (uint32_t)kSAI_Idle;
839 }
840 
841 /*!
842  * brief Aborts a SAI receive using eDMA.
843  *
844  * This function only aborts the current transfer slots, the other transfer slots' information still kept
845  * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA.
846  *
847  * param base SAI base pointer.
848  * param handle SAI eDMA handle pointer.
849  */
SAI_TransferAbortReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)850 void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
851 {
852     assert(handle != NULL);
853 
854     /* Disable dma */
855     EDMA_AbortTransfer(handle->dmaHandle);
856 
857     /* Disable the channel FIFO */
858     base->RCR3 &= ~I2S_RCR3_RCE_MASK;
859 
860     /* Disable DMA enable bit */
861     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
862 
863     /* Disable Rx */
864     SAI_RxEnable(base, false);
865 
866     /* If Rx is disabled, reset the FIFO pointer and clear error flags */
867     if ((base->RCSR & I2S_RCSR_RE_MASK) == 0UL)
868     {
869         base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK);
870         base->RCSR &= ~I2S_RCSR_SR_MASK;
871     }
872 
873     /* Handle the queue index */
874     (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
875     handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE;
876 
877     /* Set the handle state */
878     handle->state = (uint32_t)kSAI_Idle;
879 }
880 
881 /*!
882  * brief Terminate all SAI send.
883  *
884  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
885  * current transfer slot, please call SAI_TransferAbortSendEDMA.
886  *
887  * param base SAI base pointer.
888  * param handle SAI eDMA handle pointer.
889  */
SAI_TransferTerminateSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)890 void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
891 {
892     assert(handle != NULL);
893 
894     /* Abort the current transfer */
895     SAI_TransferAbortSendEDMA(base, handle);
896 
897     /* Clear all the internal information */
898     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
899     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
900     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
901 
902     handle->queueUser   = 0U;
903     handle->queueDriver = 0U;
904 }
905 
906 /*!
907  * brief Terminate all SAI receive.
908  *
909  * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the
910  * current transfer slot, please call SAI_TransferAbortReceiveEDMA.
911  *
912  * param base SAI base pointer.
913  * param handle SAI eDMA handle pointer.
914  */
SAI_TransferTerminateReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)915 void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
916 {
917     assert(handle != NULL);
918 
919     /* Abort the current transfer */
920     SAI_TransferAbortReceiveEDMA(base, handle);
921 
922     /* Clear all the internal information */
923     (void)memset(handle->tcd, 0, sizeof(handle->tcd));
924     (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue));
925     (void)memset(handle->transferSize, 0, sizeof(handle->transferSize));
926 
927     handle->queueUser   = 0U;
928     handle->queueDriver = 0U;
929 }
930 
931 /*!
932  * brief Gets byte count sent by SAI.
933  *
934  * param base SAI base pointer.
935  * param handle SAI eDMA handle pointer.
936  * param count Bytes count sent by SAI.
937  * retval kStatus_Success Succeed get the transfer count.
938  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
939  */
SAI_TransferGetSendCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)940 status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
941 {
942     assert(handle != NULL);
943 
944     status_t status = kStatus_Success;
945 
946     if (handle->state != (uint32_t)kSAI_Busy)
947     {
948         status = kStatus_NoTransferInProgress;
949     }
950     else
951     {
952         *count = (handle->transferSize[handle->queueDriver] -
953                   (uint32_t)handle->nbytes *
954                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
955     }
956 
957     return status;
958 }
959 
960 /*!
961  * brief Gets byte count received by SAI.
962  *
963  * param base SAI base pointer
964  * param handle SAI eDMA handle pointer.
965  * param count Bytes count received by SAI.
966  * retval kStatus_Success Succeed get the transfer count.
967  * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress.
968  */
SAI_TransferGetReceiveCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)969 status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
970 {
971     assert(handle != NULL);
972 
973     status_t status = kStatus_Success;
974 
975     if (handle->state != (uint32_t)kSAI_Busy)
976     {
977         status = kStatus_NoTransferInProgress;
978     }
979     else
980     {
981         *count = (handle->transferSize[handle->queueDriver] -
982                   (uint32_t)handle->nbytes *
983                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
984     }
985 
986     return status;
987 }
988 
989 /*!
990  * @rief Gets valid transfer slot.
991  *
992  * This function can be used to query the valid transfer request slot that the application can submit.
993  * It should be called in the critical section, that means the application could call it in the corresponding callback
994  * function or disable IRQ before calling it in the application, otherwise, the returned value may not correct.
995  *
996  * param base SAI base pointer
997  * param handle SAI eDMA handle pointer.
998  * retval valid slot count that application submit.
999  */
SAI_TransferGetValidTransferSlotsEDMA(I2S_Type * base,sai_edma_handle_t * handle)1000 uint32_t SAI_TransferGetValidTransferSlotsEDMA(I2S_Type *base, sai_edma_handle_t *handle)
1001 {
1002     uint32_t validSlot = 0U;
1003 
1004     for (uint32_t i = 0U; i < (uint32_t)SAI_XFER_QUEUE_SIZE; i++)
1005     {
1006         if (handle->saiQueue[i].data == NULL)
1007         {
1008             validSlot++;
1009         }
1010     }
1011 
1012     return validSlot;
1013 }
1014