aboutsummaryrefslogtreecommitdiffstats
path: root/sound/usb
diff options
context:
space:
mode:
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/Kconfig4
-rw-r--r--sound/usb/Makefile2
-rw-r--r--sound/usb/card.c70
-rw-r--r--sound/usb/card.h3
-rw-r--r--sound/usb/clock.c2
-rw-r--r--sound/usb/endpoint.c3
-rw-r--r--sound/usb/media.c318
-rw-r--r--sound/usb/media.h72
-rw-r--r--sound/usb/midi.c15
-rw-r--r--sound/usb/midi.h14
-rw-r--r--sound/usb/mixer.h3
-rw-r--r--sound/usb/mixer_quirks.c4
-rw-r--r--sound/usb/pcm.c30
-rw-r--r--sound/usb/quirks-table.h1
-rw-r--r--sound/usb/quirks.c59
-rw-r--r--sound/usb/quirks.h3
-rw-r--r--sound/usb/stream.c2
-rw-r--r--sound/usb/usbaudio.h6
18 files changed, 571 insertions, 40 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index a452ad7cec40..d14bf411515b 100644
--- a/sound/usb/Kconfig
+++ b/sound/usb/Kconfig
@@ -15,6 +15,7 @@ config SND_USB_AUDIO
select SND_RAWMIDI
select SND_PCM
select BITREVERSE
+ select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO)
help
Say Y here to include support for USB audio and USB MIDI
devices.
@@ -22,6 +23,9 @@ config SND_USB_AUDIO
To compile this driver as a module, choose M here: the module
will be called snd-usb-audio.
+config SND_USB_AUDIO_USE_MEDIA_CONTROLLER
+ bool
+
config SND_USB_UA101
tristate "Edirol UA-101/UA-1000 driver"
select SND_PCM
diff --git a/sound/usb/Makefile b/sound/usb/Makefile
index 2d2d122b069f..8dca3c407f5a 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -15,6 +15,8 @@ snd-usb-audio-objs := card.o \
quirks.o \
stream.o
+snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
+
snd-usbmidi-lib-objs := midi.o
# Toplevel Module Dependency
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 1f09d9591276..63244bbba8c7 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -66,6 +66,7 @@
#include "format.h"
#include "power.h"
#include "stream.h"
+#include "media.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
@@ -82,6 +83,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
static bool ignore_ctl_error;
static bool autoclock = true;
+static char *quirk_alias[SNDRV_CARDS];
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -100,6 +102,8 @@ MODULE_PARM_DESC(ignore_ctl_error,
"Ignore errors from USB controller for mixer interfaces.");
module_param(autoclock, bool, 0444);
MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
+module_param_array(quirk_alias, charp, NULL, 0444);
+MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
/*
* we keep the snd_usb_audio_t instances by ourselves for merging
@@ -171,8 +175,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
- int err = snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, NULL);
+ int err = __snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, NULL,
+ chip->usb_id);
if (err < 0) {
dev_err(&dev->dev,
"%u:%d: cannot create sequencer device\n",
@@ -311,6 +316,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
snd_usb_endpoint_free(ep);
mutex_destroy(&chip->mutex);
+ dev_set_drvdata(&chip->dev->dev, NULL);
kfree(chip);
return 0;
}
@@ -455,6 +461,48 @@ static int snd_usb_audio_create(struct usb_interface *intf,
return 0;
}
+/* look for a matching quirk alias id */
+static bool get_alias_id(struct usb_device *dev, unsigned int *id)
+{
+ int i;
+ unsigned int src, dst;
+
+ for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) {
+ if (!quirk_alias[i] ||
+ sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 ||
+ src != *id)
+ continue;
+ dev_info(&dev->dev,
+ "device (%04x:%04x): applying quirk alias %04x:%04x\n",
+ USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id),
+ USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst));
+ *id = dst;
+ return true;
+ }
+
+ return false;
+}
+
+static struct usb_device_id usb_audio_ids[]; /* defined below */
+
+/* look for the corresponding quirk */
+static const struct snd_usb_audio_quirk *
+get_alias_quirk(struct usb_device *dev, unsigned int id)
+{
+ const struct usb_device_id *p;
+
+ for (p = usb_audio_ids; p->match_flags; p++) {
+ /* FIXME: this checks only vendor:product pair in the list */
+ if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
+ USB_DEVICE_ID_MATCH_DEVICE &&
+ p->idVendor == USB_ID_VENDOR(id) &&
+ p->idProduct == USB_ID_PRODUCT(id))
+ return (const struct snd_usb_audio_quirk *)p->driver_info;
+ }
+
+ return NULL;
+}
+
/*
* probe the active usb device
*
@@ -481,10 +529,12 @@ static int usb_audio_probe(struct usb_interface *intf,
ifnum = get_iface_desc(alts)->bInterfaceNumber;
id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
+ if (get_alias_id(dev, &id))
+ quirk = get_alias_quirk(dev, id);
if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
return -ENXIO;
- err = snd_usb_apply_boot_quirk(dev, intf, quirk);
+ err = snd_usb_apply_boot_quirk(dev, intf, quirk, id);
if (err < 0)
return err;
@@ -503,6 +553,7 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
chip = usb_chip[i];
+ dev_set_drvdata(&dev->dev, chip);
atomic_inc(&chip->active); /* avoid autopm */
break;
}
@@ -561,6 +612,11 @@ static int usb_audio_probe(struct usb_interface *intf,
if (err < 0)
goto __error;
+ if (quirk->media_device) {
+ /* don't want to fail when media_snd_device_create() fails */
+ media_snd_device_create(chip, intf);
+ }
+
usb_chip[chip->index] = chip;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
@@ -617,6 +673,14 @@ static void usb_audio_disconnect(struct usb_interface *intf)
list_for_each(p, &chip->midi_list) {
snd_usbmidi_disconnect(p);
}
+ /*
+ * Nice to check quirk && quirk->media_device
+ * need some special handlings. Doesn't look like
+ * we have access to quirk here
+ * Acceses mixer_list
+ */
+ media_snd_device_delete(chip);
+
/* release mixer resources */
list_for_each_entry(mixer, &chip->mixer_list, list) {
snd_usb_mixer_disconnect(mixer);
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 71778ca4b26a..34a0898e2238 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -105,6 +105,8 @@ struct snd_usb_endpoint {
struct list_head list;
};
+struct media_ctl;
+
struct snd_usb_substream {
struct snd_usb_stream *stream;
struct usb_device *dev;
@@ -156,6 +158,7 @@ struct snd_usb_substream {
} dsd_dop;
bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
+ struct media_ctl *media_ctl;
};
struct snd_usb_stream {
diff --git a/sound/usb/clock.c b/sound/usb/clock.c
index 2ed260b10f6d..7ccbcaf6a147 100644
--- a/sound/usb/clock.c
+++ b/sound/usb/clock.c
@@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[3];
int err, crate;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
/* if endpoint doesn't have sampling rate control, bail out */
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7b1cb365ffab..c07a7eda42a2 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -438,6 +438,9 @@ exit_clear:
*
* New endpoints will be added to chip->ep_list and must be freed by
* calling snd_usb_endpoint_free().
+ *
+ * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
+ * bNumEndpoints > 1 beforehand.
*/
struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
struct usb_host_interface *alts,
diff --git a/sound/usb/media.c b/sound/usb/media.c
new file mode 100644
index 000000000000..93a50d01490c
--- /dev/null
+++ b/sound/usb/media.c
@@ -0,0 +1,318 @@
+/*
+ * media.c - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * This file is released under the GPLv2.
+ */
+
+/*
+ * This file adds Media Controller support to ALSA driver
+ * to use the Media Controller API to share tuner with DVB
+ * and V4L2 drivers that control media device. Media device
+ * is created based on existing quirks framework. Using this
+ * approach, the media controller API usage can be added for
+ * a specific device.
+*/
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include <sound/pcm.h>
+#include <sound/core.h>
+
+#include "usbaudio.h"
+#include "card.h"
+#include "mixer.h"
+#include "media.h"
+
+static int media_snd_enable_source(struct media_ctl *mctl)
+{
+ if (mctl && mctl->media_dev->enable_source)
+ return mctl->media_dev->enable_source(&mctl->media_entity,
+ &mctl->media_pipe);
+ return 0;
+}
+
+static void media_snd_disable_source(struct media_ctl *mctl)
+{
+ if (mctl && mctl->media_dev->disable_source)
+ mctl->media_dev->disable_source(&mctl->media_entity);
+}
+
+int media_snd_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
+ int stream)
+{
+ struct media_device *mdev;
+ struct media_ctl *mctl;
+ struct device *pcm_dev = &pcm->streams[stream].dev;
+ u32 intf_type;
+ int ret = 0;
+ u16 mixer_pad;
+ struct media_entity *entity;
+
+ mdev = subs->stream->chip->media_dev;
+ if (!mdev)
+ return -ENODEV;
+
+ if (subs->media_ctl)
+ return 0;
+
+ /* allocate media_ctl */
+ mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
+ if (!mctl)
+ return -ENOMEM;
+
+ mctl->media_dev = mdev;
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ intf_type = MEDIA_INTF_T_ALSA_PCM_PLAYBACK;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_PLAYBACK;
+ mctl->media_pad.flags = MEDIA_PAD_FL_SOURCE;
+ mixer_pad = 1;
+ } else {
+ intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE;
+ mctl->media_pad.flags = MEDIA_PAD_FL_SINK;
+ mixer_pad = 2;
+ }
+ mctl->media_entity.name = pcm->name;
+ media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad);
+ ret = media_device_register_entity(mctl->media_dev,
+ &mctl->media_entity);
+ if (ret)
+ goto free_mctl;
+
+ mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0,
+ MAJOR(pcm_dev->devt),
+ MINOR(pcm_dev->devt));
+ if (!mctl->intf_devnode) {
+ ret = -ENOMEM;
+ goto unregister_entity;
+ }
+ mctl->intf_link = media_create_intf_link(&mctl->media_entity,
+ &mctl->intf_devnode->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!mctl->intf_link) {
+ ret = -ENOMEM;
+ goto devnode_remove;
+ }
+
+ /* create link between mixer and audio */
+ media_device_for_each_entity(entity, mdev) {
+ switch (entity->function) {
+ case MEDIA_ENT_F_AUDIO_MIXER:
+ ret = media_create_pad_link(entity, mixer_pad,
+ &mctl->media_entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ goto remove_intf_link;
+ break;
+ }
+ }
+
+ subs->media_ctl = mctl;
+ return 0;
+
+remove_intf_link:
+ media_remove_intf_link(mctl->intf_link);
+devnode_remove:
+ media_devnode_remove(mctl->intf_devnode);
+unregister_entity:
+ media_device_unregister_entity(&mctl->media_entity);
+free_mctl:
+ kfree(mctl);
+ return ret;
+}
+
+void media_snd_stream_delete(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl && mctl->media_dev) {
+ struct media_device *mdev;
+
+ mdev = subs->stream->chip->media_dev;
+ if (mdev && media_devnode_is_registered(&mdev->devnode)) {
+ media_devnode_remove(mctl->intf_devnode);
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ }
+ kfree(mctl);
+ subs->media_ctl = NULL;
+ }
+}
+
+int media_snd_start_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl)
+ return media_snd_enable_source(mctl);
+ return 0;
+}
+
+void media_snd_stop_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl)
+ media_snd_disable_source(mctl);
+}
+
+int media_snd_mixer_init(struct snd_usb_audio *chip)
+{
+ struct device *ctl_dev = &chip->card->ctl_dev;
+ struct media_intf_devnode *ctl_intf;
+ struct usb_mixer_interface *mixer;
+ struct media_device *mdev = chip->media_dev;
+ struct media_mixer_ctl *mctl;
+ u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL;
+ int ret;
+
+ if (!mdev)
+ return -ENODEV;
+
+ ctl_intf = chip->ctl_intf_media_devnode;
+ if (!ctl_intf) {
+ ctl_intf = media_devnode_create(mdev, intf_type, 0,
+ MAJOR(ctl_dev->devt),
+ MINOR(ctl_dev->devt));
+ if (!ctl_intf)
+ return -ENOMEM;
+ chip->ctl_intf_media_devnode = ctl_intf;
+ }
+
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+
+ if (mixer->media_mixer_ctl)
+ continue;
+
+ /* allocate media_mixer_ctl */
+ mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
+ if (!mctl)
+ return -ENOMEM;
+
+ mctl->media_dev = mdev;
+ mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER;
+ mctl->media_entity.name = chip->card->mixername;
+ mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK;
+ mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE;
+ mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE;
+ media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX,
+ mctl->media_pad);
+ ret = media_device_register_entity(mctl->media_dev,
+ &mctl->media_entity);
+ if (ret) {
+ kfree(mctl);
+ return ret;
+ }
+
+ mctl->intf_link = media_create_intf_link(&mctl->media_entity,
+ &ctl_intf->intf,
+ MEDIA_LNK_FL_ENABLED);
+ if (!mctl->intf_link) {
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ kfree(mctl);
+ return -ENOMEM;
+ }
+ mctl->intf_devnode = ctl_intf;
+ mixer->media_mixer_ctl = mctl;
+ }
+ return 0;
+}
+
+static void media_snd_mixer_delete(struct snd_usb_audio *chip)
+{
+ struct usb_mixer_interface *mixer;
+ struct media_device *mdev = chip->media_dev;
+
+ if (!mdev)
+ return;
+
+ list_for_each_entry(mixer, &chip->mixer_list, list) {
+ struct media_mixer_ctl *mctl;
+
+ mctl = mixer->media_mixer_ctl;
+ if (!mixer->media_mixer_ctl)
+ continue;
+
+ if (media_devnode_is_registered(&mdev->devnode)) {
+ media_device_unregister_entity(&mctl->media_entity);
+ media_entity_cleanup(&mctl->media_entity);
+ }
+ kfree(mctl);
+ mixer->media_mixer_ctl = NULL;
+ }
+ if (media_devnode_is_registered(&mdev->devnode))
+ media_devnode_remove(chip->ctl_intf_media_devnode);
+ chip->ctl_intf_media_devnode = NULL;
+}
+
+int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface)
+{
+ struct media_device *mdev;
+ struct usb_device *usbdev = interface_to_usbdev(iface);
+ int ret;
+
+ mdev = media_device_get_devres(&usbdev->dev);
+ if (!mdev)
+ return -ENOMEM;
+ if (!mdev->dev) {
+ /* register media device */
+ mdev->dev = &usbdev->dev;
+ if (usbdev->product)
+ strlcpy(mdev->model, usbdev->product,
+ sizeof(mdev->model));
+ if (usbdev->serial)
+ strlcpy(mdev->serial, usbdev->serial,
+ sizeof(mdev->serial));
+ strcpy(mdev->bus_info, usbdev->devpath);
+ mdev->hw_revision = le16_to_cpu(usbdev->descriptor.bcdDevice);
+ media_device_init(mdev);
+ }
+ if (!media_devnode_is_registered(&mdev->devnode)) {
+ ret = media_device_register(mdev);
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Couldn't register media device. Error: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /* save media device - avoid lookups */
+ chip->media_dev = mdev;
+
+ /* Create media entities for mixer and control dev */
+ ret = media_snd_mixer_init(chip);
+ if (ret) {
+ dev_err(&usbdev->dev,
+ "Couldn't create media mixer entities. Error: %d\n",
+ ret);
+
+ /* clear saved media_dev */
+ chip->media_dev = NULL;
+
+ return ret;
+ }
+ return 0;
+}
+
+void media_snd_device_delete(struct snd_usb_audio *chip)
+{
+ struct media_device *mdev = chip->media_dev;
+
+ media_snd_mixer_delete(chip);
+
+ if (mdev) {
+ if (media_devnode_is_registered(&mdev->devnode))
+ media_device_unregister(mdev);
+ chip->media_dev = NULL;
+ }
+}
diff --git a/sound/usb/media.h b/sound/usb/media.h
new file mode 100644
index 000000000000..1dcdcdc5f7aa
--- /dev/null
+++ b/sound/usb/media.h
@@ -0,0 +1,72 @@
+/*
+ * media.h - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2016 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *
+ * This file is released under the GPLv2.
+ */
+
+/*
+ * This file adds Media Controller support to ALSA driver
+ * to use the Media Controller API to share tuner with DVB
+ * and V4L2 drivers that control media device. Media device
+ * is created based on existing quirks framework. Using this
+ * approach, the media controller API usage can be added for
+ * a specific device.
+*/
+#ifndef __MEDIA_H
+
+#ifdef CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER
+
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <sound/asound.h>
+
+struct media_ctl {
+ struct media_device *media_dev;
+ struct media_entity media_entity;
+ struct media_intf_devnode *intf_devnode;
+ struct media_link *intf_link;
+ struct media_pad media_pad;
+ struct media_pipeline media_pipe;
+};
+
+/*
+ * One source pad each for SNDRV_PCM_STREAM_CAPTURE and
+ * SNDRV_PCM_STREAM_PLAYBACK. One for sink pad to link
+ * to AUDIO Source
+*/
+#define MEDIA_MIXER_PAD_MAX (SNDRV_PCM_STREAM_LAST + 2)
+
+struct media_mixer_ctl {
+ struct media_device *media_dev;
+ struct media_entity media_entity;
+ struct media_intf_devnode *intf_devnode;
+ struct media_link *intf_link;
+ struct media_pad media_pad[MEDIA_MIXER_PAD_MAX];
+ struct media_pipeline media_pipe;
+};
+
+int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface);
+void media_snd_device_delete(struct snd_usb_audio *chip);
+int media_snd_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
+ int stream);
+void media_snd_stream_delete(struct snd_usb_substream *subs);
+int media_snd_start_pipeline(struct snd_usb_substream *subs);
+void media_snd_stop_pipeline(struct snd_usb_substream *subs);
+#else
+static inline int media_snd_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface)
+ { return 0; }
+static inline void media_snd_device_delete(struct snd_usb_audio *chip) { }
+static inline int media_snd_stream_init(struct snd_usb_substream *subs,
+ struct snd_pcm *pcm, int stream)
+ { return 0; }
+static inline void media_snd_stream_delete(struct snd_usb_substream *subs) { }
+static inline int media_snd_start_pipeline(struct snd_usb_substream *subs)
+ { return 0; }
+static inline void media_snd_stop_pipeline(struct snd_usb_substream *subs) { }
+#endif
+#endif /* __MEDIA_H */
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 007cf5831121..47de8af42f16 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume);
/*
* Creates and registers everything needed for a MIDI streaming interface.
*/
-int snd_usbmidi_create(struct snd_card *card,
- struct usb_interface *iface,
- struct list_head *midi_list,
- const struct snd_usb_audio_quirk *quirk)
+int __snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface *iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id)
{
struct snd_usb_midi *umidi;
struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card,
spin_lock_init(&umidi->disc_lock);
init_rwsem(&umidi->disc_rwsem);
mutex_init(&umidi->mutex);
- umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+ if (!usb_id)
+ usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
le16_to_cpu(umidi->dev->descriptor.idProduct));
+ umidi->usb_id = usb_id;
setup_timer(&umidi->error_timer, snd_usbmidi_error_timer,
(unsigned long)umidi);
@@ -2463,4 +2466,4 @@ int snd_usbmidi_create(struct snd_card *card,
list_add_tail(&umidi->list, midi_list);
return 0;
}
-EXPORT_SYMBOL(snd_usbmidi_create);
+EXPORT_SYMBOL(__snd_usbmidi_create);
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
index ad8a3211f8e7..5e25a3fd6c1d 100644
--- a/sound/usb/midi.h
+++ b/sound/usb/midi.h
@@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info {
/* for QUIRK_MIDI_AKAI, data is NULL */
-int snd_usbmidi_create(struct snd_card *card,
+int __snd_usbmidi_create(struct snd_card *card,
+ struct usb_interface *iface,
+ struct list_head *midi_list,
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
+
+static inline int snd_usbmidi_create(struct snd_card *card,
struct usb_interface *iface,
struct list_head *midi_list,
- const struct snd_usb_audio_quirk *quirk);
+ const struct snd_usb_audio_quirk *quirk)
+{
+ return __snd_usbmidi_create(card, iface, midi_list, quirk, 0);
+}
+
void snd_usbmidi_input_stop(struct list_head *p);
void snd_usbmidi_input_start(struct list_head *p);
void snd_usbmidi_disconnect(struct list_head *p);
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 3417ef347e40..f3789446ab9c 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -3,6 +3,8 @@
#include <sound/info.h>
+struct media_mixer_ctl;
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
struct usb_host_interface *hostif;
@@ -22,6 +24,7 @@ struct usb_mixer_interface {
struct urb *rc_urb;
struct usb_ctrlrequest *rc_setup_packet;
u8 rc_buffer[6];
+ struct media_mixer_ctl *media_mixer_ctl;
};
#define MAX_CHANNELS 16 /* max logical channels */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 279025650568..f6c3bf79af9a 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
/* use known values for that card: interface#1 altsetting#1 */
iface = usb_ifnum_to_if(chip->dev, 1);
+ if (!iface || iface->num_altsetting < 2)
+ return -EINVAL;
alts = &iface->altsetting[1];
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
err = snd_usb_ctl_msg(chip->dev,
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 9245f52d43bd..0e4e0640c504 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -35,6 +35,7 @@
#include "pcm.h"
#include "clock.h"
#include "power.h"
+#include "media.h"
#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
@@ -159,6 +160,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
unsigned char data[1];
int err;
+ if (get_iface_desc(alts)->bNumEndpoints < 1)
+ return -EINVAL;
ep = get_endpoint(alts, 0)->bEndpointAddress;
data[0] = 1;
@@ -715,10 +718,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct audioformat *fmt;
int ret;
+ ret = media_snd_start_pipeline(subs);
+ if (ret)
+ return ret;
+
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
- return ret;
+ goto err_ret;
subs->pcm_format = params_format(hw_params);
subs->period_bytes = params_period_bytes(hw_params);
@@ -732,22 +739,27 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
dev_dbg(&subs->dev->dev,
"cannot set format: format = %#x, rate = %d, channels = %d\n",
subs->pcm_format, subs->cur_rate, subs->channels);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_ret;
}
ret = snd_usb_lock_shutdown(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto err_ret;
ret = set_format(subs, fmt);
snd_usb_unlock_shutdown(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto err_ret;
subs->interface = fmt->iface;
subs->altset_idx = fmt->altset_idx;
subs->need_setup_ep = true;
return 0;
+
+err_ret:
+ media_snd_stop_pipeline(subs);
+ return ret;
}
/*
@@ -759,6 +771,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
+ media_snd_stop_pipeline(subs);
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
@@ -1219,6 +1232,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
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];
+ int ret;
subs->interface = -1;
subs->altset_idx = 0;
@@ -1232,7 +1246,12 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
subs->dsd_dop.channel = 0;
subs->dsd_dop.marker = 1;
- return setup_hw_info(runtime, subs);
+ ret = setup_hw_info(runtime, subs);
+ if (ret == 0)
+ ret = media_snd_stream_init(subs, as->pcm, direction);
+ if (ret)
+ snd_usb_autosuspend(subs->stream->chip);
+ return ret;
}
static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
@@ -1241,6 +1260,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
struct snd_usb_substream *subs = &as->substream[direction];
stop_endpoints(subs, true);
+ media_snd_stop_pipeline(subs);
if (subs->interface >= 0 &&
!snd_usb_lock_shutdown(subs->stream->chip)) {
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index c60a776e815d..9d087b19c70c 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2886,6 +2886,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.product_name = pname, \
.ifnum = QUIRK_ANY_INTERFACE, \
.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
+ .media_device = 1, \
} \
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c458d60d5030..fb62bce2435c 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -167,19 +167,20 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
stream = (fp->endpoint & USB_DIR_IN)
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
err = snd_usb_add_audio_stream(chip, stream, fp);
- if (err < 0) {
- kfree(fp);
- kfree(rate_table);
- return err;
- }
+ if (err < 0)
+ goto error;
if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
fp->altset_idx >= iface->num_altsetting) {
- kfree(fp);
- kfree(rate_table);
- return -EINVAL;
+ err = -EINVAL;
+ goto error;
}
alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts);
+ if (altsd->bNumEndpoints < 1) {
+ err = -EINVAL;
+ goto error;
+ }
+
fp->protocol = altsd->bInterfaceProtocol;
if (fp->datainterval == 0)
@@ -190,6 +191,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
snd_usb_init_pitch(chip, fp->iface, alts, fp);
snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
return 0;
+
+ error:
+ kfree(fp);
+ kfree(rate_table);
+ return err;
}
static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
@@ -446,8 +452,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
const struct snd_usb_audio_quirk *quirk =
chip->usb_id == USB_ID(0x0582, 0x002b)
? &ua700_quirk : &uaxx_quirk;
- return snd_usbmidi_create(chip->card, iface,
- &chip->midi_list, quirk);
+ return __snd_usbmidi_create(chip->card, iface,
+ &chip->midi_list, quirk,
+ chip->usb_id);
}
if (altsd->bNumEndpoints != 1)
@@ -974,11 +981,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
- const struct snd_usb_audio_quirk *quirk)
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int id)
{
- u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
-
switch (id) {
case USB_ID(0x041e, 0x3000):
/* SB Extigy needs special boot-up sequence */
@@ -1121,6 +1126,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
switch (chip->usb_id) {
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
+ case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
@@ -1184,7 +1190,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
* "Playback Design" products send bogus feedback data at the start
* of the stream. Ignore them.
*/
- if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+ if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
ep->skip_packets = 4;
@@ -1203,11 +1209,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
void snd_usb_set_interface_quirk(struct usb_device *dev)
{
+ struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+ if (!chip)
+ return;
/*
* "Playback Design" products need a 50ms delay after setting the
* USB interface.
*/
- switch (le16_to_cpu(dev->descriptor.idVendor)) {
+ switch (USB_ID_VENDOR(chip->usb_id)) {
case 0x23ba: /* Playback Design */
case 0x0644: /* TEAC Corp. */
mdelay(50);
@@ -1215,15 +1225,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
}
}
+/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value,
__u16 index, void *data, __u16 size)
{
+ struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+ if (!chip)
+ return;
/*
* "Playback Design" products need a 20ms delay after each
* class compliant request
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
@@ -1231,23 +1246,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
* "TEAC Corp." products need a 20ms delay after each
* class compliant request
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) &&
+ if (USB_ID_VENDOR(chip->usb_id) == 0x0644 &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
/* Marantz/Denon devices with USB DAC functionality need a delay
* after each class compliant request
*/
- if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct)))
+ if (is_marantz_denon_dac(chip->usb_id)
&& (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
/* Zoom R16/24 needs a tiny delay here, otherwise requests like
* get/set frequency return as failed despite actually succeeding.
*/
- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) &&
- (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) &&
+ if (chip->usb_id == USB_ID(0x1686, 0x00dd) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(1);
}
@@ -1264,7 +1277,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
unsigned int sample_bytes)
{
/* Playback Designs */
- if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+ if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
switch (fp->altsetting) {
case 1:
fp->dsd_dop = true;
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 2cd71ed1201f..192ff5ce9452 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
int snd_usb_apply_boot_quirk(struct usb_device *dev,
struct usb_interface *intf,
- const struct snd_usb_audio_quirk *quirk);
+ const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id);
void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
struct audioformat *fmt);
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index c4dc577ab1bd..51258a15f653 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -36,6 +36,7 @@
#include "format.h"
#include "clock.h"
#include "stream.h"
+#include "media.h"
/*
* free a substream
@@ -52,6 +53,7 @@ static void free_substream(struct snd_usb_substream *subs)
kfree(fp);
}
kfree(subs->rate_list.list);
+ media_snd_stream_delete(subs);
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index b665d85555cb..a161c7c1b126 100644
--- a/sound/usb/usbaudio.h
+++ b/sound/usb/usbaudio.h
@@ -30,6 +30,9 @@
*
*/
+struct media_device;
+struct media_intf_devnode;
+
struct snd_usb_audio {
int index;
struct usb_device *dev;
@@ -60,6 +63,8 @@ struct snd_usb_audio {
bool autoclock; /* from the 'autoclock' module param */
struct usb_host_interface *ctrl_intf; /* the audio control interface */
+ struct media_device *media_dev;
+ struct media_intf_devnode *ctl_intf_media_devnode;
};
#define usb_audio_err(chip, fmt, args...) \
@@ -110,6 +115,7 @@ struct snd_usb_audio_quirk {
const char *product_name;
int16_t ifnum;
uint16_t type;
+ bool media_device;
const void *data;
};