File Index
Files and directories
Sign in
FILE

media.c

File preview with syntax highlighting.

linux-6.19.14/sound/usb/media.c C
  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	guard(mutex)(&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	return ret;
148}
149
150void snd_media_stop_pipeline(struct snd_usb_substream *subs)
151{
152	struct media_ctl *mctl = subs->media_ctl;
153
154	if (!mctl)
155		return;
156
157	guard(mutex)(&mctl->media_dev->graph_mutex);
158	if (mctl->media_dev->disable_source)
159		mctl->media_dev->disable_source(&mctl->media_entity);
160}
161
162static int snd_media_mixer_init(struct snd_usb_audio *chip)
163{
164	struct device *ctl_dev = chip->card->ctl_dev;
165	struct media_intf_devnode *ctl_intf;
166	struct usb_mixer_interface *mixer;
167	struct media_device *mdev = chip->media_dev;
168	struct media_mixer_ctl *mctl;
169	u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL;
170	int ret;
171
172	if (!mdev)
173		return -ENODEV;
174
175	ctl_intf = chip->ctl_intf_media_devnode;
176	if (!ctl_intf) {
177		ctl_intf = media_devnode_create(mdev, intf_type, 0,
178						MAJOR(ctl_dev->devt),
179						MINOR(ctl_dev->devt));
180		if (!ctl_intf)
181			return -ENOMEM;
182		chip->ctl_intf_media_devnode = ctl_intf;
183	}
184
185	list_for_each_entry(mixer, &chip->mixer_list, list) {
186
187		if (mixer->media_mixer_ctl)
188			continue;
189
190		/* allocate media_mixer_ctl */
191		mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
192		if (!mctl)
193			return -ENOMEM;
194
195		mctl->media_dev = mdev;
196		mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER;
197		mctl->media_entity.name = chip->card->mixername;
198		mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK;
199		mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE;
200		mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE;
201		media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX,
202				  mctl->media_pad);
203		ret =  media_device_register_entity(mctl->media_dev,
204						    &mctl->media_entity);
205		if (ret) {
206			kfree(mctl);
207			return ret;
208		}
209
210		mctl->intf_link = media_create_intf_link(&mctl->media_entity,
211							 &ctl_intf->intf,
212							 MEDIA_LNK_FL_ENABLED);
213		if (!mctl->intf_link) {
214			media_device_unregister_entity(&mctl->media_entity);
215			media_entity_cleanup(&mctl->media_entity);
216			kfree(mctl);
217			return -ENOMEM;
218		}
219		mctl->intf_devnode = ctl_intf;
220		mixer->media_mixer_ctl = mctl;
221	}
222	return 0;
223}
224
225static void snd_media_mixer_delete(struct snd_usb_audio *chip)
226{
227	struct usb_mixer_interface *mixer;
228	struct media_device *mdev = chip->media_dev;
229
230	if (!mdev)
231		return;
232
233	list_for_each_entry(mixer, &chip->mixer_list, list) {
234		struct media_mixer_ctl *mctl;
235
236		mctl = mixer->media_mixer_ctl;
237		if (!mixer->media_mixer_ctl)
238			continue;
239
240		if (media_devnode_is_registered(mdev->devnode)) {
241			media_device_unregister_entity(&mctl->media_entity);
242			media_entity_cleanup(&mctl->media_entity);
243		}
244		kfree(mctl);
245		mixer->media_mixer_ctl = NULL;
246	}
247	if (media_devnode_is_registered(mdev->devnode))
248		media_devnode_remove(chip->ctl_intf_media_devnode);
249	chip->ctl_intf_media_devnode = NULL;
250}
251
252int snd_media_device_create(struct snd_usb_audio *chip,
253			struct usb_interface *iface)
254{
255	struct media_device *mdev;
256	struct usb_device *usbdev = interface_to_usbdev(iface);
257	int ret = 0;
258
259	/* usb-audio driver is probed for each usb interface, and
260	 * there are multiple interfaces per device. Avoid calling
261	 * media_device_usb_allocate() each time usb_audio_probe()
262	 * is called. Do it only once.
263	 */
264	if (chip->media_dev) {
265		mdev = chip->media_dev;
266		goto snd_mixer_init;
267	}
268
269	mdev = media_device_usb_allocate(usbdev, KBUILD_MODNAME, THIS_MODULE);
270	if (IS_ERR(mdev))
271		return -ENOMEM;
272
273	/* save media device - avoid lookups */
274	chip->media_dev = mdev;
275
276snd_mixer_init:
277	/* Create media entities for mixer and control dev */
278	ret = snd_media_mixer_init(chip);
279	/* media_device might be registered, print error and continue */
280	if (ret)
281		dev_err(&usbdev->dev,
282			"Couldn't create media mixer entities. Error: %d\n",
283			ret);
284
285	if (!media_devnode_is_registered(mdev->devnode)) {
286		/* don't register if snd_media_mixer_init() failed */
287		if (ret)
288			goto create_fail;
289
290		/* register media_device */
291		ret = media_device_register(mdev);
292create_fail:
293		if (ret) {
294			snd_media_mixer_delete(chip);
295			media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
296			/* clear saved media_dev */
297			chip->media_dev = NULL;
298			dev_err(&usbdev->dev,
299				"Couldn't register media device. Error: %d\n",
300				ret);
301			return ret;
302		}
303	}
304
305	return ret;
306}
307
308void snd_media_device_delete(struct snd_usb_audio *chip)
309{
310	struct media_device *mdev = chip->media_dev;
311	struct snd_usb_stream *stream;
312
313	/* release resources */
314	list_for_each_entry(stream, &chip->pcm_list, list) {
315		snd_media_stream_delete(&stream->substream[0]);
316		snd_media_stream_delete(&stream->substream[1]);
317	}
318
319	snd_media_mixer_delete(chip);
320
321	if (mdev) {
322		media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
323		chip->media_dev = NULL;
324	}
325}
~/ Read-only file preview.