aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb/pcm.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2018-05-27 13:01:17 +0200
committerTakashi Iwai <tiwai@suse.de>2018-05-29 10:01:54 +0200
commitf274baa49be67dd8a9f318cd95da6ef9f565d06b (patch)
treea8d94fa80a0630b108c93a37ab5433081112e182 /sound/usb/pcm.c
parentALSA: hda: Add Intel NUC5i7RY to the power_save blacklist (diff)
downloadlinux-dev-f274baa49be67dd8a9f318cd95da6ef9f565d06b.tar.xz
linux-dev-f274baa49be67dd8a9f318cd95da6ef9f565d06b.zip
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: Daniel Danzberger <daniel@dd-wrt.com> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r--sound/usb/pcm.c59
1 files changed, 54 insertions, 5 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 897a2cbef6de..78d1cad08a0a 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -728,7 +728,11 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct audioformat *fmt;
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));
if (ret < 0)
return ret;
@@ -781,7 +785,11 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
snd_usb_endpoint_deactivate(subs->data_endpoint);
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 = {
.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)
{
- snd_pcm_set_ops(pcm, stream,
- stream == SNDRV_PCM_STREAM_PLAYBACK ?
- &snd_usb_playback_ops : &snd_usb_capture_ops);
+ const struct snd_pcm_ops *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);
}