summaryrefslogtreecommitdiffstats
path: root/sound/soc/fsl/fsl_dma.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-20 17:19:27 +0100
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-08-20 17:19:27 +0100
commitbf557a50f59fc62dfd89fa5bf08c6f5d96fb2f45 (patch)
tree81a2cd45044970ccceec9ae4e1547e8f5dee850d /sound/soc/fsl/fsl_dma.c
parent26b01ccdc8ded270a1a65721b99acacf0c44e088 (diff)
parent3fabe089ad8b8f238bc9de3e7586ae8d2a81f57c (diff)
Merge branch 'for-2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6 into for-2.6.37
Diffstat (limited to 'sound/soc/fsl/fsl_dma.c')
-rw-r--r--sound/soc/fsl/fsl_dma.c67
1 files changed, 51 insertions, 16 deletions
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 5a6f56d6375..f039e8db076 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -60,6 +60,7 @@ struct dma_object {
struct snd_soc_platform_driver dai;
dma_addr_t ssi_stx_phys;
dma_addr_t ssi_srx_phys;
+ unsigned int ssi_fifo_depth;
struct ccsr_dma_channel __iomem *channel;
unsigned int irq;
bool assigned;
@@ -99,6 +100,7 @@ struct fsl_dma_private {
unsigned int irq;
struct snd_pcm_substream *substream;
dma_addr_t ssi_sxx_phys;
+ unsigned int ssi_fifo_depth;
dma_addr_t ld_buf_phys;
unsigned int current_link;
dma_addr_t dma_buf_phys;
@@ -439,6 +441,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
else
dma_private->ssi_sxx_phys = dma->ssi_srx_phys;
+ dma_private->ssi_fifo_depth = dma->ssi_fifo_depth;
dma_private->dma_channel = dma->channel;
dma_private->irq = dma->irq;
dma_private->substream = substream;
@@ -552,11 +555,11 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
struct device *dev = rtd->platform->dev;
/* Number of bits per sample */
- unsigned int sample_size =
+ unsigned int sample_bits =
snd_pcm_format_physical_width(params_format(hw_params));
/* Number of bytes per frame */
- unsigned int frame_size = 2 * (sample_size / 8);
+ unsigned int sample_bytes = sample_bits / 8;
/* Bus address of SSI STX register */
dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys;
@@ -596,7 +599,7 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* that offset here. While we're at it, also tell the DMA controller
* how much data to transfer per sample.
*/
- switch (sample_size) {
+ switch (sample_bits) {
case 8:
mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
ssi_sxx_phys += 3;
@@ -610,22 +613,42 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
break;
default:
/* We should never get here */
- dev_err(dev, "unsupported sample size %u\n", sample_size);
+ dev_err(dev, "unsupported sample size %u\n", sample_bits);
return -EINVAL;
}
/*
- * BWC should always be a multiple of the frame size. BWC determines
- * how many bytes are sent/received before the DMA controller checks the
- * SSI to see if it needs to stop. For playback, the transmit FIFO can
- * hold three frames, so we want to send two frames at a time. For
- * capture, the receive FIFO is triggered when it contains one frame, so
- * we want to receive one frame at a time.
+ * BWC determines how many bytes are sent/received before the DMA
+ * controller checks the SSI to see if it needs to stop. BWC should
+ * always be a multiple of the frame size, so that we always transmit
+ * whole frames. Each frame occupies two slots in the FIFO. The
+ * parameter for CCSR_DMA_MR_BWC() is rounded down the next power of two
+ * (MR[BWC] can only represent even powers of two).
+ *
+ * To simplify the process, we set BWC to the largest value that is
+ * less than or equal to the FIFO watermark. For playback, this ensures
+ * that we transfer the maximum amount without overrunning the FIFO.
+ * For capture, this ensures that we transfer the maximum amount without
+ * underrunning the FIFO.
+ *
+ * f = SSI FIFO depth
+ * w = SSI watermark value (which equals f - 2)
+ * b = DMA bandwidth count (in bytes)
+ * s = sample size (in bytes, which equals frame_size * 2)
+ *
+ * For playback, we never transmit more than the transmit FIFO
+ * watermark, otherwise we might write more data than the FIFO can hold.
+ * The watermark is equal to the FIFO depth minus two.
+ *
+ * For capture, two equations must hold:
+ * w > f - (b / s)
+ * w >= b / s
+ *
+ * So, b > 2 * s, but b must also be <= s * w. To simplify, we set
+ * b = s * w, which is equal to
+ * (dma_private->ssi_fifo_depth - 2) * sample_bytes.
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- mr |= CCSR_DMA_MR_BWC(2 * frame_size);
- else
- mr |= CCSR_DMA_MR_BWC(frame_size);
+ mr |= CCSR_DMA_MR_BWC((dma_private->ssi_fifo_depth - 2) * sample_bytes);
out_be32(&dma_channel->mr, mr);
@@ -879,6 +902,7 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev,
struct device_node *np = of_dev->dev.of_node;
struct device_node *ssi_np;
struct resource res;
+ const uint32_t *iprop;
int ret;
/* Find the SSI node that points to us. */
@@ -889,15 +913,17 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev,
}
ret = of_address_to_resource(ssi_np, 0, &res);
- of_node_put(ssi_np);
if (ret) {
- dev_err(&of_dev->dev, "could not determine device resources\n");
+ dev_err(&of_dev->dev, "could not determine resources for %s\n",
+ ssi_np->full_name);
+ of_node_put(ssi_np);
return ret;
}
dma = kzalloc(sizeof(*dma) + strlen(np->full_name), GFP_KERNEL);
if (!dma) {
dev_err(&of_dev->dev, "could not allocate dma object\n");
+ of_node_put(ssi_np);
return -ENOMEM;
}
@@ -910,6 +936,15 @@ static int __devinit fsl_soc_dma_probe(struct of_device *of_dev,
dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0);
dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0);
+ iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
+ if (iprop)
+ dma->ssi_fifo_depth = *iprop;
+ else
+ /* Older 8610 DTs didn't have the fifo-depth property */
+ dma->ssi_fifo_depth = 8;
+
+ of_node_put(ssi_np);
+
ret = snd_soc_register_platform(&of_dev->dev, &dma->dai);
if (ret) {
dev_err(&of_dev->dev, "could not register platform\n");