summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-11-09 15:01:18 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-11-09 15:01:18 -0600
commit9e5b2df0167e8251e4dbb0d925796063fc28f887 (patch)
treee1b9f4a9483aaea3a758c703b03e7e75af472486
parent813276dddc21b99a8d197a7064c94f92a10a15d1 (diff)
downloadnuttx-9e5b2df0167e8251e4dbb0d925796063fc28f887.tar.gz
nuttx-9e5b2df0167e8251e4dbb0d925796063fc28f887.tar.bz2
nuttx-9e5b2df0167e8251e4dbb0d925796063fc28f887.zip
SAMA5 I2S: Try to chain as many DMAs together as possible
-rw-r--r--nuttx/arch/arm/src/sama5/sam_ssc.c214
1 files changed, 144 insertions, 70 deletions
diff --git a/nuttx/arch/arm/src/sama5/sam_ssc.c b/nuttx/arch/arm/src/sama5/sam_ssc.c
index 686627b60..cdad95aaa 100644
--- a/nuttx/arch/arm/src/sama5/sam_ssc.c
+++ b/nuttx/arch/arm/src/sama5/sam_ssc.c
@@ -275,16 +275,16 @@ struct sam_ssc_s
DMA_HANDLE rxdma; /* SSC RX DMA handle */
WDOG_ID rxdog; /* Watchdog that handles RX DMA timeouts */
sq_queue_t rxpend; /* A queue of pending RX transfers */
+ sq_queue_t rxact; /* A queue of active RX transfers */
sq_queue_t rxdone; /* A queue of completed RX transfers */
- struct sam_buffer_s *rxact; /* The active RX transfer */
struct work_s rxwork; /* Supports worker thread RX operations */
#endif
#ifdef SSC_HAVE_TX
DMA_HANDLE txdma; /* SSC TX DMA handle */
WDOG_ID txdog; /* Watchdog that handles TX DMA timeouts */
sq_queue_t txpend; /* A queue of pending TX transfers */
+ sq_queue_t txact; /* A queue of active TX transfers */
sq_queue_t txdone; /* A queue of completed TX transfers */
- struct sam_buffer_s *txact; /* The active TX transfer */
struct work_s txwork; /* Supports worker thread TX operations */
#endif
@@ -1014,24 +1014,22 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
struct sam_buffer_s *bfcontainer;
uintptr_t paddr;
uintptr_t maddr;
+ uint32_t timeout;
+ bool notimeout;
int ret;
/* If there is already an active transmission in progress, then bail
* returning success.
*/
- if (priv->rxact != NULL)
+ if (!sq_empty(&priv->rxact))
{
return OK;
}
- /* Remove the pending RX transfer at the head of the RX pending queue. */
-
- bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->rxpend);
-
/* If there are no pending transfer, then bail returning success */
- if (!bfcontainer)
+ if (sq_empty(&priv->rxpend))
{
return OK;
}
@@ -1040,16 +1038,47 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
ssc_rxdma_sampleinit(priv);
- /* Physical address of the SSC RHR register and of the buffer location in
- * RAM.
- */
+ /* Loop, adding each pending DMA */
+
+ timeout = 0;
+ notimeout = false;
+
+ do
+ {
+ /* Remove the pending RX transfer at the head of the RX pending queue. */
+
+ bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->rxpend);
+
+ /* Physical address of the SSC RHR register and of the buffer location
+ * in RAM.
+ */
+
+ paddr = ssc_physregaddr(priv, SAM_SSC_RHR_OFFSET);
+ maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
+
+ /* Configure the RX DMA */
+
+ sam_dmarxsetup(priv->rxdma, paddr, maddr, bfcontainer->nbytes);
+
+ /* Increment the DMA timeout */
- paddr = ssc_physregaddr(priv, SAM_SSC_RHR_OFFSET);
- maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
+ if (bfcontainer->timeout > 0)
+ {
+ timeout += timeout;
+ }
+ else
+ {
+ notimeout = true;
+ }
+
+ /* Add the container to the list of active DMAs */
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->rxact);
+ }
+ while (!sq_empty(&priv->rxpend));
- /* Configure the RX DMA */
+ /* Sample DMA registers */
- sam_dmarxsetup(priv->rxdma, paddr, maddr, bfcontainer->nbytes);
ssc_rxdma_sample(priv, DMA_AFTER_SETUP);
/* Invalidate the data cache so that nothing gets flush into the
@@ -1061,7 +1090,6 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
/* Start the DMA, saving the container as the current active transfer */
- priv->rxact = bfcontainer;
sam_dmastart(priv->rxdma, ssc_rxdma_callback, priv);
ssc_rxdma_sample(priv, DMA_AFTER_START);
@@ -1071,10 +1099,10 @@ static int ssc_rxdma_setup(struct sam_ssc_s *priv)
/* Start a watchdog to catch DMA timeouts */
- if (priv->rxact->timeout > 0)
+ if (!notimeout)
{
- ret = wd_start(priv->rxdog, priv->rxact->timeout,
- (wdentry_t)ssc_rxdma_timeout, 1, (uint32_t)priv);
+ ret = wd_start(priv->rxdog, timeout, (wdentry_t)ssc_rxdma_timeout,
+ 1, (uint32_t)priv);
/* Check if we have successfully started the watchdog timer. Note
* that we do nothing in the case of failure to start the timer. We
@@ -1115,14 +1143,14 @@ static void ssc_rx_worker(FAR void *arg)
DEBUGASSERT(priv);
- /* When the transfer was started, the active buffer container was removed
- * from the rxpend queue and saved in as rxact. We get here when the
- * DMA if finished... either successfully, with a DMA error, or with a DMA
+ /* When the transfer was started, the active buffer containers were removed
+ * from the rxpend queue and saved in the rxact queue. We get here when the
+ * DMA is finished... either successfully, with a DMA error, or with a DMA
* timeout.
*
- * In any case, the buffer container in rxact will be moved to the end
- * of the rxdone queue and rxact will be nullified before this worker is
- * started.
+ * In any case, the buffer containers in rxact will be moved to the end
+ * of the rxdone queue and rxact queue will be emptied before this worker
+ * is started.
*
* REVISIT: Normal DMA callback processing should restart the DMA
* immediately to avoid audio artifacts at the boundaries between DMA
@@ -1131,11 +1159,12 @@ static void ssc_rx_worker(FAR void *arg)
* So we have to start the next DMA here.
*/
- i2svdbg("txact=%p rxdone.head=%p\n", priv->txact, priv->txdone.head);
+ i2svdbg("rxact.head=%p rxdone.head=%p\n",
+ priv->rxact.head, priv->rxdone.head);
/* Check if the DMA is IDLE */
- if (priv->rxact == NULL)
+ if (sq_empty(&priv->rxact))
{
#ifdef CONFIG_SAMA5_SSC_DMADEBUG
bfcontainer = (struct sam_buffer_s *)sq_peek(&priv->rxdone);
@@ -1206,22 +1235,31 @@ static void ssc_rx_worker(FAR void *arg)
#ifdef SSC_HAVE_RX
static void ssc_rx_schedule(struct sam_ssc_s *priv, int result)
{
+ struct sam_buffer_s *bfcontainer;
int ret;
- /* Upon entry, the transfer that just complete is the one at head at
- * priv->rxact. It does not reside in any queue.
+ /* Upon entry, the transfer that just complete is the one at tail of the
+ * priv->rxact queue.
*/
- DEBUGASSERT(priv->rxact != NULL);
+ DEBUGASSERT(!sq_empty(&priv->rxact));
- /* Report the result of the transfer */
+ /* Move all entries from the rxact queue to the rxdone queue */
+
+ while (!sq_empty(&priv->rxact))
+ {
+ /* Remove the next buffer container from the rxact list */
- priv->rxact->result = result;
+ bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->rxact);
- /* Add the completed buffer to the tail of the txdone queue */
+ /* Report the result of the transfer */
- sq_addlast((sq_entry_t *)priv->rxact, &priv->rxdone);
- priv->rxact = NULL;
+ bfcontainer->result = result;
+
+ /* Add the completed buffer container to the tail of the rxdone queue */
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->rxdone);
+ }
/* If the worker has completed running, then reschedule the working thread.
* REVISIT: There may be a race condition here.
@@ -1342,24 +1380,22 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
struct sam_buffer_s *bfcontainer;
uintptr_t paddr;
uintptr_t maddr;
+ uint32_t timeout;
+ bool notimeout;
int ret;
/* If there is already an active transmission in progress, then bail
* returning success.
*/
- if (priv->txact)
+ if (!sq_empty(&priv->txact))
{
return OK;
}
- /* Remove the pending TX transfer at the head of the TX pending queue. */
-
- bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->txpend);
-
/* If there are no pending transfer, then bail returning success */
- if (!bfcontainer)
+ if (sq_empty(&priv->txpend))
{
return OK;
}
@@ -1368,16 +1404,48 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
ssc_txdma_sampleinit(priv);
- /* Physical address of the SSC THR register and of the buffer location in
- * RAM.
- */
+ /* Loop, adding each pending DMA */
+
+ timeout = 0;
+ notimeout = false;
+
+ do
+ {
+ /* Remove the pending TX transfer at the head of the TX pending queue. */
+
+ bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->txpend);
+
+ /* Physical address of the SSC THR register and of the buffer location
+ * in
+ * RAM.
+ */
+
+ paddr = ssc_physregaddr(priv, SAM_SSC_THR_OFFSET);
+ maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
+
+ /* Configure the TX DMA */
+
+ sam_dmatxsetup(priv->txdma, paddr, maddr, bfcontainer->nbytes);
+
+ /* Increment the DMA timeout */
- paddr = ssc_physregaddr(priv, SAM_SSC_THR_OFFSET);
- maddr = sam_physramaddr((uintptr_t)bfcontainer->buffer);
+ if (bfcontainer->timeout > 0)
+ {
+ timeout += timeout;
+ }
+ else
+ {
+ notimeout = true;
+ }
- /* Configure the TX DMA */
+ /* Add the container to the list of active DMAs */
- sam_dmatxsetup(priv->txdma, paddr, maddr, bfcontainer->nbytes);
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->txact);
+ }
+ while (!sq_empty(&priv->txpend));
+
+ /* Sample DMA registers */
+
ssc_txdma_sample(priv, DMA_AFTER_SETUP);
/* Flush the data cache so that everything is in the physical memory
@@ -1389,7 +1457,6 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
/* Start the DMA, saving the container as the current active transfer */
- priv->txact = bfcontainer;
sam_dmastart(priv->txdma, ssc_txdma_callback, priv);
ssc_txdma_sample(priv, DMA_AFTER_START);
@@ -1399,10 +1466,10 @@ static int ssc_txdma_setup(struct sam_ssc_s *priv)
/* Start a watchdog to catch DMA timeouts */
- if (priv->txact->timeout > 0)
+ if (!notimeout)
{
- ret = wd_start(priv->txdog, priv->txact->timeout,
- (wdentry_t)ssc_txdma_timeout, 1, (uint32_t)priv);
+ ret = wd_start(priv->txdog, timeout, (wdentry_t)ssc_txdma_timeout,
+ 1, (uint32_t)priv);
/* Check if we have successfully started the watchdog timer. Note
* that we do nothing in the case of failure to start the timer. We
@@ -1443,13 +1510,13 @@ static void ssc_tx_worker(FAR void *arg)
DEBUGASSERT(priv);
- /* When the transfer was started, the active buffer container was removed
- * from the txpend queue and saved in as txact. We get here when the
- * DMA if finished... either successfully, with a DMA error, or with a DMA
+ /* When the transfer was started, the active buffer containers were removed
+ * from the txpend queue and saved in the txact queue. We get here when the
+ * DMA is finished... either successfully, with a DMA error, or with a DMA
* timeout.
*
- * In any case, the buffer container in txact will be moved to the end
- * of the txdone queue and txact will be nullified before this worker is
+ * In any case, the buffer containers in txact will be moved to the end
+ * of the txdone queue and txact will be emptied before this worker is
* started.
*
* REVISIT: Normal DMA callback processing should restart the DMA
@@ -1459,11 +1526,12 @@ static void ssc_tx_worker(FAR void *arg)
* So we have to start the next DMA here.
*/
- i2svdbg("txact=%p txdone.head=%p\n", priv->txact, priv->txdone.head);
+ i2svdbg("txact.head=%p txdone.head=%p\n",
+ priv->txact.head, priv->txdone.head);
/* Check if the DMA is IDLE */
- if (priv->txact == NULL)
+ if (sq_empty(&priv->txact))
{
#ifdef CONFIG_SAMA5_SSC_DMADEBUG
bfcontainer = (struct sam_buffer_s *)sq_peek(&priv->txdone);
@@ -1475,9 +1543,6 @@ static void ssc_tx_worker(FAR void *arg)
}
#endif
- /* Dump the DMA registers only if the DMA is IDLE */
-
-
/* Then start the next DMA. This must be done with interrupts
* disabled.
*/
@@ -1538,22 +1603,31 @@ static void ssc_tx_worker(FAR void *arg)
#ifdef SSC_HAVE_TX
static void ssc_tx_schedule(struct sam_ssc_s *priv, int result)
{
+ struct sam_buffer_s *bfcontainer;
int ret;
- /* Upon entry, the transfer that just complete is the one at head at
- * priv->txact. It does not reside in any queue.
+ /* Upon entry, the transfer that just completed is the one at head at
+ * end of the priv->txact queue.
*/
- DEBUGASSERT(priv->txact != NULL);
+ DEBUGASSERT(!sq_empty(&priv->txact));
- /* Report the result of the transfer */
+ /* Move all entries from the txact queue to the txdone queue */
+
+ while (!sq_empty(&priv->txact))
+ {
+ /* Remove the next buffer container from the txact list */
- priv->txact->result = result;
+ bfcontainer = (struct sam_buffer_s *)sq_remfirst(&priv->txact);
- /* Add the completed buffer to the tail of the txdone queue */
+ /* Report the result of the transfer */
- sq_addlast((sq_entry_t *)priv->txact, &priv->txdone);
- priv->txact = NULL;
+ bfcontainer->result = result;
+
+ /* Add the completed buffer container to the tail of the txdone queue */
+
+ sq_addlast((sq_entry_t *)bfcontainer, &priv->txdone);
+ }
/* If the worker has completed running, then reschedule the working thread.
* REVISIT: There may be a race condition here.