Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1e1406c authored by Lars-Peter Clausen's avatar Lars-Peter Clausen Committed by Mark Brown
Browse files

ASoC: generic-dmaengine-pcm: Add support for half-duplex



Some platforms which are half-duplex share the same DMA channel between the
playback and capture stream. Add support for this to the generic dmaengine PCM
driver.

Signed-off-by: default avatarLars-Peter Clausen <lars@metafoo.de>
Tested-by: default avatarShawn Guo <shawn.guo@linaro.org>
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 22f38f79
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -91,6 +91,11 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
 * bytes that are still left to transfer.
 */
#define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(2)
/*
 * The PCM is half duplex and the DMA channel is shared between capture and
 * playback.
 */
#define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)

/**
 * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
+30 −13
Original line number Diff line number Diff line
@@ -29,7 +29,7 @@ struct dmaengine_pcm {
	struct dma_chan *chan[SNDRV_PCM_STREAM_CAPTURE + 1];
	const struct snd_dmaengine_pcm_config *config;
	struct snd_soc_platform platform;
	bool compat;
	unsigned int flags;
};

static struct dmaengine_pcm *soc_platform_to_pcm(struct snd_soc_platform *p)
@@ -128,6 +128,9 @@ static struct dma_chan *dmaengine_pcm_compat_request_channel(
{
	struct dmaengine_pcm *pcm = soc_platform_to_pcm(rtd->platform);

	if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) && pcm->chan[0])
		return pcm->chan[0];

	if (pcm->config->compat_request_channel)
		return pcm->config->compat_request_channel(rtd, substream);

@@ -148,7 +151,7 @@ static int dmaengine_pcm_new(struct snd_soc_pcm_runtime *rtd)
		if (!substream)
			continue;

		if (!pcm->chan[i] && pcm->compat) {
		if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
			pcm->chan[i] = dmaengine_pcm_compat_request_channel(rtd,
				substream);
		}
@@ -215,6 +218,25 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
	[SNDRV_PCM_STREAM_CAPTURE] = "rx",
};

static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
	struct device_node *of_node)
{
	unsigned int i;

	if ((pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_DT) || !of_node)
		return;

	if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
		pcm->chan[0] = of_dma_request_slave_channel(of_node, "tx_rx");
		pcm->chan[1] = pcm->chan[0];
	} else {
		for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
			pcm->chan[i] = of_dma_request_slave_channel(of_node,
					dmaengine_pcm_dma_channel_names[i]);
		}
	}
}

/**
 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
 * @dev: The parent device for the PCM device
@@ -225,23 +247,15 @@ int snd_dmaengine_pcm_register(struct device *dev,
	const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
	struct dmaengine_pcm *pcm;
	unsigned int i;

	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
	if (!pcm)
		return -ENOMEM;

	pcm->config = config;
	pcm->flags = flags;

	if (flags & SND_DMAENGINE_PCM_FLAG_COMPAT)
		pcm->compat = true;

	if (!(flags & SND_DMAENGINE_PCM_FLAG_NO_DT) && dev->of_node) {
		for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
			pcm->chan[i] = of_dma_request_slave_channel(dev->of_node,
					dmaengine_pcm_dma_channel_names[i]);
		}
	}
	dmaengine_pcm_request_chan_of(pcm, dev->of_node);

	if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
		return snd_soc_add_platform(dev, &pcm->platform,
@@ -272,8 +286,11 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
	pcm = soc_platform_to_pcm(platform);

	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
		if (pcm->chan[i])
		if (pcm->chan[i]) {
			dma_release_channel(pcm->chan[i]);
			if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
				break;
		}
	}

	snd_soc_remove_platform(platform);