xref: /qemu/hw/audio/virtio-snd.c (revision 2880e676)
12880e676SManos Pitsidianakis /*
22880e676SManos Pitsidianakis  * VIRTIO Sound Device conforming to
32880e676SManos Pitsidianakis  *
42880e676SManos Pitsidianakis  * "Virtual I/O Device (VIRTIO) Version 1.2
52880e676SManos Pitsidianakis  * Committee Specification Draft 01
62880e676SManos Pitsidianakis  * 09 May 2022"
72880e676SManos Pitsidianakis  *
82880e676SManos Pitsidianakis  * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014>
92880e676SManos Pitsidianakis  *
102880e676SManos Pitsidianakis  * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org>
112880e676SManos Pitsidianakis  * Copyright (C) 2019 OpenSynergy GmbH
122880e676SManos Pitsidianakis  *
132880e676SManos Pitsidianakis  * This work is licensed under the terms of the GNU GPL, version 2 or
142880e676SManos Pitsidianakis  * (at your option) any later version.  See the COPYING file in the
152880e676SManos Pitsidianakis  * top-level directory.
162880e676SManos Pitsidianakis  */
172880e676SManos Pitsidianakis 
182880e676SManos Pitsidianakis #include "qemu/osdep.h"
192880e676SManos Pitsidianakis #include "qemu/iov.h"
202880e676SManos Pitsidianakis #include "qemu/log.h"
212880e676SManos Pitsidianakis #include "qemu/error-report.h"
222880e676SManos Pitsidianakis #include "include/qemu/lockable.h"
232880e676SManos Pitsidianakis #include "sysemu/runstate.h"
242880e676SManos Pitsidianakis #include "trace.h"
252880e676SManos Pitsidianakis #include "qapi/error.h"
262880e676SManos Pitsidianakis #include "hw/audio/virtio-snd.h"
272880e676SManos Pitsidianakis #include "hw/core/cpu.h"
282880e676SManos Pitsidianakis 
292880e676SManos Pitsidianakis #define VIRTIO_SOUND_VM_VERSION 1
302880e676SManos Pitsidianakis #define VIRTIO_SOUND_JACK_DEFAULT 0
312880e676SManos Pitsidianakis #define VIRTIO_SOUND_STREAM_DEFAULT 1
322880e676SManos Pitsidianakis #define VIRTIO_SOUND_CHMAP_DEFAULT 0
332880e676SManos Pitsidianakis #define VIRTIO_SOUND_HDA_FN_NID 0
342880e676SManos Pitsidianakis 
352880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = {
362880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
372880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
382880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
392880e676SManos Pitsidianakis };
402880e676SManos Pitsidianakis 
412880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = {
422880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
432880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
442880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
452880e676SManos Pitsidianakis     .fields = (VMStateField[]) {
462880e676SManos Pitsidianakis         VMSTATE_VIRTIO_DEVICE,
472880e676SManos Pitsidianakis         VMSTATE_END_OF_LIST()
482880e676SManos Pitsidianakis     },
492880e676SManos Pitsidianakis };
502880e676SManos Pitsidianakis 
512880e676SManos Pitsidianakis static Property virtio_snd_properties[] = {
522880e676SManos Pitsidianakis     DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
532880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
542880e676SManos Pitsidianakis                        VIRTIO_SOUND_JACK_DEFAULT),
552880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
562880e676SManos Pitsidianakis                        VIRTIO_SOUND_STREAM_DEFAULT),
572880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
582880e676SManos Pitsidianakis                        VIRTIO_SOUND_CHMAP_DEFAULT),
592880e676SManos Pitsidianakis     DEFINE_PROP_END_OF_LIST(),
602880e676SManos Pitsidianakis };
612880e676SManos Pitsidianakis 
622880e676SManos Pitsidianakis static void
632880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
642880e676SManos Pitsidianakis {
652880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
662880e676SManos Pitsidianakis     virtio_snd_config *sndconfig =
672880e676SManos Pitsidianakis         (virtio_snd_config *)config;
682880e676SManos Pitsidianakis     trace_virtio_snd_get_config(vdev,
692880e676SManos Pitsidianakis                                 s->snd_conf.jacks,
702880e676SManos Pitsidianakis                                 s->snd_conf.streams,
712880e676SManos Pitsidianakis                                 s->snd_conf.chmaps);
722880e676SManos Pitsidianakis 
732880e676SManos Pitsidianakis     memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
742880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->jacks);
752880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->streams);
762880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->chmaps);
772880e676SManos Pitsidianakis 
782880e676SManos Pitsidianakis }
792880e676SManos Pitsidianakis 
802880e676SManos Pitsidianakis static void
812880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
822880e676SManos Pitsidianakis {
832880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
842880e676SManos Pitsidianakis     const virtio_snd_config *sndconfig =
852880e676SManos Pitsidianakis         (const virtio_snd_config *)config;
862880e676SManos Pitsidianakis 
872880e676SManos Pitsidianakis 
882880e676SManos Pitsidianakis    trace_virtio_snd_set_config(vdev,
892880e676SManos Pitsidianakis                                s->snd_conf.jacks,
902880e676SManos Pitsidianakis                                sndconfig->jacks,
912880e676SManos Pitsidianakis                                s->snd_conf.streams,
922880e676SManos Pitsidianakis                                sndconfig->streams,
932880e676SManos Pitsidianakis                                s->snd_conf.chmaps,
942880e676SManos Pitsidianakis                                sndconfig->chmaps);
952880e676SManos Pitsidianakis 
962880e676SManos Pitsidianakis     memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
972880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.jacks);
982880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.streams);
992880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.chmaps);
1002880e676SManos Pitsidianakis 
1012880e676SManos Pitsidianakis }
1022880e676SManos Pitsidianakis 
1032880e676SManos Pitsidianakis /*
1042880e676SManos Pitsidianakis  * Queue handler stub.
1052880e676SManos Pitsidianakis  *
1062880e676SManos Pitsidianakis  * @vdev: VirtIOSound device
1072880e676SManos Pitsidianakis  * @vq: virtqueue
1082880e676SManos Pitsidianakis  */
1092880e676SManos Pitsidianakis static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {}
1102880e676SManos Pitsidianakis 
1112880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
1122880e676SManos Pitsidianakis                              Error **errp)
1132880e676SManos Pitsidianakis {
1142880e676SManos Pitsidianakis     /*
1152880e676SManos Pitsidianakis      * virtio-v1.2-csd01, 5.14.3,
1162880e676SManos Pitsidianakis      * Feature Bits
1172880e676SManos Pitsidianakis      * None currently defined.
1182880e676SManos Pitsidianakis      */
1192880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
1202880e676SManos Pitsidianakis     features |= s->features;
1212880e676SManos Pitsidianakis 
1222880e676SManos Pitsidianakis     trace_virtio_snd_get_features(vdev, features);
1232880e676SManos Pitsidianakis 
1242880e676SManos Pitsidianakis     return features;
1252880e676SManos Pitsidianakis }
1262880e676SManos Pitsidianakis 
1272880e676SManos Pitsidianakis static void
1282880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running,
1292880e676SManos Pitsidianakis                                        RunState state)
1302880e676SManos Pitsidianakis {
1312880e676SManos Pitsidianakis     if (running) {
1322880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_running();
1332880e676SManos Pitsidianakis     } else {
1342880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_stopped();
1352880e676SManos Pitsidianakis     }
1362880e676SManos Pitsidianakis }
1372880e676SManos Pitsidianakis 
1382880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp)
1392880e676SManos Pitsidianakis {
1402880e676SManos Pitsidianakis     ERRP_GUARD();
1412880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
1422880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1432880e676SManos Pitsidianakis 
1442880e676SManos Pitsidianakis     vsnd->vmstate =
1452880e676SManos Pitsidianakis         qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
1462880e676SManos Pitsidianakis 
1472880e676SManos Pitsidianakis     trace_virtio_snd_realize(vsnd);
1482880e676SManos Pitsidianakis 
1492880e676SManos Pitsidianakis     virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
1502880e676SManos Pitsidianakis     virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
1512880e676SManos Pitsidianakis 
1522880e676SManos Pitsidianakis     /* set number of jacks and streams */
1532880e676SManos Pitsidianakis     if (vsnd->snd_conf.jacks > 8) {
1542880e676SManos Pitsidianakis         error_setg(errp,
1552880e676SManos Pitsidianakis                    "Invalid number of jacks: %"PRIu32,
1562880e676SManos Pitsidianakis                    vsnd->snd_conf.jacks);
1572880e676SManos Pitsidianakis         return;
1582880e676SManos Pitsidianakis     }
1592880e676SManos Pitsidianakis     if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
1602880e676SManos Pitsidianakis         error_setg(errp,
1612880e676SManos Pitsidianakis                    "Invalid number of streams: %"PRIu32,
1622880e676SManos Pitsidianakis                     vsnd->snd_conf.streams);
1632880e676SManos Pitsidianakis         return;
1642880e676SManos Pitsidianakis     }
1652880e676SManos Pitsidianakis 
1662880e676SManos Pitsidianakis     if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
1672880e676SManos Pitsidianakis         error_setg(errp,
1682880e676SManos Pitsidianakis                    "Invalid number of channel maps: %"PRIu32,
1692880e676SManos Pitsidianakis                    vsnd->snd_conf.chmaps);
1702880e676SManos Pitsidianakis         return;
1712880e676SManos Pitsidianakis     }
1722880e676SManos Pitsidianakis 
1732880e676SManos Pitsidianakis     AUD_register_card("virtio-sound", &vsnd->card, errp);
1742880e676SManos Pitsidianakis 
1752880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
1762880e676SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
1772880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_EVENT] =
1782880e676SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
1792880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_TX] =
1802880e676SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
1812880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_RX] =
1822880e676SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_queue);
1832880e676SManos Pitsidianakis }
1842880e676SManos Pitsidianakis 
1852880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev)
1862880e676SManos Pitsidianakis {
1872880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1882880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
1892880e676SManos Pitsidianakis 
1902880e676SManos Pitsidianakis     qemu_del_vm_change_state_handler(vsnd->vmstate);
1912880e676SManos Pitsidianakis     trace_virtio_snd_unrealize(vsnd);
1922880e676SManos Pitsidianakis 
1932880e676SManos Pitsidianakis     AUD_remove_card(&vsnd->card);
1942880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
1952880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
1962880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
1972880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
1982880e676SManos Pitsidianakis     virtio_cleanup(vdev);
1992880e676SManos Pitsidianakis }
2002880e676SManos Pitsidianakis 
2012880e676SManos Pitsidianakis 
2022880e676SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev) {}
2032880e676SManos Pitsidianakis 
2042880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data)
2052880e676SManos Pitsidianakis {
2062880e676SManos Pitsidianakis     DeviceClass *dc = DEVICE_CLASS(klass);
2072880e676SManos Pitsidianakis     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
2082880e676SManos Pitsidianakis 
2092880e676SManos Pitsidianakis 
2102880e676SManos Pitsidianakis     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
2112880e676SManos Pitsidianakis     device_class_set_props(dc, virtio_snd_properties);
2122880e676SManos Pitsidianakis 
2132880e676SManos Pitsidianakis     dc->vmsd = &vmstate_virtio_snd;
2142880e676SManos Pitsidianakis     vdc->vmsd = &vmstate_virtio_snd_device;
2152880e676SManos Pitsidianakis     vdc->realize = virtio_snd_realize;
2162880e676SManos Pitsidianakis     vdc->unrealize = virtio_snd_unrealize;
2172880e676SManos Pitsidianakis     vdc->get_config = virtio_snd_get_config;
2182880e676SManos Pitsidianakis     vdc->set_config = virtio_snd_set_config;
2192880e676SManos Pitsidianakis     vdc->get_features = get_features;
2202880e676SManos Pitsidianakis     vdc->reset = virtio_snd_reset;
2212880e676SManos Pitsidianakis     vdc->legacy_features = 0;
2222880e676SManos Pitsidianakis }
2232880e676SManos Pitsidianakis 
2242880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = {
2252880e676SManos Pitsidianakis     {
2262880e676SManos Pitsidianakis       .name          = TYPE_VIRTIO_SND,
2272880e676SManos Pitsidianakis       .parent        = TYPE_VIRTIO_DEVICE,
2282880e676SManos Pitsidianakis       .instance_size = sizeof(VirtIOSound),
2292880e676SManos Pitsidianakis       .class_init    = virtio_snd_class_init,
2302880e676SManos Pitsidianakis     }
2312880e676SManos Pitsidianakis };
2322880e676SManos Pitsidianakis 
2332880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types)
234