aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King - ARM Linux <linux@arm.linux.org.uk>2014-06-25 08:00:33 -0400
committerVinod Koul <vinod.koul@intel.com>2014-07-01 02:53:42 -0400
commitd1a792f3b4072bfac4150bb62aa34917b77fdb6d (patch)
treeb76492b72cb6a162a391a706a7e73f2e8f43c57f
parent13bbfb5c4eb4fc85bf977245f9b3624df0187184 (diff)
Update imx-sdma cyclic handling to report residue
I received a report this morning from one of the Novena developers that the behaviour of the iMX6 ASoC codec driver (using imx-pcm-dma.c) was sub-optimal under high system load. While there are issues relating to system load remaining, upon reviewing the ASoC imx-pcm-dma.c driver, it was noticed that it not using the residue support, because SDMA doesn't support it. This has the effect that SDMA has to make multiple calls into the ASoC and ALSA code, one for each period. Since ALSA's snd_pcm_elapsed() does not need to be called multiple times and it is entirely sufficient to call it once to update ALSA with the current buffer position via the pointer method, we can do better here. We can also avoid stopping the DMA entirely, just like real cyclic DMA implementations behave. While this means that we replay some old samples, this is a nicer behaviour than having audio stop and restart. The changes to achieve this are relatively minor - imx-sdma.c can track where the DMA is to the nearest descriptor boundary - it does this already when deciding how many callbacks to issue. In doing this, buf_tail always points at the descriptor which will complete next. The residue is defined by the bytes remaining to the end of the buffer, when the buffer is viewed as a single block of memory [start...end]. So, when we start out, there's a full buffer worth of residue, and this counts down as we approach the end of the buffer, eventually becoming zero at the end, before returning to the full buffer worth when we wrap back to the start. Moving the walking of the descriptors into the interrupt handler means that we can update the BD_DONE flag at interrupt time, thus avoiding a delayed tasklet stopping the cyclic DMA. This means that the residue can be calculated from (total descriptors - buf_tail) * descriptor size. This is what the change below does. We update imx-pcm-dma.c to remove the NO_RESIDUE flag since we now provide the residue. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Tested-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
-rw-r--r--drivers/dma/imx-sdma.c22
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c1
2 files changed, 18 insertions, 5 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 128714622bf5..14867e3ac8ff 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -255,6 +255,7 @@ struct sdma_channel {
255 enum dma_slave_buswidth word_size; 255 enum dma_slave_buswidth word_size;
256 unsigned int buf_tail; 256 unsigned int buf_tail;
257 unsigned int num_bd; 257 unsigned int num_bd;
258 unsigned int period_len;
258 struct sdma_buffer_descriptor *bd; 259 struct sdma_buffer_descriptor *bd;
259 dma_addr_t bd_phys; 260 dma_addr_t bd_phys;
260 unsigned int pc_from_device, pc_to_device; 261 unsigned int pc_from_device, pc_to_device;
@@ -593,6 +594,12 @@ static void sdma_event_disable(struct sdma_channel *sdmac, unsigned int event)
593 594
594static void sdma_handle_channel_loop(struct sdma_channel *sdmac) 595static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
595{ 596{
597 if (sdmac->desc.callback)
598 sdmac->desc.callback(sdmac->desc.callback_param);
599}
600
601static void sdma_update_channel_loop(struct sdma_channel *sdmac)
602{
596 struct sdma_buffer_descriptor *bd; 603 struct sdma_buffer_descriptor *bd;
597 604
598 /* 605 /*
@@ -611,9 +618,6 @@ static void sdma_handle_channel_loop(struct sdma_channel *sdmac)
611 bd->mode.status |= BD_DONE; 618 bd->mode.status |= BD_DONE;
612 sdmac->buf_tail++; 619 sdmac->buf_tail++;
613 sdmac->buf_tail %= sdmac->num_bd; 620 sdmac->buf_tail %= sdmac->num_bd;
614
615 if (sdmac->desc.callback)
616 sdmac->desc.callback(sdmac->desc.callback_param);
617 } 621 }
618} 622}
619 623
@@ -669,6 +673,9 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
669 int channel = fls(stat) - 1; 673 int channel = fls(stat) - 1;
670 struct sdma_channel *sdmac = &sdma->channel[channel]; 674 struct sdma_channel *sdmac = &sdma->channel[channel];
671 675
676 if (sdmac->flags & IMX_DMA_SG_LOOP)
677 sdma_update_channel_loop(sdmac);
678
672 tasklet_schedule(&sdmac->tasklet); 679 tasklet_schedule(&sdmac->tasklet);
673 680
674 __clear_bit(channel, &stat); 681 __clear_bit(channel, &stat);
@@ -1129,6 +1136,7 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
1129 sdmac->status = DMA_IN_PROGRESS; 1136 sdmac->status = DMA_IN_PROGRESS;
1130 1137
1131 sdmac->buf_tail = 0; 1138 sdmac->buf_tail = 0;
1139 sdmac->period_len = period_len;
1132 1140
1133 sdmac->flags |= IMX_DMA_SG_LOOP; 1141 sdmac->flags |= IMX_DMA_SG_LOOP;
1134 sdmac->direction = direction; 1142 sdmac->direction = direction;
@@ -1225,9 +1233,15 @@ static enum dma_status sdma_tx_status(struct dma_chan *chan,
1225 struct dma_tx_state *txstate) 1233 struct dma_tx_state *txstate)
1226{ 1234{
1227 struct sdma_channel *sdmac = to_sdma_chan(chan); 1235 struct sdma_channel *sdmac = to_sdma_chan(chan);
1236 u32 residue;
1237
1238 if (sdmac->flags & IMX_DMA_SG_LOOP)
1239 residue = (sdmac->num_bd - sdmac->buf_tail) * sdmac->period_len;
1240 else
1241 residue = sdmac->chn_count - sdmac->chn_real_count;
1228 1242
1229 dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie, 1243 dma_set_tx_state(txstate, chan->completed_cookie, chan->cookie,
1230 sdmac->chn_count - sdmac->chn_real_count); 1244 residue);
1231 1245
1232 return sdmac->status; 1246 return sdmac->status;
1233} 1247}
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index 0849b7b83f0a..0db94f492e97 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -59,7 +59,6 @@ int imx_pcm_dma_init(struct platform_device *pdev)
59{ 59{
60 return devm_snd_dmaengine_pcm_register(&pdev->dev, 60 return devm_snd_dmaengine_pcm_register(&pdev->dev,
61 &imx_dmaengine_pcm_config, 61 &imx_dmaengine_pcm_config,
62 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
63 SND_DMAENGINE_PCM_FLAG_COMPAT); 62 SND_DMAENGINE_PCM_FLAG_COMPAT);
64} 63}
65EXPORT_SYMBOL_GPL(imx_pcm_dma_init); 64EXPORT_SYMBOL_GPL(imx_pcm_dma_init);