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