Commit 75aac820 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/dma' into asoc-next

parents 01ad154e 2b67f8ba
...@@ -540,6 +540,8 @@ EXPORT_SYMBOL_GPL(dma_get_slave_channel); ...@@ -540,6 +540,8 @@ EXPORT_SYMBOL_GPL(dma_get_slave_channel);
* @mask: capabilities that the channel must satisfy * @mask: capabilities that the channel must satisfy
* @fn: optional callback to disposition available channels * @fn: optional callback to disposition available channels
* @fn_param: opaque parameter to pass to dma_filter_fn * @fn_param: opaque parameter to pass to dma_filter_fn
*
* Returns pointer to appropriate DMA channel on success or NULL.
*/ */
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
dma_filter_fn fn, void *fn_param) dma_filter_fn fn, void *fn_param)
...@@ -591,18 +593,43 @@ EXPORT_SYMBOL_GPL(__dma_request_channel); ...@@ -591,18 +593,43 @@ EXPORT_SYMBOL_GPL(__dma_request_channel);
* dma_request_slave_channel - try to allocate an exclusive slave channel * dma_request_slave_channel - try to allocate an exclusive slave channel
* @dev: pointer to client device structure * @dev: pointer to client device structure
* @name: slave channel name * @name: slave channel name
*
* Returns pointer to appropriate DMA channel on success or an error pointer.
*/ */
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name) struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
const char *name)
{ {
struct dma_chan *chan;
/* If device-tree is present get slave info from here */ /* If device-tree is present get slave info from here */
if (dev->of_node) if (dev->of_node)
return of_dma_request_slave_channel(dev->of_node, name); return of_dma_request_slave_channel(dev->of_node, name);
/* If device was enumerated by ACPI get slave info from here */ /* If device was enumerated by ACPI get slave info from here */
if (ACPI_HANDLE(dev)) if (ACPI_HANDLE(dev)) {
return acpi_dma_request_slave_chan_by_name(dev, name); chan = acpi_dma_request_slave_chan_by_name(dev, name);
if (chan)
return chan;
}
return NULL; return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL_GPL(dma_request_slave_channel_reason);
/**
* dma_request_slave_channel - try to allocate an exclusive slave channel
* @dev: pointer to client device structure
* @name: slave channel name
*
* Returns pointer to appropriate DMA channel on success or NULL.
*/
struct dma_chan *dma_request_slave_channel(struct device *dev,
const char *name)
{
struct dma_chan *ch = dma_request_slave_channel_reason(dev, name);
if (IS_ERR(ch))
return NULL;
return ch;
} }
EXPORT_SYMBOL_GPL(dma_request_slave_channel); EXPORT_SYMBOL_GPL(dma_request_slave_channel);
......
...@@ -143,7 +143,7 @@ static int of_dma_match_channel(struct device_node *np, const char *name, ...@@ -143,7 +143,7 @@ static int of_dma_match_channel(struct device_node *np, const char *name,
* @np: device node to get DMA request from * @np: device node to get DMA request from
* @name: name of desired channel * @name: name of desired channel
* *
* Returns pointer to appropriate dma channel on success or NULL on error. * Returns pointer to appropriate DMA channel on success or an error pointer.
*/ */
struct dma_chan *of_dma_request_slave_channel(struct device_node *np, struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
const char *name) const char *name)
...@@ -152,17 +152,18 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, ...@@ -152,17 +152,18 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
struct of_dma *ofdma; struct of_dma *ofdma;
struct dma_chan *chan; struct dma_chan *chan;
int count, i; int count, i;
int ret_no_channel = -ENODEV;
if (!np || !name) { if (!np || !name) {
pr_err("%s: not enough information provided\n", __func__); pr_err("%s: not enough information provided\n", __func__);
return NULL; return ERR_PTR(-ENODEV);
} }
count = of_property_count_strings(np, "dma-names"); count = of_property_count_strings(np, "dma-names");
if (count < 0) { if (count < 0) {
pr_err("%s: dma-names property of node '%s' missing or empty\n", pr_err("%s: dma-names property of node '%s' missing or empty\n",
__func__, np->full_name); __func__, np->full_name);
return NULL; return ERR_PTR(-ENODEV);
} }
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
...@@ -172,10 +173,12 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, ...@@ -172,10 +173,12 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
mutex_lock(&of_dma_lock); mutex_lock(&of_dma_lock);
ofdma = of_dma_find_controller(&dma_spec); ofdma = of_dma_find_controller(&dma_spec);
if (ofdma) if (ofdma) {
chan = ofdma->of_dma_xlate(&dma_spec, ofdma); chan = ofdma->of_dma_xlate(&dma_spec, ofdma);
else } else {
ret_no_channel = -EPROBE_DEFER;
chan = NULL; chan = NULL;
}
mutex_unlock(&of_dma_lock); mutex_unlock(&of_dma_lock);
...@@ -185,7 +188,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, ...@@ -185,7 +188,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np,
return chan; return chan;
} }
return NULL; return ERR_PTR(ret_no_channel);
} }
/** /**
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#define LINUX_DMAENGINE_H #define LINUX_DMAENGINE_H
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
...@@ -1040,6 +1041,8 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx); ...@@ -1040,6 +1041,8 @@ enum dma_status dma_wait_for_async_tx(struct dma_async_tx_descriptor *tx);
void dma_issue_pending_all(void); void dma_issue_pending_all(void);
struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
dma_filter_fn fn, void *fn_param); dma_filter_fn fn, void *fn_param);
struct dma_chan *dma_request_slave_channel_reason(struct device *dev,
const char *name);
struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name); struct dma_chan *dma_request_slave_channel(struct device *dev, const char *name);
void dma_release_channel(struct dma_chan *chan); void dma_release_channel(struct dma_chan *chan);
#else #else
...@@ -1063,6 +1066,11 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask, ...@@ -1063,6 +1066,11 @@ static inline struct dma_chan *__dma_request_channel(const dma_cap_mask_t *mask,
{ {
return NULL; return NULL;
} }
static inline struct dma_chan *dma_request_slave_channel_reason(
struct device *dev, const char *name)
{
return ERR_PTR(-ENODEV);
}
static inline struct dma_chan *dma_request_slave_channel(struct device *dev, static inline struct dma_chan *dma_request_slave_channel(struct device *dev,
const char *name) const char *name)
{ {
......
...@@ -114,6 +114,10 @@ void snd_dmaengine_pcm_set_config_from_dai_data( ...@@ -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 * @compat_filter_fn: Will be used as the filter function when requesting a
* channel for platforms which do not use devicetree. The filter parameter * channel for platforms which do not use devicetree. The filter parameter
* will be the DAI's DMA data. * 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. * @pcm_hardware: snd_pcm_hardware struct to be used for the PCM.
* @prealloc_buffer_size: Size of the preallocated audio buffer. * @prealloc_buffer_size: Size of the preallocated audio buffer.
* *
...@@ -130,6 +134,8 @@ struct snd_dmaengine_pcm_config { ...@@ -130,6 +134,8 @@ struct snd_dmaengine_pcm_config {
struct snd_soc_pcm_runtime *rtd, struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_substream *substream); struct snd_pcm_substream *substream);
dma_filter_fn compat_filter_fn; 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; const struct snd_pcm_hardware *pcm_hardware;
unsigned int prealloc_buffer_size; unsigned int prealloc_buffer_size;
...@@ -140,6 +146,10 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -140,6 +146,10 @@ int snd_dmaengine_pcm_register(struct device *dev,
unsigned int flags); unsigned int flags);
void snd_dmaengine_pcm_unregister(struct device *dev); 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, int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config); struct dma_slave_config *slave_config);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
static void devm_component_release(struct device *dev, void *res) 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) ...@@ -84,3 +85,43 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card); 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 ...@@ -137,6 +137,9 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
hw.buffer_bytes_max = SIZE_MAX; hw.buffer_bytes_max = SIZE_MAX;
hw.fifo_size = dma_data->fifo_size; 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); ret = dma_get_slave_caps(chan, &dma_caps);
if (ret == 0) { if (ret == 0) {
if (dma_caps.cmd_pause) if (dma_caps.cmd_pause)
...@@ -284,25 +287,54 @@ static const char * const dmaengine_pcm_dma_channel_names[] = { ...@@ -284,25 +287,54 @@ static const char * const dmaengine_pcm_dma_channel_names[] = {
[SNDRV_PCM_STREAM_CAPTURE] = "rx", [SNDRV_PCM_STREAM_CAPTURE] = "rx",
}; };
static void dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm,
struct device *dev) struct device *dev, const struct snd_dmaengine_pcm_config *config)
{ {
unsigned int i; unsigned int i;
const char *name;
struct dma_chan *chan;
if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT | if ((pcm->flags & (SND_DMAENGINE_PCM_FLAG_NO_DT |
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) || SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME)) ||
!dev->of_node) !dev->of_node)
return; return 0;
if (config && 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;
}
if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) { for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE;
pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx"); i++) {
pcm->chan[1] = pcm->chan[0]; if (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX)
} else { name = "rx-tx";
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) { else
pcm->chan[i] = dma_request_slave_channel(dev, name = dmaengine_pcm_dma_channel_names[i];
dmaengine_pcm_dma_channel_names[i]); if (config && 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) static void dmaengine_pcm_release_chan(struct dmaengine_pcm *pcm)
...@@ -338,7 +370,9 @@ int snd_dmaengine_pcm_register(struct device *dev, ...@@ -338,7 +370,9 @@ int snd_dmaengine_pcm_register(struct device *dev,
pcm->config = config; pcm->config = config;
pcm->flags = flags; 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) if (flags & SND_DMAENGINE_PCM_FLAG_NO_RESIDUE)
ret = snd_soc_add_platform(dev, &pcm->platform, ret = snd_soc_add_platform(dev, &pcm->platform,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment