cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

media.c (8273B)


      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		/* 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(&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}