1*2880e676SManos Pitsidianakis /* 2*2880e676SManos Pitsidianakis * VIRTIO Sound Device conforming to 3*2880e676SManos Pitsidianakis * 4*2880e676SManos Pitsidianakis * "Virtual I/O Device (VIRTIO) Version 1.2 5*2880e676SManos Pitsidianakis * Committee Specification Draft 01 6*2880e676SManos Pitsidianakis * 09 May 2022" 7*2880e676SManos Pitsidianakis * 8*2880e676SManos Pitsidianakis * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014> 9*2880e676SManos Pitsidianakis * 10*2880e676SManos Pitsidianakis * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> 11*2880e676SManos Pitsidianakis * Copyright (C) 2019 OpenSynergy GmbH 12*2880e676SManos Pitsidianakis * 13*2880e676SManos Pitsidianakis * This work is licensed under the terms of the GNU GPL, version 2 or 14*2880e676SManos Pitsidianakis * (at your option) any later version. See the COPYING file in the 15*2880e676SManos Pitsidianakis * top-level directory. 16*2880e676SManos Pitsidianakis */ 17*2880e676SManos Pitsidianakis 18*2880e676SManos Pitsidianakis #include "qemu/osdep.h" 19*2880e676SManos Pitsidianakis #include "qemu/iov.h" 20*2880e676SManos Pitsidianakis #include "qemu/log.h" 21*2880e676SManos Pitsidianakis #include "qemu/error-report.h" 22*2880e676SManos Pitsidianakis #include "include/qemu/lockable.h" 23*2880e676SManos Pitsidianakis #include "sysemu/runstate.h" 24*2880e676SManos Pitsidianakis #include "trace.h" 25*2880e676SManos Pitsidianakis #include "qapi/error.h" 26*2880e676SManos Pitsidianakis #include "hw/audio/virtio-snd.h" 27*2880e676SManos Pitsidianakis #include "hw/core/cpu.h" 28*2880e676SManos Pitsidianakis 29*2880e676SManos Pitsidianakis #define VIRTIO_SOUND_VM_VERSION 1 30*2880e676SManos Pitsidianakis #define VIRTIO_SOUND_JACK_DEFAULT 0 31*2880e676SManos Pitsidianakis #define VIRTIO_SOUND_STREAM_DEFAULT 1 32*2880e676SManos Pitsidianakis #define VIRTIO_SOUND_CHMAP_DEFAULT 0 33*2880e676SManos Pitsidianakis #define VIRTIO_SOUND_HDA_FN_NID 0 34*2880e676SManos Pitsidianakis 35*2880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = { 36*2880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 37*2880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 38*2880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 39*2880e676SManos Pitsidianakis }; 40*2880e676SManos Pitsidianakis 41*2880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = { 42*2880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 43*2880e676SManos Pitsidianakis .minimum_version_id = VIRTIO_SOUND_VM_VERSION, 44*2880e676SManos Pitsidianakis .version_id = VIRTIO_SOUND_VM_VERSION, 45*2880e676SManos Pitsidianakis .fields = (VMStateField[]) { 46*2880e676SManos Pitsidianakis VMSTATE_VIRTIO_DEVICE, 47*2880e676SManos Pitsidianakis VMSTATE_END_OF_LIST() 48*2880e676SManos Pitsidianakis }, 49*2880e676SManos Pitsidianakis }; 50*2880e676SManos Pitsidianakis 51*2880e676SManos Pitsidianakis static Property virtio_snd_properties[] = { 52*2880e676SManos Pitsidianakis DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), 53*2880e676SManos Pitsidianakis DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, 54*2880e676SManos Pitsidianakis VIRTIO_SOUND_JACK_DEFAULT), 55*2880e676SManos Pitsidianakis DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, 56*2880e676SManos Pitsidianakis VIRTIO_SOUND_STREAM_DEFAULT), 57*2880e676SManos Pitsidianakis DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, 58*2880e676SManos Pitsidianakis VIRTIO_SOUND_CHMAP_DEFAULT), 59*2880e676SManos Pitsidianakis DEFINE_PROP_END_OF_LIST(), 60*2880e676SManos Pitsidianakis }; 61*2880e676SManos Pitsidianakis 62*2880e676SManos Pitsidianakis static void 63*2880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) 64*2880e676SManos Pitsidianakis { 65*2880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 66*2880e676SManos Pitsidianakis virtio_snd_config *sndconfig = 67*2880e676SManos Pitsidianakis (virtio_snd_config *)config; 68*2880e676SManos Pitsidianakis trace_virtio_snd_get_config(vdev, 69*2880e676SManos Pitsidianakis s->snd_conf.jacks, 70*2880e676SManos Pitsidianakis s->snd_conf.streams, 71*2880e676SManos Pitsidianakis s->snd_conf.chmaps); 72*2880e676SManos Pitsidianakis 73*2880e676SManos Pitsidianakis memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); 74*2880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->jacks); 75*2880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->streams); 76*2880e676SManos Pitsidianakis cpu_to_le32s(&sndconfig->chmaps); 77*2880e676SManos Pitsidianakis 78*2880e676SManos Pitsidianakis } 79*2880e676SManos Pitsidianakis 80*2880e676SManos Pitsidianakis static void 81*2880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) 82*2880e676SManos Pitsidianakis { 83*2880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 84*2880e676SManos Pitsidianakis const virtio_snd_config *sndconfig = 85*2880e676SManos Pitsidianakis (const virtio_snd_config *)config; 86*2880e676SManos Pitsidianakis 87*2880e676SManos Pitsidianakis 88*2880e676SManos Pitsidianakis trace_virtio_snd_set_config(vdev, 89*2880e676SManos Pitsidianakis s->snd_conf.jacks, 90*2880e676SManos Pitsidianakis sndconfig->jacks, 91*2880e676SManos Pitsidianakis s->snd_conf.streams, 92*2880e676SManos Pitsidianakis sndconfig->streams, 93*2880e676SManos Pitsidianakis s->snd_conf.chmaps, 94*2880e676SManos Pitsidianakis sndconfig->chmaps); 95*2880e676SManos Pitsidianakis 96*2880e676SManos Pitsidianakis memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); 97*2880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.jacks); 98*2880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.streams); 99*2880e676SManos Pitsidianakis le32_to_cpus(&s->snd_conf.chmaps); 100*2880e676SManos Pitsidianakis 101*2880e676SManos Pitsidianakis } 102*2880e676SManos Pitsidianakis 103*2880e676SManos Pitsidianakis /* 104*2880e676SManos Pitsidianakis * Queue handler stub. 105*2880e676SManos Pitsidianakis * 106*2880e676SManos Pitsidianakis * @vdev: VirtIOSound device 107*2880e676SManos Pitsidianakis * @vq: virtqueue 108*2880e676SManos Pitsidianakis */ 109*2880e676SManos Pitsidianakis static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {} 110*2880e676SManos Pitsidianakis 111*2880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features, 112*2880e676SManos Pitsidianakis Error **errp) 113*2880e676SManos Pitsidianakis { 114*2880e676SManos Pitsidianakis /* 115*2880e676SManos Pitsidianakis * virtio-v1.2-csd01, 5.14.3, 116*2880e676SManos Pitsidianakis * Feature Bits 117*2880e676SManos Pitsidianakis * None currently defined. 118*2880e676SManos Pitsidianakis */ 119*2880e676SManos Pitsidianakis VirtIOSound *s = VIRTIO_SND(vdev); 120*2880e676SManos Pitsidianakis features |= s->features; 121*2880e676SManos Pitsidianakis 122*2880e676SManos Pitsidianakis trace_virtio_snd_get_features(vdev, features); 123*2880e676SManos Pitsidianakis 124*2880e676SManos Pitsidianakis return features; 125*2880e676SManos Pitsidianakis } 126*2880e676SManos Pitsidianakis 127*2880e676SManos Pitsidianakis static void 128*2880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running, 129*2880e676SManos Pitsidianakis RunState state) 130*2880e676SManos Pitsidianakis { 131*2880e676SManos Pitsidianakis if (running) { 132*2880e676SManos Pitsidianakis trace_virtio_snd_vm_state_running(); 133*2880e676SManos Pitsidianakis } else { 134*2880e676SManos Pitsidianakis trace_virtio_snd_vm_state_stopped(); 135*2880e676SManos Pitsidianakis } 136*2880e676SManos Pitsidianakis } 137*2880e676SManos Pitsidianakis 138*2880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp) 139*2880e676SManos Pitsidianakis { 140*2880e676SManos Pitsidianakis ERRP_GUARD(); 141*2880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 142*2880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 143*2880e676SManos Pitsidianakis 144*2880e676SManos Pitsidianakis vsnd->vmstate = 145*2880e676SManos Pitsidianakis qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); 146*2880e676SManos Pitsidianakis 147*2880e676SManos Pitsidianakis trace_virtio_snd_realize(vsnd); 148*2880e676SManos Pitsidianakis 149*2880e676SManos Pitsidianakis virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); 150*2880e676SManos Pitsidianakis virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); 151*2880e676SManos Pitsidianakis 152*2880e676SManos Pitsidianakis /* set number of jacks and streams */ 153*2880e676SManos Pitsidianakis if (vsnd->snd_conf.jacks > 8) { 154*2880e676SManos Pitsidianakis error_setg(errp, 155*2880e676SManos Pitsidianakis "Invalid number of jacks: %"PRIu32, 156*2880e676SManos Pitsidianakis vsnd->snd_conf.jacks); 157*2880e676SManos Pitsidianakis return; 158*2880e676SManos Pitsidianakis } 159*2880e676SManos Pitsidianakis if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { 160*2880e676SManos Pitsidianakis error_setg(errp, 161*2880e676SManos Pitsidianakis "Invalid number of streams: %"PRIu32, 162*2880e676SManos Pitsidianakis vsnd->snd_conf.streams); 163*2880e676SManos Pitsidianakis return; 164*2880e676SManos Pitsidianakis } 165*2880e676SManos Pitsidianakis 166*2880e676SManos Pitsidianakis if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { 167*2880e676SManos Pitsidianakis error_setg(errp, 168*2880e676SManos Pitsidianakis "Invalid number of channel maps: %"PRIu32, 169*2880e676SManos Pitsidianakis vsnd->snd_conf.chmaps); 170*2880e676SManos Pitsidianakis return; 171*2880e676SManos Pitsidianakis } 172*2880e676SManos Pitsidianakis 173*2880e676SManos Pitsidianakis AUD_register_card("virtio-sound", &vsnd->card, errp); 174*2880e676SManos Pitsidianakis 175*2880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_CONTROL] = 176*2880e676SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_queue); 177*2880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_EVENT] = 178*2880e676SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_queue); 179*2880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_TX] = 180*2880e676SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_queue); 181*2880e676SManos Pitsidianakis vsnd->queues[VIRTIO_SND_VQ_RX] = 182*2880e676SManos Pitsidianakis virtio_add_queue(vdev, 64, virtio_snd_handle_queue); 183*2880e676SManos Pitsidianakis } 184*2880e676SManos Pitsidianakis 185*2880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev) 186*2880e676SManos Pitsidianakis { 187*2880e676SManos Pitsidianakis VirtIODevice *vdev = VIRTIO_DEVICE(dev); 188*2880e676SManos Pitsidianakis VirtIOSound *vsnd = VIRTIO_SND(dev); 189*2880e676SManos Pitsidianakis 190*2880e676SManos Pitsidianakis qemu_del_vm_change_state_handler(vsnd->vmstate); 191*2880e676SManos Pitsidianakis trace_virtio_snd_unrealize(vsnd); 192*2880e676SManos Pitsidianakis 193*2880e676SManos Pitsidianakis AUD_remove_card(&vsnd->card); 194*2880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); 195*2880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); 196*2880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); 197*2880e676SManos Pitsidianakis virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); 198*2880e676SManos Pitsidianakis virtio_cleanup(vdev); 199*2880e676SManos Pitsidianakis } 200*2880e676SManos Pitsidianakis 201*2880e676SManos Pitsidianakis 202*2880e676SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev) {} 203*2880e676SManos Pitsidianakis 204*2880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data) 205*2880e676SManos Pitsidianakis { 206*2880e676SManos Pitsidianakis DeviceClass *dc = DEVICE_CLASS(klass); 207*2880e676SManos Pitsidianakis VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 208*2880e676SManos Pitsidianakis 209*2880e676SManos Pitsidianakis 210*2880e676SManos Pitsidianakis set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 211*2880e676SManos Pitsidianakis device_class_set_props(dc, virtio_snd_properties); 212*2880e676SManos Pitsidianakis 213*2880e676SManos Pitsidianakis dc->vmsd = &vmstate_virtio_snd; 214*2880e676SManos Pitsidianakis vdc->vmsd = &vmstate_virtio_snd_device; 215*2880e676SManos Pitsidianakis vdc->realize = virtio_snd_realize; 216*2880e676SManos Pitsidianakis vdc->unrealize = virtio_snd_unrealize; 217*2880e676SManos Pitsidianakis vdc->get_config = virtio_snd_get_config; 218*2880e676SManos Pitsidianakis vdc->set_config = virtio_snd_set_config; 219*2880e676SManos Pitsidianakis vdc->get_features = get_features; 220*2880e676SManos Pitsidianakis vdc->reset = virtio_snd_reset; 221*2880e676SManos Pitsidianakis vdc->legacy_features = 0; 222*2880e676SManos Pitsidianakis } 223*2880e676SManos Pitsidianakis 224*2880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = { 225*2880e676SManos Pitsidianakis { 226*2880e676SManos Pitsidianakis .name = TYPE_VIRTIO_SND, 227*2880e676SManos Pitsidianakis .parent = TYPE_VIRTIO_DEVICE, 228*2880e676SManos Pitsidianakis .instance_size = sizeof(VirtIOSound), 229*2880e676SManos Pitsidianakis .class_init = virtio_snd_class_init, 230*2880e676SManos Pitsidianakis } 231*2880e676SManos Pitsidianakis }; 232*2880e676SManos Pitsidianakis 233*2880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types) 234