1de3a9980SAnton Yakovlev // SPDX-License-Identifier: GPL-2.0+ 2de3a9980SAnton Yakovlev /* 3de3a9980SAnton Yakovlev * virtio-snd: Virtio sound device 4de3a9980SAnton Yakovlev * Copyright (C) 2021 OpenSynergy GmbH 5de3a9980SAnton Yakovlev */ 6de3a9980SAnton Yakovlev #include <linux/module.h> 7de3a9980SAnton Yakovlev #include <linux/moduleparam.h> 8de3a9980SAnton Yakovlev #include <linux/virtio_config.h> 9de3a9980SAnton Yakovlev #include <sound/initval.h> 10de3a9980SAnton Yakovlev #include <uapi/linux/virtio_ids.h> 11de3a9980SAnton Yakovlev 12de3a9980SAnton Yakovlev #include "virtio_card.h" 13de3a9980SAnton Yakovlev 149d45e514SAnton Yakovlev u32 virtsnd_msg_timeout_ms = MSEC_PER_SEC; 159d45e514SAnton Yakovlev module_param_named(msg_timeout_ms, virtsnd_msg_timeout_ms, uint, 0644); 169d45e514SAnton Yakovlev MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds"); 179d45e514SAnton Yakovlev 18de3a9980SAnton Yakovlev static void virtsnd_remove(struct virtio_device *vdev); 19de3a9980SAnton Yakovlev 20de3a9980SAnton Yakovlev /** 21de3a9980SAnton Yakovlev * virtsnd_event_send() - Add an event to the event queue. 22de3a9980SAnton Yakovlev * @vqueue: Underlying event virtqueue. 23de3a9980SAnton Yakovlev * @event: Event. 24de3a9980SAnton Yakovlev * @notify: Indicates whether or not to send a notification to the device. 25de3a9980SAnton Yakovlev * @gfp: Kernel flags for memory allocation. 26de3a9980SAnton Yakovlev * 27de3a9980SAnton Yakovlev * Context: Any context. 28de3a9980SAnton Yakovlev */ 29de3a9980SAnton Yakovlev static void virtsnd_event_send(struct virtqueue *vqueue, 30de3a9980SAnton Yakovlev struct virtio_snd_event *event, bool notify, 31de3a9980SAnton Yakovlev gfp_t gfp) 32de3a9980SAnton Yakovlev { 33de3a9980SAnton Yakovlev struct scatterlist sg; 34de3a9980SAnton Yakovlev struct scatterlist *psgs[1] = { &sg }; 35de3a9980SAnton Yakovlev 36de3a9980SAnton Yakovlev /* reset event content */ 37de3a9980SAnton Yakovlev memset(event, 0, sizeof(*event)); 38de3a9980SAnton Yakovlev 39de3a9980SAnton Yakovlev sg_init_one(&sg, event, sizeof(*event)); 40de3a9980SAnton Yakovlev 41de3a9980SAnton Yakovlev if (virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp) || !notify) 42de3a9980SAnton Yakovlev return; 43de3a9980SAnton Yakovlev 44de3a9980SAnton Yakovlev if (virtqueue_kick_prepare(vqueue)) 45de3a9980SAnton Yakovlev virtqueue_notify(vqueue); 46de3a9980SAnton Yakovlev } 47de3a9980SAnton Yakovlev 48de3a9980SAnton Yakovlev /** 49de3a9980SAnton Yakovlev * virtsnd_event_dispatch() - Dispatch an event from the device side. 50de3a9980SAnton Yakovlev * @snd: VirtIO sound device. 51de3a9980SAnton Yakovlev * @event: VirtIO sound event. 52de3a9980SAnton Yakovlev * 53de3a9980SAnton Yakovlev * Context: Any context. 54de3a9980SAnton Yakovlev */ 55de3a9980SAnton Yakovlev static void virtsnd_event_dispatch(struct virtio_snd *snd, 56de3a9980SAnton Yakovlev struct virtio_snd_event *event) 57de3a9980SAnton Yakovlev { 58f40a2867SAnton Yakovlev switch (le32_to_cpu(event->hdr.code)) { 59ca61a41fSAnton Yakovlev case VIRTIO_SND_EVT_JACK_CONNECTED: 60ca61a41fSAnton Yakovlev case VIRTIO_SND_EVT_JACK_DISCONNECTED: 61ca61a41fSAnton Yakovlev virtsnd_jack_event(snd, event); 62ca61a41fSAnton Yakovlev break; 63f40a2867SAnton Yakovlev case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: 64f40a2867SAnton Yakovlev case VIRTIO_SND_EVT_PCM_XRUN: 65f40a2867SAnton Yakovlev virtsnd_pcm_event(snd, event); 66f40a2867SAnton Yakovlev break; 67f40a2867SAnton Yakovlev } 68de3a9980SAnton Yakovlev } 69de3a9980SAnton Yakovlev 70de3a9980SAnton Yakovlev /** 71de3a9980SAnton Yakovlev * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. 72de3a9980SAnton Yakovlev * @vqueue: Underlying event virtqueue. 73de3a9980SAnton Yakovlev * 74de3a9980SAnton Yakovlev * This callback function is called upon a vring interrupt request from the 75de3a9980SAnton Yakovlev * device. 76de3a9980SAnton Yakovlev * 77de3a9980SAnton Yakovlev * Context: Interrupt context. 78de3a9980SAnton Yakovlev */ 79de3a9980SAnton Yakovlev static void virtsnd_event_notify_cb(struct virtqueue *vqueue) 80de3a9980SAnton Yakovlev { 81de3a9980SAnton Yakovlev struct virtio_snd *snd = vqueue->vdev->priv; 82de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 83de3a9980SAnton Yakovlev struct virtio_snd_event *event; 84de3a9980SAnton Yakovlev u32 length; 85de3a9980SAnton Yakovlev unsigned long flags; 86de3a9980SAnton Yakovlev 87de3a9980SAnton Yakovlev spin_lock_irqsave(&queue->lock, flags); 88de3a9980SAnton Yakovlev do { 89de3a9980SAnton Yakovlev virtqueue_disable_cb(vqueue); 90de3a9980SAnton Yakovlev while ((event = virtqueue_get_buf(vqueue, &length))) { 91de3a9980SAnton Yakovlev virtsnd_event_dispatch(snd, event); 92de3a9980SAnton Yakovlev virtsnd_event_send(vqueue, event, true, GFP_ATOMIC); 93de3a9980SAnton Yakovlev } 94de3a9980SAnton Yakovlev if (unlikely(virtqueue_is_broken(vqueue))) 95de3a9980SAnton Yakovlev break; 96de3a9980SAnton Yakovlev } while (!virtqueue_enable_cb(vqueue)); 97de3a9980SAnton Yakovlev spin_unlock_irqrestore(&queue->lock, flags); 98de3a9980SAnton Yakovlev } 99de3a9980SAnton Yakovlev 100de3a9980SAnton Yakovlev /** 101de3a9980SAnton Yakovlev * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. 102de3a9980SAnton Yakovlev * @snd: VirtIO sound device. 103de3a9980SAnton Yakovlev * 104de3a9980SAnton Yakovlev * After calling this function, the event queue is disabled. 105de3a9980SAnton Yakovlev * 106de3a9980SAnton Yakovlev * Context: Any context. 107de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure. 108de3a9980SAnton Yakovlev */ 109de3a9980SAnton Yakovlev static int virtsnd_find_vqs(struct virtio_snd *snd) 110de3a9980SAnton Yakovlev { 111de3a9980SAnton Yakovlev struct virtio_device *vdev = snd->vdev; 112de3a9980SAnton Yakovlev static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { 1139d45e514SAnton Yakovlev [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb, 114f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb, 115f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb, 116f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb 117de3a9980SAnton Yakovlev }; 118de3a9980SAnton Yakovlev static const char *names[VIRTIO_SND_VQ_MAX] = { 1199d45e514SAnton Yakovlev [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", 120f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_EVENT] = "virtsnd-event", 121f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_TX] = "virtsnd-tx", 122f40a2867SAnton Yakovlev [VIRTIO_SND_VQ_RX] = "virtsnd-rx" 123de3a9980SAnton Yakovlev }; 124de3a9980SAnton Yakovlev struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; 125de3a9980SAnton Yakovlev unsigned int i; 126de3a9980SAnton Yakovlev unsigned int n; 127de3a9980SAnton Yakovlev int rc; 128de3a9980SAnton Yakovlev 129de3a9980SAnton Yakovlev rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, 130de3a9980SAnton Yakovlev NULL); 131de3a9980SAnton Yakovlev if (rc) { 132de3a9980SAnton Yakovlev dev_err(&vdev->dev, "failed to initialize virtqueues\n"); 133de3a9980SAnton Yakovlev return rc; 134de3a9980SAnton Yakovlev } 135de3a9980SAnton Yakovlev 136de3a9980SAnton Yakovlev for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) 137de3a9980SAnton Yakovlev snd->queues[i].vqueue = vqs[i]; 138de3a9980SAnton Yakovlev 139de3a9980SAnton Yakovlev /* Allocate events and populate the event queue */ 140de3a9980SAnton Yakovlev virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); 141de3a9980SAnton Yakovlev 142de3a9980SAnton Yakovlev n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); 143de3a9980SAnton Yakovlev 144de3a9980SAnton Yakovlev snd->event_msgs = kmalloc_array(n, sizeof(*snd->event_msgs), 145de3a9980SAnton Yakovlev GFP_KERNEL); 146de3a9980SAnton Yakovlev if (!snd->event_msgs) 147de3a9980SAnton Yakovlev return -ENOMEM; 148de3a9980SAnton Yakovlev 149de3a9980SAnton Yakovlev for (i = 0; i < n; ++i) 150de3a9980SAnton Yakovlev virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], 151de3a9980SAnton Yakovlev &snd->event_msgs[i], false, GFP_KERNEL); 152de3a9980SAnton Yakovlev 153de3a9980SAnton Yakovlev return 0; 154de3a9980SAnton Yakovlev } 155de3a9980SAnton Yakovlev 156de3a9980SAnton Yakovlev /** 157de3a9980SAnton Yakovlev * virtsnd_enable_event_vq() - Enable the event virtqueue. 158de3a9980SAnton Yakovlev * @snd: VirtIO sound device. 159de3a9980SAnton Yakovlev * 160de3a9980SAnton Yakovlev * Context: Any context. 161de3a9980SAnton Yakovlev */ 162de3a9980SAnton Yakovlev static void virtsnd_enable_event_vq(struct virtio_snd *snd) 163de3a9980SAnton Yakovlev { 164de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 165de3a9980SAnton Yakovlev 166de3a9980SAnton Yakovlev if (!virtqueue_enable_cb(queue->vqueue)) 167de3a9980SAnton Yakovlev virtsnd_event_notify_cb(queue->vqueue); 168de3a9980SAnton Yakovlev } 169de3a9980SAnton Yakovlev 170de3a9980SAnton Yakovlev /** 171de3a9980SAnton Yakovlev * virtsnd_disable_event_vq() - Disable the event virtqueue. 172de3a9980SAnton Yakovlev * @snd: VirtIO sound device. 173de3a9980SAnton Yakovlev * 174de3a9980SAnton Yakovlev * Context: Any context. 175de3a9980SAnton Yakovlev */ 176de3a9980SAnton Yakovlev static void virtsnd_disable_event_vq(struct virtio_snd *snd) 177de3a9980SAnton Yakovlev { 178de3a9980SAnton Yakovlev struct virtio_snd_queue *queue = virtsnd_event_queue(snd); 179de3a9980SAnton Yakovlev struct virtio_snd_event *event; 180de3a9980SAnton Yakovlev u32 length; 181de3a9980SAnton Yakovlev unsigned long flags; 182de3a9980SAnton Yakovlev 183de3a9980SAnton Yakovlev if (queue->vqueue) { 184de3a9980SAnton Yakovlev spin_lock_irqsave(&queue->lock, flags); 185de3a9980SAnton Yakovlev virtqueue_disable_cb(queue->vqueue); 186de3a9980SAnton Yakovlev while ((event = virtqueue_get_buf(queue->vqueue, &length))) 187de3a9980SAnton Yakovlev virtsnd_event_dispatch(snd, event); 188de3a9980SAnton Yakovlev spin_unlock_irqrestore(&queue->lock, flags); 189de3a9980SAnton Yakovlev } 190de3a9980SAnton Yakovlev } 191de3a9980SAnton Yakovlev 192de3a9980SAnton Yakovlev /** 193de3a9980SAnton Yakovlev * virtsnd_build_devs() - Read configuration and build ALSA devices. 194de3a9980SAnton Yakovlev * @snd: VirtIO sound device. 195de3a9980SAnton Yakovlev * 196de3a9980SAnton Yakovlev * Context: Any context that permits to sleep. 197de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure. 198de3a9980SAnton Yakovlev */ 199de3a9980SAnton Yakovlev static int virtsnd_build_devs(struct virtio_snd *snd) 200de3a9980SAnton Yakovlev { 201de3a9980SAnton Yakovlev struct virtio_device *vdev = snd->vdev; 202de3a9980SAnton Yakovlev struct device *dev = &vdev->dev; 203de3a9980SAnton Yakovlev int rc; 204de3a9980SAnton Yakovlev 205de3a9980SAnton Yakovlev rc = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, 206de3a9980SAnton Yakovlev THIS_MODULE, 0, &snd->card); 207de3a9980SAnton Yakovlev if (rc < 0) 208de3a9980SAnton Yakovlev return rc; 209de3a9980SAnton Yakovlev 210de3a9980SAnton Yakovlev snd->card->private_data = snd; 211de3a9980SAnton Yakovlev 212de3a9980SAnton Yakovlev strscpy(snd->card->driver, VIRTIO_SND_CARD_DRIVER, 213de3a9980SAnton Yakovlev sizeof(snd->card->driver)); 214de3a9980SAnton Yakovlev strscpy(snd->card->shortname, VIRTIO_SND_CARD_NAME, 215de3a9980SAnton Yakovlev sizeof(snd->card->shortname)); 216de3a9980SAnton Yakovlev if (dev->parent->bus) 217de3a9980SAnton Yakovlev snprintf(snd->card->longname, sizeof(snd->card->longname), 218de3a9980SAnton Yakovlev VIRTIO_SND_CARD_NAME " at %s/%s/%s", 219de3a9980SAnton Yakovlev dev->parent->bus->name, dev_name(dev->parent), 220de3a9980SAnton Yakovlev dev_name(dev)); 221de3a9980SAnton Yakovlev else 222de3a9980SAnton Yakovlev snprintf(snd->card->longname, sizeof(snd->card->longname), 223de3a9980SAnton Yakovlev VIRTIO_SND_CARD_NAME " at %s/%s", 224de3a9980SAnton Yakovlev dev_name(dev->parent), dev_name(dev)); 225de3a9980SAnton Yakovlev 226ca61a41fSAnton Yakovlev rc = virtsnd_jack_parse_cfg(snd); 227ca61a41fSAnton Yakovlev if (rc) 228ca61a41fSAnton Yakovlev return rc; 229ca61a41fSAnton Yakovlev 23029b96bf5SAnton Yakovlev rc = virtsnd_pcm_parse_cfg(snd); 23129b96bf5SAnton Yakovlev if (rc) 23229b96bf5SAnton Yakovlev return rc; 23329b96bf5SAnton Yakovlev 23419325fedSAnton Yakovlev rc = virtsnd_chmap_parse_cfg(snd); 23519325fedSAnton Yakovlev if (rc) 23619325fedSAnton Yakovlev return rc; 23719325fedSAnton Yakovlev 238ca61a41fSAnton Yakovlev if (snd->njacks) { 239ca61a41fSAnton Yakovlev rc = virtsnd_jack_build_devs(snd); 240ca61a41fSAnton Yakovlev if (rc) 241ca61a41fSAnton Yakovlev return rc; 242ca61a41fSAnton Yakovlev } 243ca61a41fSAnton Yakovlev 24429b96bf5SAnton Yakovlev if (snd->nsubstreams) { 24529b96bf5SAnton Yakovlev rc = virtsnd_pcm_build_devs(snd); 24629b96bf5SAnton Yakovlev if (rc) 24729b96bf5SAnton Yakovlev return rc; 24829b96bf5SAnton Yakovlev } 24929b96bf5SAnton Yakovlev 25019325fedSAnton Yakovlev if (snd->nchmaps) { 25119325fedSAnton Yakovlev rc = virtsnd_chmap_build_devs(snd); 25219325fedSAnton Yakovlev if (rc) 25319325fedSAnton Yakovlev return rc; 25419325fedSAnton Yakovlev } 25519325fedSAnton Yakovlev 256de3a9980SAnton Yakovlev return snd_card_register(snd->card); 257de3a9980SAnton Yakovlev } 258de3a9980SAnton Yakovlev 259de3a9980SAnton Yakovlev /** 260de3a9980SAnton Yakovlev * virtsnd_validate() - Validate if the device can be started. 261de3a9980SAnton Yakovlev * @vdev: VirtIO parent device. 262de3a9980SAnton Yakovlev * 263de3a9980SAnton Yakovlev * Context: Any context. 264de3a9980SAnton Yakovlev * Return: 0 on success, -EINVAL on failure. 265de3a9980SAnton Yakovlev */ 266de3a9980SAnton Yakovlev static int virtsnd_validate(struct virtio_device *vdev) 267de3a9980SAnton Yakovlev { 268de3a9980SAnton Yakovlev if (!vdev->config->get) { 269de3a9980SAnton Yakovlev dev_err(&vdev->dev, "configuration access disabled\n"); 270de3a9980SAnton Yakovlev return -EINVAL; 271de3a9980SAnton Yakovlev } 272de3a9980SAnton Yakovlev 273de3a9980SAnton Yakovlev if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { 274de3a9980SAnton Yakovlev dev_err(&vdev->dev, 275de3a9980SAnton Yakovlev "device does not comply with spec version 1.x\n"); 276de3a9980SAnton Yakovlev return -EINVAL; 277de3a9980SAnton Yakovlev } 278de3a9980SAnton Yakovlev 2799d45e514SAnton Yakovlev if (!virtsnd_msg_timeout_ms) { 2809d45e514SAnton Yakovlev dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n"); 2819d45e514SAnton Yakovlev return -EINVAL; 2829d45e514SAnton Yakovlev } 2839d45e514SAnton Yakovlev 28429b96bf5SAnton Yakovlev if (virtsnd_pcm_validate(vdev)) 28529b96bf5SAnton Yakovlev return -EINVAL; 28629b96bf5SAnton Yakovlev 287de3a9980SAnton Yakovlev return 0; 288de3a9980SAnton Yakovlev } 289de3a9980SAnton Yakovlev 290de3a9980SAnton Yakovlev /** 291de3a9980SAnton Yakovlev * virtsnd_probe() - Create and initialize the device. 292de3a9980SAnton Yakovlev * @vdev: VirtIO parent device. 293de3a9980SAnton Yakovlev * 294de3a9980SAnton Yakovlev * Context: Any context that permits to sleep. 295de3a9980SAnton Yakovlev * Return: 0 on success, -errno on failure. 296de3a9980SAnton Yakovlev */ 297de3a9980SAnton Yakovlev static int virtsnd_probe(struct virtio_device *vdev) 298de3a9980SAnton Yakovlev { 299de3a9980SAnton Yakovlev struct virtio_snd *snd; 300de3a9980SAnton Yakovlev unsigned int i; 301de3a9980SAnton Yakovlev int rc; 302de3a9980SAnton Yakovlev 303de3a9980SAnton Yakovlev snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); 304de3a9980SAnton Yakovlev if (!snd) 305de3a9980SAnton Yakovlev return -ENOMEM; 306de3a9980SAnton Yakovlev 307de3a9980SAnton Yakovlev snd->vdev = vdev; 3089d45e514SAnton Yakovlev INIT_LIST_HEAD(&snd->ctl_msgs); 30929b96bf5SAnton Yakovlev INIT_LIST_HEAD(&snd->pcm_list); 310de3a9980SAnton Yakovlev 311de3a9980SAnton Yakovlev vdev->priv = snd; 312de3a9980SAnton Yakovlev 313de3a9980SAnton Yakovlev for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) 314de3a9980SAnton Yakovlev spin_lock_init(&snd->queues[i].lock); 315de3a9980SAnton Yakovlev 316de3a9980SAnton Yakovlev rc = virtsnd_find_vqs(snd); 317de3a9980SAnton Yakovlev if (rc) 318de3a9980SAnton Yakovlev goto on_exit; 319de3a9980SAnton Yakovlev 320de3a9980SAnton Yakovlev virtio_device_ready(vdev); 321de3a9980SAnton Yakovlev 322de3a9980SAnton Yakovlev rc = virtsnd_build_devs(snd); 323de3a9980SAnton Yakovlev if (rc) 324de3a9980SAnton Yakovlev goto on_exit; 325de3a9980SAnton Yakovlev 326de3a9980SAnton Yakovlev virtsnd_enable_event_vq(snd); 327de3a9980SAnton Yakovlev 328de3a9980SAnton Yakovlev on_exit: 329de3a9980SAnton Yakovlev if (rc) 330de3a9980SAnton Yakovlev virtsnd_remove(vdev); 331de3a9980SAnton Yakovlev 332de3a9980SAnton Yakovlev return rc; 333de3a9980SAnton Yakovlev } 334de3a9980SAnton Yakovlev 335de3a9980SAnton Yakovlev /** 336de3a9980SAnton Yakovlev * virtsnd_remove() - Remove VirtIO and ALSA devices. 337de3a9980SAnton Yakovlev * @vdev: VirtIO parent device. 338de3a9980SAnton Yakovlev * 339de3a9980SAnton Yakovlev * Context: Any context that permits to sleep. 340de3a9980SAnton Yakovlev */ 341de3a9980SAnton Yakovlev static void virtsnd_remove(struct virtio_device *vdev) 342de3a9980SAnton Yakovlev { 343de3a9980SAnton Yakovlev struct virtio_snd *snd = vdev->priv; 34429b96bf5SAnton Yakovlev unsigned int i; 345de3a9980SAnton Yakovlev 346de3a9980SAnton Yakovlev virtsnd_disable_event_vq(snd); 3479d45e514SAnton Yakovlev virtsnd_ctl_msg_cancel_all(snd); 348de3a9980SAnton Yakovlev 349de3a9980SAnton Yakovlev if (snd->card) 350de3a9980SAnton Yakovlev snd_card_free(snd->card); 351de3a9980SAnton Yakovlev 352de3a9980SAnton Yakovlev vdev->config->del_vqs(vdev); 353*d9679d00SMichael S. Tsirkin virtio_reset_device(vdev); 354de3a9980SAnton Yakovlev 355f40a2867SAnton Yakovlev for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) { 356f40a2867SAnton Yakovlev struct virtio_pcm_substream *vss = &snd->substreams[i]; 357f40a2867SAnton Yakovlev 358f40a2867SAnton Yakovlev cancel_work_sync(&vss->elapsed_period); 359f40a2867SAnton Yakovlev virtsnd_pcm_msg_free(vss); 360f40a2867SAnton Yakovlev } 36129b96bf5SAnton Yakovlev 362de3a9980SAnton Yakovlev kfree(snd->event_msgs); 363de3a9980SAnton Yakovlev } 364de3a9980SAnton Yakovlev 365575483e9SAnton Yakovlev #ifdef CONFIG_PM_SLEEP 366575483e9SAnton Yakovlev /** 367575483e9SAnton Yakovlev * virtsnd_freeze() - Suspend device. 368575483e9SAnton Yakovlev * @vdev: VirtIO parent device. 369575483e9SAnton Yakovlev * 370575483e9SAnton Yakovlev * Context: Any context. 371575483e9SAnton Yakovlev * Return: 0 on success, -errno on failure. 372575483e9SAnton Yakovlev */ 373575483e9SAnton Yakovlev static int virtsnd_freeze(struct virtio_device *vdev) 374575483e9SAnton Yakovlev { 375575483e9SAnton Yakovlev struct virtio_snd *snd = vdev->priv; 376575483e9SAnton Yakovlev unsigned int i; 377575483e9SAnton Yakovlev 378575483e9SAnton Yakovlev virtsnd_disable_event_vq(snd); 379575483e9SAnton Yakovlev virtsnd_ctl_msg_cancel_all(snd); 380575483e9SAnton Yakovlev 381575483e9SAnton Yakovlev vdev->config->del_vqs(vdev); 382*d9679d00SMichael S. Tsirkin virtio_reset_device(vdev); 383575483e9SAnton Yakovlev 384575483e9SAnton Yakovlev for (i = 0; i < snd->nsubstreams; ++i) 385575483e9SAnton Yakovlev cancel_work_sync(&snd->substreams[i].elapsed_period); 386575483e9SAnton Yakovlev 387575483e9SAnton Yakovlev kfree(snd->event_msgs); 388575483e9SAnton Yakovlev snd->event_msgs = NULL; 389575483e9SAnton Yakovlev 390575483e9SAnton Yakovlev return 0; 391575483e9SAnton Yakovlev } 392575483e9SAnton Yakovlev 393575483e9SAnton Yakovlev /** 394575483e9SAnton Yakovlev * virtsnd_restore() - Resume device. 395575483e9SAnton Yakovlev * @vdev: VirtIO parent device. 396575483e9SAnton Yakovlev * 397575483e9SAnton Yakovlev * Context: Any context. 398575483e9SAnton Yakovlev * Return: 0 on success, -errno on failure. 399575483e9SAnton Yakovlev */ 400575483e9SAnton Yakovlev static int virtsnd_restore(struct virtio_device *vdev) 401575483e9SAnton Yakovlev { 402575483e9SAnton Yakovlev struct virtio_snd *snd = vdev->priv; 403575483e9SAnton Yakovlev int rc; 404575483e9SAnton Yakovlev 405575483e9SAnton Yakovlev rc = virtsnd_find_vqs(snd); 406575483e9SAnton Yakovlev if (rc) 407575483e9SAnton Yakovlev return rc; 408575483e9SAnton Yakovlev 409575483e9SAnton Yakovlev virtio_device_ready(vdev); 410575483e9SAnton Yakovlev 411575483e9SAnton Yakovlev virtsnd_enable_event_vq(snd); 412575483e9SAnton Yakovlev 413575483e9SAnton Yakovlev return 0; 414575483e9SAnton Yakovlev } 415575483e9SAnton Yakovlev #endif /* CONFIG_PM_SLEEP */ 416575483e9SAnton Yakovlev 417de3a9980SAnton Yakovlev static const struct virtio_device_id id_table[] = { 418de3a9980SAnton Yakovlev { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, 419de3a9980SAnton Yakovlev { 0 }, 420de3a9980SAnton Yakovlev }; 421de3a9980SAnton Yakovlev 422de3a9980SAnton Yakovlev static struct virtio_driver virtsnd_driver = { 423de3a9980SAnton Yakovlev .driver.name = KBUILD_MODNAME, 424de3a9980SAnton Yakovlev .driver.owner = THIS_MODULE, 425de3a9980SAnton Yakovlev .id_table = id_table, 426de3a9980SAnton Yakovlev .validate = virtsnd_validate, 427de3a9980SAnton Yakovlev .probe = virtsnd_probe, 428de3a9980SAnton Yakovlev .remove = virtsnd_remove, 429575483e9SAnton Yakovlev #ifdef CONFIG_PM_SLEEP 430575483e9SAnton Yakovlev .freeze = virtsnd_freeze, 431575483e9SAnton Yakovlev .restore = virtsnd_restore, 432575483e9SAnton Yakovlev #endif 433de3a9980SAnton Yakovlev }; 434de3a9980SAnton Yakovlev 435d91cbe83SChen Huang module_virtio_driver(virtsnd_driver); 436de3a9980SAnton Yakovlev 437de3a9980SAnton Yakovlev MODULE_DEVICE_TABLE(virtio, id_table); 438de3a9980SAnton Yakovlev MODULE_DESCRIPTION("Virtio sound card driver"); 439de3a9980SAnton Yakovlev MODULE_LICENSE("GPL"); 440