brintos

brintos / linux-shallow public Read only

0
0
Text · 8.1 KiB · d48db6f Raw
328 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * media.c - Media Controller specific ALSA driver code4 *5 * Copyright (c) 2019 Shuah Khan <shuah@kernel.org>6 *7 */8 9/*10 * This file adds Media Controller support to the ALSA driver11 * to use the Media Controller API to share the tuner with DVB12 * 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 for16 * 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, and262	 * there are multiple interfaces per device. Avoid calling263	 * 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}328