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(size: 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(entity: &mctl->media_entity, num_pads: 1, pads: &mctl->media_pad);
70 ret = media_device_register_entity(mdev: mctl->media_dev,
71 entity: &mctl->media_entity);
72 if (ret)
73 goto free_mctl;
74
75 mctl->intf_devnode = media_devnode_create(mdev, type: intf_type, flags: 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(entity: &mctl->media_entity,
83 intf: &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(source: entity, source_pad: mixer_pad,
95 sink: &mctl->media_entity, sink_pad: 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(link: mctl->intf_link);
108devnode_remove:
109 media_devnode_remove(devnode: mctl->intf_devnode);
110unregister_entity:
111 media_device_unregister_entity(entity: &mctl->media_entity);
112free_mctl:
113 kfree(objp: 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(devnode: mdev->devnode)) {
126 media_devnode_remove(devnode: mctl->intf_devnode);
127 media_device_unregister_entity(entity: &mctl->media_entity);
128 media_entity_cleanup(entity: &mctl->media_entity);
129 }
130 kfree(objp: 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(lock: &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(lock: &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, type: intf_type, flags: 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(size: 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(entity: &mctl->media_entity, MEDIA_MIXER_PAD_MAX,
204 pads: mctl->media_pad);
205 ret = media_device_register_entity(mdev: mctl->media_dev,
206 entity: &mctl->media_entity);
207 if (ret) {
208 kfree(objp: mctl);
209 return ret;
210 }
211
212 mctl->intf_link = media_create_intf_link(entity: &mctl->media_entity,
213 intf: &ctl_intf->intf,
214 MEDIA_LNK_FL_ENABLED);
215 if (!mctl->intf_link) {
216 media_device_unregister_entity(entity: &mctl->media_entity);
217 media_entity_cleanup(entity: &mctl->media_entity);
218 kfree(objp: 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(devnode: mdev->devnode)) {
243 media_device_unregister_entity(entity: &mctl->media_entity);
244 media_entity_cleanup(entity: &mctl->media_entity);
245 }
246 kfree(objp: mctl);
247 mixer->media_mixer_ctl = NULL;
248 }
249 if (media_devnode_is_registered(devnode: mdev->devnode))
250 media_devnode_remove(devnode: 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(udev: usbdev, KBUILD_MODNAME, THIS_MODULE);
272 if (IS_ERR(ptr: 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(devnode: mdev->devnode)) {
288 /* don't 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(subs: &stream->substream[0]);
318 snd_media_stream_delete(subs: &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}
328

source code of linux/sound/usb/media.c