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