diff options
Diffstat (limited to 'hw/audio')
-rw-r--r-- | hw/audio/Kconfig | 5 | ||||
-rw-r--r-- | hw/audio/meson.build | 1 | ||||
-rw-r--r-- | hw/audio/trace-events | 9 | ||||
-rw-r--r-- | hw/audio/virtio-snd.c | 233 |
4 files changed, 248 insertions, 0 deletions
diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig index d0993514a1..daf060e1be 100644 --- a/hw/audio/Kconfig +++ b/hw/audio/Kconfig @@ -50,3 +50,8 @@ config CS4231 config ASC bool + +config VIRTIO_SND + bool + default y + depends on VIRTIO diff --git a/hw/audio/meson.build b/hw/audio/meson.build index 8805322f5c..7a503be1fd 100644 --- a/hw/audio/meson.build +++ b/hw/audio/meson.build @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_PL041', if_true: files('pl041.c', 'lm4549.c')) system_ss.add(when: 'CONFIG_SB16', if_true: files('sb16.c')) system_ss.add(when: 'CONFIG_VT82C686', if_true: files('via-ac97.c')) system_ss.add(when: 'CONFIG_WM8750', if_true: files('wm8750.c')) +system_ss.add(when: ['CONFIG_VIRTIO_SND', 'CONFIG_VIRTIO'], if_true: files('virtio-snd.c')) diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 059ce451f5..525ced2b34 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -38,3 +38,12 @@ asc_write_fifo(const char fifo, int reg, unsigned size, int wrptr, int cnt, uint asc_write_reg(int reg, unsigned size, uint64_t value) "reg=0x%03x size=%u value=0x%"PRIx64 asc_write_extreg(const char fifo, int reg, unsigned size, uint64_t value) "fifo %c reg=0x%03x size=%u value=0x%"PRIx64 asc_update_irq(int irq, int a, int b) "set IRQ to %d (A: 0x%x B: 0x%x)" + +#virtio-snd.c +virtio_snd_get_config(void *vdev, uint32_t jacks, uint32_t streams, uint32_t chmaps) "snd %p: get_config jacks=%"PRIu32" streams=%"PRIu32" chmaps=%"PRIu32"" +virtio_snd_set_config(void *vdev, uint32_t jacks, uint32_t new_jacks, uint32_t streams, uint32_t new_streams, uint32_t chmaps, uint32_t new_chmaps) "snd %p: set_config jacks from %"PRIu32"->%"PRIu32", streams from %"PRIu32"->%"PRIu32", chmaps from %"PRIu32"->%"PRIu32 +virtio_snd_get_features(void *vdev, uint64_t features) "snd %p: get_features 0x%"PRIx64 +virtio_snd_vm_state_running(void) "vm state running" +virtio_snd_vm_state_stopped(void) "vm state stopped" +virtio_snd_realize(void *snd) "snd %p: realize" +virtio_snd_unrealize(void *snd) "snd %p: unrealize" diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c new file mode 100644 index 0000000000..3a4b441c20 --- /dev/null +++ b/hw/audio/virtio-snd.c @@ -0,0 +1,233 @@ +/* + * VIRTIO Sound Device conforming to + * + * "Virtual I/O Device (VIRTIO) Version 1.2 + * Committee Specification Draft 01 + * 09 May 2022" + * + * <https://docs.oasis-open.org/virtio/virtio/v1.2/csd01/virtio-v1.2-csd01.html#x1-52900014> + * + * Copyright (c) 2023 Emmanouil Pitsidianakis <manos.pitsidianakis@linaro.org> + * Copyright (C) 2019 OpenSynergy GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "include/qemu/lockable.h" +#include "sysemu/runstate.h" +#include "trace.h" +#include "qapi/error.h" +#include "hw/audio/virtio-snd.h" +#include "hw/core/cpu.h" + +#define VIRTIO_SOUND_VM_VERSION 1 +#define VIRTIO_SOUND_JACK_DEFAULT 0 +#define VIRTIO_SOUND_STREAM_DEFAULT 1 +#define VIRTIO_SOUND_CHMAP_DEFAULT 0 +#define VIRTIO_SOUND_HDA_FN_NID 0 + +static const VMStateDescription vmstate_virtio_snd_device = { + .name = TYPE_VIRTIO_SND, + .version_id = VIRTIO_SOUND_VM_VERSION, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, +}; + +static const VMStateDescription vmstate_virtio_snd = { + .name = TYPE_VIRTIO_SND, + .minimum_version_id = VIRTIO_SOUND_VM_VERSION, + .version_id = VIRTIO_SOUND_VM_VERSION, + .fields = (VMStateField[]) { + VMSTATE_VIRTIO_DEVICE, + VMSTATE_END_OF_LIST() + }, +}; + +static Property virtio_snd_properties[] = { + DEFINE_AUDIO_PROPERTIES(VirtIOSound, card), + DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks, + VIRTIO_SOUND_JACK_DEFAULT), + DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams, + VIRTIO_SOUND_STREAM_DEFAULT), + DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps, + VIRTIO_SOUND_CHMAP_DEFAULT), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + virtio_snd_config *sndconfig = + (virtio_snd_config *)config; + trace_virtio_snd_get_config(vdev, + s->snd_conf.jacks, + s->snd_conf.streams, + s->snd_conf.chmaps); + + memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf)); + cpu_to_le32s(&sndconfig->jacks); + cpu_to_le32s(&sndconfig->streams); + cpu_to_le32s(&sndconfig->chmaps); + +} + +static void +virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config) +{ + VirtIOSound *s = VIRTIO_SND(vdev); + const virtio_snd_config *sndconfig = + (const virtio_snd_config *)config; + + + trace_virtio_snd_set_config(vdev, + s->snd_conf.jacks, + sndconfig->jacks, + s->snd_conf.streams, + sndconfig->streams, + s->snd_conf.chmaps, + sndconfig->chmaps); + + memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config)); + le32_to_cpus(&s->snd_conf.jacks); + le32_to_cpus(&s->snd_conf.streams); + le32_to_cpus(&s->snd_conf.chmaps); + +} + +/* + * Queue handler stub. + * + * @vdev: VirtIOSound device + * @vq: virtqueue + */ +static void virtio_snd_handle_queue(VirtIODevice *vdev, VirtQueue *vq) {} + +static uint64_t get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + /* + * virtio-v1.2-csd01, 5.14.3, + * Feature Bits + * None currently defined. + */ + VirtIOSound *s = VIRTIO_SND(vdev); + features |= s->features; + + trace_virtio_snd_get_features(vdev, features); + + return features; +} + +static void +virtio_snd_vm_state_change(void *opaque, bool running, + RunState state) +{ + if (running) { + trace_virtio_snd_vm_state_running(); + } else { + trace_virtio_snd_vm_state_stopped(); + } +} + +static void virtio_snd_realize(DeviceState *dev, Error **errp) +{ + ERRP_GUARD(); + VirtIOSound *vsnd = VIRTIO_SND(dev); + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + + vsnd->vmstate = + qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd); + + trace_virtio_snd_realize(vsnd); + + virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config)); + virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1); + + /* set number of jacks and streams */ + if (vsnd->snd_conf.jacks > 8) { + error_setg(errp, + "Invalid number of jacks: %"PRIu32, + vsnd->snd_conf.jacks); + return; + } + if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) { + error_setg(errp, + "Invalid number of streams: %"PRIu32, + vsnd->snd_conf.streams); + return; + } + + if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) { + error_setg(errp, + "Invalid number of channel maps: %"PRIu32, + vsnd->snd_conf.chmaps); + return; + } + + AUD_register_card("virtio-sound", &vsnd->card, errp); + + vsnd->queues[VIRTIO_SND_VQ_CONTROL] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_EVENT] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_TX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); + vsnd->queues[VIRTIO_SND_VQ_RX] = + virtio_add_queue(vdev, 64, virtio_snd_handle_queue); +} + +static void virtio_snd_unrealize(DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOSound *vsnd = VIRTIO_SND(dev); + + qemu_del_vm_change_state_handler(vsnd->vmstate); + trace_virtio_snd_unrealize(vsnd); + + AUD_remove_card(&vsnd->card); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]); + virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]); + virtio_cleanup(vdev); +} + + +static void virtio_snd_reset(VirtIODevice *vdev) {} + +static void virtio_snd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + + set_bit(DEVICE_CATEGORY_SOUND, dc->categories); + device_class_set_props(dc, virtio_snd_properties); + + dc->vmsd = &vmstate_virtio_snd; + vdc->vmsd = &vmstate_virtio_snd_device; + vdc->realize = virtio_snd_realize; + vdc->unrealize = virtio_snd_unrealize; + vdc->get_config = virtio_snd_get_config; + vdc->set_config = virtio_snd_set_config; + vdc->get_features = get_features; + vdc->reset = virtio_snd_reset; + vdc->legacy_features = 0; +} + +static const TypeInfo virtio_snd_types[] = { + { + .name = TYPE_VIRTIO_SND, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VirtIOSound), + .class_init = virtio_snd_class_init, + } +}; + +DEFINE_TYPES(virtio_snd_types) |