xref: /openbmc/linux/sound/usb/media.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
166354f18SShuah Khan // SPDX-License-Identifier: GPL-2.0
266354f18SShuah Khan /*
366354f18SShuah Khan  * media.c - Media Controller specific ALSA driver code
466354f18SShuah Khan  *
566354f18SShuah Khan  * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>
666354f18SShuah Khan  *
766354f18SShuah Khan  */
866354f18SShuah Khan 
966354f18SShuah Khan /*
1066354f18SShuah Khan  * This file adds Media Controller support to the ALSA driver
1166354f18SShuah Khan  * to use the Media Controller API to share the tuner with DVB
1266354f18SShuah Khan  * and V4L2 drivers that control the media device.
1366354f18SShuah Khan  *
1466354f18SShuah Khan  * The media device is created based on the existing quirks framework.
1566354f18SShuah Khan  * Using this approach, the media controller API usage can be added for
1666354f18SShuah Khan  * a specific device.
1766354f18SShuah Khan  */
1866354f18SShuah Khan 
1966354f18SShuah Khan #include <linux/init.h>
2066354f18SShuah Khan #include <linux/list.h>
2166354f18SShuah Khan #include <linux/mutex.h>
2266354f18SShuah Khan #include <linux/slab.h>
2366354f18SShuah Khan #include <linux/usb.h>
2466354f18SShuah Khan 
2566354f18SShuah Khan #include <sound/pcm.h>
2666354f18SShuah Khan #include <sound/core.h>
2766354f18SShuah Khan 
2866354f18SShuah Khan #include "usbaudio.h"
2966354f18SShuah Khan #include "card.h"
3066354f18SShuah Khan #include "mixer.h"
3166354f18SShuah Khan #include "media.h"
3266354f18SShuah Khan 
snd_media_stream_init(struct snd_usb_substream * subs,struct snd_pcm * pcm,int stream)3366354f18SShuah Khan int snd_media_stream_init(struct snd_usb_substream *subs, struct snd_pcm *pcm,
3466354f18SShuah Khan 			  int stream)
3566354f18SShuah Khan {
3666354f18SShuah Khan 	struct media_device *mdev;
3766354f18SShuah Khan 	struct media_ctl *mctl;
38*bc41a722STakashi Iwai 	struct device *pcm_dev = pcm->streams[stream].dev;
3966354f18SShuah Khan 	u32 intf_type;
4066354f18SShuah Khan 	int ret = 0;
4166354f18SShuah Khan 	u16 mixer_pad;
4266354f18SShuah Khan 	struct media_entity *entity;
4366354f18SShuah Khan 
4466354f18SShuah Khan 	mdev = subs->stream->chip->media_dev;
4566354f18SShuah Khan 	if (!mdev)
4666354f18SShuah Khan 		return 0;
4766354f18SShuah Khan 
4866354f18SShuah Khan 	if (subs->media_ctl)
4966354f18SShuah Khan 		return 0;
5066354f18SShuah Khan 
5166354f18SShuah Khan 	/* allocate media_ctl */
5266354f18SShuah Khan 	mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
5366354f18SShuah Khan 	if (!mctl)
5466354f18SShuah Khan 		return -ENOMEM;
5566354f18SShuah Khan 
5666354f18SShuah Khan 	mctl->media_dev = mdev;
5766354f18SShuah Khan 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
5866354f18SShuah Khan 		intf_type = MEDIA_INTF_T_ALSA_PCM_PLAYBACK;
5966354f18SShuah Khan 		mctl->media_entity.function = MEDIA_ENT_F_AUDIO_PLAYBACK;
6066354f18SShuah Khan 		mctl->media_pad.flags = MEDIA_PAD_FL_SOURCE;
6166354f18SShuah Khan 		mixer_pad = 1;
6266354f18SShuah Khan 	} else {
6366354f18SShuah Khan 		intf_type = MEDIA_INTF_T_ALSA_PCM_CAPTURE;
6466354f18SShuah Khan 		mctl->media_entity.function = MEDIA_ENT_F_AUDIO_CAPTURE;
6566354f18SShuah Khan 		mctl->media_pad.flags = MEDIA_PAD_FL_SINK;
6666354f18SShuah Khan 		mixer_pad = 2;
6766354f18SShuah Khan 	}
6866354f18SShuah Khan 	mctl->media_entity.name = pcm->name;
6966354f18SShuah Khan 	media_entity_pads_init(&mctl->media_entity, 1, &mctl->media_pad);
7066354f18SShuah Khan 	ret =  media_device_register_entity(mctl->media_dev,
7166354f18SShuah Khan 					    &mctl->media_entity);
7266354f18SShuah Khan 	if (ret)
7366354f18SShuah Khan 		goto free_mctl;
7466354f18SShuah Khan 
7566354f18SShuah Khan 	mctl->intf_devnode = media_devnode_create(mdev, intf_type, 0,
7666354f18SShuah Khan 						  MAJOR(pcm_dev->devt),
7766354f18SShuah Khan 						  MINOR(pcm_dev->devt));
7866354f18SShuah Khan 	if (!mctl->intf_devnode) {
7966354f18SShuah Khan 		ret = -ENOMEM;
8066354f18SShuah Khan 		goto unregister_entity;
8166354f18SShuah Khan 	}
8266354f18SShuah Khan 	mctl->intf_link = media_create_intf_link(&mctl->media_entity,
8366354f18SShuah Khan 						 &mctl->intf_devnode->intf,
8466354f18SShuah Khan 						 MEDIA_LNK_FL_ENABLED);
8566354f18SShuah Khan 	if (!mctl->intf_link) {
8666354f18SShuah Khan 		ret = -ENOMEM;
8766354f18SShuah Khan 		goto devnode_remove;
8866354f18SShuah Khan 	}
8966354f18SShuah Khan 
9066354f18SShuah Khan 	/* create link between mixer and audio */
9166354f18SShuah Khan 	media_device_for_each_entity(entity, mdev) {
9266354f18SShuah Khan 		switch (entity->function) {
9366354f18SShuah Khan 		case MEDIA_ENT_F_AUDIO_MIXER:
9466354f18SShuah Khan 			ret = media_create_pad_link(entity, mixer_pad,
9566354f18SShuah Khan 						    &mctl->media_entity, 0,
9666354f18SShuah Khan 						    MEDIA_LNK_FL_ENABLED);
9766354f18SShuah Khan 			if (ret)
9866354f18SShuah Khan 				goto remove_intf_link;
9966354f18SShuah Khan 			break;
10066354f18SShuah Khan 		}
10166354f18SShuah Khan 	}
10266354f18SShuah Khan 
10366354f18SShuah Khan 	subs->media_ctl = mctl;
10466354f18SShuah Khan 	return 0;
10566354f18SShuah Khan 
10666354f18SShuah Khan remove_intf_link:
10766354f18SShuah Khan 	media_remove_intf_link(mctl->intf_link);
10866354f18SShuah Khan devnode_remove:
10966354f18SShuah Khan 	media_devnode_remove(mctl->intf_devnode);
11066354f18SShuah Khan unregister_entity:
11166354f18SShuah Khan 	media_device_unregister_entity(&mctl->media_entity);
11266354f18SShuah Khan free_mctl:
11366354f18SShuah Khan 	kfree(mctl);
11466354f18SShuah Khan 	return ret;
11566354f18SShuah Khan }
11666354f18SShuah Khan 
snd_media_stream_delete(struct snd_usb_substream * subs)11766354f18SShuah Khan void snd_media_stream_delete(struct snd_usb_substream *subs)
11866354f18SShuah Khan {
11966354f18SShuah Khan 	struct media_ctl *mctl = subs->media_ctl;
12066354f18SShuah Khan 
12166354f18SShuah Khan 	if (mctl) {
12266354f18SShuah Khan 		struct media_device *mdev;
12366354f18SShuah Khan 
12466354f18SShuah Khan 		mdev = mctl->media_dev;
12566354f18SShuah Khan 		if (mdev && media_devnode_is_registered(mdev->devnode)) {
12666354f18SShuah Khan 			media_devnode_remove(mctl->intf_devnode);
12766354f18SShuah Khan 			media_device_unregister_entity(&mctl->media_entity);
12866354f18SShuah Khan 			media_entity_cleanup(&mctl->media_entity);
12966354f18SShuah Khan 		}
13066354f18SShuah Khan 		kfree(mctl);
13166354f18SShuah Khan 		subs->media_ctl = NULL;
13266354f18SShuah Khan 	}
13366354f18SShuah Khan }
13466354f18SShuah Khan 
snd_media_start_pipeline(struct snd_usb_substream * subs)13566354f18SShuah Khan int snd_media_start_pipeline(struct snd_usb_substream *subs)
13666354f18SShuah Khan {
13766354f18SShuah Khan 	struct media_ctl *mctl = subs->media_ctl;
13866354f18SShuah Khan 	int ret = 0;
13966354f18SShuah Khan 
14066354f18SShuah Khan 	if (!mctl)
14166354f18SShuah Khan 		return 0;
14266354f18SShuah Khan 
14366354f18SShuah Khan 	mutex_lock(&mctl->media_dev->graph_mutex);
14466354f18SShuah Khan 	if (mctl->media_dev->enable_source)
14566354f18SShuah Khan 		ret = mctl->media_dev->enable_source(&mctl->media_entity,
14666354f18SShuah Khan 						     &mctl->media_pipe);
14766354f18SShuah Khan 	mutex_unlock(&mctl->media_dev->graph_mutex);
14866354f18SShuah Khan 	return ret;
14966354f18SShuah Khan }
15066354f18SShuah Khan 
snd_media_stop_pipeline(struct snd_usb_substream * subs)15166354f18SShuah Khan void snd_media_stop_pipeline(struct snd_usb_substream *subs)
15266354f18SShuah Khan {
15366354f18SShuah Khan 	struct media_ctl *mctl = subs->media_ctl;
15466354f18SShuah Khan 
15566354f18SShuah Khan 	if (!mctl)
15666354f18SShuah Khan 		return;
15766354f18SShuah Khan 
15866354f18SShuah Khan 	mutex_lock(&mctl->media_dev->graph_mutex);
15966354f18SShuah Khan 	if (mctl->media_dev->disable_source)
16066354f18SShuah Khan 		mctl->media_dev->disable_source(&mctl->media_entity);
16166354f18SShuah Khan 	mutex_unlock(&mctl->media_dev->graph_mutex);
16266354f18SShuah Khan }
16366354f18SShuah Khan 
snd_media_mixer_init(struct snd_usb_audio * chip)16466354f18SShuah Khan static int snd_media_mixer_init(struct snd_usb_audio *chip)
16566354f18SShuah Khan {
1666a66b01dSTakashi Iwai 	struct device *ctl_dev = chip->card->ctl_dev;
16766354f18SShuah Khan 	struct media_intf_devnode *ctl_intf;
16866354f18SShuah Khan 	struct usb_mixer_interface *mixer;
16966354f18SShuah Khan 	struct media_device *mdev = chip->media_dev;
17066354f18SShuah Khan 	struct media_mixer_ctl *mctl;
17166354f18SShuah Khan 	u32 intf_type = MEDIA_INTF_T_ALSA_CONTROL;
17266354f18SShuah Khan 	int ret;
17366354f18SShuah Khan 
17466354f18SShuah Khan 	if (!mdev)
17566354f18SShuah Khan 		return -ENODEV;
17666354f18SShuah Khan 
17766354f18SShuah Khan 	ctl_intf = chip->ctl_intf_media_devnode;
17866354f18SShuah Khan 	if (!ctl_intf) {
17966354f18SShuah Khan 		ctl_intf = media_devnode_create(mdev, intf_type, 0,
18066354f18SShuah Khan 						MAJOR(ctl_dev->devt),
18166354f18SShuah Khan 						MINOR(ctl_dev->devt));
18266354f18SShuah Khan 		if (!ctl_intf)
18366354f18SShuah Khan 			return -ENOMEM;
18466354f18SShuah Khan 		chip->ctl_intf_media_devnode = ctl_intf;
18566354f18SShuah Khan 	}
18666354f18SShuah Khan 
18766354f18SShuah Khan 	list_for_each_entry(mixer, &chip->mixer_list, list) {
18866354f18SShuah Khan 
18966354f18SShuah Khan 		if (mixer->media_mixer_ctl)
19066354f18SShuah Khan 			continue;
19166354f18SShuah Khan 
19266354f18SShuah Khan 		/* allocate media_mixer_ctl */
19366354f18SShuah Khan 		mctl = kzalloc(sizeof(*mctl), GFP_KERNEL);
19466354f18SShuah Khan 		if (!mctl)
19566354f18SShuah Khan 			return -ENOMEM;
19666354f18SShuah Khan 
19766354f18SShuah Khan 		mctl->media_dev = mdev;
19866354f18SShuah Khan 		mctl->media_entity.function = MEDIA_ENT_F_AUDIO_MIXER;
19966354f18SShuah Khan 		mctl->media_entity.name = chip->card->mixername;
20066354f18SShuah Khan 		mctl->media_pad[0].flags = MEDIA_PAD_FL_SINK;
20166354f18SShuah Khan 		mctl->media_pad[1].flags = MEDIA_PAD_FL_SOURCE;
20266354f18SShuah Khan 		mctl->media_pad[2].flags = MEDIA_PAD_FL_SOURCE;
20366354f18SShuah Khan 		media_entity_pads_init(&mctl->media_entity, MEDIA_MIXER_PAD_MAX,
20466354f18SShuah Khan 				  mctl->media_pad);
20566354f18SShuah Khan 		ret =  media_device_register_entity(mctl->media_dev,
20666354f18SShuah Khan 						    &mctl->media_entity);
20766354f18SShuah Khan 		if (ret) {
20866354f18SShuah Khan 			kfree(mctl);
20966354f18SShuah Khan 			return ret;
21066354f18SShuah Khan 		}
21166354f18SShuah Khan 
21266354f18SShuah Khan 		mctl->intf_link = media_create_intf_link(&mctl->media_entity,
21366354f18SShuah Khan 							 &ctl_intf->intf,
21466354f18SShuah Khan 							 MEDIA_LNK_FL_ENABLED);
21566354f18SShuah Khan 		if (!mctl->intf_link) {
21666354f18SShuah Khan 			media_device_unregister_entity(&mctl->media_entity);
21766354f18SShuah Khan 			media_entity_cleanup(&mctl->media_entity);
21866354f18SShuah Khan 			kfree(mctl);
21966354f18SShuah Khan 			return -ENOMEM;
22066354f18SShuah Khan 		}
22166354f18SShuah Khan 		mctl->intf_devnode = ctl_intf;
22266354f18SShuah Khan 		mixer->media_mixer_ctl = mctl;
22366354f18SShuah Khan 	}
22466354f18SShuah Khan 	return 0;
22566354f18SShuah Khan }
22666354f18SShuah Khan 
snd_media_mixer_delete(struct snd_usb_audio * chip)22766354f18SShuah Khan static void snd_media_mixer_delete(struct snd_usb_audio *chip)
22866354f18SShuah Khan {
22966354f18SShuah Khan 	struct usb_mixer_interface *mixer;
23066354f18SShuah Khan 	struct media_device *mdev = chip->media_dev;
23166354f18SShuah Khan 
23266354f18SShuah Khan 	if (!mdev)
23366354f18SShuah Khan 		return;
23466354f18SShuah Khan 
23566354f18SShuah Khan 	list_for_each_entry(mixer, &chip->mixer_list, list) {
23666354f18SShuah Khan 		struct media_mixer_ctl *mctl;
23766354f18SShuah Khan 
23866354f18SShuah Khan 		mctl = mixer->media_mixer_ctl;
23966354f18SShuah Khan 		if (!mixer->media_mixer_ctl)
24066354f18SShuah Khan 			continue;
24166354f18SShuah Khan 
24266354f18SShuah Khan 		if (media_devnode_is_registered(mdev->devnode)) {
24366354f18SShuah Khan 			media_device_unregister_entity(&mctl->media_entity);
24466354f18SShuah Khan 			media_entity_cleanup(&mctl->media_entity);
24566354f18SShuah Khan 		}
24666354f18SShuah Khan 		kfree(mctl);
24766354f18SShuah Khan 		mixer->media_mixer_ctl = NULL;
24866354f18SShuah Khan 	}
24966354f18SShuah Khan 	if (media_devnode_is_registered(mdev->devnode))
25066354f18SShuah Khan 		media_devnode_remove(chip->ctl_intf_media_devnode);
25166354f18SShuah Khan 	chip->ctl_intf_media_devnode = NULL;
25266354f18SShuah Khan }
25366354f18SShuah Khan 
snd_media_device_create(struct snd_usb_audio * chip,struct usb_interface * iface)25466354f18SShuah Khan int snd_media_device_create(struct snd_usb_audio *chip,
25566354f18SShuah Khan 			struct usb_interface *iface)
25666354f18SShuah Khan {
25766354f18SShuah Khan 	struct media_device *mdev;
25866354f18SShuah Khan 	struct usb_device *usbdev = interface_to_usbdev(iface);
25966354f18SShuah Khan 	int ret = 0;
26066354f18SShuah Khan 
26166354f18SShuah Khan 	/* usb-audio driver is probed for each usb interface, and
26266354f18SShuah Khan 	 * there are multiple interfaces per device. Avoid calling
26366354f18SShuah Khan 	 * media_device_usb_allocate() each time usb_audio_probe()
26466354f18SShuah Khan 	 * is called. Do it only once.
26566354f18SShuah Khan 	 */
26666354f18SShuah Khan 	if (chip->media_dev) {
26766354f18SShuah Khan 		mdev = chip->media_dev;
26866354f18SShuah Khan 		goto snd_mixer_init;
26966354f18SShuah Khan 	}
27066354f18SShuah Khan 
27166354f18SShuah Khan 	mdev = media_device_usb_allocate(usbdev, KBUILD_MODNAME, THIS_MODULE);
27266354f18SShuah Khan 	if (IS_ERR(mdev))
27366354f18SShuah Khan 		return -ENOMEM;
27466354f18SShuah Khan 
27566354f18SShuah Khan 	/* save media device - avoid lookups */
27666354f18SShuah Khan 	chip->media_dev = mdev;
27766354f18SShuah Khan 
27866354f18SShuah Khan snd_mixer_init:
27966354f18SShuah Khan 	/* Create media entities for mixer and control dev */
28066354f18SShuah Khan 	ret = snd_media_mixer_init(chip);
28166354f18SShuah Khan 	/* media_device might be registered, print error and continue */
28266354f18SShuah Khan 	if (ret)
28366354f18SShuah Khan 		dev_err(&usbdev->dev,
28466354f18SShuah Khan 			"Couldn't create media mixer entities. Error: %d\n",
28566354f18SShuah Khan 			ret);
28666354f18SShuah Khan 
28766354f18SShuah Khan 	if (!media_devnode_is_registered(mdev->devnode)) {
288ff630b6aSgushengxian 		/* don't register if snd_media_mixer_init() failed */
28966354f18SShuah Khan 		if (ret)
29066354f18SShuah Khan 			goto create_fail;
29166354f18SShuah Khan 
29266354f18SShuah Khan 		/* register media_device */
29366354f18SShuah Khan 		ret = media_device_register(mdev);
29466354f18SShuah Khan create_fail:
29566354f18SShuah Khan 		if (ret) {
29666354f18SShuah Khan 			snd_media_mixer_delete(chip);
29766354f18SShuah Khan 			media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
29866354f18SShuah Khan 			/* clear saved media_dev */
29966354f18SShuah Khan 			chip->media_dev = NULL;
30066354f18SShuah Khan 			dev_err(&usbdev->dev,
30166354f18SShuah Khan 				"Couldn't register media device. Error: %d\n",
30266354f18SShuah Khan 				ret);
30366354f18SShuah Khan 			return ret;
30466354f18SShuah Khan 		}
30566354f18SShuah Khan 	}
30666354f18SShuah Khan 
30766354f18SShuah Khan 	return ret;
30866354f18SShuah Khan }
30966354f18SShuah Khan 
snd_media_device_delete(struct snd_usb_audio * chip)31066354f18SShuah Khan void snd_media_device_delete(struct snd_usb_audio *chip)
31166354f18SShuah Khan {
31266354f18SShuah Khan 	struct media_device *mdev = chip->media_dev;
31366354f18SShuah Khan 	struct snd_usb_stream *stream;
31466354f18SShuah Khan 
31566354f18SShuah Khan 	/* release resources */
31666354f18SShuah Khan 	list_for_each_entry(stream, &chip->pcm_list, list) {
31766354f18SShuah Khan 		snd_media_stream_delete(&stream->substream[0]);
31866354f18SShuah Khan 		snd_media_stream_delete(&stream->substream[1]);
31966354f18SShuah Khan 	}
32066354f18SShuah Khan 
32166354f18SShuah Khan 	snd_media_mixer_delete(chip);
32266354f18SShuah Khan 
32366354f18SShuah Khan 	if (mdev) {
32466354f18SShuah Khan 		media_device_delete(mdev, KBUILD_MODNAME, THIS_MODULE);
32566354f18SShuah Khan 		chip->media_dev = NULL;
32666354f18SShuah Khan 	}
32766354f18SShuah Khan }
328