Merge remote-tracking branch 'asoc/topic/dma' into asoc-next
This commit is contained in:
commit
75aac82060
|
@ -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);
|
||||||
* 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,
|
||||||
* @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,
|
||||||
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,
|
||||||
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,
|
||||||
return chan;
|
return chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return ERR_PTR(ret_no_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
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,
|
||||||
{
|
{
|
||||||
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(
|
||||||
* @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 {
|
||||||
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,
|
||||||
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 @@
|
||||||
#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)
|
||||||
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
|
||||||
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[] = {
|
||||||
[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 (pcm->flags & SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX) {
|
if (config && config->dma_dev) {
|
||||||
pcm->chan[0] = dma_request_slave_channel(dev, "rx-tx");
|
/*
|
||||||
pcm->chan[1] = pcm->chan[0];
|
* If this warning is seen, it probably means that your Linux
|
||||||
} else {
|
* device structure does not match your HW device structure.
|
||||||
for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
|
* It would be best to refactor the Linux device structure to
|
||||||
pcm->chan[i] = dma_request_slave_channel(dev,
|
* correctly match the HW structure.
|
||||||
dmaengine_pcm_dma_channel_names[i]);
|
*/
|
||||||
}
|
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 && 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,
|
||||||
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,
|
||||||
|
|
Loading…
Reference in a new issue