Commit f274baa4 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usb-audio: Allow non-vmalloc buffer for PCM buffers

Currently, USB-audio driver allocates the PCM buffer via vmalloc(), as
this serves merely as an intermediate buffer that is copied to each
URB transfer buffer.  This works well in general on x86, but on some
archs this may result in cache coherency issues when mmap is used.
OTOH, it works also on such arch unless mmap is used.

This patch is a step for mitigating the inconvenience; a new module
option "use_vmalloc" is provided so that user can choose to allocate
the DMA coherent buffer instead of the existing vmalloc buffer.
The drawback is that it'd be the standard dma_alloc_coherent() calls
and the system would require contiguous pages on non-x86 archs.

Note that it's a global option and not dynamically switchable since
the buffer is pre-allocated at the probe time.  In theory, it's
possible to be switchable, but it'd be trickier and racier.

As default use_vmalloc option is set to true, so that the old behavior
is kept.  For allowing the coherent mmap on ARM or MIPS, pass
use_vmalloc=0 option explicitly.
Reported-and-tested-by: default avatarDaniel Danzberger <daniel@dd-wrt.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f91f1806
...@@ -2224,6 +2224,13 @@ quirk_alias ...@@ -2224,6 +2224,13 @@ quirk_alias
Quirk alias list, pass strings like ``0123abcd:5678beef``, which Quirk alias list, pass strings like ``0123abcd:5678beef``, which
applies the existing quirk for the device 5678:beef to a new applies the existing quirk for the device 5678:beef to a new
device 0123:abcd. device 0123:abcd.
use_vmalloc
Use vmalloc() for allocations of the PCM buffers (default: yes).
For architectures with non-coherent memory like ARM or MIPS, the
mmap access may give inconsistent results with vmalloc'ed
buffers. If mmap is used on such architectures, turn off this
option, so that the DMA-coherent buffers are allocated and used
instead.
This module supports multiple devices, autoprobe and hotplugging. This module supports multiple devices, autoprobe and hotplugging.
......
...@@ -86,6 +86,8 @@ static bool ignore_ctl_error; ...@@ -86,6 +86,8 @@ static bool ignore_ctl_error;
static bool autoclock = true; static bool autoclock = true;
static char *quirk_alias[SNDRV_CARDS]; static char *quirk_alias[SNDRV_CARDS];
bool snd_usb_use_vmalloc = true;
module_param_array(index, int, NULL, 0444); module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter."); MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
module_param_array(id, charp, NULL, 0444); module_param_array(id, charp, NULL, 0444);
...@@ -105,6 +107,8 @@ module_param(autoclock, bool, 0444); ...@@ -105,6 +107,8 @@ module_param(autoclock, bool, 0444);
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes)."); MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
module_param_array(quirk_alias, charp, NULL, 0444); module_param_array(quirk_alias, charp, NULL, 0444);
MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef."); MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
module_param_named(use_vmalloc, snd_usb_use_vmalloc, bool, 0444);
MODULE_PARM_DESC(use_vmalloc, "Use vmalloc for PCM intermediate buffers (default: yes).");
/* /*
* we keep the snd_usb_audio_t instances by ourselves for merging * we keep the snd_usb_audio_t instances by ourselves for merging
......
...@@ -728,7 +728,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, ...@@ -728,7 +728,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct audioformat *fmt; struct audioformat *fmt;
int ret; int ret;
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, if (snd_usb_use_vmalloc)
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
else
ret = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params)); params_buffer_bytes(hw_params));
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -781,7 +785,11 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) ...@@ -781,7 +785,11 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
snd_usb_endpoint_deactivate(subs->data_endpoint); snd_usb_endpoint_deactivate(subs->data_endpoint);
snd_usb_unlock_shutdown(subs->stream->chip); snd_usb_unlock_shutdown(subs->stream->chip);
} }
return snd_pcm_lib_free_vmalloc_buffer(substream);
if (snd_usb_use_vmalloc)
return snd_pcm_lib_free_vmalloc_buffer(substream);
else
return snd_pcm_lib_free_pages(substream);
} }
/* /*
...@@ -1702,9 +1710,50 @@ static const struct snd_pcm_ops snd_usb_capture_ops = { ...@@ -1702,9 +1710,50 @@ static const struct snd_pcm_ops snd_usb_capture_ops = {
.mmap = snd_pcm_lib_mmap_vmalloc, .mmap = snd_pcm_lib_mmap_vmalloc,
}; };
static const struct snd_pcm_ops snd_usb_playback_dev_ops = {
.open = snd_usb_pcm_open,
.close = snd_usb_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_usb_hw_params,
.hw_free = snd_usb_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_playback_trigger,
.pointer = snd_usb_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
static const struct snd_pcm_ops snd_usb_capture_dev_ops = {
.open = snd_usb_pcm_open,
.close = snd_usb_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_usb_hw_params,
.hw_free = snd_usb_hw_free,
.prepare = snd_usb_pcm_prepare,
.trigger = snd_usb_substream_capture_trigger,
.pointer = snd_usb_pcm_pointer,
.page = snd_pcm_sgbuf_ops_page,
};
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream) void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream)
{ {
snd_pcm_set_ops(pcm, stream, const struct snd_pcm_ops *ops;
stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_ops : &snd_usb_capture_ops); if (snd_usb_use_vmalloc)
ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_ops : &snd_usb_capture_ops;
else
ops = stream == SNDRV_PCM_STREAM_PLAYBACK ?
&snd_usb_playback_dev_ops : &snd_usb_capture_dev_ops;
snd_pcm_set_ops(pcm, stream, ops);
}
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs)
{
struct snd_pcm *pcm = subs->stream->pcm;
struct snd_pcm_substream *s = pcm->streams[subs->direction].substream;
struct device *dev = subs->dev->bus->controller;
if (!snd_usb_use_vmalloc)
snd_pcm_lib_preallocate_pages(s, SNDRV_DMA_TYPE_DEV_SG,
dev, 64*1024, 512*1024);
} }
...@@ -10,6 +10,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); ...@@ -10,6 +10,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
struct usb_host_interface *alts, struct usb_host_interface *alts,
struct audioformat *fmt); struct audioformat *fmt);
void snd_usb_preallocate_buffer(struct snd_usb_substream *subs);
#endif /* __USBAUDIO_PCM_H */ #endif /* __USBAUDIO_PCM_H */
...@@ -106,6 +106,8 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, ...@@ -106,6 +106,8 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
subs->ep_num = fp->endpoint; subs->ep_num = fp->endpoint;
if (fp->channels > subs->channels_max) if (fp->channels > subs->channels_max)
subs->channels_max = fp->channels; subs->channels_max = fp->channels;
snd_usb_preallocate_buffer(subs);
} }
/* kctl callbacks for usb-audio channel maps */ /* kctl callbacks for usb-audio channel maps */
......
...@@ -127,4 +127,6 @@ struct snd_usb_audio_quirk { ...@@ -127,4 +127,6 @@ struct snd_usb_audio_quirk {
int snd_usb_lock_shutdown(struct snd_usb_audio *chip); int snd_usb_lock_shutdown(struct snd_usb_audio *chip);
void snd_usb_unlock_shutdown(struct snd_usb_audio *chip); void snd_usb_unlock_shutdown(struct snd_usb_audio *chip);
extern bool snd_usb_use_vmalloc;
#endif /* __USBAUDIO_H */ #endif /* __USBAUDIO_H */
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