Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kylemanna/c7dd795167f8a8c315c31894394fdc8e to your computer and use it in GitHub Desktop.
Save kylemanna/c7dd795167f8a8c315c31894394fdc8e to your computer and use it in GitHub Desktop.
From bce26662da4b31e3f333142ebcbc24bef629c45e Mon Sep 17 00:00:00 2001
From: Kyle Manna <kyle@kylemanna.com>
Date: Fri, 20 Jan 2017 13:27:41 -0800
Subject: [PATCH] sai: Delay SAI DMA TX disable until FIFO is empty
This resolves a bug that otherwise would cause the last frame to be
written to the FIFO but the FIFO and transmitter are disable before the
frame is sent. The frame will be sent on the next enable of the
transmitter.
Instead, disable the transmitter after the FIFO has consumed the last
frame.
---
devices/MKL27Z4/drivers/fsl_sai.c | 31 +++++++++++++++++++++++++++++++
devices/MKL27Z4/drivers/fsl_sai.h | 10 ++++++++++
devices/MKL27Z4/drivers/fsl_sai_dma.c | 4 ++--
3 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/devices/MKL27Z4/drivers/fsl_sai.c b/devices/MKL27Z4/drivers/fsl_sai.c
index 0d19d09..ee58cc7 100644
--- a/devices/MKL27Z4/drivers/fsl_sai.c
+++ b/devices/MKL27Z4/drivers/fsl_sai.c
@@ -690,6 +690,37 @@ void SAI_TransferTxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transf
EnableIRQ(s_saiTxIRQ[SAI_GetInstance(base)]);
}
+static void SAI_TxDisableAfterFIFOEmptyCallback(I2S_Type *base, sai_handle_t *handle)
+{
+ /* Handle isn't actually a handle in this case */
+ void *userData = (void*)handle;
+ assert(userData);
+
+ SAI_TxDisableInterrupts(base, kSAI_FIFOWarningInterruptEnable);
+ SAI_TxEnable(base, false);
+}
+
+void SAI_TxDisableAfterFIFOEmpty(I2S_Type *base, void *userData)
+{
+ /* When called from SAI_TransferAbortSendDMA a sai_dma_handle_t* is passed
+ * in as userData and is only used to satisfy the IRQ test for the handle
+ * being set before calling SAI_TxDisableAfterFIFOEmptyCallback().
+ *
+ * This is a hack.
+ */
+ assert(userData);
+
+ /* Handle isn't actually a handle in this case */
+ s_saiHandle[SAI_GetInstance(base)][0] = userData;
+
+ /* Set the isr pointer */
+ s_saiTxIsr = SAI_TxDisableAfterFIFOEmptyCallback;
+
+ /* Enable Tx irq */
+ SAI_TxEnableInterrupts(base, kSAI_FIFOWarningInterruptEnable);
+ EnableIRQ(s_saiTxIRQ[SAI_GetInstance(base)]);
+}
+
void SAI_TransferRxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData)
{
assert(handle);
diff --git a/devices/MKL27Z4/drivers/fsl_sai.h b/devices/MKL27Z4/drivers/fsl_sai.h
index be7563b..ac5a295 100644
--- a/devices/MKL27Z4/drivers/fsl_sai.h
+++ b/devices/MKL27Z4/drivers/fsl_sai.h
@@ -838,6 +838,16 @@ void SAI_TransferTxHandleIRQ(I2S_Type *base, sai_handle_t *handle);
*/
void SAI_TransferRxHandleIRQ(I2S_Type *base, sai_handle_t *handle);
+/*!
+ * @brief Called when a DMA TX needs to disable after the last byte is sent
+ * otherwise DMA driver will disable the driver when the last byte is
+ * written to the FIFO and disable the transmitter before it is sent.
+ *
+ * @param base SAI base pointer.
+ * @param handle Pointer to the sai_handle_t structure.
+ */
+void SAI_TxDisableAfterFIFOEmpty(I2S_Type *base, void *userData);
+
/*! @} */
#if defined(__cplusplus)
diff --git a/devices/MKL27Z4/drivers/fsl_sai_dma.c b/devices/MKL27Z4/drivers/fsl_sai_dma.c
index b8afec2..80fc2ad 100644
--- a/devices/MKL27Z4/drivers/fsl_sai_dma.c
+++ b/devices/MKL27Z4/drivers/fsl_sai_dma.c
@@ -361,8 +361,8 @@ void SAI_TransferAbortSendDMA(I2S_Type *base, sai_dma_handle_t *handle)
SAI_TxEnableDMA(base, kSAI_FIFOWarningDMAEnable, false);
#endif
- /* Disable Tx */
- SAI_TxEnable(base, false);
+ /* Disable Tx after the FIFO is empty */
+ SAI_TxDisableAfterFIFOEmpty(base, handle);
/* Set the handle state */
handle->state = kSAI_Idle;
--
2.11.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment