summaryrefslogtreecommitdiff
path: root/sound/usb/pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb/pcm.c')
-rw-r--r--sound/usb/pcm.c270
1 files changed, 148 insertions, 122 deletions
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 3cbfae6604f9..78d1cad08a0a 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);
@@ -164,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;
@@ -185,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;
@@ -321,6 +322,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 +332,27 @@ 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(0x2466, 0x8010): /* Fractal Audio Axe-Fx III */
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 = 2;
+ goto add_sync_ep_from_ifnum;
+ case USB_ID(0x1397, 0x0002): /* Behringer UFX1204 */
+ ep = 0x81;
+ 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 +367,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,
@@ -507,7 +500,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;
@@ -517,21 +510,21 @@ 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;
}
/* 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;
@@ -545,12 +538,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);
@@ -736,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;
@@ -789,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);
}
/*
@@ -1181,9 +1181,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)
@@ -1192,30 +1189,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,
@@ -1225,19 +1229,18 @@ 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, 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];
@@ -1257,14 +1260,16 @@ 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];
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;
@@ -1311,7 +1316,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);
}
@@ -1619,26 +1624,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)
{
@@ -1700,8 +1685,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,
@@ -1713,8 +1698,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,
@@ -1725,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);
}