1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2016-2017 NXP
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * o Redistributions of source code must retain the above copyright notice, this list
9  *   of conditions and the following disclaimer.
10  *
11  * o Redistributions in binary form must reproduce the above copyright notice, this
12  *   list of conditions and the following disclaimer in the documentation and/or
13  *   other materials provided with the distribution.
14  *
15  * o Neither the name of the copyright holder nor the names of its
16  *   contributors may be used to endorse or promote products derived from this
17  *   software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fsl_sai_edma.h"
32 
33 /*******************************************************************************
34  * Definitations
35  ******************************************************************************/
36 /* Used for 32byte aligned */
37 #define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)address + 32) & ~0x1FU)
38 
39 /*<! Structure definition for uart_edma_private_handle_t. The structure is private. */
40 typedef struct _sai_edma_private_handle
41 {
42     I2S_Type *base;
43     sai_edma_handle_t *handle;
44 } sai_edma_private_handle_t;
45 
46 enum _sai_edma_transfer_state
47 {
48     kSAI_Busy = 0x0U, /*!< SAI is busy */
49     kSAI_Idle,        /*!< Transfer is done. */
50 };
51 
52 /*<! Private handle only used for internally. */
53 static sai_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_I2S_COUNT][2];
54 
55 /*******************************************************************************
56  * Prototypes
57  ******************************************************************************/
58 /*!
59  * @brief Get the instance number for SAI.
60  *
61  * @param base SAI base pointer.
62  */
63 extern uint32_t SAI_GetInstance(I2S_Type *base);
64 
65 /*!
66  * @brief SAI EDMA callback for send.
67  *
68  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
69  * @param userData Parameter for user callback.
70  * @param done If the DMA transfer finished.
71  * @param tcds The TCD index.
72  */
73 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
74 
75 /*!
76  * @brief SAI EDMA callback for receive.
77  *
78  * @param handle pointer to sai_edma_handle_t structure which stores the transfer state.
79  * @param userData Parameter for user callback.
80  * @param done If the DMA transfer finished.
81  * @param tcds The TCD index.
82  */
83 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds);
84 
85 /*******************************************************************************
86 * Code
87 ******************************************************************************/
SAI_TxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)88 static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
89 {
90     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
91     sai_edma_handle_t *saiHandle = privHandle->handle;
92 
93     /* If finished a blcok, call the callback function */
94     memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
95     saiHandle->queueDriver = (saiHandle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
96     if (saiHandle->callback)
97     {
98         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_TxIdle, saiHandle->userData);
99     }
100 
101     /* If all data finished, just stop the transfer */
102     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
103     {
104         SAI_TransferAbortSendEDMA(privHandle->base, saiHandle);
105     }
106 }
107 
SAI_RxEDMACallback(edma_handle_t * handle,void * userData,bool done,uint32_t tcds)108 static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds)
109 {
110     sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData;
111     sai_edma_handle_t *saiHandle = privHandle->handle;
112 
113     /* If finished a blcok, call the callback function */
114     memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
115     saiHandle->queueDriver = (saiHandle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
116     if (saiHandle->callback)
117     {
118         (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_RxIdle, saiHandle->userData);
119     }
120 
121     /* If all data finished, just stop the transfer */
122     if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
123     {
124         SAI_TransferAbortReceiveEDMA(privHandle->base, saiHandle);
125     }
126 }
127 
SAI_TransferTxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)128 void SAI_TransferTxCreateHandleEDMA(
129     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle)
130 {
131     assert(handle && dmaHandle);
132 
133     uint32_t instance = SAI_GetInstance(base);
134 
135     /* Zero the handle */
136     memset(handle, 0, sizeof(*handle));
137 
138     /* Set sai base to handle */
139     handle->dmaHandle = dmaHandle;
140     handle->callback = callback;
141     handle->userData = userData;
142 
143     /* Set SAI state to idle */
144     handle->state = kSAI_Idle;
145 
146     s_edmaPrivateHandle[instance][0].base = base;
147     s_edmaPrivateHandle[instance][0].handle = handle;
148 
149     /* Need to use scatter gather */
150     EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
151 
152     /* Install callback for Tx dma channel */
153     EDMA_SetCallback(dmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
154 }
155 
SAI_TransferRxCreateHandleEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_edma_callback_t callback,void * userData,edma_handle_t * dmaHandle)156 void SAI_TransferRxCreateHandleEDMA(
157     I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle)
158 {
159     assert(handle && dmaHandle);
160 
161     uint32_t instance = SAI_GetInstance(base);
162 
163     /* Zero the handle */
164     memset(handle, 0, sizeof(*handle));
165 
166     /* Set sai base to handle */
167     handle->dmaHandle = dmaHandle;
168     handle->callback = callback;
169     handle->userData = userData;
170 
171     /* Set SAI state to idle */
172     handle->state = kSAI_Idle;
173 
174     s_edmaPrivateHandle[instance][1].base = base;
175     s_edmaPrivateHandle[instance][1].handle = handle;
176 
177     /* Need to use scatter gather */
178     EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
179 
180     /* Install callback for Tx dma channel */
181     EDMA_SetCallback(dmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
182 }
183 
SAI_TransferTxSetFormatEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)184 void SAI_TransferTxSetFormatEDMA(I2S_Type *base,
185                                  sai_edma_handle_t *handle,
186                                  sai_transfer_format_t *format,
187                                  uint32_t mclkSourceClockHz,
188                                  uint32_t bclkSourceClockHz)
189 {
190     assert(handle && format);
191 
192     /* Configure the audio format to SAI registers */
193     SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
194 
195     /* Get the tranfer size from format, this should be used in EDMA configuration */
196     if (format->bitWidth == 24U)
197     {
198         handle->bytesPerFrame = 4U;
199     }
200     else
201     {
202         handle->bytesPerFrame = format->bitWidth / 8U;
203     }
204 
205     /* Update the data channel SAI used */
206     handle->channel = format->channel;
207 #if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
208     handle->count = FSL_FEATURE_SAI_FIFO_COUNT - format->watermark;
209 #else
210     handle->count = 1U;
211 #endif /* FSL_FEATURE_SAI_FIFO_COUNT */
212 }
213 
SAI_TransferRxSetFormatEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_format_t * format,uint32_t mclkSourceClockHz,uint32_t bclkSourceClockHz)214 void SAI_TransferRxSetFormatEDMA(I2S_Type *base,
215                                  sai_edma_handle_t *handle,
216                                  sai_transfer_format_t *format,
217                                  uint32_t mclkSourceClockHz,
218                                  uint32_t bclkSourceClockHz)
219 {
220     assert(handle && format);
221 
222     /* Configure the audio format to SAI registers */
223     SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
224 
225     /* Get the tranfer size from format, this should be used in EDMA configuration */
226     if (format->bitWidth == 24U)
227     {
228         handle->bytesPerFrame = 4U;
229     }
230     else
231     {
232         handle->bytesPerFrame = format->bitWidth / 8U;
233     }
234 
235     /* Update the data channel SAI used */
236     handle->channel = format->channel;
237 
238 #if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
239     handle->count = format->watermark;
240 #else
241     handle->count = 1U;
242 #endif /* FSL_FEATURE_SAI_FIFO_COUNT */
243 }
244 
SAI_TransferSendEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)245 status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
246 {
247     assert(handle && xfer);
248 
249     edma_transfer_config_t config = {0};
250     uint32_t destAddr = SAI_TxGetDataRegisterAddress(base, handle->channel);
251 
252     /* Check if input parameter invalid */
253     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
254     {
255         return kStatus_InvalidArgument;
256     }
257 
258     if (handle->saiQueue[handle->queueUser].data)
259     {
260         return kStatus_SAI_QueueFull;
261     }
262 
263     /* Change the state of handle */
264     handle->state = kSAI_Busy;
265 
266     /* Update the queue state */
267     handle->transferSize[handle->queueUser] = xfer->dataSize;
268     handle->saiQueue[handle->queueUser].data = xfer->data;
269     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
270     handle->queueUser = (handle->queueUser + 1) % SAI_XFER_QUEUE_SIZE;
271 
272     /* Prepare edma configure */
273     EDMA_PrepareTransfer(&config, xfer->data, handle->bytesPerFrame, (void *)destAddr, handle->bytesPerFrame,
274                          handle->count * handle->bytesPerFrame, xfer->dataSize, kEDMA_MemoryToPeripheral);
275 
276     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
277     handle->nbytes = handle->count * handle->bytesPerFrame;
278 
279     EDMA_SubmitTransfer(handle->dmaHandle, &config);
280 
281     /* Start DMA transfer */
282     EDMA_StartTransfer(handle->dmaHandle);
283 
284     /* Enable DMA enable bit */
285     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
286 
287     /* Enable SAI Tx clock */
288     SAI_TxEnable(base, true);
289 
290     return kStatus_Success;
291 }
292 
SAI_TransferReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle,sai_transfer_t * xfer)293 status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
294 {
295     assert(handle && xfer);
296 
297     edma_transfer_config_t config = {0};
298     uint32_t srcAddr = SAI_RxGetDataRegisterAddress(base, handle->channel);
299 
300     /* Check if input parameter invalid */
301     if ((xfer->data == NULL) || (xfer->dataSize == 0U))
302     {
303         return kStatus_InvalidArgument;
304     }
305 
306     if (handle->saiQueue[handle->queueUser].data)
307     {
308         return kStatus_SAI_QueueFull;
309     }
310 
311     /* Change the state of handle */
312     handle->state = kSAI_Busy;
313 
314     /* Update queue state  */
315     handle->transferSize[handle->queueUser] = xfer->dataSize;
316     handle->saiQueue[handle->queueUser].data = xfer->data;
317     handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize;
318     handle->queueUser = (handle->queueUser + 1) % SAI_XFER_QUEUE_SIZE;
319 
320     /* Prepare edma configure */
321     EDMA_PrepareTransfer(&config, (void *)srcAddr, handle->bytesPerFrame, xfer->data, handle->bytesPerFrame,
322                          handle->count * handle->bytesPerFrame, xfer->dataSize, kEDMA_PeripheralToMemory);
323 
324     /* Store the initially configured eDMA minor byte transfer count into the SAI handle */
325     handle->nbytes = handle->count * handle->bytesPerFrame;
326 
327     EDMA_SubmitTransfer(handle->dmaHandle, &config);
328 
329     /* Start DMA transfer */
330     EDMA_StartTransfer(handle->dmaHandle);
331 
332     /* Enable DMA enable bit */
333     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
334 
335     /* Enable SAI Rx clock */
336     SAI_RxEnable(base, true);
337 
338     return kStatus_Success;
339 }
340 
SAI_TransferAbortSendEDMA(I2S_Type * base,sai_edma_handle_t * handle)341 void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
342 {
343     assert(handle);
344 
345     /* Disable dma */
346     EDMA_AbortTransfer(handle->dmaHandle);
347 
348     /* Disable DMA enable bit */
349     SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
350 
351     /* Disable Tx */
352     SAI_TxEnable(base, false);
353 
354     /* Set the handle state */
355     handle->state = kSAI_Idle;
356 }
357 
SAI_TransferAbortReceiveEDMA(I2S_Type * base,sai_edma_handle_t * handle)358 void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
359 {
360     assert(handle);
361 
362     /* Disable dma */
363     EDMA_AbortTransfer(handle->dmaHandle);
364 
365     /* Disable DMA enable bit */
366     SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
367 
368     /* Disable Rx */
369     SAI_RxEnable(base, false);
370 
371     /* Set the handle state */
372     handle->state = kSAI_Idle;
373 }
374 
SAI_TransferGetSendCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)375 status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
376 {
377     assert(handle);
378 
379     status_t status = kStatus_Success;
380 
381     if (handle->state != kSAI_Busy)
382     {
383         status = kStatus_NoTransferInProgress;
384     }
385     else
386     {
387         *count = (handle->transferSize[handle->queueDriver] -
388                   (uint32_t)handle->nbytes *
389                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
390     }
391 
392     return status;
393 }
394 
SAI_TransferGetReceiveCountEDMA(I2S_Type * base,sai_edma_handle_t * handle,size_t * count)395 status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
396 {
397     assert(handle);
398 
399     status_t status = kStatus_Success;
400 
401     if (handle->state != kSAI_Busy)
402     {
403         status = kStatus_NoTransferInProgress;
404     }
405     else
406     {
407         *count = (handle->transferSize[handle->queueDriver] -
408                   (uint32_t)handle->nbytes *
409                       EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel));
410     }
411 
412     return status;
413 }
414