summary refs log tree commit diff
path: root/sound/soc/fsl
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
parent26b01ccdc8ded270a1a65721b99acacf0c44e088 (diff)
parent3fabe089ad8b8f238bc9de3e7586ae8d2a81f57c (diff)
downloadlinux-bf557a50f59fc62dfd89fa5bf08c6f5d96fb2f45.tar.gz
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')
-rw-r--r--sound/soc/fsl/fsl_dma.c67
-rw-r--r--sound/soc/fsl/fsl_ssi.c25
2 files changed, 73 insertions, 19 deletions
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 5a6f56d63756..f039e8db0765 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");
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 7939c337ed9d..d1c855ade8fb 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -93,6 +93,7 @@ struct fsl_ssi_private {
 	unsigned int playback;
 	unsigned int capture;
 	int asynchronous;
+	unsigned int fifo_depth;
 	struct snd_soc_dai_driver cpu_dai_drv;
 	struct device_attribute dev_attr;
 	struct platform_device *pdev;
@@ -337,11 +338,20 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
 
 		/*
 		 * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
-		 * don't use FIFO 1.  Since the SSI only supports stereo, the
-		 * watermark should never be an odd number.
+		 * don't use FIFO 1.  We program the transmit water to signal a
+		 * DMA transfer if there are only two (or fewer) elements left
+		 * in the FIFO.  Two elements equals one frame (left channel,
+		 * right channel).  This value, however, depends on the depth of
+		 * the transmit buffer.
+		 *
+		 * We program the receive FIFO to notify us if at least two
+		 * elements (one frame) have been written to the FIFO.  We could
+		 * make this value larger (and maybe we should), but this way
+		 * data will be written to memory as soon as it's available.
 		 */
 		out_be32(&ssi->sfcsr,
-			 CCSR_SSI_SFCSR_TFWM0(6) | CCSR_SSI_SFCSR_RFWM0(2));
+			CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
+			CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2));
 
 		/*
 		 * We keep the SSI disabled because if we enable it, then the
@@ -622,6 +632,7 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev,
 	struct device_attribute *dev_attr = NULL;
 	struct device_node *np = of_dev->dev.of_node;
 	const char *p, *sprop;
+	const uint32_t *iprop;
 	struct resource res;
 	char name[64];
 
@@ -678,6 +689,14 @@ static int __devinit fsl_ssi_probe(struct of_device *of_dev,
 	else
 		ssi_private->cpu_dai_drv.symmetric_rates = 1;
 
+	/* Determine the FIFO depth. */
+	iprop = of_get_property(np, "fsl,fifo-depth", NULL);
+	if (iprop)
+		ssi_private->fifo_depth = *iprop;
+	else
+                /* Older 8610 DTs didn't have the fifo-depth property */
+		ssi_private->fifo_depth = 8;
+
 	/* Initialize the the device_attribute structure */
 	dev_attr = &ssi_private->dev_attr;
 	dev_attr->attr.name = "statistics";