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 33 int 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 106 remove_intf_link: 107 media_remove_intf_link(mctl->intf_link); 108 devnode_remove: 109 media_devnode_remove(mctl->intf_devnode); 110 unregister_entity: 111 media_device_unregister_entity(&mctl->media_entity); 112 free_mctl: 113 kfree(mctl); 114 return ret; 115 } 116 117 void 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 135 int 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 151 void 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 164 static 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 227 static 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 254 int 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 278 snd_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); 294 create_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 310 void 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.