xref: /qemu/hw/audio/virtio-snd.c (revision 0ff05dd2)
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 
35eb9ad377SManos Pitsidianakis static uint32_t supported_formats = BIT(VIRTIO_SND_PCM_FMT_S8)
36eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U8)
37eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_S16)
38eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U16)
39eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_S32)
40eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_U32)
41eb9ad377SManos Pitsidianakis                                   | BIT(VIRTIO_SND_PCM_FMT_FLOAT);
42eb9ad377SManos Pitsidianakis 
43eb9ad377SManos Pitsidianakis static uint32_t supported_rates = BIT(VIRTIO_SND_PCM_RATE_5512)
44eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_8000)
45eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_11025)
46eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_16000)
47eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_22050)
48eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_32000)
49eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_44100)
50eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_48000)
51eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_64000)
52eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_88200)
53eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_96000)
54eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_176400)
55eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_192000)
56eb9ad377SManos Pitsidianakis                                 | BIT(VIRTIO_SND_PCM_RATE_384000);
57eb9ad377SManos Pitsidianakis 
582880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd_device = {
592880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
602880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
612880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
622880e676SManos Pitsidianakis };
632880e676SManos Pitsidianakis 
642880e676SManos Pitsidianakis static const VMStateDescription vmstate_virtio_snd = {
652880e676SManos Pitsidianakis     .name = TYPE_VIRTIO_SND,
662880e676SManos Pitsidianakis     .minimum_version_id = VIRTIO_SOUND_VM_VERSION,
672880e676SManos Pitsidianakis     .version_id = VIRTIO_SOUND_VM_VERSION,
682880e676SManos Pitsidianakis     .fields = (VMStateField[]) {
692880e676SManos Pitsidianakis         VMSTATE_VIRTIO_DEVICE,
702880e676SManos Pitsidianakis         VMSTATE_END_OF_LIST()
712880e676SManos Pitsidianakis     },
722880e676SManos Pitsidianakis };
732880e676SManos Pitsidianakis 
742880e676SManos Pitsidianakis static Property virtio_snd_properties[] = {
752880e676SManos Pitsidianakis     DEFINE_AUDIO_PROPERTIES(VirtIOSound, card),
762880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("jacks", VirtIOSound, snd_conf.jacks,
772880e676SManos Pitsidianakis                        VIRTIO_SOUND_JACK_DEFAULT),
782880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("streams", VirtIOSound, snd_conf.streams,
792880e676SManos Pitsidianakis                        VIRTIO_SOUND_STREAM_DEFAULT),
802880e676SManos Pitsidianakis     DEFINE_PROP_UINT32("chmaps", VirtIOSound, snd_conf.chmaps,
812880e676SManos Pitsidianakis                        VIRTIO_SOUND_CHMAP_DEFAULT),
822880e676SManos Pitsidianakis     DEFINE_PROP_END_OF_LIST(),
832880e676SManos Pitsidianakis };
842880e676SManos Pitsidianakis 
852880e676SManos Pitsidianakis static void
862880e676SManos Pitsidianakis virtio_snd_get_config(VirtIODevice *vdev, uint8_t *config)
872880e676SManos Pitsidianakis {
882880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
892880e676SManos Pitsidianakis     virtio_snd_config *sndconfig =
902880e676SManos Pitsidianakis         (virtio_snd_config *)config;
912880e676SManos Pitsidianakis     trace_virtio_snd_get_config(vdev,
922880e676SManos Pitsidianakis                                 s->snd_conf.jacks,
932880e676SManos Pitsidianakis                                 s->snd_conf.streams,
942880e676SManos Pitsidianakis                                 s->snd_conf.chmaps);
952880e676SManos Pitsidianakis 
962880e676SManos Pitsidianakis     memcpy(sndconfig, &s->snd_conf, sizeof(s->snd_conf));
972880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->jacks);
982880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->streams);
992880e676SManos Pitsidianakis     cpu_to_le32s(&sndconfig->chmaps);
1002880e676SManos Pitsidianakis 
1012880e676SManos Pitsidianakis }
1022880e676SManos Pitsidianakis 
1032880e676SManos Pitsidianakis static void
1042880e676SManos Pitsidianakis virtio_snd_set_config(VirtIODevice *vdev, const uint8_t *config)
1052880e676SManos Pitsidianakis {
1062880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
1072880e676SManos Pitsidianakis     const virtio_snd_config *sndconfig =
1082880e676SManos Pitsidianakis         (const virtio_snd_config *)config;
1092880e676SManos Pitsidianakis 
1102880e676SManos Pitsidianakis 
1112880e676SManos Pitsidianakis    trace_virtio_snd_set_config(vdev,
1122880e676SManos Pitsidianakis                                s->snd_conf.jacks,
1132880e676SManos Pitsidianakis                                sndconfig->jacks,
1142880e676SManos Pitsidianakis                                s->snd_conf.streams,
1152880e676SManos Pitsidianakis                                sndconfig->streams,
1162880e676SManos Pitsidianakis                                s->snd_conf.chmaps,
1172880e676SManos Pitsidianakis                                sndconfig->chmaps);
1182880e676SManos Pitsidianakis 
1192880e676SManos Pitsidianakis     memcpy(&s->snd_conf, sndconfig, sizeof(virtio_snd_config));
1202880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.jacks);
1212880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.streams);
1222880e676SManos Pitsidianakis     le32_to_cpus(&s->snd_conf.chmaps);
1232880e676SManos Pitsidianakis 
1242880e676SManos Pitsidianakis }
1252880e676SManos Pitsidianakis 
126eb9ad377SManos Pitsidianakis static void
127eb9ad377SManos Pitsidianakis virtio_snd_ctrl_cmd_free(virtio_snd_ctrl_command *cmd)
128eb9ad377SManos Pitsidianakis {
129eb9ad377SManos Pitsidianakis     g_free(cmd->elem);
130eb9ad377SManos Pitsidianakis     g_free(cmd);
131eb9ad377SManos Pitsidianakis }
132eb9ad377SManos Pitsidianakis 
1332880e676SManos Pitsidianakis /*
134eb9ad377SManos Pitsidianakis  * Get a specific stream from the virtio sound card device.
135eb9ad377SManos Pitsidianakis  * Returns NULL if @stream_id is invalid or not allocated.
136eb9ad377SManos Pitsidianakis  *
137eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
138eb9ad377SManos Pitsidianakis  * @stream_id: stream id
139eb9ad377SManos Pitsidianakis  */
140eb9ad377SManos Pitsidianakis static VirtIOSoundPCMStream *virtio_snd_pcm_get_stream(VirtIOSound *s,
141eb9ad377SManos Pitsidianakis                                                        uint32_t stream_id)
142eb9ad377SManos Pitsidianakis {
143eb9ad377SManos Pitsidianakis     return stream_id >= s->snd_conf.streams ? NULL :
144eb9ad377SManos Pitsidianakis         s->pcm->streams[stream_id];
145eb9ad377SManos Pitsidianakis }
146eb9ad377SManos Pitsidianakis 
147eb9ad377SManos Pitsidianakis /*
148eb9ad377SManos Pitsidianakis  * Get params for a specific stream.
149eb9ad377SManos Pitsidianakis  *
150eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
151eb9ad377SManos Pitsidianakis  * @stream_id: stream id
152eb9ad377SManos Pitsidianakis  */
153eb9ad377SManos Pitsidianakis static virtio_snd_pcm_set_params *virtio_snd_pcm_get_params(VirtIOSound *s,
154eb9ad377SManos Pitsidianakis                                                             uint32_t stream_id)
155eb9ad377SManos Pitsidianakis {
156eb9ad377SManos Pitsidianakis     return stream_id >= s->snd_conf.streams ? NULL
157eb9ad377SManos Pitsidianakis         : &s->pcm->pcm_params[stream_id];
158eb9ad377SManos Pitsidianakis }
159eb9ad377SManos Pitsidianakis 
160eb9ad377SManos Pitsidianakis /*
1610ff05dd2SManos Pitsidianakis  * Handle the VIRTIO_SND_R_PCM_INFO request.
1620ff05dd2SManos Pitsidianakis  * The function writes the info structs to the request element.
1630ff05dd2SManos Pitsidianakis  *
1640ff05dd2SManos Pitsidianakis  * @s: VirtIOSound device
1650ff05dd2SManos Pitsidianakis  * @cmd: The request command queue element from VirtIOSound cmdq field
1660ff05dd2SManos Pitsidianakis  */
1670ff05dd2SManos Pitsidianakis static void virtio_snd_handle_pcm_info(VirtIOSound *s,
1680ff05dd2SManos Pitsidianakis                                        virtio_snd_ctrl_command *cmd)
1690ff05dd2SManos Pitsidianakis {
1700ff05dd2SManos Pitsidianakis     uint32_t stream_id, start_id, count, size;
1710ff05dd2SManos Pitsidianakis     virtio_snd_pcm_info val;
1720ff05dd2SManos Pitsidianakis     virtio_snd_query_info req;
1730ff05dd2SManos Pitsidianakis     VirtIOSoundPCMStream *stream = NULL;
1740ff05dd2SManos Pitsidianakis     g_autofree virtio_snd_pcm_info *pcm_info = NULL;
1750ff05dd2SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
1760ff05dd2SManos Pitsidianakis                                cmd->elem->out_num,
1770ff05dd2SManos Pitsidianakis                                0,
1780ff05dd2SManos Pitsidianakis                                &req,
1790ff05dd2SManos Pitsidianakis                                sizeof(virtio_snd_query_info));
1800ff05dd2SManos Pitsidianakis 
1810ff05dd2SManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_query_info)) {
1820ff05dd2SManos Pitsidianakis         /*
1830ff05dd2SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
1840ff05dd2SManos Pitsidianakis          */
1850ff05dd2SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
1860ff05dd2SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
1870ff05dd2SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_query_info));
1880ff05dd2SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
1890ff05dd2SManos Pitsidianakis         return;
1900ff05dd2SManos Pitsidianakis     }
1910ff05dd2SManos Pitsidianakis 
1920ff05dd2SManos Pitsidianakis     start_id = le32_to_cpu(req.start_id);
1930ff05dd2SManos Pitsidianakis     count = le32_to_cpu(req.count);
1940ff05dd2SManos Pitsidianakis     size = le32_to_cpu(req.size);
1950ff05dd2SManos Pitsidianakis 
1960ff05dd2SManos Pitsidianakis     if (iov_size(cmd->elem->in_sg, cmd->elem->in_num) <
1970ff05dd2SManos Pitsidianakis         sizeof(virtio_snd_hdr) + size * count) {
1980ff05dd2SManos Pitsidianakis         /*
1990ff05dd2SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
2000ff05dd2SManos Pitsidianakis          */
2010ff05dd2SManos Pitsidianakis         error_report("pcm info: buffer too small, got: %zu, needed: %zu",
2020ff05dd2SManos Pitsidianakis                 iov_size(cmd->elem->in_sg, cmd->elem->in_num),
2030ff05dd2SManos Pitsidianakis                 sizeof(virtio_snd_pcm_info));
2040ff05dd2SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2050ff05dd2SManos Pitsidianakis         return;
2060ff05dd2SManos Pitsidianakis     }
2070ff05dd2SManos Pitsidianakis 
2080ff05dd2SManos Pitsidianakis     pcm_info = g_new0(virtio_snd_pcm_info, count);
2090ff05dd2SManos Pitsidianakis     for (uint32_t i = 0; i < count; i++) {
2100ff05dd2SManos Pitsidianakis         stream_id = i + start_id;
2110ff05dd2SManos Pitsidianakis         trace_virtio_snd_handle_pcm_info(stream_id);
2120ff05dd2SManos Pitsidianakis         stream = virtio_snd_pcm_get_stream(s, stream_id);
2130ff05dd2SManos Pitsidianakis         if (!stream) {
2140ff05dd2SManos Pitsidianakis             error_report("Invalid stream id: %"PRIu32, stream_id);
2150ff05dd2SManos Pitsidianakis             cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
2160ff05dd2SManos Pitsidianakis             return;
2170ff05dd2SManos Pitsidianakis         }
2180ff05dd2SManos Pitsidianakis         val = stream->info;
2190ff05dd2SManos Pitsidianakis         val.hdr.hda_fn_nid = cpu_to_le32(val.hdr.hda_fn_nid);
2200ff05dd2SManos Pitsidianakis         val.features = cpu_to_le32(val.features);
2210ff05dd2SManos Pitsidianakis         val.formats = cpu_to_le64(val.formats);
2220ff05dd2SManos Pitsidianakis         val.rates = cpu_to_le64(val.rates);
2230ff05dd2SManos Pitsidianakis         /*
2240ff05dd2SManos Pitsidianakis          * 5.14.6.6.2.1 Device Requirements: Stream Information The device MUST
2250ff05dd2SManos Pitsidianakis          * NOT set undefined feature, format, rate and direction values. The
2260ff05dd2SManos Pitsidianakis          * device MUST initialize the padding bytes to 0.
2270ff05dd2SManos Pitsidianakis          */
2280ff05dd2SManos Pitsidianakis         pcm_info[i] = val;
2290ff05dd2SManos Pitsidianakis         memset(&pcm_info[i].padding, 0, 5);
2300ff05dd2SManos Pitsidianakis     }
2310ff05dd2SManos Pitsidianakis 
2320ff05dd2SManos Pitsidianakis     cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
2330ff05dd2SManos Pitsidianakis     iov_from_buf(cmd->elem->in_sg,
2340ff05dd2SManos Pitsidianakis                  cmd->elem->in_num,
2350ff05dd2SManos Pitsidianakis                  sizeof(virtio_snd_hdr),
2360ff05dd2SManos Pitsidianakis                  pcm_info,
2370ff05dd2SManos Pitsidianakis                  sizeof(virtio_snd_pcm_info) * count);
2380ff05dd2SManos Pitsidianakis }
2390ff05dd2SManos Pitsidianakis 
2400ff05dd2SManos Pitsidianakis /*
241eb9ad377SManos Pitsidianakis  * Set the given stream params.
242eb9ad377SManos Pitsidianakis  * Called by both virtio_snd_handle_pcm_set_params and during device
243eb9ad377SManos Pitsidianakis  * initialization.
244eb9ad377SManos Pitsidianakis  * Returns the response status code. (VIRTIO_SND_S_*).
245eb9ad377SManos Pitsidianakis  *
246eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
247eb9ad377SManos Pitsidianakis  * @params: The PCM params as defined in the virtio specification
248eb9ad377SManos Pitsidianakis  */
249eb9ad377SManos Pitsidianakis static
250eb9ad377SManos Pitsidianakis uint32_t virtio_snd_set_pcm_params(VirtIOSound *s,
251eb9ad377SManos Pitsidianakis                                    uint32_t stream_id,
252eb9ad377SManos Pitsidianakis                                    virtio_snd_pcm_set_params *params)
253eb9ad377SManos Pitsidianakis {
254eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params *st_params;
255eb9ad377SManos Pitsidianakis 
256eb9ad377SManos Pitsidianakis     if (stream_id >= s->snd_conf.streams || s->pcm->pcm_params == NULL) {
257eb9ad377SManos Pitsidianakis         /*
258eb9ad377SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
259eb9ad377SManos Pitsidianakis          */
260eb9ad377SManos Pitsidianakis         virtio_error(VIRTIO_DEVICE(s), "Streams have not been initialized.\n");
261eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
262eb9ad377SManos Pitsidianakis     }
263eb9ad377SManos Pitsidianakis 
264eb9ad377SManos Pitsidianakis     st_params = virtio_snd_pcm_get_params(s, stream_id);
265eb9ad377SManos Pitsidianakis 
266eb9ad377SManos Pitsidianakis     if (params->channels < 1 || params->channels > AUDIO_MAX_CHANNELS) {
267eb9ad377SManos Pitsidianakis         error_report("Number of channels is not supported.");
268eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
269eb9ad377SManos Pitsidianakis     }
270eb9ad377SManos Pitsidianakis     if (!(supported_formats & BIT(params->format))) {
271eb9ad377SManos Pitsidianakis         error_report("Stream format is not supported.");
272eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
273eb9ad377SManos Pitsidianakis     }
274eb9ad377SManos Pitsidianakis     if (!(supported_rates & BIT(params->rate))) {
275eb9ad377SManos Pitsidianakis         error_report("Stream rate is not supported.");
276eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
277eb9ad377SManos Pitsidianakis     }
278eb9ad377SManos Pitsidianakis 
279eb9ad377SManos Pitsidianakis     st_params->buffer_bytes = le32_to_cpu(params->buffer_bytes);
280eb9ad377SManos Pitsidianakis     st_params->period_bytes = le32_to_cpu(params->period_bytes);
281eb9ad377SManos Pitsidianakis     st_params->features = le32_to_cpu(params->features);
282eb9ad377SManos Pitsidianakis     /* the following are uint8_t, so there's no need to bswap the values. */
283eb9ad377SManos Pitsidianakis     st_params->channels = params->channels;
284eb9ad377SManos Pitsidianakis     st_params->format = params->format;
285eb9ad377SManos Pitsidianakis     st_params->rate = params->rate;
286eb9ad377SManos Pitsidianakis 
287eb9ad377SManos Pitsidianakis     return cpu_to_le32(VIRTIO_SND_S_OK);
288eb9ad377SManos Pitsidianakis }
289eb9ad377SManos Pitsidianakis 
290eb9ad377SManos Pitsidianakis /*
291eb9ad377SManos Pitsidianakis  * Get a QEMU Audiosystem compatible format value from a VIRTIO_SND_PCM_FMT_*
292eb9ad377SManos Pitsidianakis  */
293eb9ad377SManos Pitsidianakis static AudioFormat virtio_snd_get_qemu_format(uint32_t format)
294eb9ad377SManos Pitsidianakis {
295eb9ad377SManos Pitsidianakis     #define CASE(FMT)               \
296eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_FMT_##FMT:  \
297eb9ad377SManos Pitsidianakis         return AUDIO_FORMAT_##FMT;
298eb9ad377SManos Pitsidianakis 
299eb9ad377SManos Pitsidianakis     switch (format) {
300eb9ad377SManos Pitsidianakis     CASE(U8)
301eb9ad377SManos Pitsidianakis     CASE(S8)
302eb9ad377SManos Pitsidianakis     CASE(U16)
303eb9ad377SManos Pitsidianakis     CASE(S16)
304eb9ad377SManos Pitsidianakis     CASE(U32)
305eb9ad377SManos Pitsidianakis     CASE(S32)
306eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_FMT_FLOAT:
307eb9ad377SManos Pitsidianakis         return AUDIO_FORMAT_F32;
308eb9ad377SManos Pitsidianakis     default:
309eb9ad377SManos Pitsidianakis         g_assert_not_reached();
310eb9ad377SManos Pitsidianakis     }
311eb9ad377SManos Pitsidianakis 
312eb9ad377SManos Pitsidianakis     #undef CASE
313eb9ad377SManos Pitsidianakis }
314eb9ad377SManos Pitsidianakis 
315eb9ad377SManos Pitsidianakis /*
316eb9ad377SManos Pitsidianakis  * Get a QEMU Audiosystem compatible frequency value from a
317eb9ad377SManos Pitsidianakis  * VIRTIO_SND_PCM_RATE_*
318eb9ad377SManos Pitsidianakis  */
319eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_get_qemu_freq(uint32_t rate)
320eb9ad377SManos Pitsidianakis {
321eb9ad377SManos Pitsidianakis     #define CASE(RATE)               \
322eb9ad377SManos Pitsidianakis     case VIRTIO_SND_PCM_RATE_##RATE: \
323eb9ad377SManos Pitsidianakis         return RATE;
324eb9ad377SManos Pitsidianakis 
325eb9ad377SManos Pitsidianakis     switch (rate) {
326eb9ad377SManos Pitsidianakis     CASE(5512)
327eb9ad377SManos Pitsidianakis     CASE(8000)
328eb9ad377SManos Pitsidianakis     CASE(11025)
329eb9ad377SManos Pitsidianakis     CASE(16000)
330eb9ad377SManos Pitsidianakis     CASE(22050)
331eb9ad377SManos Pitsidianakis     CASE(32000)
332eb9ad377SManos Pitsidianakis     CASE(44100)
333eb9ad377SManos Pitsidianakis     CASE(48000)
334eb9ad377SManos Pitsidianakis     CASE(64000)
335eb9ad377SManos Pitsidianakis     CASE(88200)
336eb9ad377SManos Pitsidianakis     CASE(96000)
337eb9ad377SManos Pitsidianakis     CASE(176400)
338eb9ad377SManos Pitsidianakis     CASE(192000)
339eb9ad377SManos Pitsidianakis     CASE(384000)
340eb9ad377SManos Pitsidianakis     default:
341eb9ad377SManos Pitsidianakis         g_assert_not_reached();
342eb9ad377SManos Pitsidianakis     }
343eb9ad377SManos Pitsidianakis 
344eb9ad377SManos Pitsidianakis     #undef CASE
345eb9ad377SManos Pitsidianakis }
346eb9ad377SManos Pitsidianakis 
347eb9ad377SManos Pitsidianakis /*
348eb9ad377SManos Pitsidianakis  * Get QEMU Audiosystem compatible audsettings from virtio based pcm stream
349eb9ad377SManos Pitsidianakis  * params.
350eb9ad377SManos Pitsidianakis  */
351eb9ad377SManos Pitsidianakis static void virtio_snd_get_qemu_audsettings(audsettings *as,
352eb9ad377SManos Pitsidianakis                                             virtio_snd_pcm_set_params *params)
353eb9ad377SManos Pitsidianakis {
354eb9ad377SManos Pitsidianakis     as->nchannels = MIN(AUDIO_MAX_CHANNELS, params->channels);
355eb9ad377SManos Pitsidianakis     as->fmt = virtio_snd_get_qemu_format(params->format);
356eb9ad377SManos Pitsidianakis     as->freq = virtio_snd_get_qemu_freq(params->rate);
357eb9ad377SManos Pitsidianakis     as->endianness = target_words_bigendian() ? 1 : 0;
358eb9ad377SManos Pitsidianakis }
359eb9ad377SManos Pitsidianakis 
360eb9ad377SManos Pitsidianakis /*
361eb9ad377SManos Pitsidianakis  * Close a stream and free all its resources.
362eb9ad377SManos Pitsidianakis  *
363eb9ad377SManos Pitsidianakis  * @stream: VirtIOSoundPCMStream *stream
364eb9ad377SManos Pitsidianakis  */
365eb9ad377SManos Pitsidianakis static void virtio_snd_pcm_close(VirtIOSoundPCMStream *stream)
366eb9ad377SManos Pitsidianakis {
367eb9ad377SManos Pitsidianakis }
368eb9ad377SManos Pitsidianakis 
369eb9ad377SManos Pitsidianakis /*
370eb9ad377SManos Pitsidianakis  * Prepares a VirtIOSound card stream.
371eb9ad377SManos Pitsidianakis  * Returns the response status code. (VIRTIO_SND_S_*).
372eb9ad377SManos Pitsidianakis  *
373eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
374eb9ad377SManos Pitsidianakis  * @stream_id: stream id
375eb9ad377SManos Pitsidianakis  */
376eb9ad377SManos Pitsidianakis static uint32_t virtio_snd_pcm_prepare(VirtIOSound *s, uint32_t stream_id)
377eb9ad377SManos Pitsidianakis {
378eb9ad377SManos Pitsidianakis     audsettings as;
379eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params *params;
380eb9ad377SManos Pitsidianakis     VirtIOSoundPCMStream *stream;
381eb9ad377SManos Pitsidianakis 
382eb9ad377SManos Pitsidianakis     if (s->pcm->streams == NULL ||
383eb9ad377SManos Pitsidianakis         s->pcm->pcm_params == NULL ||
384eb9ad377SManos Pitsidianakis         stream_id >= s->snd_conf.streams) {
385eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
386eb9ad377SManos Pitsidianakis     }
387eb9ad377SManos Pitsidianakis 
388eb9ad377SManos Pitsidianakis     params = virtio_snd_pcm_get_params(s, stream_id);
389eb9ad377SManos Pitsidianakis     if (params == NULL) {
390eb9ad377SManos Pitsidianakis         return cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
391eb9ad377SManos Pitsidianakis     }
392eb9ad377SManos Pitsidianakis 
393eb9ad377SManos Pitsidianakis     stream = virtio_snd_pcm_get_stream(s, stream_id);
394eb9ad377SManos Pitsidianakis     if (stream == NULL) {
395eb9ad377SManos Pitsidianakis         stream = g_new0(VirtIOSoundPCMStream, 1);
396eb9ad377SManos Pitsidianakis         stream->active = false;
397eb9ad377SManos Pitsidianakis         stream->id = stream_id;
398eb9ad377SManos Pitsidianakis         stream->pcm = s->pcm;
399eb9ad377SManos Pitsidianakis         stream->s = s;
400eb9ad377SManos Pitsidianakis 
401eb9ad377SManos Pitsidianakis         /*
402eb9ad377SManos Pitsidianakis          * stream_id >= s->snd_conf.streams was checked before so this is
403eb9ad377SManos Pitsidianakis          * in-bounds
404eb9ad377SManos Pitsidianakis          */
405eb9ad377SManos Pitsidianakis         s->pcm->streams[stream_id] = stream;
406eb9ad377SManos Pitsidianakis     }
407eb9ad377SManos Pitsidianakis 
408eb9ad377SManos Pitsidianakis     virtio_snd_get_qemu_audsettings(&as, params);
409eb9ad377SManos Pitsidianakis     stream->info.direction = stream_id < s->snd_conf.streams / 2 +
410eb9ad377SManos Pitsidianakis         (s->snd_conf.streams & 1) ? VIRTIO_SND_D_OUTPUT : VIRTIO_SND_D_INPUT;
411eb9ad377SManos Pitsidianakis     stream->info.hdr.hda_fn_nid = VIRTIO_SOUND_HDA_FN_NID;
412eb9ad377SManos Pitsidianakis     stream->info.features = 0;
413eb9ad377SManos Pitsidianakis     stream->info.channels_min = 1;
414eb9ad377SManos Pitsidianakis     stream->info.channels_max = as.nchannels;
415eb9ad377SManos Pitsidianakis     stream->info.formats = supported_formats;
416eb9ad377SManos Pitsidianakis     stream->info.rates = supported_rates;
417eb9ad377SManos Pitsidianakis     stream->params = *params;
418eb9ad377SManos Pitsidianakis 
419eb9ad377SManos Pitsidianakis     stream->positions[0] = VIRTIO_SND_CHMAP_FL;
420eb9ad377SManos Pitsidianakis     stream->positions[1] = VIRTIO_SND_CHMAP_FR;
421eb9ad377SManos Pitsidianakis     stream->as = as;
422eb9ad377SManos Pitsidianakis 
423eb9ad377SManos Pitsidianakis     return cpu_to_le32(VIRTIO_SND_S_OK);
424eb9ad377SManos Pitsidianakis }
425eb9ad377SManos Pitsidianakis 
426eb9ad377SManos Pitsidianakis static const char *print_code(uint32_t code)
427eb9ad377SManos Pitsidianakis {
428eb9ad377SManos Pitsidianakis     #define CASE(CODE)            \
429eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_##CODE:     \
430eb9ad377SManos Pitsidianakis         return "VIRTIO_SND_R_"#CODE
431eb9ad377SManos Pitsidianakis 
432eb9ad377SManos Pitsidianakis     switch (code) {
433eb9ad377SManos Pitsidianakis     CASE(JACK_INFO);
434eb9ad377SManos Pitsidianakis     CASE(JACK_REMAP);
435eb9ad377SManos Pitsidianakis     CASE(PCM_INFO);
436eb9ad377SManos Pitsidianakis     CASE(PCM_SET_PARAMS);
437eb9ad377SManos Pitsidianakis     CASE(PCM_PREPARE);
438eb9ad377SManos Pitsidianakis     CASE(PCM_RELEASE);
439eb9ad377SManos Pitsidianakis     CASE(PCM_START);
440eb9ad377SManos Pitsidianakis     CASE(PCM_STOP);
441eb9ad377SManos Pitsidianakis     CASE(CHMAP_INFO);
442eb9ad377SManos Pitsidianakis     default:
443eb9ad377SManos Pitsidianakis         return "invalid code";
444eb9ad377SManos Pitsidianakis     }
445eb9ad377SManos Pitsidianakis 
446eb9ad377SManos Pitsidianakis     #undef CASE
447eb9ad377SManos Pitsidianakis };
448eb9ad377SManos Pitsidianakis 
449eb9ad377SManos Pitsidianakis /*
450eb9ad377SManos Pitsidianakis  * The actual processing done in virtio_snd_process_cmdq().
451eb9ad377SManos Pitsidianakis  *
452eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
453eb9ad377SManos Pitsidianakis  * @cmd: control command request
454eb9ad377SManos Pitsidianakis  */
455eb9ad377SManos Pitsidianakis static inline void
456eb9ad377SManos Pitsidianakis process_cmd(VirtIOSound *s, virtio_snd_ctrl_command *cmd)
457eb9ad377SManos Pitsidianakis {
458eb9ad377SManos Pitsidianakis     uint32_t code;
459eb9ad377SManos Pitsidianakis     size_t msg_sz = iov_to_buf(cmd->elem->out_sg,
460eb9ad377SManos Pitsidianakis                                cmd->elem->out_num,
461eb9ad377SManos Pitsidianakis                                0,
462eb9ad377SManos Pitsidianakis                                &cmd->ctrl,
463eb9ad377SManos Pitsidianakis                                sizeof(virtio_snd_hdr));
464eb9ad377SManos Pitsidianakis 
465eb9ad377SManos Pitsidianakis     if (msg_sz != sizeof(virtio_snd_hdr)) {
466eb9ad377SManos Pitsidianakis         /*
467eb9ad377SManos Pitsidianakis          * TODO: do we need to set DEVICE_NEEDS_RESET?
468eb9ad377SManos Pitsidianakis          */
469eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_GUEST_ERROR,
470eb9ad377SManos Pitsidianakis                 "%s: virtio-snd command size incorrect %zu vs \
471eb9ad377SManos Pitsidianakis                 %zu\n", __func__, msg_sz, sizeof(virtio_snd_hdr));
472eb9ad377SManos Pitsidianakis         return;
473eb9ad377SManos Pitsidianakis     }
474eb9ad377SManos Pitsidianakis 
475eb9ad377SManos Pitsidianakis     code = le32_to_cpu(cmd->ctrl.code);
476eb9ad377SManos Pitsidianakis 
477eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_code(code, print_code(code));
478eb9ad377SManos Pitsidianakis 
479eb9ad377SManos Pitsidianakis     switch (code) {
480eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_JACK_INFO:
481eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_JACK_REMAP:
482eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_UNIMP,
483eb9ad377SManos Pitsidianakis                      "virtio_snd: jack functionality is unimplemented.\n");
484eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
485eb9ad377SManos Pitsidianakis         break;
486eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_INFO:
4870ff05dd2SManos Pitsidianakis         virtio_snd_handle_pcm_info(s, cmd);
4880ff05dd2SManos Pitsidianakis         break;
489eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_SET_PARAMS:
490eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_PREPARE:
491eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_START:
492eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_STOP:
493eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_PCM_RELEASE:
494eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
495eb9ad377SManos Pitsidianakis         break;
496eb9ad377SManos Pitsidianakis     case VIRTIO_SND_R_CHMAP_INFO:
497eb9ad377SManos Pitsidianakis         qemu_log_mask(LOG_UNIMP,
498eb9ad377SManos Pitsidianakis                      "virtio_snd: chmap info functionality is unimplemented.\n");
499eb9ad377SManos Pitsidianakis         trace_virtio_snd_handle_chmap_info();
500eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_NOT_SUPP);
501eb9ad377SManos Pitsidianakis         break;
502eb9ad377SManos Pitsidianakis     default:
503eb9ad377SManos Pitsidianakis         /* error */
504eb9ad377SManos Pitsidianakis         error_report("virtio snd header not recognized: %"PRIu32, code);
505eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_BAD_MSG);
506eb9ad377SManos Pitsidianakis     }
507eb9ad377SManos Pitsidianakis 
508eb9ad377SManos Pitsidianakis     iov_from_buf(cmd->elem->in_sg,
509eb9ad377SManos Pitsidianakis                  cmd->elem->in_num,
510eb9ad377SManos Pitsidianakis                  0,
511eb9ad377SManos Pitsidianakis                  &cmd->resp,
512eb9ad377SManos Pitsidianakis                  sizeof(virtio_snd_hdr));
513eb9ad377SManos Pitsidianakis     virtqueue_push(cmd->vq, cmd->elem, sizeof(virtio_snd_hdr));
514eb9ad377SManos Pitsidianakis     virtio_notify(VIRTIO_DEVICE(s), cmd->vq);
515eb9ad377SManos Pitsidianakis }
516eb9ad377SManos Pitsidianakis 
517eb9ad377SManos Pitsidianakis /*
518eb9ad377SManos Pitsidianakis  * Consume all elements in command queue.
519eb9ad377SManos Pitsidianakis  *
520eb9ad377SManos Pitsidianakis  * @s: VirtIOSound device
521eb9ad377SManos Pitsidianakis  */
522eb9ad377SManos Pitsidianakis static void virtio_snd_process_cmdq(VirtIOSound *s)
523eb9ad377SManos Pitsidianakis {
524eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
525eb9ad377SManos Pitsidianakis 
526eb9ad377SManos Pitsidianakis     if (unlikely(qatomic_read(&s->processing_cmdq))) {
527eb9ad377SManos Pitsidianakis         return;
528eb9ad377SManos Pitsidianakis     }
529eb9ad377SManos Pitsidianakis 
530eb9ad377SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
531eb9ad377SManos Pitsidianakis         qatomic_set(&s->processing_cmdq, true);
532eb9ad377SManos Pitsidianakis         while (!QTAILQ_EMPTY(&s->cmdq)) {
533eb9ad377SManos Pitsidianakis             cmd = QTAILQ_FIRST(&s->cmdq);
534eb9ad377SManos Pitsidianakis 
535eb9ad377SManos Pitsidianakis             /* process command */
536eb9ad377SManos Pitsidianakis             process_cmd(s, cmd);
537eb9ad377SManos Pitsidianakis 
538eb9ad377SManos Pitsidianakis             QTAILQ_REMOVE(&s->cmdq, cmd, next);
539eb9ad377SManos Pitsidianakis 
540eb9ad377SManos Pitsidianakis             virtio_snd_ctrl_cmd_free(cmd);
541eb9ad377SManos Pitsidianakis         }
542eb9ad377SManos Pitsidianakis         qatomic_set(&s->processing_cmdq, false);
543eb9ad377SManos Pitsidianakis     }
544eb9ad377SManos Pitsidianakis }
545eb9ad377SManos Pitsidianakis 
546eb9ad377SManos Pitsidianakis /*
547eb9ad377SManos Pitsidianakis  * The control message handler. Pops an element from the control virtqueue,
548eb9ad377SManos Pitsidianakis  * and stores them to VirtIOSound's cmdq queue and finally calls
549eb9ad377SManos Pitsidianakis  * virtio_snd_process_cmdq() for processing.
550eb9ad377SManos Pitsidianakis  *
551eb9ad377SManos Pitsidianakis  * @vdev: VirtIOSound device
552eb9ad377SManos Pitsidianakis  * @vq: Control virtqueue
553eb9ad377SManos Pitsidianakis  */
554eb9ad377SManos Pitsidianakis static void virtio_snd_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
555eb9ad377SManos Pitsidianakis {
556eb9ad377SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
557eb9ad377SManos Pitsidianakis     VirtQueueElement *elem;
558eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
559eb9ad377SManos Pitsidianakis 
560eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_ctrl(vdev, vq);
561eb9ad377SManos Pitsidianakis 
562eb9ad377SManos Pitsidianakis     if (!virtio_queue_ready(vq)) {
563eb9ad377SManos Pitsidianakis         return;
564eb9ad377SManos Pitsidianakis     }
565eb9ad377SManos Pitsidianakis 
566eb9ad377SManos Pitsidianakis     elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
567eb9ad377SManos Pitsidianakis     while (elem) {
568eb9ad377SManos Pitsidianakis         cmd = g_new0(virtio_snd_ctrl_command, 1);
569eb9ad377SManos Pitsidianakis         cmd->elem = elem;
570eb9ad377SManos Pitsidianakis         cmd->vq = vq;
571eb9ad377SManos Pitsidianakis         cmd->resp.code = cpu_to_le32(VIRTIO_SND_S_OK);
572eb9ad377SManos Pitsidianakis         QTAILQ_INSERT_TAIL(&s->cmdq, cmd, next);
573eb9ad377SManos Pitsidianakis         elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
574eb9ad377SManos Pitsidianakis     }
575eb9ad377SManos Pitsidianakis 
576eb9ad377SManos Pitsidianakis     virtio_snd_process_cmdq(s);
577eb9ad377SManos Pitsidianakis }
578eb9ad377SManos Pitsidianakis 
579eb9ad377SManos Pitsidianakis /*
580eb9ad377SManos Pitsidianakis  * The event virtqueue handler.
581eb9ad377SManos Pitsidianakis  * Not implemented yet.
582eb9ad377SManos Pitsidianakis  *
583eb9ad377SManos Pitsidianakis  * @vdev: VirtIOSound device
584eb9ad377SManos Pitsidianakis  * @vq: event vq
585eb9ad377SManos Pitsidianakis  */
586eb9ad377SManos Pitsidianakis static void virtio_snd_handle_event(VirtIODevice *vdev, VirtQueue *vq)
587eb9ad377SManos Pitsidianakis {
588eb9ad377SManos Pitsidianakis     qemu_log_mask(LOG_UNIMP, "virtio_snd: event queue is unimplemented.\n");
589eb9ad377SManos Pitsidianakis     trace_virtio_snd_handle_event();
590eb9ad377SManos Pitsidianakis }
591eb9ad377SManos Pitsidianakis 
592eb9ad377SManos Pitsidianakis /*
593eb9ad377SManos Pitsidianakis  * Stub buffer virtqueue handler.
5942880e676SManos Pitsidianakis  *
5952880e676SManos Pitsidianakis  * @vdev: VirtIOSound device
5962880e676SManos Pitsidianakis  * @vq: virtqueue
5972880e676SManos Pitsidianakis  */
598eb9ad377SManos Pitsidianakis static void virtio_snd_handle_xfer(VirtIODevice *vdev, VirtQueue *vq) {}
5992880e676SManos Pitsidianakis 
6002880e676SManos Pitsidianakis static uint64_t get_features(VirtIODevice *vdev, uint64_t features,
6012880e676SManos Pitsidianakis                              Error **errp)
6022880e676SManos Pitsidianakis {
6032880e676SManos Pitsidianakis     /*
6042880e676SManos Pitsidianakis      * virtio-v1.2-csd01, 5.14.3,
6052880e676SManos Pitsidianakis      * Feature Bits
6062880e676SManos Pitsidianakis      * None currently defined.
6072880e676SManos Pitsidianakis      */
6082880e676SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
6092880e676SManos Pitsidianakis     features |= s->features;
6102880e676SManos Pitsidianakis 
6112880e676SManos Pitsidianakis     trace_virtio_snd_get_features(vdev, features);
6122880e676SManos Pitsidianakis 
6132880e676SManos Pitsidianakis     return features;
6142880e676SManos Pitsidianakis }
6152880e676SManos Pitsidianakis 
6162880e676SManos Pitsidianakis static void
6172880e676SManos Pitsidianakis virtio_snd_vm_state_change(void *opaque, bool running,
6182880e676SManos Pitsidianakis                                        RunState state)
6192880e676SManos Pitsidianakis {
6202880e676SManos Pitsidianakis     if (running) {
6212880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_running();
6222880e676SManos Pitsidianakis     } else {
6232880e676SManos Pitsidianakis         trace_virtio_snd_vm_state_stopped();
6242880e676SManos Pitsidianakis     }
6252880e676SManos Pitsidianakis }
6262880e676SManos Pitsidianakis 
6272880e676SManos Pitsidianakis static void virtio_snd_realize(DeviceState *dev, Error **errp)
6282880e676SManos Pitsidianakis {
6292880e676SManos Pitsidianakis     ERRP_GUARD();
6302880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
6312880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
632eb9ad377SManos Pitsidianakis     virtio_snd_pcm_set_params default_params = { 0 };
633eb9ad377SManos Pitsidianakis     uint32_t status;
6342880e676SManos Pitsidianakis 
635eb9ad377SManos Pitsidianakis     vsnd->pcm = NULL;
6362880e676SManos Pitsidianakis     vsnd->vmstate =
6372880e676SManos Pitsidianakis         qemu_add_vm_change_state_handler(virtio_snd_vm_state_change, vsnd);
6382880e676SManos Pitsidianakis 
6392880e676SManos Pitsidianakis     trace_virtio_snd_realize(vsnd);
6402880e676SManos Pitsidianakis 
641eb9ad377SManos Pitsidianakis     vsnd->pcm = g_new0(VirtIOSoundPCM, 1);
642eb9ad377SManos Pitsidianakis     vsnd->pcm->snd = vsnd;
643eb9ad377SManos Pitsidianakis     vsnd->pcm->streams =
644eb9ad377SManos Pitsidianakis         g_new0(VirtIOSoundPCMStream *, vsnd->snd_conf.streams);
645eb9ad377SManos Pitsidianakis     vsnd->pcm->pcm_params =
646eb9ad377SManos Pitsidianakis         g_new0(virtio_snd_pcm_set_params, vsnd->snd_conf.streams);
647eb9ad377SManos Pitsidianakis 
6482880e676SManos Pitsidianakis     virtio_init(vdev, VIRTIO_ID_SOUND, sizeof(virtio_snd_config));
6492880e676SManos Pitsidianakis     virtio_add_feature(&vsnd->features, VIRTIO_F_VERSION_1);
6502880e676SManos Pitsidianakis 
6512880e676SManos Pitsidianakis     /* set number of jacks and streams */
6522880e676SManos Pitsidianakis     if (vsnd->snd_conf.jacks > 8) {
6532880e676SManos Pitsidianakis         error_setg(errp,
6542880e676SManos Pitsidianakis                    "Invalid number of jacks: %"PRIu32,
6552880e676SManos Pitsidianakis                    vsnd->snd_conf.jacks);
6562880e676SManos Pitsidianakis         return;
6572880e676SManos Pitsidianakis     }
6582880e676SManos Pitsidianakis     if (vsnd->snd_conf.streams < 1 || vsnd->snd_conf.streams > 10) {
6592880e676SManos Pitsidianakis         error_setg(errp,
6602880e676SManos Pitsidianakis                    "Invalid number of streams: %"PRIu32,
6612880e676SManos Pitsidianakis                     vsnd->snd_conf.streams);
6622880e676SManos Pitsidianakis         return;
6632880e676SManos Pitsidianakis     }
6642880e676SManos Pitsidianakis 
6652880e676SManos Pitsidianakis     if (vsnd->snd_conf.chmaps > VIRTIO_SND_CHMAP_MAX_SIZE) {
6662880e676SManos Pitsidianakis         error_setg(errp,
6672880e676SManos Pitsidianakis                    "Invalid number of channel maps: %"PRIu32,
6682880e676SManos Pitsidianakis                    vsnd->snd_conf.chmaps);
6692880e676SManos Pitsidianakis         return;
6702880e676SManos Pitsidianakis     }
6712880e676SManos Pitsidianakis 
6722880e676SManos Pitsidianakis     AUD_register_card("virtio-sound", &vsnd->card, errp);
6732880e676SManos Pitsidianakis 
674eb9ad377SManos Pitsidianakis     /* set default params for all streams */
675eb9ad377SManos Pitsidianakis     default_params.features = 0;
676eb9ad377SManos Pitsidianakis     default_params.buffer_bytes = cpu_to_le32(8192);
677eb9ad377SManos Pitsidianakis     default_params.period_bytes = cpu_to_le32(2048);
678eb9ad377SManos Pitsidianakis     default_params.channels = 2;
679eb9ad377SManos Pitsidianakis     default_params.format = VIRTIO_SND_PCM_FMT_S16;
680eb9ad377SManos Pitsidianakis     default_params.rate = VIRTIO_SND_PCM_RATE_48000;
6812880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_CONTROL] =
682eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_ctrl);
6832880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_EVENT] =
684eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_event);
6852880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_TX] =
686eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
6872880e676SManos Pitsidianakis     vsnd->queues[VIRTIO_SND_VQ_RX] =
688eb9ad377SManos Pitsidianakis         virtio_add_queue(vdev, 64, virtio_snd_handle_xfer);
689eb9ad377SManos Pitsidianakis     qemu_mutex_init(&vsnd->cmdq_mutex);
690eb9ad377SManos Pitsidianakis     QTAILQ_INIT(&vsnd->cmdq);
691eb9ad377SManos Pitsidianakis 
692eb9ad377SManos Pitsidianakis     for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
693eb9ad377SManos Pitsidianakis         status = virtio_snd_set_pcm_params(vsnd, i, &default_params);
694eb9ad377SManos Pitsidianakis         if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
695eb9ad377SManos Pitsidianakis             error_setg(errp,
696eb9ad377SManos Pitsidianakis                        "Can't initalize stream params, device responded with %s.",
697eb9ad377SManos Pitsidianakis                        print_code(status));
698eb9ad377SManos Pitsidianakis             return;
699eb9ad377SManos Pitsidianakis         }
700eb9ad377SManos Pitsidianakis         status = virtio_snd_pcm_prepare(vsnd, i);
701eb9ad377SManos Pitsidianakis         if (status != cpu_to_le32(VIRTIO_SND_S_OK)) {
702eb9ad377SManos Pitsidianakis             error_setg(errp,
703eb9ad377SManos Pitsidianakis                        "Can't prepare streams, device responded with %s.",
704eb9ad377SManos Pitsidianakis                        print_code(status));
705eb9ad377SManos Pitsidianakis             return;
706eb9ad377SManos Pitsidianakis         }
707eb9ad377SManos Pitsidianakis     }
7082880e676SManos Pitsidianakis }
7092880e676SManos Pitsidianakis 
7102880e676SManos Pitsidianakis static void virtio_snd_unrealize(DeviceState *dev)
7112880e676SManos Pitsidianakis {
7122880e676SManos Pitsidianakis     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
7132880e676SManos Pitsidianakis     VirtIOSound *vsnd = VIRTIO_SND(dev);
714eb9ad377SManos Pitsidianakis     VirtIOSoundPCMStream *stream;
7152880e676SManos Pitsidianakis 
7162880e676SManos Pitsidianakis     qemu_del_vm_change_state_handler(vsnd->vmstate);
7172880e676SManos Pitsidianakis     trace_virtio_snd_unrealize(vsnd);
7182880e676SManos Pitsidianakis 
719eb9ad377SManos Pitsidianakis     if (vsnd->pcm) {
720eb9ad377SManos Pitsidianakis         if (vsnd->pcm->streams) {
721eb9ad377SManos Pitsidianakis             for (uint32_t i = 0; i < vsnd->snd_conf.streams; i++) {
722eb9ad377SManos Pitsidianakis                 stream = vsnd->pcm->streams[i];
723eb9ad377SManos Pitsidianakis                 if (stream) {
724eb9ad377SManos Pitsidianakis                     virtio_snd_process_cmdq(stream->s);
725eb9ad377SManos Pitsidianakis                     virtio_snd_pcm_close(stream);
726eb9ad377SManos Pitsidianakis                     g_free(stream);
727eb9ad377SManos Pitsidianakis                 }
728eb9ad377SManos Pitsidianakis             }
729eb9ad377SManos Pitsidianakis             g_free(vsnd->pcm->streams);
730eb9ad377SManos Pitsidianakis         }
731eb9ad377SManos Pitsidianakis         g_free(vsnd->pcm->pcm_params);
732eb9ad377SManos Pitsidianakis         g_free(vsnd->pcm);
733eb9ad377SManos Pitsidianakis         vsnd->pcm = NULL;
734eb9ad377SManos Pitsidianakis     }
7352880e676SManos Pitsidianakis     AUD_remove_card(&vsnd->card);
736eb9ad377SManos Pitsidianakis     qemu_mutex_destroy(&vsnd->cmdq_mutex);
7372880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_CONTROL]);
7382880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_EVENT]);
7392880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_TX]);
7402880e676SManos Pitsidianakis     virtio_delete_queue(vsnd->queues[VIRTIO_SND_VQ_RX]);
7412880e676SManos Pitsidianakis     virtio_cleanup(vdev);
7422880e676SManos Pitsidianakis }
7432880e676SManos Pitsidianakis 
7442880e676SManos Pitsidianakis 
745eb9ad377SManos Pitsidianakis static void virtio_snd_reset(VirtIODevice *vdev)
746eb9ad377SManos Pitsidianakis {
747eb9ad377SManos Pitsidianakis     VirtIOSound *s = VIRTIO_SND(vdev);
748eb9ad377SManos Pitsidianakis     virtio_snd_ctrl_command *cmd;
749eb9ad377SManos Pitsidianakis 
750eb9ad377SManos Pitsidianakis     WITH_QEMU_LOCK_GUARD(&s->cmdq_mutex) {
751eb9ad377SManos Pitsidianakis         while (!QTAILQ_EMPTY(&s->cmdq)) {
752eb9ad377SManos Pitsidianakis             cmd = QTAILQ_FIRST(&s->cmdq);
753eb9ad377SManos Pitsidianakis             QTAILQ_REMOVE(&s->cmdq, cmd, next);
754eb9ad377SManos Pitsidianakis             virtio_snd_ctrl_cmd_free(cmd);
755eb9ad377SManos Pitsidianakis         }
756eb9ad377SManos Pitsidianakis     }
757eb9ad377SManos Pitsidianakis }
7582880e676SManos Pitsidianakis 
7592880e676SManos Pitsidianakis static void virtio_snd_class_init(ObjectClass *klass, void *data)
7602880e676SManos Pitsidianakis {
7612880e676SManos Pitsidianakis     DeviceClass *dc = DEVICE_CLASS(klass);
7622880e676SManos Pitsidianakis     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
7632880e676SManos Pitsidianakis 
7642880e676SManos Pitsidianakis 
7652880e676SManos Pitsidianakis     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
7662880e676SManos Pitsidianakis     device_class_set_props(dc, virtio_snd_properties);
7672880e676SManos Pitsidianakis 
7682880e676SManos Pitsidianakis     dc->vmsd = &vmstate_virtio_snd;
7692880e676SManos Pitsidianakis     vdc->vmsd = &vmstate_virtio_snd_device;
7702880e676SManos Pitsidianakis     vdc->realize = virtio_snd_realize;
7712880e676SManos Pitsidianakis     vdc->unrealize = virtio_snd_unrealize;
7722880e676SManos Pitsidianakis     vdc->get_config = virtio_snd_get_config;
7732880e676SManos Pitsidianakis     vdc->set_config = virtio_snd_set_config;
7742880e676SManos Pitsidianakis     vdc->get_features = get_features;
7752880e676SManos Pitsidianakis     vdc->reset = virtio_snd_reset;
7762880e676SManos Pitsidianakis     vdc->legacy_features = 0;
7772880e676SManos Pitsidianakis }
7782880e676SManos Pitsidianakis 
7792880e676SManos Pitsidianakis static const TypeInfo virtio_snd_types[] = {
7802880e676SManos Pitsidianakis     {
7812880e676SManos Pitsidianakis       .name          = TYPE_VIRTIO_SND,
7822880e676SManos Pitsidianakis       .parent        = TYPE_VIRTIO_DEVICE,
7832880e676SManos Pitsidianakis       .instance_size = sizeof(VirtIOSound),
7842880e676SManos Pitsidianakis       .class_init    = virtio_snd_class_init,
7852880e676SManos Pitsidianakis     }
7862880e676SManos Pitsidianakis };
7872880e676SManos Pitsidianakis 
7882880e676SManos Pitsidianakis DEFINE_TYPES(virtio_snd_types)
789