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: Lars-Peter Clausen <lars@metafoo.de> Tested-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
This commit is contained in:
parent
22f38f792e
commit
d1e1406c6e
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue