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