summaryrefslogtreecommitdiff
path: root/sound/soc/fsl/fsl_sai_ac97.c
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2016-02-24 13:41:14 -0800
committerStefan Agner <stefan.agner@toradex.com>2016-02-25 15:14:11 -0800
commitf25d83e0b9220c718415b7af8c4dd9e510416c11 (patch)
treef3cb35a96fac03de69ede09579fa66977fc0fe9f /sound/soc/fsl/fsl_sai_ac97.c
parent7826cd2effcb6413c0e98c75c5ba29149c61a6ba (diff)
ASoC: fsl_sai_ac97: do not rely on RX/TX DMA running in sync
Do not rely on RX/TX DMA running synchrounous. This allows to wait until the answer frame has been received or timeout after 10 frames. In the write case, we wait until the write request has been transmitted to the TX FIFO. This changes should make AC97 read/write more robust. Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
Diffstat (limited to 'sound/soc/fsl/fsl_sai_ac97.c')
-rw-r--r--sound/soc/fsl/fsl_sai_ac97.c110
1 files changed, 61 insertions, 49 deletions
diff --git a/sound/soc/fsl/fsl_sai_ac97.c b/sound/soc/fsl/fsl_sai_ac97.c
index 23acac2cf45e..a46e11869a92 100644
--- a/sound/soc/fsl/fsl_sai_ac97.c
+++ b/sound/soc/fsl/fsl_sai_ac97.c
@@ -303,10 +303,10 @@ static int vf610_sai_ac97_read_write(struct snd_ac97 *ac97, bool isread,
struct dma_tx_state rx_state;
struct ac97_tx *tx_aclink;
struct ac97_rx *rx_aclink;
- int rxbufidstart, txbufid, rxbufid, curbufid;
+ int rxbufidstart, txbufidstart, txbufid, rxbufid, curbufid;
unsigned long flags;
- int timeout = 20;
int ret = 0;
+ int rxbufmaxcheck = 10;
/*
* We need to disable interrupts to make sure we insert the message
@@ -320,15 +320,15 @@ static int vf610_sai_ac97_read_write(struct snd_ac97 *ac97, bool isread,
/* Calculate next DMA buffer sent out to the AC97 codec */
rxbufidstart = (SAI_AC97_RBUF_SIZE_TOT - rx_state.residue) / SAI_AC97_DMABUF_SIZE;
- txbufid = (SAI_AC97_RBUF_SIZE_TOT - tx_state.residue) / SAI_AC97_DMABUF_SIZE;
- txbufid += 2;
+ rxbufidstart %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+ txbufidstart = (SAI_AC97_RBUF_SIZE_TOT - tx_state.residue) / SAI_AC97_DMABUF_SIZE;
+ txbufidstart %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+
+ /* Safety margin, use next buffer in case current buffer is DMA'ed now */
+ txbufid = txbufidstart + 1;
txbufid %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
tx_aclink = (struct ac97_tx *)(info->tx_buf.area + (txbufid * SAI_AC97_DMABUF_SIZE));
- /* We do assume that the RX and TX DMA are running synchrounous */
- rxbufid = (txbufid + 1);
- rxbufid %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
-
/* Put our request into the next AC97 frame */
tx_aclink->valid = 1;
tx_aclink->slot_valid |= (1 << 11);
@@ -343,18 +343,38 @@ static int vf610_sai_ac97_read_write(struct snd_ac97 *ac97, bool isread,
local_irq_restore(flags);
+ /* Wait at least until TX frame is in FIFO... */
+ if (!isread) {
+ do {
+ usleep_range(50, 200);
+ tx_status = dmaengine_tx_status(info->dma_tx_chan, info->dma_tx_cookie,
+ &tx_state);
+ curbufid = ((SAI_AC97_RBUF_SIZE_TOT - tx_state.residue) / SAI_AC97_DMABUF_SIZE);
+
+ if (likely(txbufid > txbufidstart) &&
+ (curbufid > txbufid || curbufid < txbufidstart))
+ break;
+
+ /* Wrap-around case */
+ if (unlikely(txbufid < txbufidstart) &&
+ (curbufid > txbufid && curbufid < txbufidstart))
+ break;
+ } while (true);
+ goto clear_command;
+ }
+
/*
- * Wait until the TX frame has been sent and the RX frame has been
- * transmitted back and transferred to the RX buffer by the DMA.
- * Also take into account that the bufid might wrap around.
- *
- * Note also that the transmit DMA is always a bit ahead due to the
- * transmit FIFO of 32 bytes (~2 AC97 frames).
+ * Look into every frame starting at the RX frame which was
+ * last copied by DMA at command insert time. Typically, the
+ * answer is in RX start frame +4. Factors which sum up to
+ * this delay are:
+ * - TX send delay (+1 safety margin, +2 TX FIFO)
+ * - AC97 codec sends back the answer in the next frame (+1)
*
* TX ring buffer
* |------|------|------|------|------|------|------|------|
- * | | |txbuf | |txbuf | | | |
- * | | |start | | | | | |
+ * | | | |txbuf |txbuf | | | |
+ * | | | |start | | | | |
* |------|------|------|------|------|------|------|------|
*
* RX ring buffer
@@ -364,44 +384,36 @@ static int vf610_sai_ac97_read_write(struct snd_ac97 *ac97, bool isread,
* |------|------|------|------|------|------|------|------|
*
*/
+ rxbufid = rxbufidstart;
+ curbufid = rxbufid;
do {
- rx_status = dmaengine_tx_status(info->dma_rx_chan, info->dma_rx_cookie,
- &rx_state);
- curbufid = ((SAI_AC97_RBUF_SIZE_TOT - rx_state.residue) / SAI_AC97_DMABUF_SIZE);
-
- if (likely(rxbufid > rxbufidstart) &&
- (curbufid > rxbufid || curbufid < rxbufidstart))
- break;
+ while (rxbufid == curbufid)
+ {
+ /* Wait for frames being transmitted/received... */
+ usleep_range(50, 200);
+ rx_status = dmaengine_tx_status(info->dma_rx_chan, info->dma_rx_cookie,
+ &rx_state);
+ curbufid = ((SAI_AC97_RBUF_SIZE_TOT - rx_state.residue) / SAI_AC97_DMABUF_SIZE);
+ }
- /* Wrap-around case */
- if (unlikely(rxbufid < rxbufidstart) &&
- (curbufid > rxbufid && curbufid < rxbufidstart))
+ /* Ok, check frames... */
+ rx_aclink = (struct ac97_rx *)(info->rx_buf.area + rxbufid * SAI_AC97_DMABUF_SIZE);
+ if (rx_aclink->slot_valid & (1 << 11 | 1 << 10) &&
+ rx_aclink->regindex == reg)
+ {
+ *val = rx_aclink->cmddata;
break;
+ }
- udelay(50);
- } while (--timeout);
+ rxbufmaxcheck--;
+ rxbufid++;
+ rxbufid %= SAI_AC97_RBUF_COUNT * SAI_AC97_RBUF_FRAMES;
+ } while (rxbufmaxcheck);
- if (!timeout) {
- pr_err("timeout, current buffers: tx: %d, rx: %d, rx cur: %d\n",
- txbufid, rxbufid, curbufid);
+ if (!rxbufmaxcheck) {
+ pr_err("%s: rx timeout, checked buffer %d to %d, current %d\n",
+ __func__, rxbufidstart, rxbufid, curbufid);
ret = -ETIMEDOUT;
- goto clear_command;
- }
-
- /* At this point, we should have an answer in the RX buffer... */
- rx_aclink = (struct ac97_rx *)(info->rx_buf.area + rxbufid * SAI_AC97_DMABUF_SIZE);
-
- if (isread) {
- if (rx_aclink->slot_valid & (1 << 11 | 1 << 10) &&
- rx_aclink->regindex == reg)
- *val = rx_aclink->cmddata;
- else {
- pr_warn("no valid answer for read command received\n");
- pr_warn("current rx buffers, start: %d, data: %d, cur: %d\n",
- rxbufidstart, rxbufid, curbufid);
- ret = -EBADMSG;
- goto clear_command;
- }
}
clear_command:
@@ -411,7 +423,7 @@ clear_command:
tx_aclink->cmdindex = 0;
tx_aclink->cmddata = 0;
- return 0;
+ return ret;
}
static unsigned short vf610_sai_ac97_read(struct snd_ac97 *ac97,