summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShuah Khan <shuah@kernel.org>2019-04-01 20:40:22 -0400
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>2019-04-22 11:21:06 -0400
commit66354f18fe5fbb65f7b10a519654013d6df09f80 (patch)
treecbb92334e39e07c0672f86d12319a16eded35aa3
parente377d3e98e70ecd72b3eda158428d88c36881224 (diff)
media: sound/usb: Use Media Controller API to share media resources
Media Device Allocator API to allows multiple drivers share a media device. This API solves a very common use-case for media devices where one physical device (an USB stick) provides both audio and video. When such media device exposes a standard USB Audio class, a proprietary Video class, two or more independent drivers will share a single physical USB bridge. In such cases, it is necessary to coordinate access to the shared resource. Using this API, drivers can allocate a media device with the shared struct device as the key. Once the media device is allocated by a driver, other drivers can get a reference to it. The media device is released when all the references are released. Change the ALSA driver to use the Media Controller API to share media resources with DVB, and V4L2 drivers on a AU0828 media device. The Media Controller specific initialization is done after sound card is registered. ALSA creates Media interface and entity function graph nodes for Control, Mixer, PCM Playback, and PCM Capture devices. snd_usb_hw_params() will call Media Controller enable source handler interface to request the media resource. If resource request is granted, it will release it from snd_usb_hw_free(). If resource is busy, -EBUSY is returned. Media specific cleanup is done in usb_audio_disconnect(). Reviewed-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Shuah Khan <shuah@kernel.org> Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
-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/media.c327
-rw-r--r--sound/usb/media.h74
-rw-r--r--sound/usb/mixer.h3
-rw-r--r--sound/usb/pcm.c29
-rw-r--r--sound/usb/quirks-table.h1
-rw-r--r--sound/usb/stream.c2
-rw-r--r--sound/usb/usbaudio.h6
11 files changed, 461 insertions, 4 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
15 select SND_RAWMIDI 15 select SND_RAWMIDI
16 select SND_PCM 16 select SND_PCM
17 select BITREVERSE 17 select BITREVERSE
18 select SND_USB_AUDIO_USE_MEDIA_CONTROLLER if MEDIA_CONTROLLER && (MEDIA_SUPPORT=y || MEDIA_SUPPORT=SND_USB_AUDIO)
18 help 19 help
19 Say Y here to include support for USB audio and USB MIDI 20 Say Y here to include support for USB audio and USB MIDI
20 devices. 21 devices.
@@ -22,6 +23,9 @@ config SND_USB_AUDIO
22 To compile this driver as a module, choose M here: the module 23 To compile this driver as a module, choose M here: the module
23 will be called snd-usb-audio. 24 will be called snd-usb-audio.
24 25
26config SND_USB_AUDIO_USE_MEDIA_CONTROLLER
27 bool
28
25config SND_USB_UA101 29config SND_USB_UA101
26 tristate "Edirol UA-101/UA-1000 driver" 30 tristate "Edirol UA-101/UA-1000 driver"
27 select SND_PCM 31 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 \
18 quirks.o \ 18 quirks.o \
19 stream.o 19 stream.o
20 20
21snd-usb-audio-$(CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER) += media.o
22
21snd-usbmidi-lib-objs := midi.o 23snd-usbmidi-lib-objs := midi.o
22 24
23# Toplevel Module Dependency 25# 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 @@
68#include "format.h" 68#include "format.h"
69#include "power.h" 69#include "power.h"
70#include "stream.h" 70#include "stream.h"
71#include "media.h"
71 72
72MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); 73MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
73MODULE_DESCRIPTION("USB Audio"); 74MODULE_DESCRIPTION("USB Audio");
@@ -673,6 +674,11 @@ static int usb_audio_probe(struct usb_interface *intf,
673 if (err < 0) 674 if (err < 0)
674 goto __error; 675 goto __error;
675 676
677 if (quirk && quirk->shares_media_device) {
678 /* don't want to fail when snd_media_device_create() fails */
679 snd_media_device_create(chip, intf);
680 }
681
676 usb_chip[chip->index] = chip; 682 usb_chip[chip->index] = chip;
677 chip->num_interfaces++; 683 chip->num_interfaces++;
678 usb_set_intfdata(intf, chip); 684 usb_set_intfdata(intf, chip);
@@ -732,6 +738,14 @@ static void usb_audio_disconnect(struct usb_interface *intf)
732 list_for_each(p, &chip->midi_list) { 738 list_for_each(p, &chip->midi_list) {
733 snd_usbmidi_disconnect(p); 739 snd_usbmidi_disconnect(p);
734 } 740 }
741 /*
742 * Nice to check quirk && quirk->shares_media_device and
743 * then call the snd_media_device_delete(). Don't have
744 * access to the quirk here. snd_media_device_delete()
745 * accesses mixer_list
746 */
747 snd_media_device_delete(chip);
748
735 /* release mixer resources */ 749 /* release mixer resources */
736 list_for_each_entry(mixer, &chip->mixer_list, list) { 750 list_for_each_entry(mixer, &chip->mixer_list, list) {
737 snd_usb_mixer_disconnect(mixer); 751 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 {
109 struct list_head list; 109 struct list_head list;
110}; 110};
111 111
112struct media_ctl;
113
112struct snd_usb_substream { 114struct snd_usb_substream {
113 struct snd_usb_stream *stream; 115 struct snd_usb_stream *stream;
114 struct usb_device *dev; 116 struct usb_device *dev;
@@ -161,6 +163,7 @@ struct snd_usb_substream {
161 } dsd_dop; 163 } dsd_dop;
162 164
163 bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */ 165 bool trigger_tstamp_pending_update; /* trigger timestamp being updated from initial estimate */
166 struct media_ctl *media_ctl;
164}; 167};
165 168
166struct snd_usb_stream { 169struct snd_usb_stream {
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 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * media.c - Media Controller specific ALSA driver code
4 *
5 * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
6 *
7 */
8
9/*
10 * This file adds Media Controller support to the ALSA driver
11 * to use the Media Controller API to share the tuner with DVB
12 * and V4L2 drivers that control the media device.
13 *
14 * The media device is created based on the existing quirks framework.
15 * Using this approach, the media controller API usage can be added for
16 * a specific device.
17 */
18
19#include <linux/init.h>
20#include <linux/list.h>
21#include <linux/mutex.h>
22#include <linux/slab.h>
23#include <linux/usb.h>
24
25#include <sound/pcm.h>
26#include <sound/core.h>
27
28#include "usbaudio.h"
29#include "card.h"
30#include "mixer.h"
31#include "media.h"
32
33int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
34 int stream)
35{
36 struct media_device *mdev;
37 struct media_ctl *mctl;
38 struct device *pcm_dev = &pcm->streams[stream].dev;
39 u32 intf_type;
40 int ret = 0;
41 u16 mixer_pad;
42 struct media_entity *entity;
43
44 mdev = subs->stream->chip->media_dev;
45 if (!mdev)
46 return 0;
47
48 if (subs->media_ctl)
49 return 0;
50
51 /* allocate media_ctl */
52 mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
53 if (!mctl)
54 return -ENOMEM;
55
56 mctl->media_dev = mdev;
57 if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
58 intf_type = MEDIA_INTF_T_ALSA_PCM_PLAYBACK;
59 mctl->media_entity.function = MEDIA_ENT_F_AUDIO_PLAYBACK;
60 mctl->media_pad.flags = MEDIA_PAD_FL_SOURCE;
61 mixer_pad = 1;
62 } else {
63 intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE;
64 mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE;
65 mctl->media_pad.flags = MEDIA_PAD_FL_SINK;
66 mixer_pad = 2;
67 }
68 mctl->media_entity.name = pcm->name;
69 media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad);
70 ret = media_device_register_entity(mctl->media_dev,
71 &mctl->media_entity);
72 if (ret)
73 goto free_mctl;
74
75 mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0,
76 MAJOR(pcm_dev->devt),
77 MINOR(pcm_dev->devt));
78 if (!mctl->intf_devnode) {
79 ret = -ENOMEM;
80 goto unregister_entity;
81 }
82 mctl->intf_link = media_create_intf_link(&mctl->media_entity,
83 &mctl->intf_devnode->intf,
84 MEDIA_LNK_FL_ENABLED);
85 if (!mctl->intf_link) {
86 ret = -ENOMEM;
87 goto devnode_remove;
88 }
89
90 /* create link between mixer and audio */
91 media_device_for_each_entity(entity, mdev) {
92 switch (entity->function) {
93 case MEDIA_ENT_F_AUDIO_MIXER:
94 ret = media_create_pad_link(entity, mixer_pad,
95 &mctl->media_entity, 0,
96 MEDIA_LNK_FL_ENABLED);
97 if (ret)
98 goto remove_intf_link;
99 break;
100 }
101 }
102
103 subs->media_ctl = mctl;
104 return 0;
105
106remove_intf_link:
107 media_remove_intf_link(mctl->intf_link);
108devnode_remove:
109 media_devnode_remove(mctl->intf_devnode);
110unregister_entity:
111 media_device_unregister_entity(&mctl->media_entity);
112free_mctl:
113 kfree(mctl);
114 return ret;
115}
116
117void snd_media_stream_delete(struct snd_usb_substream *subs)
118{
119 struct media_ctl *mctl = subs->media_ctl;
120
121 if (mctl) {
122 struct media_device *mdev;
123
124 mdev = mctl->media_dev;
125 if (mdev && media_devnode_is_registered(mdev->devnode)) {
126 media_devnode_remove(mctl->intf_devnode);
127 media_device_unregister_entity(&mctl->media_entity);
128 media_entity_cleanup(&mctl->media_entity);
129 }
130 kfree(mctl);
131 subs->media_ctl = NULL;
132 }
133}
134
135int snd_media_start_pipeline(struct snd_usb_substream *subs)
136{
137 struct media_ctl *mctl = subs->media_ctl;
138 int ret = 0;
139
140 if (!mctl)
141 return 0;
142
143 mutex_lock(&mctl->media_dev->graph_mutex);
144 if (mctl->media_dev->enable_source)
145 ret = mctl->media_dev->enable_source(&mctl->media_entity,
146 &mctl->media_pipe);
147 mutex_unlock(&mctl->media_dev->graph_mutex);
148 return ret;
149}
150
151void snd_media_stop_pipeline(struct snd_usb_substream *subs)
152{
153 struct media_ctl *mctl = subs->media_ctl;
154
155 if (!mctl)
156 return;
157
158 mutex_lock(&mctl->media_dev->graph_mutex);
159 if (mctl->media_dev->disable_source)
160 mctl->media_dev->disable_source(&mctl->media_entity);
161 mutex_unlock(&mctl->media_dev->graph_mutex);
162}
163
164static int snd_media_mixer_init(struct snd_usb_audio *chip)
165{
166 struct device *ctl_dev = &chip->card->ctl_dev;
167 struct media_intf_devnode *ctl_intf;
168 struct usb_mixer_interface *mixer;
169 struct media_device *mdev = chip->media_dev;
170 struct media_mixer_ctl *mctl;
171 u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL;
172 int ret;
173
174 if (!mdev)
175 return -ENODEV;
176
177 ctl_intf = chip->ctl_intf_media_devnode;
178 if (!ctl_intf) {
179 ctl_intf = media_devnode_create(mdev, intf_type, 0,
180 MAJOR(ctl_dev->devt),
181 MINOR(ctl_dev->devt));
182 if (!ctl_intf)
183 return -ENOMEM;
184 chip->ctl_intf_media_devnode = ctl_intf;
185 }
186
187 list_for_each_entry(mixer, &chip->mixer_list, list) {
188
189 if (mixer->media_mixer_ctl)
190 continue;
191
192 /* allocate media_mixer_ctl */
193 mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
194 if (!mctl)
195 return -ENOMEM;
196
197 mctl->media_dev = mdev;
198 mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER;
199 mctl->media_entity.name = chip->card->mixername;
200 mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK;
201 mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE;
202 mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE;
203 media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX,
204 mctl->media_pad);
205 ret = media_device_register_entity(mctl->media_dev,
206 &mctl->media_entity);
207 if (ret) {
208 kfree(mctl);
209 return ret;
210 }
211
212 mctl->intf_link = media_create_intf_link(&mctl->media_entity,
213 &ctl_intf->intf,
214 MEDIA_LNK_FL_ENABLED);
215 if (!mctl->intf_link) {
216 media_device_unregister_entity(&mctl->media_entity);
217 media_entity_cleanup(&mctl->media_entity);
218 kfree(mctl);
219 return -ENOMEM;
220 }
221 mctl->intf_devnode = ctl_intf;
222 mixer->media_mixer_ctl = mctl;
223 }
224 return 0;
225}
226
227static void snd_media_mixer_delete(struct snd_usb_audio *chip)
228{
229 struct usb_mixer_interface *mixer;
230 struct media_device *mdev = chip->media_dev;
231
232 if (!mdev)
233 return;
234
235 list_for_each_entry(mixer, &chip->mixer_list, list) {
236 struct media_mixer_ctl *mctl;
237
238 mctl = mixer->media_mixer_ctl;
239 if (!mixer->media_mixer_ctl)
240 continue;
241
242 if (media_devnode_is_registered(mdev->devnode)) {
243 media_device_unregister_entity(&mctl->media_entity);
244 media_entity_cleanup(&mctl->media_entity);
245 }
246 kfree(mctl);
247 mixer->media_mixer_ctl = NULL;
248 }
249 if (media_devnode_is_registered(mdev->devnode))
250 media_devnode_remove(chip->ctl_intf_media_devnode);
251 chip->ctl_intf_media_devnode = NULL;
252}
253
254int snd_media_device_create(struct snd_usb_audio *chip,
255 struct usb_interface *iface)
256{
257 struct media_device *mdev;
258 struct usb_device *usbdev = interface_to_usbdev(iface);
259 int ret = 0;
260
261 /* usb-audio driver is probed for each usb interface, and
262 * there are multiple interfaces per device. Avoid calling
263 * media_device_usb_allocate() each time usb_audio_probe()
264 * is called. Do it only once.
265 */
266 if (chip->media_dev) {
267 mdev = chip->media_dev;
268 goto snd_mixer_init;
269 }
270
271 mdev = media_device_usb_allocate(usbdev, KBUILD_MODNAME, THIS_MODULE);
272 if (IS_ERR(mdev))
273 return -ENOMEM;
274
275 /* save media device - avoid lookups */
276 chip->media_dev = mdev;
277
278snd_mixer_init:
279 /* Create media entities for mixer and control dev */
280 ret = snd_media_mixer_init(chip);
281 /* media_device might be registered, print error and continue */
282 if (ret)
283 dev_err(&usbdev->dev,
284 "Couldn't create media mixer entities. Error: %d\n",
285 ret);
286
287 if (!media_devnode_is_registered(mdev->devnode)) {
288 /* dont'register if snd_media_mixer_init() failed */
289 if (ret)
290 goto create_fail;
291
292 /* register media_device */
293 ret = media_device_register(mdev);
294create_fail:
295 if (ret) {
296 snd_media_mixer_delete(chip);
297 media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
298 /* clear saved media_dev */
299 chip->media_dev = NULL;
300 dev_err(&usbdev->dev,
301 "Couldn't register media device. Error: %d\n",
302 ret);
303 return ret;
304 }
305 }
306
307 return ret;
308}
309
310void snd_media_device_delete(struct snd_usb_audio *chip)
311{
312 struct media_device *mdev = chip->media_dev;
313 struct snd_usb_stream *stream;
314
315 /* release resources */
316 list_for_each_entry(stream, &chip->pcm_list, list) {
317 snd_media_stream_delete(&stream->substream[0]);
318 snd_media_stream_delete(&stream->substream[1]);
319 }
320
321 snd_media_mixer_delete(chip);
322
323 if (mdev) {
324 media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
325 chip->media_dev = NULL;
326 }
327}
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 @@
1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * media.h - Media Controller specific ALSA driver code
4 *
5 * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
6 *
7 */
8
9/*
10 * This file adds Media Controller support to the ALSA driver
11 * to use the Media Controller API to share the tuner with DVB
12 * and V4L2 drivers that control the media device.
13 *
14 * The media device is created based on the existing quirks framework.
15 * Using this approach, the media controller API usage can be added for
16 * a specific device.
17 */
18#ifndef __MEDIA_H
19
20#ifdef CONFIG_SND_USB_AUDIO_USE_MEDIA_CONTROLLER
21
22#include <linux/media.h>
23#include <media/media-device.h>
24#include <media/media-entity.h>
25#include <media/media-dev-allocator.h>
26#include <sound/asound.h>
27
28struct media_ctl {
29 struct media_device *media_dev;
30 struct media_entity media_entity;
31 struct media_intf_devnode *intf_devnode;
32 struct media_link *intf_link;
33 struct media_pad media_pad;
34 struct media_pipeline media_pipe;
35};
36
37/*
38 * One source pad each for SNDRV_PCM_STREAM_CAPTURE and
39 * SNDRV_PCM_STREAM_PLAYBACK. One for sink pad to link
40 * to AUDIO Source
41 */
42#define MEDIA_MIXER_PAD_MAX (SNDRV_PCM_STREAM_LAST + 2)
43
44struct media_mixer_ctl {
45 struct media_device *media_dev;
46 struct media_entity media_entity;
47 struct media_intf_devnode *intf_devnode;
48 struct media_link *intf_link;
49 struct media_pad media_pad[MEDIA_MIXER_PAD_MAX];
50 struct media_pipeline media_pipe;
51};
52
53int snd_media_device_create(struct snd_usb_audio *chip,
54 struct usb_interface *iface);
55void snd_media_device_delete(struct snd_usb_audio *chip);
56int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
57 int stream);
58void snd_media_stream_delete(struct snd_usb_substream *subs);
59int snd_media_start_pipeline(struct snd_usb_substream *subs);
60void snd_media_stop_pipeline(struct snd_usb_substream *subs);
61#else
62static inline int snd_media_device_create(struct snd_usb_audio *chip,
63 struct usb_interface *iface)
64 { return 0; }
65static inline void snd_media_device_delete(struct snd_usb_audio *chip) { }
66static inline int snd_media_stream_init(struct snd_usb_substream *subs,
67 struct snd_pcm *pcm, int stream)
68 { return 0; }
69static inline void snd_media_stream_delete(struct snd_usb_substream *subs) { }
70static inline int snd_media_start_pipeline(struct snd_usb_substream *subs)
71 { return 0; }
72static inline void snd_media_stop_pipeline(struct snd_usb_substream *subs) { }
73#endif
74#endif /* __MEDIA_H */
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 @@
4 4
5#include <sound/info.h> 5#include <sound/info.h>
6 6
7struct media_mixer_ctl;
8
7struct usb_mixer_interface { 9struct usb_mixer_interface {
8 struct snd_usb_audio *chip; 10 struct snd_usb_audio *chip;
9 struct usb_host_interface *hostif; 11 struct usb_host_interface *hostif;
@@ -23,6 +25,7 @@ struct usb_mixer_interface {
23 struct urb *rc_urb; 25 struct urb *rc_urb;
24 struct usb_ctrlrequest *rc_setup_packet; 26 struct usb_ctrlrequest *rc_setup_packet;
25 u8 rc_buffer[6]; 27 u8 rc_buffer[6];
28 struct media_mixer_ctl *media_mixer_ctl;
26 29
27 bool disconnected; 30 bool disconnected;
28}; 31};
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 @@
35#include "pcm.h" 35#include "pcm.h"
36#include "clock.h" 36#include "clock.h"
37#include "power.h" 37#include "power.h"
38#include "media.h"
38 39
39#define SUBSTREAM_FLAG_DATA_EP_STARTED 0 40#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
40#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1 41#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
@@ -787,6 +788,10 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
787 struct audioformat *fmt; 788 struct audioformat *fmt;
788 int ret; 789 int ret;
789 790
791 ret = snd_media_start_pipeline(subs);
792 if (ret)
793 return ret;
794
790 if (snd_usb_use_vmalloc) 795 if (snd_usb_use_vmalloc)
791 ret = snd_pcm_lib_alloc_vmalloc_buffer(substream, 796 ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
792 params_buffer_bytes(hw_params)); 797 params_buffer_bytes(hw_params));
@@ -794,7 +799,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
794 ret = snd_pcm_lib_malloc_pages(substream, 799 ret = snd_pcm_lib_malloc_pages(substream,
795 params_buffer_bytes(hw_params)); 800 params_buffer_bytes(hw_params));
796 if (ret < 0) 801 if (ret < 0)
797 return ret; 802 goto stop_pipeline;
798 803
799 subs->pcm_format = params_format(hw_params); 804 subs->pcm_format = params_format(hw_params);
800 subs->period_bytes = params_period_bytes(hw_params); 805 subs->period_bytes = params_period_bytes(hw_params);
@@ -808,12 +813,13 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
808 dev_dbg(&subs->dev->dev, 813 dev_dbg(&subs->dev->dev,
809 "cannot set format: format = %#x, rate = %d, channels = %d\n", 814 "cannot set format: format = %#x, rate = %d, channels = %d\n",
810 subs->pcm_format, subs->cur_rate, subs->channels); 815 subs->pcm_format, subs->cur_rate, subs->channels);
811 return -EINVAL; 816 ret = -EINVAL;
817 goto stop_pipeline;
812 } 818 }
813 819
814 ret = snd_usb_lock_shutdown(subs->stream->chip); 820 ret = snd_usb_lock_shutdown(subs->stream->chip);
815 if (ret < 0) 821 if (ret < 0)
816 return ret; 822 goto stop_pipeline;
817 823
818 ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0); 824 ret = snd_usb_pcm_change_state(subs, UAC3_PD_STATE_D0);
819 if (ret < 0) 825 if (ret < 0)
@@ -829,6 +835,12 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
829 835
830 unlock: 836 unlock:
831 snd_usb_unlock_shutdown(subs->stream->chip); 837 snd_usb_unlock_shutdown(subs->stream->chip);
838 if (ret < 0)
839 goto stop_pipeline;
840 return ret;
841
842 stop_pipeline:
843 snd_media_stop_pipeline(subs);
832 return ret; 844 return ret;
833} 845}
834 846
@@ -841,6 +853,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
841{ 853{
842 struct snd_usb_substream *subs = substream->runtime->private_data; 854 struct snd_usb_substream *subs = substream->runtime->private_data;
843 855
856 snd_media_stop_pipeline(subs);
844 subs->cur_audiofmt = NULL; 857 subs->cur_audiofmt = NULL;
845 subs->cur_rate = 0; 858 subs->cur_rate = 0;
846 subs->period_bytes = 0; 859 subs->period_bytes = 0;
@@ -1313,6 +1326,7 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
1313 struct snd_usb_stream *as = snd_pcm_substream_chip(substream); 1326 struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
1314 struct snd_pcm_runtime *runtime = substream->runtime; 1327 struct snd_pcm_runtime *runtime = substream->runtime;
1315 struct snd_usb_substream *subs = &as->substream[direction]; 1328 struct snd_usb_substream *subs = &as->substream[direction];
1329 int ret;
1316 1330
1317 subs->interface = -1; 1331 subs->interface = -1;
1318 subs->altset_idx = 0; 1332 subs->altset_idx = 0;
@@ -1326,7 +1340,13 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
1326 subs->dsd_dop.channel = 0; 1340 subs->dsd_dop.channel = 0;
1327 subs->dsd_dop.marker = 1; 1341 subs->dsd_dop.marker = 1;
1328 1342
1329 return setup_hw_info(runtime, subs); 1343 ret = setup_hw_info(runtime, subs);
1344 if (ret == 0) {
1345 ret = snd_media_stream_init(subs, as->pcm, direction);
1346 if (ret)
1347 snd_usb_autosuspend(subs->stream->chip);
1348 }
1349 return ret;
1330} 1350}
1331 1351
1332static int snd_usb_pcm_close(struct snd_pcm_substream *substream) 1352static 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)
1337 int ret; 1357 int ret;
1338 1358
1339 stop_endpoints(subs, true); 1359 stop_endpoints(subs, true);
1360 snd_media_stop_pipeline(subs);
1340 1361
1341 if (!as->chip->keep_iface && 1362 if (!as->chip->keep_iface &&
1342 subs->interface >= 0 && 1363 subs->interface >= 0 &&
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 86e80916a029..8cbca137ee6f 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -2887,6 +2887,7 @@ YAMAHA_DEVICE(0x7010, "UB99"),
2887 .product_name = pname, \ 2887 .product_name = pname, \
2888 .ifnum = QUIRK_ANY_INTERFACE, \ 2888 .ifnum = QUIRK_ANY_INTERFACE, \
2889 .type = QUIRK_AUDIO_ALIGN_TRANSFER, \ 2889 .type = QUIRK_AUDIO_ALIGN_TRANSFER, \
2890 .shares_media_device = 1, \
2890 } \ 2891 } \
2891} 2892}
2892 2893
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 @@
38#include "clock.h" 38#include "clock.h"
39#include "stream.h" 39#include "stream.h"
40#include "power.h" 40#include "power.h"
41#include "media.h"
41 42
42/* 43/*
43 * free a substream 44 * free a substream
@@ -55,6 +56,7 @@ static void free_substream(struct snd_usb_substream *subs)
55 } 56 }
56 kfree(subs->rate_list.list); 57 kfree(subs->rate_list.list);
57 kfree(subs->str_pd); 58 kfree(subs->str_pd);
59 snd_media_stream_delete(subs);
58} 60}
59 61
60 62
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 @@
30 * 30 *
31 */ 31 */
32 32
33struct media_device;
34struct media_intf_devnode;
35
33struct snd_usb_audio { 36struct snd_usb_audio {
34 int index; 37 int index;
35 struct usb_device *dev; 38 struct usb_device *dev;
@@ -66,6 +69,8 @@ struct snd_usb_audio {
66 */ 69 */
67 70
68 struct usb_host_interface *ctrl_intf; /* the audio control interface */ 71 struct usb_host_interface *ctrl_intf; /* the audio control interface */
72 struct media_device *media_dev;
73 struct media_intf_devnode *ctl_intf_media_devnode;
69}; 74};
70 75
71#define usb_audio_err(chip, fmt, args...) \ 76#define usb_audio_err(chip, fmt, args...) \
@@ -117,6 +122,7 @@ struct snd_usb_audio_quirk {
117 const char *profile_name; /* override the card->longname */ 122 const char *profile_name; /* override the card->longname */
118 int16_t ifnum; 123 int16_t ifnum;
119 uint16_t type; 124 uint16_t type;
125 bool shares_media_device;
120 const void *data; 126 const void *data;
121}; 127};
122 128