浏览代码

Merge remote-tracking branch 'asoc/topic/dma' into for-tiwai

Mark Brown 11 年之前
父节点
当前提交
7cfa7b5473

+ 1 - 0
drivers/dma/pl330.c

@@ -2884,6 +2884,7 @@ static int pl330_dma_device_slave_caps(struct dma_chan *dchan,
 	caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
 	caps->cmd_pause = false;
 	caps->cmd_terminate = true;
+	caps->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
 
 	return 0;
 }

+ 28 - 0
include/linux/dmaengine.h

@@ -364,6 +364,32 @@ struct dma_slave_config {
 	unsigned int slave_id;
 };
 
+/**
+ * enum dma_residue_granularity - Granularity of the reported transfer residue
+ * @DMA_RESIDUE_GRANULARITY_DESCRIPTOR: Residue reporting is not support. The
+ *  DMA channel is only able to tell whether a descriptor has been completed or
+ *  not, which means residue reporting is not supported by this channel. The
+ *  residue field of the dma_tx_state field will always be 0.
+ * @DMA_RESIDUE_GRANULARITY_SEGMENT: Residue is updated after each successfully
+ *  completed segment of the transfer (For cyclic transfers this is after each
+ *  period). This is typically implemented by having the hardware generate an
+ *  interrupt after each transferred segment and then the drivers updates the
+ *  outstanding residue by the size of the segment. Another possibility is if
+ *  the hardware supports scatter-gather and the segment descriptor has a field
+ *  which gets set after the segment has been completed. The driver then counts
+ *  the number of segments without the flag set to compute the residue.
+ * @DMA_RESIDUE_GRANULARITY_BURST: Residue is updated after each transferred
+ *  burst. This is typically only supported if the hardware has a progress
+ *  register of some sort (E.g. a register with the current read/write address
+ *  or a register with the amount of bursts/beats/bytes that have been
+ *  transferred or still need to be transferred).
+ */
+enum dma_residue_granularity {
+	DMA_RESIDUE_GRANULARITY_DESCRIPTOR = 0,
+	DMA_RESIDUE_GRANULARITY_SEGMENT = 1,
+	DMA_RESIDUE_GRANULARITY_BURST = 2,
+};
+
 /* struct dma_slave_caps - expose capabilities of a slave channel only
  *
  * @src_addr_widths: bit mask of src addr widths the channel supports
@@ -374,6 +400,7 @@ struct dma_slave_config {
  * 	should be checked by controller as well
  * @cmd_pause: true, if pause and thereby resume is supported
  * @cmd_terminate: true, if terminate cmd is supported
+ * @residue_granularity: granularity of the reported transfer residue
  */
 struct dma_slave_caps {
 	u32 src_addr_widths;
@@ -381,6 +408,7 @@ struct dma_slave_caps {
 	u32 directions;
 	bool cmd_pause;
 	bool cmd_terminate;
+	enum dma_residue_granularity residue_granularity;
 };
 
 static inline const char *dma_chan_name(struct dma_chan *chan)

+ 1 - 2
sound/soc/adi/axi-i2s.c

@@ -236,8 +236,7 @@ static int axi_i2s_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_clk_disable;
 
-	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
-			SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 	if (ret)
 		goto err_clk_disable;
 

+ 1 - 2
sound/soc/adi/axi-spdif.c

@@ -229,8 +229,7 @@ static int axi_spdif_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_clk_disable;
 
-	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
-			SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
+	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
 	if (ret)
 		goto err_clk_disable;
 

+ 0 - 1
sound/soc/samsung/dmaengine.c

@@ -68,7 +68,6 @@ int samsung_asoc_dma_platform_register(struct device *dev)
 {
 	return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
 					  SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
-					  SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
 					  SND_DMAENGINE_PCM_FLAG_COMPAT);
 }
 EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);

+ 42 - 24
sound/soc/soc-generic-dmaengine-pcm.c

@@ -144,6 +144,8 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
 	if (ret == 0) {
 		if (dma_caps.cmd_pause)
 			hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
+		if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
+			hw.info |= SNDRV_PCM_INFO_BATCH;
 	}
 
 	return snd_soc_set_runtime_hwparams(substream, &hw);
@@ -187,6 +189,21 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
 						 dma_data->filter_data);
 }
 
+static bool dmaengine_pcm_can_report_residue(struct dma_chan *chan)
+{
+	struct dma_slave_caps dma_caps;
+	int ret;
+
+	ret = dma_get_slave_caps(chan, &dma_caps);
+	if (ret != 0)
+		return true;
+
+	if (dma_caps.residue_granularity == DMA_RESIDUE_GRANULARITY_DESCRIPTOR)
+		return false;
+
+	return true;
+}
+
 static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
@@ -239,6 +256,16 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
 				max_buffer_size);
 		if (ret)
 			goto err_free;
+
+		/*
+		 * This will only return false if we know for sure that at least
+		 * one channel does not support residue reporting. If the DMA
+		 * driver does not implement the slave_caps API we rely having
+		 * the NO_RESIDUE flag set manually in case residue reporting is
+		 * not supported.
+		 */
+		if (!dmaengine_pcm_can_report_residue(pcm->chan[i]))
+			pcm->flags |= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE;
 	}
 
 	return 0;
@@ -248,6 +275,18 @@ err_free:
 	return ret;
 }
 
+static snd_pcm_uframes_t dmaengine_pcm_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);
+
+	if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
+		return snd_dmaengine_pcm_pointer_no_residue(substream);
+	else
+		return snd_dmaengine_pcm_pointer(substream);
+}
+
 static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.open		= dmaengine_pcm_open,
 	.close		= snd_dmaengine_pcm_close,
@@ -255,7 +294,7 @@ static const struct snd_pcm_ops dmaengine_pcm_ops = {
 	.hw_params	= dmaengine_pcm_hw_params,
 	.hw_free	= snd_pcm_lib_free_pages,
 	.trigger	= snd_dmaengine_pcm_trigger,
-	.pointer	= snd_dmaengine_pcm_pointer,
+	.pointer	= dmaengine_pcm_pointer,
 };
 
 static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
@@ -265,23 +304,6 @@ static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
 	.probe_order	= SND_SOC_COMP_ORDER_LATE,
 };
 
-static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops = {
-	.open		= dmaengine_pcm_open,
-	.close		= snd_dmaengine_pcm_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= dmaengine_pcm_hw_params,
-	.hw_free	= snd_pcm_lib_free_pages,
-	.trigger	= snd_dmaengine_pcm_trigger,
-	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
-};
-
-static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform = {
-	.ops		= &dmaengine_no_residue_pcm_ops,
-	.pcm_new	= dmaengine_pcm_new,
-	.pcm_free	= dmaengine_pcm_free,
-	.probe_order	= SND_SOC_COMP_ORDER_LATE,
-};
-
 static const char * const dmaengine_pcm_dma_channel_names[] = {
 	[SNDRV_PCM_STREAM_PLAYBACK] = "tx",
 	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
@@ -374,12 +396,8 @@ int snd_dmaengine_pcm_register(struct device *dev,
 	if (ret)
 		goto err_free_dma;
 
-	if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
-		ret = snd_soc_add_platform(dev, &pcm->platform,
-				&dmaengine_no_residue_pcm_platform);
-	else
-		ret = snd_soc_add_platform(dev, &pcm->platform,
-				&dmaengine_pcm_platform);
+	ret = snd_soc_add_platform(dev, &pcm->platform,
+		&dmaengine_pcm_platform);
 	if (ret)
 		goto err_free_dma;