From 103e9625647ad74d201e26fb74afcd8479142a37 Mon Sep 17 00:00:00 2001 From: Alberto Aguirre Date: Wed, 18 Apr 2018 09:35:34 -0500 Subject: ALSA: usb-audio: simplify set_sync_ep_implicit_fb_quirk Signed-off-by: Alberto Aguirre Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 52 ++++++++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 3cbfae6604f9..c0746cc20ac4 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -321,6 +321,7 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, struct usb_host_interface *alts; struct usb_interface *iface; unsigned int ep; + unsigned int ifnum; /* Implicit feedback sync EPs consumers are always playback EPs */ if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK) @@ -330,44 +331,23 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */ ep = 0x81; - iface = usb_ifnum_to_if(dev, 3); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - break; + ifnum = 3; + goto add_sync_ep_from_ifnum; case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ case USB_ID(0x0763, 0x2081): ep = 0x81; - iface = usb_ifnum_to_if(dev, 2); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - case USB_ID(0x2466, 0x8003): + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x2466, 0x8003): /* Fractal Audio Axe-Fx II */ ep = 0x86; - iface = usb_ifnum_to_if(dev, 2); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - case USB_ID(0x1397, 0x0002): + ifnum = 2; + goto add_sync_ep_from_ifnum; + case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ ep = 0x81; - iface = usb_ifnum_to_if(dev, 1); - - if (!iface || iface->num_altsetting == 0) - return -EINVAL; - - alts = &iface->altsetting[1]; - goto add_sync_ep; - + ifnum = 1; + goto add_sync_ep_from_ifnum; } + if (attr == USB_ENDPOINT_SYNC_ASYNC && altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC && altsd->bInterfaceProtocol == 2 && @@ -382,6 +362,14 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, /* No quirk */ return 0; +add_sync_ep_from_ifnum: + iface = usb_ifnum_to_if(dev, ifnum); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + add_sync_ep: subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, ep, !subs->direction, -- cgit v1.2.2 From 91a8561d0eed710330956a06487f5c888f5ae743 Mon Sep 17 00:00:00 2001 From: Alberto Aguirre Date: Wed, 18 Apr 2018 09:35:35 -0500 Subject: ALSA: usb-audio: add implicit fb quirk for Axe-Fx III The Axe-Fx III implicit feedback end point and the data sink endpoint are in different interface descriptors. Add quirk to ensure a sync endpoint is properly configured. Signed-off-by: Alberto Aguirre Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index c0746cc20ac4..ad39b3cca247 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -342,6 +342,10 @@ static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs, ep = 0x86; ifnum = 2; goto add_sync_ep_from_ifnum; + case USB_ID(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */ + ep = 0x81; + ifnum = 2; + goto add_sync_ep_from_ifnum; case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */ ep = 0x81; ifnum = 1; -- cgit v1.2.2 From b099b9693d23d035c77c218508e083484ff63024 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 2 May 2018 09:36:28 +0200 Subject: ALSA: usb-audio: Avoid superfluous usb_set_interface() calls This is a preliminary change for the upcoming quirk implementation. Currently USB-audio driver tries to call usb_set_interface() whenever the format change with interface/altset modification happens. In this patch, the check is replaced with the comparison of cur_altsetting and the targeted altsetting pointer, so that the driver may skip the unnecessary function calls. Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ad39b3cca247..ae7d8a0a0a0a 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -499,7 +499,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) iface = usb_ifnum_to_if(dev, fmt->iface); if (WARN_ON(!iface)) return -EINVAL; - alts = &iface->altsetting[fmt->altset_idx]; + alts = usb_altnum_to_altsetting(iface, fmt->altsetting); altsd = get_iface_desc(alts); if (WARN_ON(altsd->bAlternateSetting != fmt->altsetting)) return -EINVAL; @@ -521,9 +521,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } /* set interface */ - if (subs->interface != fmt->iface || - subs->altset_idx != fmt->altset_idx) { - + if (iface->cur_altsetting != alts) { err = snd_usb_select_mode_quirk(subs, fmt); if (err < 0) return -EIO; @@ -537,12 +535,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) } dev_dbg(&dev->dev, "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting); - subs->interface = fmt->iface; - subs->altset_idx = fmt->altset_idx; - snd_usb_set_interface_quirk(dev); } + subs->interface = fmt->iface; + subs->altset_idx = fmt->altset_idx; subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, alts, fmt->endpoint, subs->direction, SND_USB_ENDPOINT_TYPE_DATA); -- cgit v1.2.2 From 8a463225b11047455b374729d18c8a371fe6e591 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 2 May 2018 10:04:27 +0200 Subject: ALSA: usb-audio: Add keep_iface flag Introduce a new flag to struct snd_usb_audio for allowing the device to skip usb_set_interface() calls at changing or closing the stream. As of this patch, the flag is nowhere set, so it's just a place holder. The dynamic switching will be added in the following patch. A background information for this change: Dell WD15 dock with Realtek chip gives a very long pause at each time the driver changes the altset, which eventually happens at every PCM stream open/close and parameter change. As the long pause happens in each usb_set_interface() call, there is nothing we can do as long as it's called. The workaround is to reduce calling it as much as possible, and this flag indicates that behavior. Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ae7d8a0a0a0a..dc2dfec9effd 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -509,12 +509,14 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* close the old interface */ if (subs->interface >= 0 && subs->interface != fmt->iface) { - err = usb_set_interface(subs->dev, subs->interface, 0); - if (err < 0) { - dev_err(&dev->dev, - "%d:%d: return to setting 0 failed (%d)\n", - fmt->iface, fmt->altsetting, err); - return -EIO; + if (!subs->stream->chip->keep_iface) { + err = usb_set_interface(subs->dev, subs->interface, 0); + if (err < 0) { + dev_err(&dev->dev, + "%d:%d: return to setting 0 failed (%d)\n", + fmt->iface, fmt->altsetting, err); + return -EIO; + } } subs->interface = -1; subs->altset_idx = 0; @@ -1253,7 +1255,8 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) stop_endpoints(subs, true); - if (subs->interface >= 0 && + if (!as->chip->keep_iface && + subs->interface >= 0 && !snd_usb_lock_shutdown(subs->stream->chip)) { usb_set_interface(subs->dev, subs->interface, 0); subs->interface = -1; -- cgit v1.2.2 From 377a879d9832f4ba69bd6a1fc996bb4181b1e504 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 16 May 2018 20:07:18 +0200 Subject: ALSA: usb-audio: Apply rate limit to warning messages in URB complete callback retire_capture_urb() may print warning messages when the given URB doesn't align, and this may flood the system log easily. Put the rate limit to the message for avoiding it. Bugzilla: https://bugzilla.suse.com/show_bug.cgi?id=1093485 Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index dc2dfec9effd..20bed1c7a312 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1303,7 +1303,7 @@ static void retire_capture_urb(struct snd_usb_substream *subs, if (bytes % (runtime->sample_bits >> 3) != 0) { int oldbytes = bytes; bytes = frames * stride; - dev_warn(&subs->dev->dev, + dev_warn_ratelimited(&subs->dev->dev, "Corrected urb data len. %d->%d\n", oldbytes, bytes); } -- cgit v1.2.2 From 6fddc797878181c9bb16dff1034ad9de2b25902d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 May 2018 13:59:03 +0200 Subject: ALSA: usb-audio: Simplify PCM open/close callbacks The stream direction in open and close callbacks can be retrieved from substream->direction, hence we don't have to stick with the unique PCM ops hard-coded for each direction. Rewrite the common open/close callback functions. Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 20bed1c7a312..d5b9c30d3bb1 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -1227,8 +1227,9 @@ rep_err: return err; } -static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) +static int snd_usb_pcm_open(struct snd_pcm_substream *substream) { + int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_usb_substream *subs = &as->substream[direction]; @@ -1248,8 +1249,9 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction) return setup_hw_info(runtime, subs); } -static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) +static int snd_usb_pcm_close(struct snd_pcm_substream *substream) { + int direction = substream->stream; struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; @@ -1611,26 +1613,6 @@ static void retire_playback_urb(struct snd_usb_substream *subs, spin_unlock_irqrestore(&subs->lock, flags); } -static int snd_usb_playback_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_playback_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK); -} - -static int snd_usb_capture_open(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE); -} - -static int snd_usb_capture_close(struct snd_pcm_substream *substream) -{ - return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE); -} - static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd) { @@ -1692,8 +1674,8 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream } static const struct snd_pcm_ops snd_usb_playback_ops = { - .open = snd_usb_playback_open, - .close = snd_usb_playback_close, + .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, @@ -1705,8 +1687,8 @@ static const struct snd_pcm_ops snd_usb_playback_ops = { }; static const struct snd_pcm_ops snd_usb_capture_ops = { - .open = snd_usb_capture_open, - .close = snd_usb_capture_close, + .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, -- cgit v1.2.2 From e92be8146caf3ecd76f1211725d9ba47c239a77b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 May 2018 15:09:15 +0200 Subject: ALSA: usb-audio: Move autoresume call at the end of open ... so that we can avoid the extra goto lines. Also beautify the code to follow the standard codex. No functional changes. Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 73 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 36 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index d5b9c30d3bb1..a66bc717b952 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -76,10 +76,9 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, */ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream) { - struct snd_usb_substream *subs; + struct snd_usb_substream *subs = substream->runtime->private_data; unsigned int hwptr_done; - subs = (struct snd_usb_substream *)substream->runtime->private_data; if (atomic_read(&subs->stream->chip->shutdown)) return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); @@ -1172,9 +1171,6 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre pt = 125 * (1 << fp->datainterval); ptmin = min(ptmin, pt); } - err = snd_usb_autoresume(subs->stream->chip); - if (err < 0) - return err; param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME; if (subs->speed == USB_SPEED_FULL) @@ -1183,30 +1179,37 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre if (ptmin == 1000) /* if period time doesn't go below 1 ms, no rules needed */ param_period_time_if_needed = -1; - snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, - ptmin, UINT_MAX); - - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - hw_rule_rate, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - hw_rule_channels, subs, - SNDRV_PCM_HW_PARAM_FORMAT, - SNDRV_PCM_HW_PARAM_RATE, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; - if ((err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, - hw_rule_format, subs, - SNDRV_PCM_HW_PARAM_RATE, - SNDRV_PCM_HW_PARAM_CHANNELS, - param_period_time_if_needed, - -1)) < 0) - goto rep_err; + + err = snd_pcm_hw_constraint_minmax(runtime, + SNDRV_PCM_HW_PARAM_PERIOD_TIME, + ptmin, UINT_MAX); + if (err < 0) + return err; + + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + hw_rule_rate, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels, subs, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_RATE, + param_period_time_if_needed, + -1); + if (err < 0) + return err; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format, subs, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, + param_period_time_if_needed, + -1); + if (err < 0) + return err; if (param_period_time_if_needed >= 0) { err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_TIME, @@ -1216,15 +1219,13 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre SNDRV_PCM_HW_PARAM_RATE, -1); if (err < 0) - goto rep_err; + return err; } - if ((err = snd_usb_pcm_check_knot(runtime, subs)) < 0) - goto rep_err; - return 0; + err = snd_usb_pcm_check_knot(runtime, subs); + if (err < 0) + return err; -rep_err: - snd_usb_autosuspend(subs->stream->chip); - return err; + return snd_usb_autoresume(subs->stream->chip); } static int snd_usb_pcm_open(struct snd_pcm_substream *substream) -- cgit v1.2.2 From f25ecf8f987d51be388e53de7b9e0e5815acc10b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 May 2018 15:18:22 +0200 Subject: ALSA: usb-audio: Follow standard coding style Avoid if ((err = ...) style and expand to multiple lines instead. No change in the end result, but just the beautification. Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'sound/usb/pcm.c') diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index a66bc717b952..897a2cbef6de 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -163,10 +163,11 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface, ep = get_endpoint(alts, 0)->bEndpointAddress; data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, - USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, - UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR, + USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep, + data, sizeof(data)); + if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH\n", iface, ep); return err; @@ -184,10 +185,11 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, int err; data[0] = 1; - if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, - USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, - UAC2_EP_CS_PITCH << 8, 0, - data, sizeof(data))) < 0) { + err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, + USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, + UAC2_EP_CS_PITCH << 8, 0, + data, sizeof(data)); + if (err < 0) { usb_audio_err(chip, "%d:%d: cannot set enable PITCH (v2)\n", iface, fmt->altsetting); return err; -- cgit v1.2.2 From f274baa49be67dd8a9f318cd95da6ef9f565d06b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 27 May 2018 13:01:17 +0200 Subject: 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 Signed-off-by: Takashi Iwai --- sound/usb/pcm.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 5 deletions(-) (limited to 'sound/usb/pcm.c') 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); } -- cgit v1.2.2