summaryrefslogtreecommitdiff
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.c14
-rw-r--r--sound/usb/card.h3
-rw-r--r--sound/usb/line6/driver.c72
-rw-r--r--sound/usb/line6/driver.h4
-rw-r--r--sound/usb/line6/podhd.c21
-rw-r--r--sound/usb/line6/toneport.c47
-rw-r--r--sound/usb/media.c327
-rw-r--r--sound/usb/media.h74
-rw-r--r--sound/usb/mixer.c6
-rw-r--r--sound/usb/mixer.h3
-rw-r--r--sound/usb/pcm.c29
-rw-r--r--sound/usb/quirks-table.h85
-rw-r--r--sound/usb/stream.c2
-rw-r--r--sound/usb/usbaudio.h6
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c3
-rw-r--r--sound/usb/usx2y/usb_stream.c20
-rw-r--r--sound/usb/usx2y/usbusx2y.c7
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c6
20 files changed, 656 insertions, 79 deletions
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
index f61b5662bb89..6319b544ba3a 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 d330f74c90e6..e1ce257ab705 100644
--- a/sound/usb/Makefile
+++ b/sound/usb/Makefile
@@ -18,6 +18,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 719e10034553..04465d581204 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -68,6 +68,7 @@
#include "format.h"
#include "power.h"
#include "stream.h"
+#include "media.h"
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("USB Audio");
@@ -673,6 +674,11 @@ static int usb_audio_probe(struct usb_interface *intf,
if (err < 0)
goto __error;
+ if (quirk && quirk->shares_media_device) {
+ /* don't want to fail when snd_media_device_create() fails */
+ snd_media_device_create(chip, intf);
+ }
+
usb_chip[chip->index] = chip;
chip->num_interfaces++;
usb_set_intfdata(intf, chip);
@@ -732,6 +738,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->shares_media_device and
+ * then call the snd_media_device_delete(). Don't have
+ * access to the quirk here. snd_media_device_delete()
+ * accesses mixer_list
+ */
+ snd_media_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 79fa2a19fb7b..2991b9986f66 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -109,6 +109,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;
@@ -161,6 +163,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/line6/driver.c b/sound/usb/line6/driver.c
index 7afe8fae4939..2b57854335b3 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -351,12 +351,16 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
{
struct usb_device *usbdev = line6->usbdev;
int ret;
- unsigned char len;
+ unsigned char *len;
unsigned count;
if (address > 0xffff || datalen > 0xff)
return -EINVAL;
+ len = kmalloc(sizeof(*len), GFP_KERNEL);
+ if (!len)
+ return -ENOMEM;
+
/* query the serial number: */
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
@@ -365,7 +369,7 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
if (ret < 0) {
dev_err(line6->ifcdev, "read request failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
/* Wait for data length. We'll get 0xff until length arrives. */
@@ -375,28 +379,29 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE |
USB_DIR_IN,
- 0x0012, 0x0000, &len, 1,
+ 0x0012, 0x0000, len, 1,
LINE6_TIMEOUT * HZ);
if (ret < 0) {
dev_err(line6->ifcdev,
"receive length failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
- if (len != 0xff)
+ if (*len != 0xff)
break;
}
- if (len == 0xff) {
+ ret = -EIO;
+ if (*len == 0xff) {
dev_err(line6->ifcdev, "read failed after %d retries\n",
count);
- return -EIO;
- } else if (len != datalen) {
+ goto exit;
+ } else if (*len != datalen) {
/* should be equal or something went wrong */
dev_err(line6->ifcdev,
"length mismatch (expected %d, got %d)\n",
- (int)datalen, (int)len);
- return -EIO;
+ (int)datalen, (int)*len);
+ goto exit;
}
/* receive the result: */
@@ -405,12 +410,12 @@ int line6_read_data(struct usb_line6 *line6, unsigned address, void *data,
0x0013, 0x0000, data, datalen,
LINE6_TIMEOUT * HZ);
- if (ret < 0) {
+ if (ret < 0)
dev_err(line6->ifcdev, "read failed (error %d)\n", ret);
- return ret;
- }
- return 0;
+exit:
+ kfree(len);
+ return ret;
}
EXPORT_SYMBOL_GPL(line6_read_data);
@@ -422,12 +427,16 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
{
struct usb_device *usbdev = line6->usbdev;
int ret;
- unsigned char status;
+ unsigned char *status;
int count;
if (address > 0xffff || datalen > 0xffff)
return -EINVAL;
+ status = kmalloc(sizeof(*status), GFP_KERNEL);
+ if (!status)
+ return -ENOMEM;
+
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x0022, address, data, datalen,
@@ -436,7 +445,7 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
if (ret < 0) {
dev_err(line6->ifcdev,
"write request failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
for (count = 0; count < LINE6_READ_WRITE_MAX_RETRIES; count++) {
@@ -447,28 +456,29 @@ int line6_write_data(struct usb_line6 *line6, unsigned address, void *data,
USB_TYPE_VENDOR | USB_RECIP_DEVICE |
USB_DIR_IN,
0x0012, 0x0000,
- &status, 1, LINE6_TIMEOUT * HZ);
+ status, 1, LINE6_TIMEOUT * HZ);
if (ret < 0) {
dev_err(line6->ifcdev,
"receiving status failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
- if (status != 0xff)
+ if (*status != 0xff)
break;
}
- if (status == 0xff) {
+ if (*status == 0xff) {
dev_err(line6->ifcdev, "write failed after %d retries\n",
count);
- return -EIO;
- } else if (status != 0) {
+ ret = -EIO;
+ } else if (*status != 0) {
dev_err(line6->ifcdev, "write failed (error %d)\n", ret);
- return -EIO;
+ ret = -EIO;
}
-
- return 0;
+exit:
+ kfree(status);
+ return ret;
}
EXPORT_SYMBOL_GPL(line6_write_data);
@@ -710,6 +720,15 @@ static int line6_init_cap_control(struct usb_line6 *line6)
return 0;
}
+static void line6_startup_work(struct work_struct *work)
+{
+ struct usb_line6 *line6 =
+ container_of(work, struct usb_line6, startup_work.work);
+
+ if (line6->startup)
+ line6->startup(line6);
+}
+
/*
Probe USB device.
*/
@@ -745,6 +764,7 @@ int line6_probe(struct usb_interface *interface,
line6->properties = properties;
line6->usbdev = usbdev;
line6->ifcdev = &interface->dev;
+ INIT_DELAYED_WORK(&line6->startup_work, line6_startup_work);
strcpy(card->id, properties->id);
strcpy(card->driver, driver_name);
@@ -815,6 +835,8 @@ void line6_disconnect(struct usb_interface *interface)
if (WARN_ON(usbdev != line6->usbdev))
return;
+ cancel_delayed_work(&line6->startup_work);
+
if (line6->urb_listen != NULL)
line6_stop_listen(line6);
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 61425597eb61..650d909c9c4f 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -178,11 +178,15 @@ struct usb_line6 {
fifo;
} messages;
+ /* Work for delayed PCM startup */
+ struct delayed_work startup_work;
+
/* If MIDI is supported, buffer_message contains the pre-processed data;
* otherwise the data is only in urb_listen (buffer_incoming).
*/
void (*process_message)(struct usb_line6 *);
void (*disconnect)(struct usb_line6 *line6);
+ void (*startup)(struct usb_line6 *line6);
};
extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1,
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 36ed9c85c0eb..5f3c87264e66 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -225,28 +225,32 @@ static void podhd_startup_start_workqueue(struct timer_list *t)
static int podhd_dev_start(struct usb_line6_podhd *pod)
{
int ret;
- u8 init_bytes[8];
+ u8 *init_bytes;
int i;
struct usb_device *usbdev = pod->line6.usbdev;
+ init_bytes = kmalloc(8, GFP_KERNEL);
+ if (!init_bytes)
+ return -ENOMEM;
+
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
0x11, 0,
NULL, 0, LINE6_TIMEOUT * HZ);
if (ret < 0) {
dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
/* NOTE: looks like some kind of ping message */
ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x11, 0x0,
- &init_bytes, 3, LINE6_TIMEOUT * HZ);
+ init_bytes, 3, LINE6_TIMEOUT * HZ);
if (ret < 0) {
dev_err(pod->line6.ifcdev,
"receive length failed (error %d)\n", ret);
- return ret;
+ goto exit;
}
pod->firmware_version =
@@ -255,7 +259,7 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
for (i = 0; i <= 16; i++) {
ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
if (ret < 0)
- return ret;
+ goto exit;
}
ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
@@ -263,10 +267,9 @@ static int podhd_dev_start(struct usb_line6_podhd *pod)
USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
1, 0,
NULL, 0, LINE6_TIMEOUT * HZ);
- if (ret < 0)
- return ret;
-
- return 0;
+exit:
+ kfree(init_bytes);
+ return ret;
}
static void podhd_startup_workqueue(struct work_struct *work)
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index f47ba94e6f4a..55865f7e437d 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -54,9 +54,6 @@ struct usb_line6_toneport {
/* Firmware version (x 100) */
u8 firmware_version;
- /* Timer for delayed PCM startup */
- struct timer_list timer;
-
/* Device type */
enum line6_device_type type;
@@ -241,11 +238,8 @@ static int snd_toneport_source_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static void toneport_start_pcm(struct timer_list *t)
+static void toneport_startup(struct usb_line6 *line6)
{
- struct usb_line6_toneport *toneport = from_timer(toneport, t, timer);
- struct usb_line6 *line6 = &toneport->line6;
-
line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
}
@@ -291,8 +285,8 @@ static bool toneport_has_led(struct usb_line6_toneport *toneport)
}
}
-static const char * const led_colors[2] = { "red", "green" };
-static const int led_init_vals[2] = { 0x00, 0x26 };
+static const char * const toneport_led_colors[2] = { "red", "green" };
+static const int toneport_led_init_vals[2] = { 0x00, 0x26 };
static void toneport_update_led(struct usb_line6_toneport *toneport)
{
@@ -320,9 +314,9 @@ static int toneport_init_leds(struct usb_line6_toneport *toneport)
led->toneport = toneport;
snprintf(led->name, sizeof(led->name), "%s::%s",
- dev_name(dev), led_colors[i]);
+ dev_name(dev), toneport_led_colors[i]);
leddev->name = led->name;
- leddev->brightness = led_init_vals[i];
+ leddev->brightness = toneport_led_init_vals[i];
leddev->max_brightness = 0x26;
leddev->brightness_set = toneport_led_brightness_set;
err = led_classdev_register(dev, leddev);
@@ -365,16 +359,21 @@ static bool toneport_has_source_select(struct usb_line6_toneport *toneport)
/*
Setup Toneport device.
*/
-static void toneport_setup(struct usb_line6_toneport *toneport)
+static int toneport_setup(struct usb_line6_toneport *toneport)
{
- u32 ticks;
+ u32 *ticks;
struct usb_line6 *line6 = &toneport->line6;
struct usb_device *usbdev = line6->usbdev;
+ ticks = kmalloc(sizeof(*ticks), GFP_KERNEL);
+ if (!ticks)
+ return -ENOMEM;
+
/* sync time on device with host: */
/* note: 32-bit timestamps overflow in year 2106 */
- ticks = (u32)ktime_get_real_seconds();
- line6_write_data(line6, 0x80c6, &ticks, 4);
+ *ticks = (u32)ktime_get_real_seconds();
+ line6_write_data(line6, 0x80c6, ticks, 4);
+ kfree(ticks);
/* enable device: */
toneport_send_cmd(usbdev, 0x0301, 0x0000);
@@ -388,7 +387,9 @@ static void toneport_setup(struct usb_line6_toneport *toneport)
if (toneport_has_led(toneport))
toneport_update_led(toneport);
- mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ);
+ schedule_delayed_work(&toneport->line6.startup_work,
+ msecs_to_jiffies(TONEPORT_PCM_DELAY * 1000));
+ return 0;
}
/*
@@ -399,8 +400,6 @@ static void line6_toneport_disconnect(struct usb_line6 *line6)
struct usb_line6_toneport *toneport =
(struct usb_line6_toneport *)line6;
- del_timer_sync(&toneport->timer);
-
if (toneport_has_led(toneport))
toneport_remove_leds(toneport);
}
@@ -416,9 +415,9 @@ static int toneport_init(struct usb_line6 *line6,
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6;
toneport->type = id->driver_info;
- timer_setup(&toneport->timer, toneport_start_pcm, 0);
line6->disconnect = line6_toneport_disconnect;
+ line6->startup = toneport_startup;
/* initialize PCM subsystem: */
err = line6_init_pcm(line6, &toneport_pcm_properties);
@@ -451,7 +450,9 @@ static int toneport_init(struct usb_line6 *line6,
return err;
}
- toneport_setup(toneport);
+ err = toneport_setup(toneport);
+ if (err)
+ return err;
/* register audio system: */
return snd_card_register(line6->card);
@@ -463,7 +464,11 @@ static int toneport_init(struct usb_line6 *line6,
*/
static int toneport_reset_resume(struct usb_interface *interface)
{
- toneport_setup(usb_get_intfdata(interface));
+ int err;
+
+ err = toneport_setup(usb_get_intfdata(interface));
+ if (err)
+ return err;
return line6_resume(interface);
}
#endif
diff --git a/sound/usb/media.c b/sound/usb/media.c
new file mode 100644
index 000000000000..812017eacbcf
--- /dev/null
+++ b/sound/usb/media.c
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * media.c - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
+ *
+ */
+
+/*
+ * This file adds Media Controller support to the ALSA driver
+ * to use the Media Controller API to share the tuner with DVB
+ * and V4L2 drivers that control the media device.
+ *
+ * The media device is created based on the 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"
+
+int snd_media_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 0;
+
+ 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 snd_media_stream_delete(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (mctl) {
+ struct media_device *mdev;
+
+ mdev = mctl->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 snd_media_start_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+ int ret = 0;
+
+ if (!mctl)
+ return 0;
+
+ mutex_lock(&mctl->media_dev->graph_mutex);
+ if (mctl->media_dev->enable_source)
+ ret = mctl->media_dev->enable_source(&mctl->media_entity,
+ &mctl->media_pipe);
+ mutex_unlock(&mctl->media_dev->graph_mutex);
+ return ret;
+}
+
+void snd_media_stop_pipeline(struct snd_usb_substream *subs)
+{
+ struct media_ctl *mctl = subs->media_ctl;
+
+ if (!mctl)
+ return;
+
+ mutex_lock(&mctl->media_dev->graph_mutex);
+ if (mctl->media_dev->disable_source)
+ mctl->media_dev->disable_source(&mctl->media_entity);
+ mutex_unlock(&mctl->media_dev->graph_mutex);
+}
+
+static int snd_media_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 snd_media_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 snd_media_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 = 0;
+
+ /* usb-audio driver is probed for each usb interface, and
+ * there are multiple interfaces per device. Avoid calling
+ * media_device_usb_allocate() each time usb_audio_probe()
+ * is called. Do it only once.
+ */
+ if (chip->media_dev) {
+ mdev = chip->media_dev;
+ goto snd_mixer_init;
+ }
+
+ mdev = media_device_usb_allocate(usbdev, KBUILD_MODNAME, THIS_MODULE);
+ if (IS_ERR(mdev))
+ return -ENOMEM;
+
+ /* save media device - avoid lookups */
+ chip->media_dev = mdev;
+
+snd_mixer_init:
+ /* Create media entities for mixer and control dev */
+ ret = snd_media_mixer_init(chip);
+ /* media_device might be registered, print error and continue */
+ if (ret)
+ dev_err(&usbdev->dev,
+ "Couldn't create media mixer entities. Error: %d\n",
+ ret);
+
+ if (!media_devnode_is_registered(mdev->devnode)) {
+ /* dont'register if snd_media_mixer_init() failed */
+ if (ret)
+ goto create_fail;
+
+ /* register media_device */
+ ret = media_device_register(mdev);
+create_fail:
+ if (ret) {
+ snd_media_mixer_delete(chip);
+ media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
+ /* clear saved media_dev */
+ chip->media_dev = NULL;
+ dev_err(&usbdev->dev,
+ "Couldn't register media device. Error: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+void snd_media_device_delete(struct snd_usb_audio *chip)
+{
+ struct media_device *mdev = chip->media_dev;
+ struct snd_usb_stream *stream;
+
+ /* release resources */
+ list_for_each_entry(stream, &chip->pcm_list, list) {
+ snd_media_stream_delete(&stream->substream[0]);
+ snd_media_stream_delete(&stream->substream[1]);
+ }
+
+ snd_media_mixer_delete(chip);
+
+ if (mdev) {
+ media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
+ chip->media_dev = NULL;
+ }
+}
diff --git a/sound/usb/media.h b/sound/usb/media.h
new file mode 100644
index 000000000000..f5bdec1d602f
--- /dev/null
+++ b/sound/usb/media.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * media.h - Media Controller specific ALSA driver code
+ *
+ * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
+ *
+ */
+
+/*
+ * This file adds Media Controller support to the ALSA driver
+ * to use the Media Controller API to share the tuner with DVB
+ * and V4L2 drivers that control the media device.
+ *
+ * The media device is created based on the 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 <linux/media.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/media-dev-allocator.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 snd_media_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface);
+void snd_media_device_delete(struct snd_usb_audio *chip);
+int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
+ int stream);
+void snd_media_stream_delete(struct snd_usb_substream *subs);
+int snd_media_start_pipeline(struct snd_usb_substream *subs);
+void snd_media_stop_pipeline(struct snd_usb_substream *subs);
+#else
+static inline int snd_media_device_create(struct snd_usb_audio *chip,
+ struct usb_interface *iface)
+ { return 0; }
+static inline void snd_media_device_delete(struct snd_usb_audio *chip) { }
+static inline int snd_media_stream_init(struct snd_usb_substream *subs,
+ struct snd_pcm *pcm, int stream)
+ { return 0; }
+static inline void snd_media_stream_delete(struct snd_usb_substream *subs) { }
+static inline int snd_media_start_pipeline(struct snd_usb_substream *subs)
+ { return 0; }
+static inline void snd_media_stop_pipeline(struct snd_usb_substream *subs) { }
+#endif
+#endif /* __MEDIA_H */
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 73d7dff425c1..e003b5e7b01a 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -2675,6 +2675,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
if (! kctl) {
usb_audio_err(state->chip, "cannot malloc kcontrol\n");
+ for (i = 0; i < desc->bNrInPins; i++)
+ kfree(namelist[i]);
kfree(namelist);
kfree(cval);
return -ENOMEM;
@@ -3490,7 +3492,9 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
if (err < 0)
goto _error;
- snd_usb_mixer_apply_create_quirk(mixer);
+ err = snd_usb_mixer_apply_create_quirk(mixer);
+ if (err < 0)
+ goto _error;
err = snd_device_new(chip->card, SNDRV_DEV_CODEC, mixer, &dev_ops);
if (err < 0)
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 3d12af8bf191..394cd9107507 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -4,6 +4,8 @@
#include <sound/info.h>
+struct media_mixer_ctl;
+
struct usb_mixer_interface {
struct snd_usb_audio *chip;
struct usb_host_interface *hostif;
@@ -23,6 +25,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;
bool disconnected;
};
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 056af0a57b22..5d8494b2a026 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
@@ -787,6 +788,10 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
struct audioformat *fmt;
int ret;
+ ret = snd_media_start_pipeline(subs);
+ if (ret)
+ return ret;
+
if (snd_usb_use_vmalloc)
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
@@ -794,7 +799,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
ret = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (ret < 0)
- return ret;
+ goto stop_pipeline;
subs->pcm_format = params_format(hw_params);
subs->period_bytes = params_period_bytes(hw_params);
@@ -808,12 +813,13 @@ 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 stop_pipeline;
}
ret = snd_usb_lock_shutdown(subs->stream->chip);
if (ret < 0)
- return ret;
+ goto stop_pipeline;
ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
if (ret < 0)
@@ -829,6 +835,12 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
unlock:
snd_usb_unlock_shutdown(subs->stream->chip);
+ if (ret < 0)
+ goto stop_pipeline;
+ return ret;
+
+ stop_pipeline:
+ snd_media_stop_pipeline(subs);
return ret;
}
@@ -841,6 +853,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
{
struct snd_usb_substream *subs = substream->runtime->private_data;
+ snd_media_stop_pipeline(subs);
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
@@ -1313,6 +1326,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
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;
@@ -1326,7 +1340,13 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
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 = snd_media_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)
@@ -1337,6 +1357,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
int ret;
stop_endpoints(subs, true);
+ snd_media_stop_pipeline(subs);
if (!as->chip->keep_iface &&
subs->interface >= 0 &&
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 86e80916a029..5600143ff660 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2770,6 +2770,90 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.type = QUIRK_MIDI_NOVATION
}
},
+{
+ /*
+ * Focusrite Scarlett Solo 2nd generation
+ * Reports that playback should use Synch: Synchronous
+ * while still providing a feedback endpoint. Synchronous causes
+ * snapping on some sample rates.
+ * Force it to use Synch: Asynchronous.
+ */
+ USB_DEVICE(0x1235, 0x8205),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 2,
+ .iface = 1,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = 0,
+ .endpoint = 0x01,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC |
+ USB_ENDPOINT_SYNC_ASYNC,
+ .protocol = UAC_VERSION_2,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 44100,
+ .rate_max = 192000,
+ .nr_rates = 6,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200,
+ 96000, 176400, 192000
+ },
+ .clock = 41
+ }
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_FIXED_ENDPOINT,
+ .data = & (const struct audioformat) {
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels = 2,
+ .iface = 2,
+ .altsetting = 1,
+ .altset_idx = 1,
+ .attributes = 0,
+ .endpoint = 0x82,
+ .ep_attr = USB_ENDPOINT_XFER_ISOC |
+ USB_ENDPOINT_SYNC_ASYNC |
+ USB_ENDPOINT_USAGE_IMPLICIT_FB,
+ .protocol = UAC_VERSION_2,
+ .rates = SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_176400 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 44100,
+ .rate_max = 192000,
+ .nr_rates = 6,
+ .rate_table = (unsigned int[]) {
+ 44100, 48000, 88200,
+ 96000, 176400, 192000
+ },
+ .clock = 41
+ }
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
/* Access Music devices */
{
@@ -2887,6 +2971,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
.product_name = pname, \
.ifnum = QUIRK_ANY_INTERFACE, \
.type = QUIRK_AUDIO_ALIGN_TRANSFER, \
+ .shares_media_device = 1, \
} \
}
diff --git a/sound/usb/stream.c b/sound/usb/stream.c
index d9e3de495c16..9f1623e37fb3 100644
--- a/sound/usb/stream.c
+++ b/sound/usb/stream.c
@@ -38,6 +38,7 @@
#include "clock.h"
#include "stream.h"
#include "power.h"
+#include "media.h"
/*
* free a substream
@@ -55,6 +56,7 @@ static void free_substream(struct snd_usb_substream *subs)
}
kfree(subs->rate_list.list);
kfree(subs->str_pd);
+ snd_media_stream_delete(subs);
}
diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
index b9faeca645fd..0968a45c8925 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;
@@ -66,6 +69,8 @@ struct snd_usb_audio {
*/
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...) \
@@ -117,6 +122,7 @@ struct snd_usb_audio_quirk {
const char *profile_name; /* override the card->longname */
int16_t ifnum;
uint16_t type;
+ bool shares_media_device;
const void *data;
};
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index c1dd9a7b48df..bfe1108416cf 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -75,7 +75,8 @@ static int snd_us428ctls_mmap(struct snd_hwdep * hw, struct file *filp, struct v
if (!us428->us428ctls_sharedmem) {
init_waitqueue_head(&us428->us428ctls_wait_queue_head);
- if(!(us428->us428ctls_sharedmem = snd_malloc_pages(sizeof(struct us428ctls_sharedmem), GFP_KERNEL)))
+ us428->us428ctls_sharedmem = alloc_pages_exact(sizeof(struct us428ctls_sharedmem), GFP_KERNEL);
+ if (!us428->us428ctls_sharedmem)
return -ENOMEM;
memset(us428->us428ctls_sharedmem, -1, sizeof(struct us428ctls_sharedmem));
us428->us428ctls_sharedmem->CtlSnapShotLast = -2;
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index 221adf68bd0c..51d73111263a 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -155,9 +155,9 @@ void usb_stream_free(struct usb_stream_kernel *sk)
if (!s)
return;
- free_pages((unsigned long)sk->write_page, get_order(s->write_size));
+ free_pages_exact(sk->write_page, s->write_size);
sk->write_page = NULL;
- free_pages((unsigned long)s, get_order(s->read_size));
+ free_pages_exact(s, s->read_size);
sk->s = NULL;
}
@@ -172,7 +172,6 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
int read_size = sizeof(struct usb_stream);
int write_size;
int usb_frames = dev->speed == USB_SPEED_HIGH ? 8000 : 1000;
- int pg;
in_pipe = usb_rcvisocpipe(dev, in_endpoint);
out_pipe = usb_sndisocpipe(dev, out_endpoint);
@@ -202,11 +201,10 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
goto out;
}
- pg = get_order(read_size);
- sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
- __GFP_NOWARN, pg);
+ sk->s = alloc_pages_exact(read_size,
+ GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
if (!sk->s) {
- snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+ pr_warn("us122l: couldn't allocate read buffer\n");
goto out;
}
sk->s->cfg.version = USB_STREAM_INTERFACE_VERSION;
@@ -221,13 +219,11 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
sk->s->period_size = frame_size * period_frames;
sk->s->write_size = write_size;
- pg = get_order(write_size);
- sk->write_page =
- (void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
- __GFP_NOWARN, pg);
+ sk->write_page = alloc_pages_exact(write_size,
+ GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN);
if (!sk->write_page) {
- snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
+ pr_warn("us122l: couldn't allocate write buffer\n");
usb_stream_free(sk);
return NULL;
}
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
index da4a5a541512..e8687b3bd3c8 100644
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -293,10 +293,8 @@ int usX2Y_In04_init(struct usX2Ydev *usX2Y)
if (! (usX2Y->In04urb = usb_alloc_urb(0, GFP_KERNEL)))
return -ENOMEM;
- if (! (usX2Y->In04Buf = kmalloc(21, GFP_KERNEL))) {
- usb_free_urb(usX2Y->In04urb);
+ if (! (usX2Y->In04Buf = kmalloc(21, GFP_KERNEL)))
return -ENOMEM;
- }
init_waitqueue_head(&usX2Y->In04WaitQueue);
usb_fill_int_urb(usX2Y->In04urb, usX2Y->dev, usb_rcvintpipe(usX2Y->dev, 0x4),
@@ -437,7 +435,8 @@ static void snd_usX2Y_card_private_free(struct snd_card *card)
kfree(usX2Y(card)->In04Buf);
usb_free_urb(usX2Y(card)->In04urb);
if (usX2Y(card)->us428ctls_sharedmem)
- snd_free_pages(usX2Y(card)->us428ctls_sharedmem, sizeof(*usX2Y(card)->us428ctls_sharedmem));
+ free_pages_exact(usX2Y(card)->us428ctls_sharedmem,
+ sizeof(*usX2Y(card)->us428ctls_sharedmem));
if (usX2Y(card)->card_index >= 0 && usX2Y(card)->card_index < SNDRV_CARDS)
snd_usX2Y_card_used[usX2Y(card)->card_index] = 0;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index 714cf50d4a4c..ace8185c3f6d 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -488,7 +488,9 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
if (NULL == usX2Y->hwdep_pcm_shm) {
- if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
+ usX2Y->hwdep_pcm_shm = alloc_pages_exact(sizeof(struct snd_usX2Y_hwdep_pcm_shm),
+ GFP_KERNEL);
+ if (!usX2Y->hwdep_pcm_shm)
return -ENOMEM;
memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
}
@@ -700,7 +702,7 @@ static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
{
struct usX2Ydev *usX2Y = hwdep->private_data;
if (NULL != usX2Y->hwdep_pcm_shm)
- snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
+ free_pages_exact(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
}