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