diff options
author | Stefan Agner <stefan.agner@toradex.com> | 2015-08-07 10:56:54 +0200 |
---|---|---|
committer | Stefan Agner <stefan.agner@toradex.com> | 2015-08-07 10:56:54 +0200 |
commit | d79071b433ab0e3b426f2f480eed506856aaf0d0 (patch) | |
tree | 414d40270b1259ee396860e985e8a53683902d15 /sound | |
parent | 263197237ac830819c9e0cce0721e46e797c9e32 (diff) | |
parent | 32f1c7a543e72d2ba5f98ca6d817073eb450c548 (diff) |
Merge branch 'vf610-ac97-sai-v4.1' into toradex_vf_4.1-next
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/fsl/fsl_sai_ac97.c | 121 |
1 files changed, 72 insertions, 49 deletions
diff --git a/sound/soc/fsl/fsl_sai_ac97.c b/sound/soc/fsl/fsl_sai_ac97.c index 68bd61df2864..6eb235f5551b 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, @@ -544,6 +556,16 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) } +static bool fsl_sai_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case FSL_SAI_RDR: + return true; + default: + return false; + } +} + static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -574,6 +596,7 @@ static struct regmap_config fsl_sai_regmap_config = { .val_bits = 32, .max_register = FSL_SAI_RMR, + .precious_reg = fsl_sai_precious_reg, .readable_reg = fsl_sai_readable_reg, .volatile_reg = fsl_sai_volatile_reg, .writeable_reg = fsl_sai_writeable_reg, |