mirror of
https://github.com/torvalds/linux.git
synced 2024-12-31 07:11:47 +00:00
ASoC: dma: Generic ASoC dmaengine driver enhancements
This is the work so far on dmaengine for v3.14, it is being cross merged into the Tegra tree to support a large DMA overhaul there. The main additions are a change in the DMA request API which allows better interaction at system startup using deferred probes and methods for overriding the default device and channel names used to request DMA. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.15 (GNU/Linux) iQIcBAABAgAGBQJSqO8IAAoJELSic+t+oim9IaQP/1njSN/W+o8oFwbfG9oeMaES tVrPACPW42zW1SBVBj+px5MBuicaqJ5whH+3wQnpHsnvlT2XbYDdBumf4s2xDMkn dsdEMQpWP++wg+h1gua0ZdYVy8VTVS4WwAbVGmsv4xPxQm0BvhVitIMREyeRbfvq y0JPghp3IPH9XDTvdWUS7sFqr8sVNsyRqTpCt5132YsgYksr8MRwaRnOED4vC8fR asDI9GAlagT3sm3oQ5BU/fNPX37eGQq06y8WlBApZb6lPsechWsTV5iFOD+3bTQ9 aeOD+/urk7mIX8agrrUP8KY81CBSy2y9BzedQngA+M2i0gtqG03+xf1NgG1AcAGq MHoux0e/aCJewbmWacxrDs62LIMS8UpbHUj/clUTErGr04zxtWY3qlZbDRIA57pt x3LMo8/jBv4uh4PpX5I+IYhWB7NI+mDKij2JaSPi8L8+TR/Uk/IwsBZFjgt6Huqy bWmQvJIV92CAV+neLKVw19gAOFGcvGxD6d7hJQscMpig4QItIJ+mwfxBBUf1eaSy ceG9Xe4FDO7IyrV0xcdnuumQ7hbjbWi7YuB1Adw5kPAhJKP7iD/3t8xg97/tN6Od tcIh9EfIjwQarxOconOVQV112ZM8Urzv3LZB8/5HjGJ5tl8uF820+CeLBqEZwBN0 16E/5PWB+3BtbFkK/pd2 =90D4 -----END PGP SIGNATURE----- Merge tag 'asoc-dma-v3.14' into for-3.14/dmas-resets-rework ASoC: dma: Generic ASoC dmaengine driver enhancements This is the work so far on dmaengine for v3.14, it is being cross merged into the Tegra tree to support a large DMA overhaul there. The main additions are a change in the DMA request API which allows better interaction at system startup using deferred probes and methods for overriding the default device and channel names used to request DMA.
This commit is contained in:
commit
f229a93051
@ -114,6 +114,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
|
||||
* @compat_filter_fn: Will be used as the filter function when requesting a
|
||||
* channel for platforms which do not use devicetree. The filter parameter
|
||||
* will be the DAI's DMA data.
|
||||
* @dma_dev: If set, request DMA channel on this device rather than the DAI
|
||||
* device.
|
||||
* @chan_names: If set, these custom DMA channel names will be requested at
|
||||
* registration time.
|
||||
* @pcm_hardware: snd_pcm_hardware struct to be used for the PCM.
|
||||
* @prealloc_buffer_size: Size of the preallocated audio buffer.
|
||||
*
|
||||
@ -130,6 +134,8 @@ struct snd_dmaengine_pcm_config {
|
||||
struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_substream *substream);
|
||||
dma_filter_fn compat_filter_fn;
|
||||
struct device *dma_dev;
|
||||
const char *chan_names[SNDRV_PCM_STREAM_LAST + 1];
|
||||
|
||||
const struct snd_pcm_hardware *pcm_hardware;
|
||||
unsigned int prealloc_buffer_size;
|
||||
@ -140,6 +146,10 @@ int snd_dmaengine_pcm_register(struct device *dev,
|
||||
unsigned int flags);
|
||||
void snd_dmaengine_pcm_unregister(struct device *dev);
|
||||
|
||||
int devm_snd_dmaengine_pcm_register(struct device *dev,
|
||||
const struct snd_dmaengine_pcm_config *config,
|
||||
unsigned int flags);
|
||||
|
||||
int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct dma_slave_config *slave_config);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
static void devm_component_release(struct device *dev, void *res)
|
||||
{
|
||||
@ -84,3 +85,43 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card);
|
||||
|
||||
#ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
|
||||
static void devm_dmaengine_pcm_release(struct device *dev, void *res)
|
||||
{
|
||||
snd_dmaengine_pcm_unregister(*(struct device **)res);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_snd_dmaengine_pcm_register - resource managed dmaengine PCM registration
|
||||
* @dev: The parent device for the PCM device
|
||||
* @config: Platform specific PCM configuration
|
||||
* @flags: Platform specific quirks
|
||||
*
|
||||
* Register a dmaengine based PCM device with automatic unregistration when the
|
||||
* device is unregistered.
|
||||
*/
|
||||
int devm_snd_dmaengine_pcm_register(struct device *dev,
|
||||
const struct snd_dmaengine_pcm_config *config, unsigned int flags)
|
||||
{
|
||||
struct device **ptr;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_dmaengine_pcm_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = snd_dmaengine_pcm_register(dev, config, flags);
|
||||
if (ret == 0) {
|
||||
*ptr = dev;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_snd_dmaengine_pcm_register);
|
||||
|
||||
#endif
|
||||
|
@ -137,6 +137,9 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
|
||||
hw.buffer_bytes_max = SIZE_MAX;
|
||||
hw.fifo_size = dma_data->fifo_size;
|
||||
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
|
||||
hw.info |= SNDRV_PCM_INFO_BATCH;
|
||||
|
||||
ret = dma_get_slave_caps(chan, &dma_caps);
|
||||
if (ret == 0) {
|
||||
if (dma_caps.cmd_pause)
|
||||
@ -284,24 +287,67 @@ 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 *dev)
|
||||
static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
|
||||
struct device *dev, const struct snd_dmaengine_pcm_config *config)
|
||||
{
|
||||
unsigned int i;
|
||||
const char *name;
|
||||
struct dma_chan *chan;
|
||||
|
||||
if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
|
||||
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
|
||||
!dev->of_node)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
|
||||
pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx");
|
||||
pcm->chan[1] = pcm->chan[0];
|
||||
} else {
|
||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
|
||||
pcm->chan[i] = dma_request_slave_channel(dev,
|
||||
dmaengine_pcm_dma_channel_names[i]);
|
||||
if (config->dma_dev) {
|
||||
/*
|
||||
* If this warning is seen, it probably means that your Linux
|
||||
* device structure does not match your HW device structure.
|
||||
* It would be best to refactor the Linux device structure to
|
||||
* correctly match the HW structure.
|
||||
*/
|
||||
dev_warn(dev, "DMA channels sourced from device %s",
|
||||
dev_name(config->dma_dev));
|
||||
dev = config->dma_dev;
|
||||
}
|
||||
|
||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
|
||||
i++) {
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
|
||||
name = "rx-tx";
|
||||
else
|
||||
name = dmaengine_pcm_dma_channel_names[i];
|
||||
if (config->chan_names[i])
|
||||
name = config->chan_names[i];
|
||||
chan = dma_request_slave_channel_reason(dev, name);
|
||||
if (IS_ERR(chan)) {
|
||||
if (PTR_ERR(chan) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
pcm->chan[i] = NULL;
|
||||
} else {
|
||||
pcm->chan[i] = chan;
|
||||
}
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
|
||||
pcm->chan[1] = pcm->chan[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
|
||||
i++) {
|
||||
if (!pcm->chan[i])
|
||||
continue;
|
||||
dma_release_channel(pcm->chan[i]);
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,6 +361,7 @@ int snd_dmaengine_pcm_register(struct device *dev,
|
||||
const struct snd_dmaengine_pcm_config *config, unsigned int flags)
|
||||
{
|
||||
struct dmaengine_pcm *pcm;
|
||||
int ret;
|
||||
|
||||
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
|
||||
if (!pcm)
|
||||
@ -323,14 +370,25 @@ int snd_dmaengine_pcm_register(struct device *dev,
|
||||
pcm->config = config;
|
||||
pcm->flags = flags;
|
||||
|
||||
dmaengine_pcm_request_chan_of(pcm, dev);
|
||||
ret = dmaengine_pcm_request_chan_of(pcm, dev, config);
|
||||
if (ret)
|
||||
goto err_free_dma;
|
||||
|
||||
if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
|
||||
return snd_soc_add_platform(dev, &pcm->platform,
|
||||
ret = snd_soc_add_platform(dev, &pcm->platform,
|
||||
&dmaengine_no_residue_pcm_platform);
|
||||
else
|
||||
return snd_soc_add_platform(dev, &pcm->platform,
|
||||
ret = snd_soc_add_platform(dev, &pcm->platform,
|
||||
&dmaengine_pcm_platform);
|
||||
if (ret)
|
||||
goto err_free_dma;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_dma:
|
||||
dmaengine_pcm_release_chan(pcm);
|
||||
kfree(pcm);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register);
|
||||
|
||||
@ -345,7 +403,6 @@ void snd_dmaengine_pcm_unregister(struct device *dev)
|
||||
{
|
||||
struct snd_soc_platform *platform;
|
||||
struct dmaengine_pcm *pcm;
|
||||
unsigned int i;
|
||||
|
||||
platform = snd_soc_lookup_platform(dev);
|
||||
if (!platform)
|
||||
@ -353,15 +410,8 @@ 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]) {
|
||||
dma_release_channel(pcm->chan[i]);
|
||||
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_soc_remove_platform(platform);
|
||||
dmaengine_pcm_release_chan(pcm);
|
||||
kfree(pcm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister);
|
||||
|
Loading…
Reference in New Issue
Block a user