From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_RED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9815C433E0 for ; Sun, 24 Jan 2021 16:56:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6369522CF6 for ; Sun, 24 Jan 2021 16:56:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726523AbhAXQ4E (ORCPT ); Sun, 24 Jan 2021 11:56:04 -0500 Received: from mx1.opensynergy.com ([217.66.60.4]:34973 "EHLO mx1.opensynergy.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726282AbhAXQzU (ORCPT ); Sun, 24 Jan 2021 11:55:20 -0500 Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 214E0A147D; Sun, 24 Jan 2021 17:54:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=opensynergy.com; h=cc:cc:content-transfer-encoding:content-type:content-type :date:from:from:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=srmailgate02; bh=mj209XilldnV DEviqgC/TmGvsoxvpkhdT5z2JgsuXwQ=; b=heWBruKtEYEE4UL0/KvGV1jzTKqq 0KcfrW6YLPEsH7WO8vO4OuMVaoUsUxA2V5b0jWLhXev6ZtRVLAwT7QbSMnmUJjHv Z5bIhV1gllFNqFtk8W5shffqlXQI80RcLcICsoxtYSCC0nyZzZKMnerSxuCr4S/i fXXebLLWT3bbCIGrdIK75XujJro47IaM7lEqhWjQCflOjnDS2YRYtyJKdvhMYbn6 ocIBqwI95zk6MeS1iFAuThHTwT3Ktxsi08NQU/7DQr/9r7R8uy8uCK27EpKOEKGY KR7hyQCAw8pFvz9ewEOSgMzU3J2GdWy2kc99o6YF+gtH25wFJYdMsr0WHg== From: Anton Yakovlev To: , , CC: "Michael S. Tsirkin" , Jaroslav Kysela , Takashi Iwai , Jason Wang , Subject: [PATCH v2 2/9] ALSA: virtio: add virtio sound driver Date: Sun, 24 Jan 2021 17:54:01 +0100 Message-ID: <20210124165408.1122868-3-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> References: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: SR-MAIL-02.open-synergy.com (10.26.10.22) To SR-MAIL-01.open-synergy.com (10.26.10.21) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Introduce skeleton of the virtio sound driver. The driver implements the virtio sound device specification, which has become part of the virtio standard. Initial initialization of the device, virtqueues and creation of an empty ALSA sound device. Also, handling DEVICE_NEEDS_RESET device status. Signed-off-by: Anton Yakovlev --- MAINTAINERS | 9 + include/uapi/linux/virtio_snd.h | 361 +++++++++++++++++++++++++++ sound/Kconfig | 2 + sound/Makefile | 3 +- sound/virtio/Kconfig | 10 + sound/virtio/Makefile | 7 + sound/virtio/virtio_card.c | 415 ++++++++++++++++++++++++++++++++ sound/virtio/virtio_card.h | 76 ++++++ 8 files changed, 882 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/virtio_snd.h create mode 100644 sound/virtio/Kconfig create mode 100644 sound/virtio/Makefile create mode 100644 sound/virtio/virtio_card.c create mode 100644 sound/virtio/virtio_card.h diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..3f509d54a457 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18936,6 +18936,15 @@ W: https://virtio-mem.gitlab.io/ F: drivers/virtio/virtio_mem.c F: include/uapi/linux/virtio_mem.h +VIRTIO SOUND DRIVER +M: Anton Yakovlev +M: "Michael S. Tsirkin" +L: virtualization@lists.linux-foundation.org +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: include/uapi/linux/virtio_snd.h +F: sound/virtio/* + VIRTUAL BOX GUEST DEVICE DRIVER M: Hans de Goede M: Arnd Bergmann diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h new file mode 100644 index 000000000000..1ff6310e54d6 --- /dev/null +++ b/include/uapi/linux/virtio_snd.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2020 OpenSynergy GmbH + * + * This header is BSD licensed so anyone can use the definitions to + * implement compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of OpenSynergy GmbH nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef VIRTIO_SND_IF_H +#define VIRTIO_SND_IF_H + +#include + +/******************************************************************************* + * CONFIGURATION SPACE + */ +struct virtio_snd_config { + /* # of available physical jacks */ + __le32 jacks; + /* # of available PCM streams */ + __le32 streams; + /* # of available channel maps */ + __le32 chmaps; +}; + +enum { + /* device virtqueue indexes */ + VIRTIO_SND_VQ_CONTROL = 0, + VIRTIO_SND_VQ_EVENT, + VIRTIO_SND_VQ_TX, + VIRTIO_SND_VQ_RX, + /* # of device virtqueues */ + VIRTIO_SND_VQ_MAX +}; + +/******************************************************************************* + * COMMON DEFINITIONS + */ + +/* supported dataflow directions */ +enum { + VIRTIO_SND_D_OUTPUT = 0, + VIRTIO_SND_D_INPUT +}; + +enum { + /* jack control request types */ + VIRTIO_SND_R_JACK_INFO = 1, + VIRTIO_SND_R_JACK_REMAP, + + /* PCM control request types */ + VIRTIO_SND_R_PCM_INFO = 0x0100, + VIRTIO_SND_R_PCM_SET_PARAMS, + VIRTIO_SND_R_PCM_PREPARE, + VIRTIO_SND_R_PCM_RELEASE, + VIRTIO_SND_R_PCM_START, + VIRTIO_SND_R_PCM_STOP, + + /* channel map control request types */ + VIRTIO_SND_R_CHMAP_INFO = 0x0200, + + /* jack event types */ + VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, + VIRTIO_SND_EVT_JACK_DISCONNECTED, + + /* PCM event types */ + VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, + VIRTIO_SND_EVT_PCM_XRUN, + + /* common status codes */ + VIRTIO_SND_S_OK = 0x8000, + VIRTIO_SND_S_BAD_MSG, + VIRTIO_SND_S_NOT_SUPP, + VIRTIO_SND_S_IO_ERR +}; + +/* common header */ +struct virtio_snd_hdr { + __le32 code; +}; + +/* event notification */ +struct virtio_snd_event { + /* VIRTIO_SND_EVT_XXX */ + struct virtio_snd_hdr hdr; + /* optional event data */ + __le32 data; +}; + +/* common control request to query an item information */ +struct virtio_snd_query_info { + /* VIRTIO_SND_R_XXX_INFO */ + struct virtio_snd_hdr hdr; + /* item start identifier */ + __le32 start_id; + /* item count to query */ + __le32 count; + /* item information size in bytes */ + __le32 size; +}; + +/* common item information header */ +struct virtio_snd_info { + /* function group node id (High Definition Audio Specification 7.1.2) */ + __le32 hda_fn_nid; +}; + +/******************************************************************************* + * JACK CONTROL MESSAGES + */ +struct virtio_snd_jack_hdr { + /* VIRTIO_SND_R_JACK_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::jacks - 1 */ + __le32 jack_id; +}; + +/* supported jack features */ +enum { + VIRTIO_SND_JACK_F_REMAP = 0 +}; + +struct virtio_snd_jack_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */ + __le32 features; + /* pin configuration (High Definition Audio Specification 7.3.3.31) */ + __le32 hda_reg_defconf; + /* pin capabilities (High Definition Audio Specification 7.3.4.9) */ + __le32 hda_reg_caps; + /* current jack connection status (0: disconnected, 1: connected) */ + __u8 connected; + + __u8 padding[7]; +}; + +/* jack remapping control request */ +struct virtio_snd_jack_remap { + /* .code = VIRTIO_SND_R_JACK_REMAP */ + struct virtio_snd_jack_hdr hdr; + /* selected association number */ + __le32 association; + /* selected sequence number */ + __le32 sequence; +}; + +/******************************************************************************* + * PCM CONTROL MESSAGES + */ +struct virtio_snd_pcm_hdr { + /* VIRTIO_SND_R_PCM_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* supported PCM stream features */ +enum { + VIRTIO_SND_PCM_F_SHMEM_HOST = 0, + VIRTIO_SND_PCM_F_SHMEM_GUEST, + VIRTIO_SND_PCM_F_MSG_POLLING, + VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS, + VIRTIO_SND_PCM_F_EVT_XRUNS +}; + +/* supported PCM sample formats */ +enum { + /* analog formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /* 4 / 4 bits */ + VIRTIO_SND_PCM_FMT_MU_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_A_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_S18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_U18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_U20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_S24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_U24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_U20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_S24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_U24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_S32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT64, /* 64 / 64 bits */ + /* digital formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_DSD_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_DSD_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_DSD_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */ +}; + +/* supported PCM frame rates */ +enum { + VIRTIO_SND_PCM_RATE_5512 = 0, + VIRTIO_SND_PCM_RATE_8000, + VIRTIO_SND_PCM_RATE_11025, + VIRTIO_SND_PCM_RATE_16000, + VIRTIO_SND_PCM_RATE_22050, + VIRTIO_SND_PCM_RATE_32000, + VIRTIO_SND_PCM_RATE_44100, + VIRTIO_SND_PCM_RATE_48000, + VIRTIO_SND_PCM_RATE_64000, + VIRTIO_SND_PCM_RATE_88200, + VIRTIO_SND_PCM_RATE_96000, + VIRTIO_SND_PCM_RATE_176400, + VIRTIO_SND_PCM_RATE_192000, + VIRTIO_SND_PCM_RATE_384000 +}; + +struct virtio_snd_pcm_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */ + __le64 formats; + /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */ + __le64 rates; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* minimum # of supported channels */ + __u8 channels_min; + /* maximum # of supported channels */ + __u8 channels_max; + + __u8 padding[5]; +}; + +/* set PCM stream format */ +struct virtio_snd_pcm_set_params { + /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */ + struct virtio_snd_pcm_hdr hdr; + /* size of the hardware buffer */ + __le32 buffer_bytes; + /* size of the hardware period */ + __le32 period_bytes; + /* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* selected # of channels */ + __u8 channels; + /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */ + __u8 format; + /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */ + __u8 rate; + + __u8 padding; +}; + +/******************************************************************************* + * PCM I/O MESSAGES + */ + +/* I/O request header */ +struct virtio_snd_pcm_xfer { + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* I/O request status */ +struct virtio_snd_pcm_status { + /* VIRTIO_SND_S_XXX */ + __le32 status; + /* current device latency */ + __le32 latency_bytes; +}; + +/******************************************************************************* + * CHANNEL MAP CONTROL MESSAGES + */ +struct virtio_snd_chmap_hdr { + /* VIRTIO_SND_R_CHMAP_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::chmaps - 1 */ + __le32 chmap_id; +}; + +/* standard channel position definition */ +enum { + VIRTIO_SND_CHMAP_NONE = 0, /* undefined */ + VIRTIO_SND_CHMAP_NA, /* silent */ + VIRTIO_SND_CHMAP_MONO, /* mono stream */ + VIRTIO_SND_CHMAP_FL, /* front left */ + VIRTIO_SND_CHMAP_FR, /* front right */ + VIRTIO_SND_CHMAP_RL, /* rear left */ + VIRTIO_SND_CHMAP_RR, /* rear right */ + VIRTIO_SND_CHMAP_FC, /* front center */ + VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */ + VIRTIO_SND_CHMAP_SL, /* side left */ + VIRTIO_SND_CHMAP_SR, /* side right */ + VIRTIO_SND_CHMAP_RC, /* rear center */ + VIRTIO_SND_CHMAP_FLC, /* front left center */ + VIRTIO_SND_CHMAP_FRC, /* front right center */ + VIRTIO_SND_CHMAP_RLC, /* rear left center */ + VIRTIO_SND_CHMAP_RRC, /* rear right center */ + VIRTIO_SND_CHMAP_FLW, /* front left wide */ + VIRTIO_SND_CHMAP_FRW, /* front right wide */ + VIRTIO_SND_CHMAP_FLH, /* front left high */ + VIRTIO_SND_CHMAP_FCH, /* front center high */ + VIRTIO_SND_CHMAP_FRH, /* front right high */ + VIRTIO_SND_CHMAP_TC, /* top center */ + VIRTIO_SND_CHMAP_TFL, /* top front left */ + VIRTIO_SND_CHMAP_TFR, /* top front right */ + VIRTIO_SND_CHMAP_TFC, /* top front center */ + VIRTIO_SND_CHMAP_TRL, /* top rear left */ + VIRTIO_SND_CHMAP_TRR, /* top rear right */ + VIRTIO_SND_CHMAP_TRC, /* top rear center */ + VIRTIO_SND_CHMAP_TFLC, /* top front left center */ + VIRTIO_SND_CHMAP_TFRC, /* top front right center */ + VIRTIO_SND_CHMAP_TSL, /* top side left */ + VIRTIO_SND_CHMAP_TSR, /* top side right */ + VIRTIO_SND_CHMAP_LLFE, /* left LFE */ + VIRTIO_SND_CHMAP_RLFE, /* right LFE */ + VIRTIO_SND_CHMAP_BC, /* bottom center */ + VIRTIO_SND_CHMAP_BLC, /* bottom left center */ + VIRTIO_SND_CHMAP_BRC /* bottom right center */ +}; + +/* maximum possible number of channels */ +#define VIRTIO_SND_CHMAP_MAX_SIZE 18 + +struct virtio_snd_chmap_info { + /* common header */ + struct virtio_snd_info hdr; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* # of valid channel position values */ + __u8 channels; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; +}; + +#endif /* VIRTIO_SND_IF_H */ diff --git a/sound/Kconfig b/sound/Kconfig index 36785410fbe1..e56d96d2b11c 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -99,6 +99,8 @@ source "sound/synth/Kconfig" source "sound/xen/Kconfig" +source "sound/virtio/Kconfig" + endif # SND endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 797ecdcd35e2..04ef04b1168f 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \ + virtio/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig new file mode 100644 index 000000000000..094cba24ee5b --- /dev/null +++ b/sound/virtio/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Sound card driver for virtio + +config SND_VIRTIO + tristate "Virtio sound driver" + depends on VIRTIO + select SND_PCM + select SND_JACK + help + This is the virtual sound driver for virtio. Say Y or M. diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile new file mode 100644 index 000000000000..8c87ebb9982b --- /dev/null +++ b/sound/virtio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o + +virtio_snd-objs := \ + virtio_card.o + diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c new file mode 100644 index 000000000000..532d823fdf6f --- /dev/null +++ b/sound/virtio/virtio_card.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include + +#include "virtio_card.h" + +static void virtsnd_remove(struct virtio_device *vdev); + +/** + * virtsnd_event_send() - Add an event to the event queue. + * @vqueue: Underlying event virtqueue. + * @event: Event. + * @notify: Indicates whether or not to send a notification to the device. + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_event_send(struct virtqueue *vqueue, + struct virtio_snd_event *event, bool notify, + gfp_t gfp) +{ + struct scatterlist sg; + struct scatterlist *psgs[1] = { &sg }; + int rc; + + /* reset event content */ + memset(event, 0, sizeof(*event)); + + sg_init_one(&sg, event, sizeof(*event)); + + rc = virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp); + if (rc) + return rc; + + if (notify) + if (virtqueue_kick_prepare(vqueue)) + if (!virtqueue_notify(vqueue)) + return -EIO; + + return 0; +} + +/** + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. + * @vqueue: Underlying event virtqueue. + * + * This callback function is called upon a vring interrupt request from the + * device. + * + * Context: Interrupt context. + */ +static void virtsnd_event_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (queue->vqueue) { + virtqueue_disable_cb(queue->vqueue); + + for (;;) { + struct virtio_snd_event *event; + u32 length; + + event = virtqueue_get_buf(queue->vqueue, &length); + if (!event) + break; + + virtsnd_event_send(queue->vqueue, event, true, + GFP_ATOMIC); + } + + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + + if (virtqueue_enable_cb(queue->vqueue)) + break; + } + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. + * @snd: VirtIO sound device. + * + * After calling this function, the event queue is disabled. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_find_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { 0 }; + const char *names[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", + [VIRTIO_SND_VQ_EVENT] = "virtsnd-event", + [VIRTIO_SND_VQ_TX] = "virtsnd-tx", + [VIRTIO_SND_VQ_RX] = "virtsnd-rx" + }; + struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; + unsigned int i; + unsigned int n = 0; + int rc; + + callbacks[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb; + + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, + NULL); + if (rc) { + dev_err(&vdev->dev, "failed to initialize virtqueues\n"); + return rc; + } + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + snd->queues[i].vqueue = vqs[i]; + + /* Allocate events and populate the event queue */ + virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); + + n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); + + snd->event_msgs = devm_kcalloc(&vdev->dev, n, sizeof(*snd->event_msgs), + GFP_KERNEL); + if (!snd->event_msgs) + return -ENOMEM; + + for (i = 0; i < n; ++i) { + rc = virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], + &snd->event_msgs[i], false, GFP_KERNEL); + if (rc) + return rc; + } + + return 0; +} + +/** + * virtsnd_enable_event_vq() - Enable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_enable_event_vq(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + + if (!virtqueue_enable_cb(queue->vqueue)) + virtsnd_event_notify_cb(queue->vqueue); +} + +/** + * virtsnd_disable_vqs() - Disable all virtqueues. + * @snd: VirtIO sound device. + * + * Also free all allocated events and control messages. + * + * Context: Any context. + */ +static void virtsnd_disable_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int i; + unsigned long flags; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) { + struct virtio_snd_queue *queue = &snd->queues[i]; + + spin_lock_irqsave(&queue->lock, flags); + /* Prohibit the use of the queue */ + if (queue->vqueue) + virtqueue_disable_cb(queue->vqueue); + queue->vqueue = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + } + + if (snd->event_msgs) + devm_kfree(&vdev->dev, snd->event_msgs); + + snd->event_msgs = NULL; +} + +/** + * virtsnd_reset_fn() - Kernel worker's function to reset the device. + * @work: Reset device work. + * + * Context: Process context. + */ +static void virtsnd_reset_fn(struct work_struct *work) +{ + struct virtio_snd *snd = + container_of(work, struct virtio_snd, reset_work); + struct virtio_device *vdev = snd->vdev; + struct device *dev = &vdev->dev; + int rc; + + dev_info(dev, "sound device needs reset\n"); + + /* + * It seems that the only way to properly reset the device is to remove + * and re-create the ALSA sound card device. + * + * Also resetting the device involves a number of steps with setting the + * status bits described in the virtio specification. And the easiest + * way to get everything right is to use the virtio bus interface. + */ + rc = dev->bus->remove(dev); + if (rc) + dev_warn(dev, "bus->remove() failed: %d", rc); + + rc = dev->bus->probe(dev); + if (rc) + dev_err(dev, "bus->probe() failed: %d", rc); +} + +/** + * virtsnd_build_devs() - Read configuration and build ALSA devices. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_build_devs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + int rc; + + rc = snd_card_new(&vdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &snd->card); + if (rc < 0) + return rc; + + snd->card->private_data = snd; + + strscpy(snd->card->id, "viosnd", sizeof(snd->card->id)); + strscpy(snd->card->driver, "virtio_snd", sizeof(snd->card->driver)); + strscpy(snd->card->shortname, "VIOSND", sizeof(snd->card->shortname)); + strscpy(snd->card->longname, "VirtIO Sound Card", + sizeof(snd->card->longname)); + + return snd_card_register(snd->card); +} + +/** + * virtsnd_validate() - Validate if the device can be started. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -EINVAL on failure. + */ +static int virtsnd_validate(struct virtio_device *vdev) +{ + if (!vdev->config->get) { + dev_err(&vdev->dev, "configuration access disabled\n"); + return -EINVAL; + } + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, + "device does not comply with spec version 1.x\n"); + return -EINVAL; + } + + return 0; +} + +/** + * virtsnd_probe() - Create and initialize the device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_probe(struct virtio_device *vdev) +{ + struct virtio_snd *snd; + unsigned int i; + int rc; + + snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); + if (!snd) + return -ENOMEM; + + snd->vdev = vdev; + INIT_WORK(&snd->reset_work, virtsnd_reset_fn); + + vdev->priv = snd; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + spin_lock_init(&snd->queues[i].lock); + + rc = virtsnd_find_vqs(snd); + if (rc) + goto on_failure; + + virtio_device_ready(vdev); + + rc = virtsnd_build_devs(snd); + if (rc) + goto on_failure; + + virtsnd_enable_event_vq(snd); + +on_failure: + if (rc) + virtsnd_remove(vdev); + + return rc; +} + +/** + * virtsnd_remove() - Remove VirtIO and ALSA devices. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + */ +static void virtsnd_remove(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + if (!snd) + return; + + /* + * Make sure no one is accessing the virtqueues and sending synchronous + * requests to the device. This can happen if we got here because the + * device needs to be reset. + */ + virtsnd_disable_vqs(snd); + + if (snd->card) + snd_card_free(snd->card); + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + devm_kfree(&vdev->dev, snd); + + vdev->priv = NULL; +} + +/** + * virtsnd_config_changed() - Handle configuration change notification. + * @vdev: VirtIO parent device. + * + * This callback function is called upon a configuration change interrupt + * request from the device. Currently only used to handle NEEDS_RESET device + * status. + * + * Context: Interrupt context. + */ +static void virtsnd_config_changed(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + unsigned int status = vdev->config->get_status(vdev); + + if (status & VIRTIO_CONFIG_S_NEEDS_RESET) + schedule_work(&snd->reset_work); + else + dev_warn(&vdev->dev, + "sound device configuration was changed\n"); +} + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtsnd_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .validate = virtsnd_validate, + .probe = virtsnd_probe, + .remove = virtsnd_remove, + .config_changed = virtsnd_config_changed, +}; + +static int __init init(void) +{ + return register_virtio_driver(&virtsnd_driver); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtsnd_driver); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio sound card driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h new file mode 100644 index 000000000000..10084abaaf18 --- /dev/null +++ b/sound/virtio/virtio_card.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef VIRTIO_SND_CARD_H +#define VIRTIO_SND_CARD_H + +#include +#include +#include + +/** + * struct virtio_snd_queue - Virtqueue wrapper structure. + * @lock: Used to synchronize access to a virtqueue. + * @vqueue: Underlying virtqueue. + */ +struct virtio_snd_queue { + spinlock_t lock; + struct virtqueue *vqueue; +}; + +/** + * struct virtio_snd - VirtIO sound card device. + * @vdev: Underlying virtio device. + * @queues: Virtqueue wrappers. + * @reset_work: Reset device work. + * @card: ALSA sound card. + * @event_msgs: Device events. + */ +struct virtio_snd { + struct virtio_device *vdev; + struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; + struct work_struct reset_work; + struct snd_card *card; + struct virtio_snd_event *event_msgs; +}; + +static inline struct virtio_snd_queue * +virtsnd_control_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_CONTROL]; +} + +static inline struct virtio_snd_queue * +virtsnd_event_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_EVENT]; +} + +static inline struct virtio_snd_queue * +virtsnd_tx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_TX]; +} + +static inline struct virtio_snd_queue * +virtsnd_rx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_RX]; +} + +#endif /* VIRTIO_SND_CARD_H */ -- 2.30.0 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_RED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 509B7C433E0 for ; Sun, 24 Jan 2021 16:56:24 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id B2C6E22CF6 for ; Sun, 24 Jan 2021 16:56:23 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B2C6E22CF6 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=opensynergy.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 0DA40189A; Sun, 24 Jan 2021 17:55:32 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 0DA40189A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507382; bh=EfhaUs6k86I9q4QoPObrt7VdWJpTXlzgYRCgzp0tR/0=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=Pd7chqgdModW5BIh00ZSSf2Qz3/iHAC+0Ucvyz6TvSE9ZXFirQtobUNJEPVofQmF4 CHmAOw3trI27jAT/9nG1mAuHW0Qv79I0E83sK1zdngbwxm18PD1B1g1e4I+0JAbKcH 1xPj2Uk6sjXeer+dUCyxyC2TSrFbOy101+Uz5rgM= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 8B038F804CB; Sun, 24 Jan 2021 17:54:35 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 111B0F804CA; Sun, 24 Jan 2021 17:54:34 +0100 (CET) Received: from mx1.opensynergy.com (mx1.opensynergy.com [217.66.60.4]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id A973FF8020D for ; Sun, 24 Jan 2021 17:54:27 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz A973FF8020D Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="heWBruKt" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 214E0A147D; Sun, 24 Jan 2021 17:54:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=opensynergy.com; h=cc:cc:content-transfer-encoding:content-type:content-type :date:from:from:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=srmailgate02; bh=mj209XilldnV DEviqgC/TmGvsoxvpkhdT5z2JgsuXwQ=; b=heWBruKtEYEE4UL0/KvGV1jzTKqq 0KcfrW6YLPEsH7WO8vO4OuMVaoUsUxA2V5b0jWLhXev6ZtRVLAwT7QbSMnmUJjHv Z5bIhV1gllFNqFtk8W5shffqlXQI80RcLcICsoxtYSCC0nyZzZKMnerSxuCr4S/i fXXebLLWT3bbCIGrdIK75XujJro47IaM7lEqhWjQCflOjnDS2YRYtyJKdvhMYbn6 ocIBqwI95zk6MeS1iFAuThHTwT3Ktxsi08NQU/7DQr/9r7R8uy8uCK27EpKOEKGY KR7hyQCAw8pFvz9ewEOSgMzU3J2GdWy2kc99o6YF+gtH25wFJYdMsr0WHg== From: Anton Yakovlev To: , , Subject: [PATCH v2 2/9] ALSA: virtio: add virtio sound driver Date: Sun, 24 Jan 2021 17:54:01 +0100 Message-ID: <20210124165408.1122868-3-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> References: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: SR-MAIL-02.open-synergy.com (10.26.10.22) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, Jason Wang , Takashi Iwai , "Michael S. Tsirkin" X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: "Alsa-devel" Introduce skeleton of the virtio sound driver. The driver implements the virtio sound device specification, which has become part of the virtio standard. Initial initialization of the device, virtqueues and creation of an empty ALSA sound device. Also, handling DEVICE_NEEDS_RESET device status. Signed-off-by: Anton Yakovlev --- MAINTAINERS | 9 + include/uapi/linux/virtio_snd.h | 361 +++++++++++++++++++++++++++ sound/Kconfig | 2 + sound/Makefile | 3 +- sound/virtio/Kconfig | 10 + sound/virtio/Makefile | 7 + sound/virtio/virtio_card.c | 415 ++++++++++++++++++++++++++++++++ sound/virtio/virtio_card.h | 76 ++++++ 8 files changed, 882 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/virtio_snd.h create mode 100644 sound/virtio/Kconfig create mode 100644 sound/virtio/Makefile create mode 100644 sound/virtio/virtio_card.c create mode 100644 sound/virtio/virtio_card.h diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..3f509d54a457 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18936,6 +18936,15 @@ W: https://virtio-mem.gitlab.io/ F: drivers/virtio/virtio_mem.c F: include/uapi/linux/virtio_mem.h +VIRTIO SOUND DRIVER +M: Anton Yakovlev +M: "Michael S. Tsirkin" +L: virtualization@lists.linux-foundation.org +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: include/uapi/linux/virtio_snd.h +F: sound/virtio/* + VIRTUAL BOX GUEST DEVICE DRIVER M: Hans de Goede M: Arnd Bergmann diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h new file mode 100644 index 000000000000..1ff6310e54d6 --- /dev/null +++ b/include/uapi/linux/virtio_snd.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2020 OpenSynergy GmbH + * + * This header is BSD licensed so anyone can use the definitions to + * implement compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of OpenSynergy GmbH nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef VIRTIO_SND_IF_H +#define VIRTIO_SND_IF_H + +#include + +/******************************************************************************* + * CONFIGURATION SPACE + */ +struct virtio_snd_config { + /* # of available physical jacks */ + __le32 jacks; + /* # of available PCM streams */ + __le32 streams; + /* # of available channel maps */ + __le32 chmaps; +}; + +enum { + /* device virtqueue indexes */ + VIRTIO_SND_VQ_CONTROL = 0, + VIRTIO_SND_VQ_EVENT, + VIRTIO_SND_VQ_TX, + VIRTIO_SND_VQ_RX, + /* # of device virtqueues */ + VIRTIO_SND_VQ_MAX +}; + +/******************************************************************************* + * COMMON DEFINITIONS + */ + +/* supported dataflow directions */ +enum { + VIRTIO_SND_D_OUTPUT = 0, + VIRTIO_SND_D_INPUT +}; + +enum { + /* jack control request types */ + VIRTIO_SND_R_JACK_INFO = 1, + VIRTIO_SND_R_JACK_REMAP, + + /* PCM control request types */ + VIRTIO_SND_R_PCM_INFO = 0x0100, + VIRTIO_SND_R_PCM_SET_PARAMS, + VIRTIO_SND_R_PCM_PREPARE, + VIRTIO_SND_R_PCM_RELEASE, + VIRTIO_SND_R_PCM_START, + VIRTIO_SND_R_PCM_STOP, + + /* channel map control request types */ + VIRTIO_SND_R_CHMAP_INFO = 0x0200, + + /* jack event types */ + VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, + VIRTIO_SND_EVT_JACK_DISCONNECTED, + + /* PCM event types */ + VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, + VIRTIO_SND_EVT_PCM_XRUN, + + /* common status codes */ + VIRTIO_SND_S_OK = 0x8000, + VIRTIO_SND_S_BAD_MSG, + VIRTIO_SND_S_NOT_SUPP, + VIRTIO_SND_S_IO_ERR +}; + +/* common header */ +struct virtio_snd_hdr { + __le32 code; +}; + +/* event notification */ +struct virtio_snd_event { + /* VIRTIO_SND_EVT_XXX */ + struct virtio_snd_hdr hdr; + /* optional event data */ + __le32 data; +}; + +/* common control request to query an item information */ +struct virtio_snd_query_info { + /* VIRTIO_SND_R_XXX_INFO */ + struct virtio_snd_hdr hdr; + /* item start identifier */ + __le32 start_id; + /* item count to query */ + __le32 count; + /* item information size in bytes */ + __le32 size; +}; + +/* common item information header */ +struct virtio_snd_info { + /* function group node id (High Definition Audio Specification 7.1.2) */ + __le32 hda_fn_nid; +}; + +/******************************************************************************* + * JACK CONTROL MESSAGES + */ +struct virtio_snd_jack_hdr { + /* VIRTIO_SND_R_JACK_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::jacks - 1 */ + __le32 jack_id; +}; + +/* supported jack features */ +enum { + VIRTIO_SND_JACK_F_REMAP = 0 +}; + +struct virtio_snd_jack_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */ + __le32 features; + /* pin configuration (High Definition Audio Specification 7.3.3.31) */ + __le32 hda_reg_defconf; + /* pin capabilities (High Definition Audio Specification 7.3.4.9) */ + __le32 hda_reg_caps; + /* current jack connection status (0: disconnected, 1: connected) */ + __u8 connected; + + __u8 padding[7]; +}; + +/* jack remapping control request */ +struct virtio_snd_jack_remap { + /* .code = VIRTIO_SND_R_JACK_REMAP */ + struct virtio_snd_jack_hdr hdr; + /* selected association number */ + __le32 association; + /* selected sequence number */ + __le32 sequence; +}; + +/******************************************************************************* + * PCM CONTROL MESSAGES + */ +struct virtio_snd_pcm_hdr { + /* VIRTIO_SND_R_PCM_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* supported PCM stream features */ +enum { + VIRTIO_SND_PCM_F_SHMEM_HOST = 0, + VIRTIO_SND_PCM_F_SHMEM_GUEST, + VIRTIO_SND_PCM_F_MSG_POLLING, + VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS, + VIRTIO_SND_PCM_F_EVT_XRUNS +}; + +/* supported PCM sample formats */ +enum { + /* analog formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /* 4 / 4 bits */ + VIRTIO_SND_PCM_FMT_MU_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_A_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_S18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_U18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_U20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_S24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_U24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_U20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_S24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_U24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_S32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT64, /* 64 / 64 bits */ + /* digital formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_DSD_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_DSD_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_DSD_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */ +}; + +/* supported PCM frame rates */ +enum { + VIRTIO_SND_PCM_RATE_5512 = 0, + VIRTIO_SND_PCM_RATE_8000, + VIRTIO_SND_PCM_RATE_11025, + VIRTIO_SND_PCM_RATE_16000, + VIRTIO_SND_PCM_RATE_22050, + VIRTIO_SND_PCM_RATE_32000, + VIRTIO_SND_PCM_RATE_44100, + VIRTIO_SND_PCM_RATE_48000, + VIRTIO_SND_PCM_RATE_64000, + VIRTIO_SND_PCM_RATE_88200, + VIRTIO_SND_PCM_RATE_96000, + VIRTIO_SND_PCM_RATE_176400, + VIRTIO_SND_PCM_RATE_192000, + VIRTIO_SND_PCM_RATE_384000 +}; + +struct virtio_snd_pcm_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */ + __le64 formats; + /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */ + __le64 rates; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* minimum # of supported channels */ + __u8 channels_min; + /* maximum # of supported channels */ + __u8 channels_max; + + __u8 padding[5]; +}; + +/* set PCM stream format */ +struct virtio_snd_pcm_set_params { + /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */ + struct virtio_snd_pcm_hdr hdr; + /* size of the hardware buffer */ + __le32 buffer_bytes; + /* size of the hardware period */ + __le32 period_bytes; + /* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* selected # of channels */ + __u8 channels; + /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */ + __u8 format; + /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */ + __u8 rate; + + __u8 padding; +}; + +/******************************************************************************* + * PCM I/O MESSAGES + */ + +/* I/O request header */ +struct virtio_snd_pcm_xfer { + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* I/O request status */ +struct virtio_snd_pcm_status { + /* VIRTIO_SND_S_XXX */ + __le32 status; + /* current device latency */ + __le32 latency_bytes; +}; + +/******************************************************************************* + * CHANNEL MAP CONTROL MESSAGES + */ +struct virtio_snd_chmap_hdr { + /* VIRTIO_SND_R_CHMAP_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::chmaps - 1 */ + __le32 chmap_id; +}; + +/* standard channel position definition */ +enum { + VIRTIO_SND_CHMAP_NONE = 0, /* undefined */ + VIRTIO_SND_CHMAP_NA, /* silent */ + VIRTIO_SND_CHMAP_MONO, /* mono stream */ + VIRTIO_SND_CHMAP_FL, /* front left */ + VIRTIO_SND_CHMAP_FR, /* front right */ + VIRTIO_SND_CHMAP_RL, /* rear left */ + VIRTIO_SND_CHMAP_RR, /* rear right */ + VIRTIO_SND_CHMAP_FC, /* front center */ + VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */ + VIRTIO_SND_CHMAP_SL, /* side left */ + VIRTIO_SND_CHMAP_SR, /* side right */ + VIRTIO_SND_CHMAP_RC, /* rear center */ + VIRTIO_SND_CHMAP_FLC, /* front left center */ + VIRTIO_SND_CHMAP_FRC, /* front right center */ + VIRTIO_SND_CHMAP_RLC, /* rear left center */ + VIRTIO_SND_CHMAP_RRC, /* rear right center */ + VIRTIO_SND_CHMAP_FLW, /* front left wide */ + VIRTIO_SND_CHMAP_FRW, /* front right wide */ + VIRTIO_SND_CHMAP_FLH, /* front left high */ + VIRTIO_SND_CHMAP_FCH, /* front center high */ + VIRTIO_SND_CHMAP_FRH, /* front right high */ + VIRTIO_SND_CHMAP_TC, /* top center */ + VIRTIO_SND_CHMAP_TFL, /* top front left */ + VIRTIO_SND_CHMAP_TFR, /* top front right */ + VIRTIO_SND_CHMAP_TFC, /* top front center */ + VIRTIO_SND_CHMAP_TRL, /* top rear left */ + VIRTIO_SND_CHMAP_TRR, /* top rear right */ + VIRTIO_SND_CHMAP_TRC, /* top rear center */ + VIRTIO_SND_CHMAP_TFLC, /* top front left center */ + VIRTIO_SND_CHMAP_TFRC, /* top front right center */ + VIRTIO_SND_CHMAP_TSL, /* top side left */ + VIRTIO_SND_CHMAP_TSR, /* top side right */ + VIRTIO_SND_CHMAP_LLFE, /* left LFE */ + VIRTIO_SND_CHMAP_RLFE, /* right LFE */ + VIRTIO_SND_CHMAP_BC, /* bottom center */ + VIRTIO_SND_CHMAP_BLC, /* bottom left center */ + VIRTIO_SND_CHMAP_BRC /* bottom right center */ +}; + +/* maximum possible number of channels */ +#define VIRTIO_SND_CHMAP_MAX_SIZE 18 + +struct virtio_snd_chmap_info { + /* common header */ + struct virtio_snd_info hdr; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* # of valid channel position values */ + __u8 channels; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; +}; + +#endif /* VIRTIO_SND_IF_H */ diff --git a/sound/Kconfig b/sound/Kconfig index 36785410fbe1..e56d96d2b11c 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -99,6 +99,8 @@ source "sound/synth/Kconfig" source "sound/xen/Kconfig" +source "sound/virtio/Kconfig" + endif # SND endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 797ecdcd35e2..04ef04b1168f 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \ + virtio/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig new file mode 100644 index 000000000000..094cba24ee5b --- /dev/null +++ b/sound/virtio/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Sound card driver for virtio + +config SND_VIRTIO + tristate "Virtio sound driver" + depends on VIRTIO + select SND_PCM + select SND_JACK + help + This is the virtual sound driver for virtio. Say Y or M. diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile new file mode 100644 index 000000000000..8c87ebb9982b --- /dev/null +++ b/sound/virtio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o + +virtio_snd-objs := \ + virtio_card.o + diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c new file mode 100644 index 000000000000..532d823fdf6f --- /dev/null +++ b/sound/virtio/virtio_card.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include + +#include "virtio_card.h" + +static void virtsnd_remove(struct virtio_device *vdev); + +/** + * virtsnd_event_send() - Add an event to the event queue. + * @vqueue: Underlying event virtqueue. + * @event: Event. + * @notify: Indicates whether or not to send a notification to the device. + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_event_send(struct virtqueue *vqueue, + struct virtio_snd_event *event, bool notify, + gfp_t gfp) +{ + struct scatterlist sg; + struct scatterlist *psgs[1] = { &sg }; + int rc; + + /* reset event content */ + memset(event, 0, sizeof(*event)); + + sg_init_one(&sg, event, sizeof(*event)); + + rc = virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp); + if (rc) + return rc; + + if (notify) + if (virtqueue_kick_prepare(vqueue)) + if (!virtqueue_notify(vqueue)) + return -EIO; + + return 0; +} + +/** + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. + * @vqueue: Underlying event virtqueue. + * + * This callback function is called upon a vring interrupt request from the + * device. + * + * Context: Interrupt context. + */ +static void virtsnd_event_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (queue->vqueue) { + virtqueue_disable_cb(queue->vqueue); + + for (;;) { + struct virtio_snd_event *event; + u32 length; + + event = virtqueue_get_buf(queue->vqueue, &length); + if (!event) + break; + + virtsnd_event_send(queue->vqueue, event, true, + GFP_ATOMIC); + } + + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + + if (virtqueue_enable_cb(queue->vqueue)) + break; + } + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. + * @snd: VirtIO sound device. + * + * After calling this function, the event queue is disabled. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_find_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { 0 }; + const char *names[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", + [VIRTIO_SND_VQ_EVENT] = "virtsnd-event", + [VIRTIO_SND_VQ_TX] = "virtsnd-tx", + [VIRTIO_SND_VQ_RX] = "virtsnd-rx" + }; + struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; + unsigned int i; + unsigned int n = 0; + int rc; + + callbacks[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb; + + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, + NULL); + if (rc) { + dev_err(&vdev->dev, "failed to initialize virtqueues\n"); + return rc; + } + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + snd->queues[i].vqueue = vqs[i]; + + /* Allocate events and populate the event queue */ + virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); + + n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); + + snd->event_msgs = devm_kcalloc(&vdev->dev, n, sizeof(*snd->event_msgs), + GFP_KERNEL); + if (!snd->event_msgs) + return -ENOMEM; + + for (i = 0; i < n; ++i) { + rc = virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], + &snd->event_msgs[i], false, GFP_KERNEL); + if (rc) + return rc; + } + + return 0; +} + +/** + * virtsnd_enable_event_vq() - Enable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_enable_event_vq(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + + if (!virtqueue_enable_cb(queue->vqueue)) + virtsnd_event_notify_cb(queue->vqueue); +} + +/** + * virtsnd_disable_vqs() - Disable all virtqueues. + * @snd: VirtIO sound device. + * + * Also free all allocated events and control messages. + * + * Context: Any context. + */ +static void virtsnd_disable_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int i; + unsigned long flags; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) { + struct virtio_snd_queue *queue = &snd->queues[i]; + + spin_lock_irqsave(&queue->lock, flags); + /* Prohibit the use of the queue */ + if (queue->vqueue) + virtqueue_disable_cb(queue->vqueue); + queue->vqueue = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + } + + if (snd->event_msgs) + devm_kfree(&vdev->dev, snd->event_msgs); + + snd->event_msgs = NULL; +} + +/** + * virtsnd_reset_fn() - Kernel worker's function to reset the device. + * @work: Reset device work. + * + * Context: Process context. + */ +static void virtsnd_reset_fn(struct work_struct *work) +{ + struct virtio_snd *snd = + container_of(work, struct virtio_snd, reset_work); + struct virtio_device *vdev = snd->vdev; + struct device *dev = &vdev->dev; + int rc; + + dev_info(dev, "sound device needs reset\n"); + + /* + * It seems that the only way to properly reset the device is to remove + * and re-create the ALSA sound card device. + * + * Also resetting the device involves a number of steps with setting the + * status bits described in the virtio specification. And the easiest + * way to get everything right is to use the virtio bus interface. + */ + rc = dev->bus->remove(dev); + if (rc) + dev_warn(dev, "bus->remove() failed: %d", rc); + + rc = dev->bus->probe(dev); + if (rc) + dev_err(dev, "bus->probe() failed: %d", rc); +} + +/** + * virtsnd_build_devs() - Read configuration and build ALSA devices. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_build_devs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + int rc; + + rc = snd_card_new(&vdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &snd->card); + if (rc < 0) + return rc; + + snd->card->private_data = snd; + + strscpy(snd->card->id, "viosnd", sizeof(snd->card->id)); + strscpy(snd->card->driver, "virtio_snd", sizeof(snd->card->driver)); + strscpy(snd->card->shortname, "VIOSND", sizeof(snd->card->shortname)); + strscpy(snd->card->longname, "VirtIO Sound Card", + sizeof(snd->card->longname)); + + return snd_card_register(snd->card); +} + +/** + * virtsnd_validate() - Validate if the device can be started. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -EINVAL on failure. + */ +static int virtsnd_validate(struct virtio_device *vdev) +{ + if (!vdev->config->get) { + dev_err(&vdev->dev, "configuration access disabled\n"); + return -EINVAL; + } + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, + "device does not comply with spec version 1.x\n"); + return -EINVAL; + } + + return 0; +} + +/** + * virtsnd_probe() - Create and initialize the device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_probe(struct virtio_device *vdev) +{ + struct virtio_snd *snd; + unsigned int i; + int rc; + + snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); + if (!snd) + return -ENOMEM; + + snd->vdev = vdev; + INIT_WORK(&snd->reset_work, virtsnd_reset_fn); + + vdev->priv = snd; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + spin_lock_init(&snd->queues[i].lock); + + rc = virtsnd_find_vqs(snd); + if (rc) + goto on_failure; + + virtio_device_ready(vdev); + + rc = virtsnd_build_devs(snd); + if (rc) + goto on_failure; + + virtsnd_enable_event_vq(snd); + +on_failure: + if (rc) + virtsnd_remove(vdev); + + return rc; +} + +/** + * virtsnd_remove() - Remove VirtIO and ALSA devices. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + */ +static void virtsnd_remove(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + if (!snd) + return; + + /* + * Make sure no one is accessing the virtqueues and sending synchronous + * requests to the device. This can happen if we got here because the + * device needs to be reset. + */ + virtsnd_disable_vqs(snd); + + if (snd->card) + snd_card_free(snd->card); + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + devm_kfree(&vdev->dev, snd); + + vdev->priv = NULL; +} + +/** + * virtsnd_config_changed() - Handle configuration change notification. + * @vdev: VirtIO parent device. + * + * This callback function is called upon a configuration change interrupt + * request from the device. Currently only used to handle NEEDS_RESET device + * status. + * + * Context: Interrupt context. + */ +static void virtsnd_config_changed(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + unsigned int status = vdev->config->get_status(vdev); + + if (status & VIRTIO_CONFIG_S_NEEDS_RESET) + schedule_work(&snd->reset_work); + else + dev_warn(&vdev->dev, + "sound device configuration was changed\n"); +} + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtsnd_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .validate = virtsnd_validate, + .probe = virtsnd_probe, + .remove = virtsnd_remove, + .config_changed = virtsnd_config_changed, +}; + +static int __init init(void) +{ + return register_virtio_driver(&virtsnd_driver); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtsnd_driver); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio sound card driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h new file mode 100644 index 000000000000..10084abaaf18 --- /dev/null +++ b/sound/virtio/virtio_card.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef VIRTIO_SND_CARD_H +#define VIRTIO_SND_CARD_H + +#include +#include +#include + +/** + * struct virtio_snd_queue - Virtqueue wrapper structure. + * @lock: Used to synchronize access to a virtqueue. + * @vqueue: Underlying virtqueue. + */ +struct virtio_snd_queue { + spinlock_t lock; + struct virtqueue *vqueue; +}; + +/** + * struct virtio_snd - VirtIO sound card device. + * @vdev: Underlying virtio device. + * @queues: Virtqueue wrappers. + * @reset_work: Reset device work. + * @card: ALSA sound card. + * @event_msgs: Device events. + */ +struct virtio_snd { + struct virtio_device *vdev; + struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; + struct work_struct reset_work; + struct snd_card *card; + struct virtio_snd_event *event_msgs; +}; + +static inline struct virtio_snd_queue * +virtsnd_control_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_CONTROL]; +} + +static inline struct virtio_snd_queue * +virtsnd_event_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_EVENT]; +} + +static inline struct virtio_snd_queue * +virtsnd_tx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_TX]; +} + +static inline struct virtio_snd_queue * +virtsnd_rx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_RX]; +} + +#endif /* VIRTIO_SND_CARD_H */ -- 2.30.0 From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.6 required=3.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_RED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2065AC433DB for ; Sun, 24 Jan 2021 16:54:35 +0000 (UTC) Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id BAAA422CA2 for ; Sun, 24 Jan 2021 16:54:34 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org BAAA422CA2 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=opensynergy.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=virtualization-bounces@lists.linux-foundation.org Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id 9416D87202; Sun, 24 Jan 2021 16:54:34 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id gSukiJfPSjCc; Sun, 24 Jan 2021 16:54:31 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by hemlock.osuosl.org (Postfix) with ESMTP id C84EB870FB; Sun, 24 Jan 2021 16:54:31 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id B3052C0FA7; Sun, 24 Jan 2021 16:54:31 +0000 (UTC) Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id E8812C013A for ; Sun, 24 Jan 2021 16:54:30 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id D702486924 for ; Sun, 24 Jan 2021 16:54:30 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xMiXNNpMCJoi for ; Sun, 24 Jan 2021 16:54:28 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mx1.opensynergy.com (mx1.opensynergy.com [217.66.60.4]) by whitealder.osuosl.org (Postfix) with ESMTPS id 757D4868D2 for ; Sun, 24 Jan 2021 16:54:28 +0000 (UTC) Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 214E0A147D; Sun, 24 Jan 2021 17:54:27 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=opensynergy.com; h=cc:cc:content-transfer-encoding:content-type:content-type :date:from:from:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to; s=srmailgate02; bh=mj209XilldnV DEviqgC/TmGvsoxvpkhdT5z2JgsuXwQ=; b=heWBruKtEYEE4UL0/KvGV1jzTKqq 0KcfrW6YLPEsH7WO8vO4OuMVaoUsUxA2V5b0jWLhXev6ZtRVLAwT7QbSMnmUJjHv Z5bIhV1gllFNqFtk8W5shffqlXQI80RcLcICsoxtYSCC0nyZzZKMnerSxuCr4S/i fXXebLLWT3bbCIGrdIK75XujJro47IaM7lEqhWjQCflOjnDS2YRYtyJKdvhMYbn6 ocIBqwI95zk6MeS1iFAuThHTwT3Ktxsi08NQU/7DQr/9r7R8uy8uCK27EpKOEKGY KR7hyQCAw8pFvz9ewEOSgMzU3J2GdWy2kc99o6YF+gtH25wFJYdMsr0WHg== From: Anton Yakovlev To: , , Subject: [PATCH v2 2/9] ALSA: virtio: add virtio sound driver Date: Sun, 24 Jan 2021 17:54:01 +0100 Message-ID: <20210124165408.1122868-3-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> References: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-02.open-synergy.com (10.26.10.22) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, Jaroslav Kysela , Takashi Iwai , "Michael S. Tsirkin" X-BeenThere: virtualization@lists.linux-foundation.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Linux virtualization List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: virtualization-bounces@lists.linux-foundation.org Sender: "Virtualization" Introduce skeleton of the virtio sound driver. The driver implements the virtio sound device specification, which has become part of the virtio standard. Initial initialization of the device, virtqueues and creation of an empty ALSA sound device. Also, handling DEVICE_NEEDS_RESET device status. Signed-off-by: Anton Yakovlev --- MAINTAINERS | 9 + include/uapi/linux/virtio_snd.h | 361 +++++++++++++++++++++++++++ sound/Kconfig | 2 + sound/Makefile | 3 +- sound/virtio/Kconfig | 10 + sound/virtio/Makefile | 7 + sound/virtio/virtio_card.c | 415 ++++++++++++++++++++++++++++++++ sound/virtio/virtio_card.h | 76 ++++++ 8 files changed, 882 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/virtio_snd.h create mode 100644 sound/virtio/Kconfig create mode 100644 sound/virtio/Makefile create mode 100644 sound/virtio/virtio_card.c create mode 100644 sound/virtio/virtio_card.h diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..3f509d54a457 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18936,6 +18936,15 @@ W: https://virtio-mem.gitlab.io/ F: drivers/virtio/virtio_mem.c F: include/uapi/linux/virtio_mem.h +VIRTIO SOUND DRIVER +M: Anton Yakovlev +M: "Michael S. Tsirkin" +L: virtualization@lists.linux-foundation.org +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: include/uapi/linux/virtio_snd.h +F: sound/virtio/* + VIRTUAL BOX GUEST DEVICE DRIVER M: Hans de Goede M: Arnd Bergmann diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_snd.h new file mode 100644 index 000000000000..1ff6310e54d6 --- /dev/null +++ b/include/uapi/linux/virtio_snd.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2020 OpenSynergy GmbH + * + * This header is BSD licensed so anyone can use the definitions to + * implement compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of OpenSynergy GmbH nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef VIRTIO_SND_IF_H +#define VIRTIO_SND_IF_H + +#include + +/******************************************************************************* + * CONFIGURATION SPACE + */ +struct virtio_snd_config { + /* # of available physical jacks */ + __le32 jacks; + /* # of available PCM streams */ + __le32 streams; + /* # of available channel maps */ + __le32 chmaps; +}; + +enum { + /* device virtqueue indexes */ + VIRTIO_SND_VQ_CONTROL = 0, + VIRTIO_SND_VQ_EVENT, + VIRTIO_SND_VQ_TX, + VIRTIO_SND_VQ_RX, + /* # of device virtqueues */ + VIRTIO_SND_VQ_MAX +}; + +/******************************************************************************* + * COMMON DEFINITIONS + */ + +/* supported dataflow directions */ +enum { + VIRTIO_SND_D_OUTPUT = 0, + VIRTIO_SND_D_INPUT +}; + +enum { + /* jack control request types */ + VIRTIO_SND_R_JACK_INFO = 1, + VIRTIO_SND_R_JACK_REMAP, + + /* PCM control request types */ + VIRTIO_SND_R_PCM_INFO = 0x0100, + VIRTIO_SND_R_PCM_SET_PARAMS, + VIRTIO_SND_R_PCM_PREPARE, + VIRTIO_SND_R_PCM_RELEASE, + VIRTIO_SND_R_PCM_START, + VIRTIO_SND_R_PCM_STOP, + + /* channel map control request types */ + VIRTIO_SND_R_CHMAP_INFO = 0x0200, + + /* jack event types */ + VIRTIO_SND_EVT_JACK_CONNECTED = 0x1000, + VIRTIO_SND_EVT_JACK_DISCONNECTED, + + /* PCM event types */ + VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED = 0x1100, + VIRTIO_SND_EVT_PCM_XRUN, + + /* common status codes */ + VIRTIO_SND_S_OK = 0x8000, + VIRTIO_SND_S_BAD_MSG, + VIRTIO_SND_S_NOT_SUPP, + VIRTIO_SND_S_IO_ERR +}; + +/* common header */ +struct virtio_snd_hdr { + __le32 code; +}; + +/* event notification */ +struct virtio_snd_event { + /* VIRTIO_SND_EVT_XXX */ + struct virtio_snd_hdr hdr; + /* optional event data */ + __le32 data; +}; + +/* common control request to query an item information */ +struct virtio_snd_query_info { + /* VIRTIO_SND_R_XXX_INFO */ + struct virtio_snd_hdr hdr; + /* item start identifier */ + __le32 start_id; + /* item count to query */ + __le32 count; + /* item information size in bytes */ + __le32 size; +}; + +/* common item information header */ +struct virtio_snd_info { + /* function group node id (High Definition Audio Specification 7.1.2) */ + __le32 hda_fn_nid; +}; + +/******************************************************************************* + * JACK CONTROL MESSAGES + */ +struct virtio_snd_jack_hdr { + /* VIRTIO_SND_R_JACK_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::jacks - 1 */ + __le32 jack_id; +}; + +/* supported jack features */ +enum { + VIRTIO_SND_JACK_F_REMAP = 0 +}; + +struct virtio_snd_jack_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */ + __le32 features; + /* pin configuration (High Definition Audio Specification 7.3.3.31) */ + __le32 hda_reg_defconf; + /* pin capabilities (High Definition Audio Specification 7.3.4.9) */ + __le32 hda_reg_caps; + /* current jack connection status (0: disconnected, 1: connected) */ + __u8 connected; + + __u8 padding[7]; +}; + +/* jack remapping control request */ +struct virtio_snd_jack_remap { + /* .code = VIRTIO_SND_R_JACK_REMAP */ + struct virtio_snd_jack_hdr hdr; + /* selected association number */ + __le32 association; + /* selected sequence number */ + __le32 sequence; +}; + +/******************************************************************************* + * PCM CONTROL MESSAGES + */ +struct virtio_snd_pcm_hdr { + /* VIRTIO_SND_R_PCM_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* supported PCM stream features */ +enum { + VIRTIO_SND_PCM_F_SHMEM_HOST = 0, + VIRTIO_SND_PCM_F_SHMEM_GUEST, + VIRTIO_SND_PCM_F_MSG_POLLING, + VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS, + VIRTIO_SND_PCM_F_EVT_XRUNS +}; + +/* supported PCM sample formats */ +enum { + /* analog formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_IMA_ADPCM = 0, /* 4 / 4 bits */ + VIRTIO_SND_PCM_FMT_MU_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_A_LAW, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_S16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_S18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_U18_3, /* 18 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_U20_3, /* 20 / 24 bits */ + VIRTIO_SND_PCM_FMT_S24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_U24_3, /* 24 / 24 bits */ + VIRTIO_SND_PCM_FMT_S20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_U20, /* 20 / 32 bits */ + VIRTIO_SND_PCM_FMT_S24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_U24, /* 24 / 32 bits */ + VIRTIO_SND_PCM_FMT_S32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_FLOAT64, /* 64 / 64 bits */ + /* digital formats (width / physical width) */ + VIRTIO_SND_PCM_FMT_DSD_U8, /* 8 / 8 bits */ + VIRTIO_SND_PCM_FMT_DSD_U16, /* 16 / 16 bits */ + VIRTIO_SND_PCM_FMT_DSD_U32, /* 32 / 32 bits */ + VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME /* 32 / 32 bits */ +}; + +/* supported PCM frame rates */ +enum { + VIRTIO_SND_PCM_RATE_5512 = 0, + VIRTIO_SND_PCM_RATE_8000, + VIRTIO_SND_PCM_RATE_11025, + VIRTIO_SND_PCM_RATE_16000, + VIRTIO_SND_PCM_RATE_22050, + VIRTIO_SND_PCM_RATE_32000, + VIRTIO_SND_PCM_RATE_44100, + VIRTIO_SND_PCM_RATE_48000, + VIRTIO_SND_PCM_RATE_64000, + VIRTIO_SND_PCM_RATE_88200, + VIRTIO_SND_PCM_RATE_96000, + VIRTIO_SND_PCM_RATE_176400, + VIRTIO_SND_PCM_RATE_192000, + VIRTIO_SND_PCM_RATE_384000 +}; + +struct virtio_snd_pcm_info { + /* common header */ + struct virtio_snd_info hdr; + /* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */ + __le64 formats; + /* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */ + __le64 rates; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* minimum # of supported channels */ + __u8 channels_min; + /* maximum # of supported channels */ + __u8 channels_max; + + __u8 padding[5]; +}; + +/* set PCM stream format */ +struct virtio_snd_pcm_set_params { + /* .code = VIRTIO_SND_R_PCM_SET_PARAMS */ + struct virtio_snd_pcm_hdr hdr; + /* size of the hardware buffer */ + __le32 buffer_bytes; + /* size of the hardware period */ + __le32 period_bytes; + /* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ + __le32 features; + /* selected # of channels */ + __u8 channels; + /* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */ + __u8 format; + /* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */ + __u8 rate; + + __u8 padding; +}; + +/******************************************************************************* + * PCM I/O MESSAGES + */ + +/* I/O request header */ +struct virtio_snd_pcm_xfer { + /* 0 ... virtio_snd_config::streams - 1 */ + __le32 stream_id; +}; + +/* I/O request status */ +struct virtio_snd_pcm_status { + /* VIRTIO_SND_S_XXX */ + __le32 status; + /* current device latency */ + __le32 latency_bytes; +}; + +/******************************************************************************* + * CHANNEL MAP CONTROL MESSAGES + */ +struct virtio_snd_chmap_hdr { + /* VIRTIO_SND_R_CHMAP_XXX */ + struct virtio_snd_hdr hdr; + /* 0 ... virtio_snd_config::chmaps - 1 */ + __le32 chmap_id; +}; + +/* standard channel position definition */ +enum { + VIRTIO_SND_CHMAP_NONE = 0, /* undefined */ + VIRTIO_SND_CHMAP_NA, /* silent */ + VIRTIO_SND_CHMAP_MONO, /* mono stream */ + VIRTIO_SND_CHMAP_FL, /* front left */ + VIRTIO_SND_CHMAP_FR, /* front right */ + VIRTIO_SND_CHMAP_RL, /* rear left */ + VIRTIO_SND_CHMAP_RR, /* rear right */ + VIRTIO_SND_CHMAP_FC, /* front center */ + VIRTIO_SND_CHMAP_LFE, /* low frequency (LFE) */ + VIRTIO_SND_CHMAP_SL, /* side left */ + VIRTIO_SND_CHMAP_SR, /* side right */ + VIRTIO_SND_CHMAP_RC, /* rear center */ + VIRTIO_SND_CHMAP_FLC, /* front left center */ + VIRTIO_SND_CHMAP_FRC, /* front right center */ + VIRTIO_SND_CHMAP_RLC, /* rear left center */ + VIRTIO_SND_CHMAP_RRC, /* rear right center */ + VIRTIO_SND_CHMAP_FLW, /* front left wide */ + VIRTIO_SND_CHMAP_FRW, /* front right wide */ + VIRTIO_SND_CHMAP_FLH, /* front left high */ + VIRTIO_SND_CHMAP_FCH, /* front center high */ + VIRTIO_SND_CHMAP_FRH, /* front right high */ + VIRTIO_SND_CHMAP_TC, /* top center */ + VIRTIO_SND_CHMAP_TFL, /* top front left */ + VIRTIO_SND_CHMAP_TFR, /* top front right */ + VIRTIO_SND_CHMAP_TFC, /* top front center */ + VIRTIO_SND_CHMAP_TRL, /* top rear left */ + VIRTIO_SND_CHMAP_TRR, /* top rear right */ + VIRTIO_SND_CHMAP_TRC, /* top rear center */ + VIRTIO_SND_CHMAP_TFLC, /* top front left center */ + VIRTIO_SND_CHMAP_TFRC, /* top front right center */ + VIRTIO_SND_CHMAP_TSL, /* top side left */ + VIRTIO_SND_CHMAP_TSR, /* top side right */ + VIRTIO_SND_CHMAP_LLFE, /* left LFE */ + VIRTIO_SND_CHMAP_RLFE, /* right LFE */ + VIRTIO_SND_CHMAP_BC, /* bottom center */ + VIRTIO_SND_CHMAP_BLC, /* bottom left center */ + VIRTIO_SND_CHMAP_BRC /* bottom right center */ +}; + +/* maximum possible number of channels */ +#define VIRTIO_SND_CHMAP_MAX_SIZE 18 + +struct virtio_snd_chmap_info { + /* common header */ + struct virtio_snd_info hdr; + /* dataflow direction (VIRTIO_SND_D_XXX) */ + __u8 direction; + /* # of valid channel position values */ + __u8 channels; + /* channel position values (VIRTIO_SND_CHMAP_XXX) */ + __u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; +}; + +#endif /* VIRTIO_SND_IF_H */ diff --git a/sound/Kconfig b/sound/Kconfig index 36785410fbe1..e56d96d2b11c 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -99,6 +99,8 @@ source "sound/synth/Kconfig" source "sound/xen/Kconfig" +source "sound/virtio/Kconfig" + endif # SND endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 797ecdcd35e2..04ef04b1168f 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \ + virtio/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig new file mode 100644 index 000000000000..094cba24ee5b --- /dev/null +++ b/sound/virtio/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Sound card driver for virtio + +config SND_VIRTIO + tristate "Virtio sound driver" + depends on VIRTIO + select SND_PCM + select SND_JACK + help + This is the virtual sound driver for virtio. Say Y or M. diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile new file mode 100644 index 000000000000..8c87ebb9982b --- /dev/null +++ b/sound/virtio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o + +virtio_snd-objs := \ + virtio_card.o + diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c new file mode 100644 index 000000000000..532d823fdf6f --- /dev/null +++ b/sound/virtio/virtio_card.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include + +#include "virtio_card.h" + +static void virtsnd_remove(struct virtio_device *vdev); + +/** + * virtsnd_event_send() - Add an event to the event queue. + * @vqueue: Underlying event virtqueue. + * @event: Event. + * @notify: Indicates whether or not to send a notification to the device. + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_event_send(struct virtqueue *vqueue, + struct virtio_snd_event *event, bool notify, + gfp_t gfp) +{ + struct scatterlist sg; + struct scatterlist *psgs[1] = { &sg }; + int rc; + + /* reset event content */ + memset(event, 0, sizeof(*event)); + + sg_init_one(&sg, event, sizeof(*event)); + + rc = virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp); + if (rc) + return rc; + + if (notify) + if (virtqueue_kick_prepare(vqueue)) + if (!virtqueue_notify(vqueue)) + return -EIO; + + return 0; +} + +/** + * virtsnd_event_notify_cb() - Dispatch all reported events from the event queue. + * @vqueue: Underlying event virtqueue. + * + * This callback function is called upon a vring interrupt request from the + * device. + * + * Context: Interrupt context. + */ +static void virtsnd_event_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (queue->vqueue) { + virtqueue_disable_cb(queue->vqueue); + + for (;;) { + struct virtio_snd_event *event; + u32 length; + + event = virtqueue_get_buf(queue->vqueue, &length); + if (!event) + break; + + virtsnd_event_send(queue->vqueue, event, true, + GFP_ATOMIC); + } + + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + + if (virtqueue_enable_cb(queue->vqueue)) + break; + } + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. + * @snd: VirtIO sound device. + * + * After calling this function, the event queue is disabled. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_find_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { 0 }; + const char *names[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", + [VIRTIO_SND_VQ_EVENT] = "virtsnd-event", + [VIRTIO_SND_VQ_TX] = "virtsnd-tx", + [VIRTIO_SND_VQ_RX] = "virtsnd-rx" + }; + struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; + unsigned int i; + unsigned int n = 0; + int rc; + + callbacks[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb; + + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, + NULL); + if (rc) { + dev_err(&vdev->dev, "failed to initialize virtqueues\n"); + return rc; + } + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + snd->queues[i].vqueue = vqs[i]; + + /* Allocate events and populate the event queue */ + virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); + + n = virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); + + snd->event_msgs = devm_kcalloc(&vdev->dev, n, sizeof(*snd->event_msgs), + GFP_KERNEL); + if (!snd->event_msgs) + return -ENOMEM; + + for (i = 0; i < n; ++i) { + rc = virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], + &snd->event_msgs[i], false, GFP_KERNEL); + if (rc) + return rc; + } + + return 0; +} + +/** + * virtsnd_enable_event_vq() - Enable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_enable_event_vq(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_event_queue(snd); + + if (!virtqueue_enable_cb(queue->vqueue)) + virtsnd_event_notify_cb(queue->vqueue); +} + +/** + * virtsnd_disable_vqs() - Disable all virtqueues. + * @snd: VirtIO sound device. + * + * Also free all allocated events and control messages. + * + * Context: Any context. + */ +static void virtsnd_disable_vqs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int i; + unsigned long flags; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) { + struct virtio_snd_queue *queue = &snd->queues[i]; + + spin_lock_irqsave(&queue->lock, flags); + /* Prohibit the use of the queue */ + if (queue->vqueue) + virtqueue_disable_cb(queue->vqueue); + queue->vqueue = NULL; + spin_unlock_irqrestore(&queue->lock, flags); + } + + if (snd->event_msgs) + devm_kfree(&vdev->dev, snd->event_msgs); + + snd->event_msgs = NULL; +} + +/** + * virtsnd_reset_fn() - Kernel worker's function to reset the device. + * @work: Reset device work. + * + * Context: Process context. + */ +static void virtsnd_reset_fn(struct work_struct *work) +{ + struct virtio_snd *snd = + container_of(work, struct virtio_snd, reset_work); + struct virtio_device *vdev = snd->vdev; + struct device *dev = &vdev->dev; + int rc; + + dev_info(dev, "sound device needs reset\n"); + + /* + * It seems that the only way to properly reset the device is to remove + * and re-create the ALSA sound card device. + * + * Also resetting the device involves a number of steps with setting the + * status bits described in the virtio specification. And the easiest + * way to get everything right is to use the virtio bus interface. + */ + rc = dev->bus->remove(dev); + if (rc) + dev_warn(dev, "bus->remove() failed: %d", rc); + + rc = dev->bus->probe(dev); + if (rc) + dev_err(dev, "bus->probe() failed: %d", rc); +} + +/** + * virtsnd_build_devs() - Read configuration and build ALSA devices. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_build_devs(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + int rc; + + rc = snd_card_new(&vdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &snd->card); + if (rc < 0) + return rc; + + snd->card->private_data = snd; + + strscpy(snd->card->id, "viosnd", sizeof(snd->card->id)); + strscpy(snd->card->driver, "virtio_snd", sizeof(snd->card->driver)); + strscpy(snd->card->shortname, "VIOSND", sizeof(snd->card->shortname)); + strscpy(snd->card->longname, "VirtIO Sound Card", + sizeof(snd->card->longname)); + + return snd_card_register(snd->card); +} + +/** + * virtsnd_validate() - Validate if the device can be started. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -EINVAL on failure. + */ +static int virtsnd_validate(struct virtio_device *vdev) +{ + if (!vdev->config->get) { + dev_err(&vdev->dev, "configuration access disabled\n"); + return -EINVAL; + } + + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { + dev_err(&vdev->dev, + "device does not comply with spec version 1.x\n"); + return -EINVAL; + } + + return 0; +} + +/** + * virtsnd_probe() - Create and initialize the device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_probe(struct virtio_device *vdev) +{ + struct virtio_snd *snd; + unsigned int i; + int rc; + + snd = devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); + if (!snd) + return -ENOMEM; + + snd->vdev = vdev; + INIT_WORK(&snd->reset_work, virtsnd_reset_fn); + + vdev->priv = snd; + + for (i = 0; i < VIRTIO_SND_VQ_MAX; ++i) + spin_lock_init(&snd->queues[i].lock); + + rc = virtsnd_find_vqs(snd); + if (rc) + goto on_failure; + + virtio_device_ready(vdev); + + rc = virtsnd_build_devs(snd); + if (rc) + goto on_failure; + + virtsnd_enable_event_vq(snd); + +on_failure: + if (rc) + virtsnd_remove(vdev); + + return rc; +} + +/** + * virtsnd_remove() - Remove VirtIO and ALSA devices. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + */ +static void virtsnd_remove(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + if (!snd) + return; + + /* + * Make sure no one is accessing the virtqueues and sending synchronous + * requests to the device. This can happen if we got here because the + * device needs to be reset. + */ + virtsnd_disable_vqs(snd); + + if (snd->card) + snd_card_free(snd->card); + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + devm_kfree(&vdev->dev, snd); + + vdev->priv = NULL; +} + +/** + * virtsnd_config_changed() - Handle configuration change notification. + * @vdev: VirtIO parent device. + * + * This callback function is called upon a configuration change interrupt + * request from the device. Currently only used to handle NEEDS_RESET device + * status. + * + * Context: Interrupt context. + */ +static void virtsnd_config_changed(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + unsigned int status = vdev->config->get_status(vdev); + + if (status & VIRTIO_CONFIG_S_NEEDS_RESET) + schedule_work(&snd->reset_work); + else + dev_warn(&vdev->dev, + "sound device configuration was changed\n"); +} + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtsnd_driver = { + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .validate = virtsnd_validate, + .probe = virtsnd_probe, + .remove = virtsnd_remove, + .config_changed = virtsnd_config_changed, +}; + +static int __init init(void) +{ + return register_virtio_driver(&virtsnd_driver); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtsnd_driver); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio sound card driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h new file mode 100644 index 000000000000..10084abaaf18 --- /dev/null +++ b/sound/virtio/virtio_card.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef VIRTIO_SND_CARD_H +#define VIRTIO_SND_CARD_H + +#include +#include +#include + +/** + * struct virtio_snd_queue - Virtqueue wrapper structure. + * @lock: Used to synchronize access to a virtqueue. + * @vqueue: Underlying virtqueue. + */ +struct virtio_snd_queue { + spinlock_t lock; + struct virtqueue *vqueue; +}; + +/** + * struct virtio_snd - VirtIO sound card device. + * @vdev: Underlying virtio device. + * @queues: Virtqueue wrappers. + * @reset_work: Reset device work. + * @card: ALSA sound card. + * @event_msgs: Device events. + */ +struct virtio_snd { + struct virtio_device *vdev; + struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; + struct work_struct reset_work; + struct snd_card *card; + struct virtio_snd_event *event_msgs; +}; + +static inline struct virtio_snd_queue * +virtsnd_control_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_CONTROL]; +} + +static inline struct virtio_snd_queue * +virtsnd_event_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_EVENT]; +} + +static inline struct virtio_snd_queue * +virtsnd_tx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_TX]; +} + +static inline struct virtio_snd_queue * +virtsnd_rx_queue(struct virtio_snd *snd) +{ + return &snd->queues[VIRTIO_SND_VQ_RX]; +} + +#endif /* VIRTIO_SND_CARD_H */ -- 2.30.0 _______________________________________________ Virtualization mailing list Virtualization@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/virtualization From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: virtio-dev-return-7944-cohuck=redhat.com@lists.oasis-open.org Sender: List-Post: List-Help: List-Unsubscribe: List-Subscribe: Received: from lists.oasis-open.org (oasis-open.org [10.110.1.242]) by lists.oasis-open.org (Postfix) with ESMTP id F258D985B9F for ; Sun, 24 Jan 2021 16:54:29 +0000 (UTC) From: Anton Yakovlev Date: Sun, 24 Jan 2021 17:54:01 +0100 Message-ID: <20210124165408.1122868-3-anton.yakovlev@opensynergy.com> In-Reply-To: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> References: <20210124165408.1122868-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 Subject: [virtio-dev] [PATCH v2 2/9] ALSA: virtio: add virtio sound driver Content-Type: text/plain Content-Transfer-Encoding: quoted-printable To: virtualization@lists.linux-foundation.org, alsa-devel@alsa-project.org, virtio-dev@lists.oasis-open.org Cc: "Michael S. Tsirkin" , Jaroslav Kysela , Takashi Iwai , Jason Wang , linux-kernel@vger.kernel.org List-ID: Introduce skeleton of the virtio sound driver. The driver implements the virtio sound device specification, which has become part of the virtio standard. Initial initialization of the device, virtqueues and creation of an empty ALSA sound device. Also, handling DEVICE_NEEDS_RESET device status. Signed-off-by: Anton Yakovlev --- MAINTAINERS | 9 + include/uapi/linux/virtio_snd.h | 361 +++++++++++++++++++++++++++ sound/Kconfig | 2 + sound/Makefile | 3 +- sound/virtio/Kconfig | 10 + sound/virtio/Makefile | 7 + sound/virtio/virtio_card.c | 415 ++++++++++++++++++++++++++++++++ sound/virtio/virtio_card.h | 76 ++++++ 8 files changed, 882 insertions(+), 1 deletion(-) create mode 100644 include/uapi/linux/virtio_snd.h create mode 100644 sound/virtio/Kconfig create mode 100644 sound/virtio/Makefile create mode 100644 sound/virtio/virtio_card.c create mode 100644 sound/virtio/virtio_card.h diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..3f509d54a457 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18936,6 +18936,15 @@ W:=09https://virtio-mem.gitlab.io/ F:=09drivers/virtio/virtio_mem.c F:=09include/uapi/linux/virtio_mem.h =20 +VIRTIO SOUND DRIVER +M:=09Anton Yakovlev +M:=09"Michael S. Tsirkin" +L:=09virtualization@lists.linux-foundation.org +L:=09alsa-devel@alsa-project.org (moderated for non-subscribers) +S:=09Maintained +F:=09include/uapi/linux/virtio_snd.h +F:=09sound/virtio/* + VIRTUAL BOX GUEST DEVICE DRIVER M:=09Hans de Goede M:=09Arnd Bergmann diff --git a/include/uapi/linux/virtio_snd.h b/include/uapi/linux/virtio_sn= d.h new file mode 100644 index 000000000000..1ff6310e54d6 --- /dev/null +++ b/include/uapi/linux/virtio_snd.h @@ -0,0 +1,361 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (C) 2020 OpenSynergy GmbH + * + * This header is BSD licensed so anyone can use the definitions to + * implement compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of OpenSynergy GmbH nor the names of its contributo= rs + * may be used to endorse or promote products derived from this softwar= e + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef VIRTIO_SND_IF_H +#define VIRTIO_SND_IF_H + +#include + +/*************************************************************************= ****** + * CONFIGURATION SPACE + */ +struct virtio_snd_config { +=09/* # of available physical jacks */ +=09__le32 jacks; +=09/* # of available PCM streams */ +=09__le32 streams; +=09/* # of available channel maps */ +=09__le32 chmaps; +}; + +enum { +=09/* device virtqueue indexes */ +=09VIRTIO_SND_VQ_CONTROL =3D 0, +=09VIRTIO_SND_VQ_EVENT, +=09VIRTIO_SND_VQ_TX, +=09VIRTIO_SND_VQ_RX, +=09/* # of device virtqueues */ +=09VIRTIO_SND_VQ_MAX +}; + +/*************************************************************************= ****** + * COMMON DEFINITIONS + */ + +/* supported dataflow directions */ +enum { +=09VIRTIO_SND_D_OUTPUT =3D 0, +=09VIRTIO_SND_D_INPUT +}; + +enum { +=09/* jack control request types */ +=09VIRTIO_SND_R_JACK_INFO =3D 1, +=09VIRTIO_SND_R_JACK_REMAP, + +=09/* PCM control request types */ +=09VIRTIO_SND_R_PCM_INFO =3D 0x0100, +=09VIRTIO_SND_R_PCM_SET_PARAMS, +=09VIRTIO_SND_R_PCM_PREPARE, +=09VIRTIO_SND_R_PCM_RELEASE, +=09VIRTIO_SND_R_PCM_START, +=09VIRTIO_SND_R_PCM_STOP, + +=09/* channel map control request types */ +=09VIRTIO_SND_R_CHMAP_INFO =3D 0x0200, + +=09/* jack event types */ +=09VIRTIO_SND_EVT_JACK_CONNECTED =3D 0x1000, +=09VIRTIO_SND_EVT_JACK_DISCONNECTED, + +=09/* PCM event types */ +=09VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED =3D 0x1100, +=09VIRTIO_SND_EVT_PCM_XRUN, + +=09/* common status codes */ +=09VIRTIO_SND_S_OK =3D 0x8000, +=09VIRTIO_SND_S_BAD_MSG, +=09VIRTIO_SND_S_NOT_SUPP, +=09VIRTIO_SND_S_IO_ERR +}; + +/* common header */ +struct virtio_snd_hdr { +=09__le32 code; +}; + +/* event notification */ +struct virtio_snd_event { +=09/* VIRTIO_SND_EVT_XXX */ +=09struct virtio_snd_hdr hdr; +=09/* optional event data */ +=09__le32 data; +}; + +/* common control request to query an item information */ +struct virtio_snd_query_info { +=09/* VIRTIO_SND_R_XXX_INFO */ +=09struct virtio_snd_hdr hdr; +=09/* item start identifier */ +=09__le32 start_id; +=09/* item count to query */ +=09__le32 count; +=09/* item information size in bytes */ +=09__le32 size; +}; + +/* common item information header */ +struct virtio_snd_info { +=09/* function group node id (High Definition Audio Specification 7.1.2) *= / +=09__le32 hda_fn_nid; +}; + +/*************************************************************************= ****** + * JACK CONTROL MESSAGES + */ +struct virtio_snd_jack_hdr { +=09/* VIRTIO_SND_R_JACK_XXX */ +=09struct virtio_snd_hdr hdr; +=09/* 0 ... virtio_snd_config::jacks - 1 */ +=09__le32 jack_id; +}; + +/* supported jack features */ +enum { +=09VIRTIO_SND_JACK_F_REMAP =3D 0 +}; + +struct virtio_snd_jack_info { +=09/* common header */ +=09struct virtio_snd_info hdr; +=09/* supported feature bit map (1 << VIRTIO_SND_JACK_F_XXX) */ +=09__le32 features; +=09/* pin configuration (High Definition Audio Specification 7.3.3.31) */ +=09__le32 hda_reg_defconf; +=09/* pin capabilities (High Definition Audio Specification 7.3.4.9) */ +=09__le32 hda_reg_caps; +=09/* current jack connection status (0: disconnected, 1: connected) */ +=09__u8 connected; + +=09__u8 padding[7]; +}; + +/* jack remapping control request */ +struct virtio_snd_jack_remap { +=09/* .code =3D VIRTIO_SND_R_JACK_REMAP */ +=09struct virtio_snd_jack_hdr hdr; +=09/* selected association number */ +=09__le32 association; +=09/* selected sequence number */ +=09__le32 sequence; +}; + +/*************************************************************************= ****** + * PCM CONTROL MESSAGES + */ +struct virtio_snd_pcm_hdr { +=09/* VIRTIO_SND_R_PCM_XXX */ +=09struct virtio_snd_hdr hdr; +=09/* 0 ... virtio_snd_config::streams - 1 */ +=09__le32 stream_id; +}; + +/* supported PCM stream features */ +enum { +=09VIRTIO_SND_PCM_F_SHMEM_HOST =3D 0, +=09VIRTIO_SND_PCM_F_SHMEM_GUEST, +=09VIRTIO_SND_PCM_F_MSG_POLLING, +=09VIRTIO_SND_PCM_F_EVT_SHMEM_PERIODS, +=09VIRTIO_SND_PCM_F_EVT_XRUNS +}; + +/* supported PCM sample formats */ +enum { +=09/* analog formats (width / physical width) */ +=09VIRTIO_SND_PCM_FMT_IMA_ADPCM =3D 0,=09/* 4 / 4 bits */ +=09VIRTIO_SND_PCM_FMT_MU_LAW,=09=09/* 8 / 8 bits */ +=09VIRTIO_SND_PCM_FMT_A_LAW,=09=09/* 8 / 8 bits */ +=09VIRTIO_SND_PCM_FMT_S8,=09=09=09/* 8 / 8 bits */ +=09VIRTIO_SND_PCM_FMT_U8,=09=09=09/* 8 / 8 bits */ +=09VIRTIO_SND_PCM_FMT_S16,=09=09=09/* 16 / 16 bits */ +=09VIRTIO_SND_PCM_FMT_U16,=09=09=09/* 16 / 16 bits */ +=09VIRTIO_SND_PCM_FMT_S18_3,=09=09/* 18 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_U18_3,=09=09/* 18 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_S20_3,=09=09/* 20 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_U20_3,=09=09/* 20 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_S24_3,=09=09/* 24 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_U24_3,=09=09/* 24 / 24 bits */ +=09VIRTIO_SND_PCM_FMT_S20,=09=09=09/* 20 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_U20,=09=09=09/* 20 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_S24,=09=09=09/* 24 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_U24,=09=09=09/* 24 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_S32,=09=09=09/* 32 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_U32,=09=09=09/* 32 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_FLOAT,=09=09/* 32 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_FLOAT64,=09=09/* 64 / 64 bits */ +=09/* digital formats (width / physical width) */ +=09VIRTIO_SND_PCM_FMT_DSD_U8,=09=09/* 8 / 8 bits */ +=09VIRTIO_SND_PCM_FMT_DSD_U16,=09=09/* 16 / 16 bits */ +=09VIRTIO_SND_PCM_FMT_DSD_U32,=09=09/* 32 / 32 bits */ +=09VIRTIO_SND_PCM_FMT_IEC958_SUBFRAME=09/* 32 / 32 bits */ +}; + +/* supported PCM frame rates */ +enum { +=09VIRTIO_SND_PCM_RATE_5512 =3D 0, +=09VIRTIO_SND_PCM_RATE_8000, +=09VIRTIO_SND_PCM_RATE_11025, +=09VIRTIO_SND_PCM_RATE_16000, +=09VIRTIO_SND_PCM_RATE_22050, +=09VIRTIO_SND_PCM_RATE_32000, +=09VIRTIO_SND_PCM_RATE_44100, +=09VIRTIO_SND_PCM_RATE_48000, +=09VIRTIO_SND_PCM_RATE_64000, +=09VIRTIO_SND_PCM_RATE_88200, +=09VIRTIO_SND_PCM_RATE_96000, +=09VIRTIO_SND_PCM_RATE_176400, +=09VIRTIO_SND_PCM_RATE_192000, +=09VIRTIO_SND_PCM_RATE_384000 +}; + +struct virtio_snd_pcm_info { +=09/* common header */ +=09struct virtio_snd_info hdr; +=09/* supported feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ +=09__le32 features; +=09/* supported sample format bit map (1 << VIRTIO_SND_PCM_FMT_XXX) */ +=09__le64 formats; +=09/* supported frame rate bit map (1 << VIRTIO_SND_PCM_RATE_XXX) */ +=09__le64 rates; +=09/* dataflow direction (VIRTIO_SND_D_XXX) */ +=09__u8 direction; +=09/* minimum # of supported channels */ +=09__u8 channels_min; +=09/* maximum # of supported channels */ +=09__u8 channels_max; + +=09__u8 padding[5]; +}; + +/* set PCM stream format */ +struct virtio_snd_pcm_set_params { +=09/* .code =3D VIRTIO_SND_R_PCM_SET_PARAMS */ +=09struct virtio_snd_pcm_hdr hdr; +=09/* size of the hardware buffer */ +=09__le32 buffer_bytes; +=09/* size of the hardware period */ +=09__le32 period_bytes; +=09/* selected feature bit map (1 << VIRTIO_SND_PCM_F_XXX) */ +=09__le32 features; +=09/* selected # of channels */ +=09__u8 channels; +=09/* selected sample format (VIRTIO_SND_PCM_FMT_XXX) */ +=09__u8 format; +=09/* selected frame rate (VIRTIO_SND_PCM_RATE_XXX) */ +=09__u8 rate; + +=09__u8 padding; +}; + +/*************************************************************************= ****** + * PCM I/O MESSAGES + */ + +/* I/O request header */ +struct virtio_snd_pcm_xfer { +=09/* 0 ... virtio_snd_config::streams - 1 */ +=09__le32 stream_id; +}; + +/* I/O request status */ +struct virtio_snd_pcm_status { +=09/* VIRTIO_SND_S_XXX */ +=09__le32 status; +=09/* current device latency */ +=09__le32 latency_bytes; +}; + +/*************************************************************************= ****** + * CHANNEL MAP CONTROL MESSAGES + */ +struct virtio_snd_chmap_hdr { +=09/* VIRTIO_SND_R_CHMAP_XXX */ +=09struct virtio_snd_hdr hdr; +=09/* 0 ... virtio_snd_config::chmaps - 1 */ +=09__le32 chmap_id; +}; + +/* standard channel position definition */ +enum { +=09VIRTIO_SND_CHMAP_NONE =3D 0,=09/* undefined */ +=09VIRTIO_SND_CHMAP_NA,=09=09/* silent */ +=09VIRTIO_SND_CHMAP_MONO,=09=09/* mono stream */ +=09VIRTIO_SND_CHMAP_FL,=09=09/* front left */ +=09VIRTIO_SND_CHMAP_FR,=09=09/* front right */ +=09VIRTIO_SND_CHMAP_RL,=09=09/* rear left */ +=09VIRTIO_SND_CHMAP_RR,=09=09/* rear right */ +=09VIRTIO_SND_CHMAP_FC,=09=09/* front center */ +=09VIRTIO_SND_CHMAP_LFE,=09=09/* low frequency (LFE) */ +=09VIRTIO_SND_CHMAP_SL,=09=09/* side left */ +=09VIRTIO_SND_CHMAP_SR,=09=09/* side right */ +=09VIRTIO_SND_CHMAP_RC,=09=09/* rear center */ +=09VIRTIO_SND_CHMAP_FLC,=09=09/* front left center */ +=09VIRTIO_SND_CHMAP_FRC,=09=09/* front right center */ +=09VIRTIO_SND_CHMAP_RLC,=09=09/* rear left center */ +=09VIRTIO_SND_CHMAP_RRC,=09=09/* rear right center */ +=09VIRTIO_SND_CHMAP_FLW,=09=09/* front left wide */ +=09VIRTIO_SND_CHMAP_FRW,=09=09/* front right wide */ +=09VIRTIO_SND_CHMAP_FLH,=09=09/* front left high */ +=09VIRTIO_SND_CHMAP_FCH,=09=09/* front center high */ +=09VIRTIO_SND_CHMAP_FRH,=09=09/* front right high */ +=09VIRTIO_SND_CHMAP_TC,=09=09/* top center */ +=09VIRTIO_SND_CHMAP_TFL,=09=09/* top front left */ +=09VIRTIO_SND_CHMAP_TFR,=09=09/* top front right */ +=09VIRTIO_SND_CHMAP_TFC,=09=09/* top front center */ +=09VIRTIO_SND_CHMAP_TRL,=09=09/* top rear left */ +=09VIRTIO_SND_CHMAP_TRR,=09=09/* top rear right */ +=09VIRTIO_SND_CHMAP_TRC,=09=09/* top rear center */ +=09VIRTIO_SND_CHMAP_TFLC,=09=09/* top front left center */ +=09VIRTIO_SND_CHMAP_TFRC,=09=09/* top front right center */ +=09VIRTIO_SND_CHMAP_TSL,=09=09/* top side left */ +=09VIRTIO_SND_CHMAP_TSR,=09=09/* top side right */ +=09VIRTIO_SND_CHMAP_LLFE,=09=09/* left LFE */ +=09VIRTIO_SND_CHMAP_RLFE,=09=09/* right LFE */ +=09VIRTIO_SND_CHMAP_BC,=09=09/* bottom center */ +=09VIRTIO_SND_CHMAP_BLC,=09=09/* bottom left center */ +=09VIRTIO_SND_CHMAP_BRC=09=09/* bottom right center */ +}; + +/* maximum possible number of channels */ +#define VIRTIO_SND_CHMAP_MAX_SIZE=0918 + +struct virtio_snd_chmap_info { +=09/* common header */ +=09struct virtio_snd_info hdr; +=09/* dataflow direction (VIRTIO_SND_D_XXX) */ +=09__u8 direction; +=09/* # of valid channel position values */ +=09__u8 channels; +=09/* channel position values (VIRTIO_SND_CHMAP_XXX) */ +=09__u8 positions[VIRTIO_SND_CHMAP_MAX_SIZE]; +}; + +#endif /* VIRTIO_SND_IF_H */ diff --git a/sound/Kconfig b/sound/Kconfig index 36785410fbe1..e56d96d2b11c 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -99,6 +99,8 @@ source "sound/synth/Kconfig" =20 source "sound/xen/Kconfig" =20 +source "sound/virtio/Kconfig" + endif # SND =20 endif # !UML diff --git a/sound/Makefile b/sound/Makefile index 797ecdcd35e2..04ef04b1168f 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_SOUND) +=3D soundcore.o obj-$(CONFIG_DMASOUND) +=3D oss/dmasound/ obj-$(CONFIG_SND) +=3D core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ = usb/ \ -=09firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ +=09firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ = \ +=09virtio/ obj-$(CONFIG_SND_AOA) +=3D aoa/ =20 # This one must be compilable even if sound is configured out diff --git a/sound/virtio/Kconfig b/sound/virtio/Kconfig new file mode 100644 index 000000000000..094cba24ee5b --- /dev/null +++ b/sound/virtio/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Sound card driver for virtio + +config SND_VIRTIO +=09tristate "Virtio sound driver" +=09depends on VIRTIO +=09select SND_PCM +=09select SND_JACK +=09help + This is the virtual sound driver for virtio. Say Y or M. diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile new file mode 100644 index 000000000000..8c87ebb9982b --- /dev/null +++ b/sound/virtio/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ + +obj-$(CONFIG_SND_VIRTIO) +=3D virtio_snd.o + +virtio_snd-objs :=3D \ +=09virtio_card.o + diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c new file mode 100644 index 000000000000..532d823fdf6f --- /dev/null +++ b/sound/virtio/virtio_card.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#include +#include +#include +#include +#include + +#include "virtio_card.h" + +static void virtsnd_remove(struct virtio_device *vdev); + +/** + * virtsnd_event_send() - Add an event to the event queue. + * @vqueue: Underlying event virtqueue. + * @event: Event. + * @notify: Indicates whether or not to send a notification to the device. + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_event_send(struct virtqueue *vqueue, +=09=09=09 struct virtio_snd_event *event, bool notify, +=09=09=09 gfp_t gfp) +{ +=09struct scatterlist sg; +=09struct scatterlist *psgs[1] =3D { &sg }; +=09int rc; + +=09/* reset event content */ +=09memset(event, 0, sizeof(*event)); + +=09sg_init_one(&sg, event, sizeof(*event)); + +=09rc =3D virtqueue_add_sgs(vqueue, psgs, 0, 1, event, gfp); +=09if (rc) +=09=09return rc; + +=09if (notify) +=09=09if (virtqueue_kick_prepare(vqueue)) +=09=09=09if (!virtqueue_notify(vqueue)) +=09=09=09=09return -EIO; + +=09return 0; +} + +/** + * virtsnd_event_notify_cb() - Dispatch all reported events from the event= queue. + * @vqueue: Underlying event virtqueue. + * + * This callback function is called upon a vring interrupt request from th= e + * device. + * + * Context: Interrupt context. + */ +static void virtsnd_event_notify_cb(struct virtqueue *vqueue) +{ +=09struct virtio_snd *snd =3D vqueue->vdev->priv; +=09struct virtio_snd_queue *queue =3D virtsnd_event_queue(snd); +=09unsigned long flags; + +=09spin_lock_irqsave(&queue->lock, flags); +=09while (queue->vqueue) { +=09=09virtqueue_disable_cb(queue->vqueue); + +=09=09for (;;) { +=09=09=09struct virtio_snd_event *event; +=09=09=09u32 length; + +=09=09=09event =3D virtqueue_get_buf(queue->vqueue, &length); +=09=09=09if (!event) +=09=09=09=09break; + +=09=09=09virtsnd_event_send(queue->vqueue, event, true, +=09=09=09=09=09 GFP_ATOMIC); +=09=09} + +=09=09if (unlikely(virtqueue_is_broken(queue->vqueue))) +=09=09=09break; + +=09=09if (virtqueue_enable_cb(queue->vqueue)) +=09=09=09break; +=09} +=09spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_find_vqs() - Enumerate and initialize all virtqueues. + * @snd: VirtIO sound device. + * + * After calling this function, the event queue is disabled. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_find_vqs(struct virtio_snd *snd) +{ +=09struct virtio_device *vdev =3D snd->vdev; +=09vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] =3D { 0 }; +=09const char *names[VIRTIO_SND_VQ_MAX] =3D { +=09=09[VIRTIO_SND_VQ_CONTROL] =3D "virtsnd-ctl", +=09=09[VIRTIO_SND_VQ_EVENT] =3D "virtsnd-event", +=09=09[VIRTIO_SND_VQ_TX] =3D "virtsnd-tx", +=09=09[VIRTIO_SND_VQ_RX] =3D "virtsnd-rx" +=09}; +=09struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] =3D { 0 }; +=09unsigned int i; +=09unsigned int n =3D 0; +=09int rc; + +=09callbacks[VIRTIO_SND_VQ_EVENT] =3D virtsnd_event_notify_cb; + +=09rc =3D virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, +=09=09=09 NULL); +=09if (rc) { +=09=09dev_err(&vdev->dev, "failed to initialize virtqueues\n"); +=09=09return rc; +=09} + +=09for (i =3D 0; i < VIRTIO_SND_VQ_MAX; ++i) +=09=09snd->queues[i].vqueue =3D vqs[i]; + +=09/* Allocate events and populate the event queue */ +=09virtqueue_disable_cb(vqs[VIRTIO_SND_VQ_EVENT]); + +=09n =3D virtqueue_get_vring_size(vqs[VIRTIO_SND_VQ_EVENT]); + +=09snd->event_msgs =3D devm_kcalloc(&vdev->dev, n, sizeof(*snd->event_msgs= ), +=09=09=09=09 GFP_KERNEL); +=09if (!snd->event_msgs) +=09=09return -ENOMEM; + +=09for (i =3D 0; i < n; ++i) { +=09=09rc =3D virtsnd_event_send(vqs[VIRTIO_SND_VQ_EVENT], +=09=09=09=09=09&snd->event_msgs[i], false, GFP_KERNEL); +=09=09if (rc) +=09=09=09return rc; +=09} + +=09return 0; +} + +/** + * virtsnd_enable_event_vq() - Enable the event virtqueue. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +static void virtsnd_enable_event_vq(struct virtio_snd *snd) +{ +=09struct virtio_snd_queue *queue =3D virtsnd_event_queue(snd); + +=09if (!virtqueue_enable_cb(queue->vqueue)) +=09=09virtsnd_event_notify_cb(queue->vqueue); +} + +/** + * virtsnd_disable_vqs() - Disable all virtqueues. + * @snd: VirtIO sound device. + * + * Also free all allocated events and control messages. + * + * Context: Any context. + */ +static void virtsnd_disable_vqs(struct virtio_snd *snd) +{ +=09struct virtio_device *vdev =3D snd->vdev; +=09unsigned int i; +=09unsigned long flags; + +=09for (i =3D 0; i < VIRTIO_SND_VQ_MAX; ++i) { +=09=09struct virtio_snd_queue *queue =3D &snd->queues[i]; + +=09=09spin_lock_irqsave(&queue->lock, flags); +=09=09/* Prohibit the use of the queue */ +=09=09if (queue->vqueue) +=09=09=09virtqueue_disable_cb(queue->vqueue); +=09=09queue->vqueue =3D NULL; +=09=09spin_unlock_irqrestore(&queue->lock, flags); +=09} + +=09if (snd->event_msgs) +=09=09devm_kfree(&vdev->dev, snd->event_msgs); + +=09snd->event_msgs =3D NULL; +} + +/** + * virtsnd_reset_fn() - Kernel worker's function to reset the device. + * @work: Reset device work. + * + * Context: Process context. + */ +static void virtsnd_reset_fn(struct work_struct *work) +{ +=09struct virtio_snd *snd =3D +=09=09container_of(work, struct virtio_snd, reset_work); +=09struct virtio_device *vdev =3D snd->vdev; +=09struct device *dev =3D &vdev->dev; +=09int rc; + +=09dev_info(dev, "sound device needs reset\n"); + +=09/* +=09 * It seems that the only way to properly reset the device is to remove +=09 * and re-create the ALSA sound card device. +=09 * +=09 * Also resetting the device involves a number of steps with setting th= e +=09 * status bits described in the virtio specification. And the easiest +=09 * way to get everything right is to use the virtio bus interface. +=09 */ +=09rc =3D dev->bus->remove(dev); +=09if (rc) +=09=09dev_warn(dev, "bus->remove() failed: %d", rc); + +=09rc =3D dev->bus->probe(dev); +=09if (rc) +=09=09dev_err(dev, "bus->probe() failed: %d", rc); +} + +/** + * virtsnd_build_devs() - Read configuration and build ALSA devices. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_build_devs(struct virtio_snd *snd) +{ +=09struct virtio_device *vdev =3D snd->vdev; +=09int rc; + +=09rc =3D snd_card_new(&vdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, +=09=09=09 THIS_MODULE, 0, &snd->card); +=09if (rc < 0) +=09=09return rc; + +=09snd->card->private_data =3D snd; + +=09strscpy(snd->card->id, "viosnd", sizeof(snd->card->id)); +=09strscpy(snd->card->driver, "virtio_snd", sizeof(snd->card->driver)); +=09strscpy(snd->card->shortname, "VIOSND", sizeof(snd->card->shortname)); +=09strscpy(snd->card->longname, "VirtIO Sound Card", +=09=09sizeof(snd->card->longname)); + +=09return snd_card_register(snd->card); +} + +/** + * virtsnd_validate() - Validate if the device can be started. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -EINVAL on failure. + */ +static int virtsnd_validate(struct virtio_device *vdev) +{ +=09if (!vdev->config->get) { +=09=09dev_err(&vdev->dev, "configuration access disabled\n"); +=09=09return -EINVAL; +=09} + +=09if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { +=09=09dev_err(&vdev->dev, +=09=09=09"device does not comply with spec version 1.x\n"); +=09=09return -EINVAL; +=09} + +=09return 0; +} + +/** + * virtsnd_probe() - Create and initialize the device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_probe(struct virtio_device *vdev) +{ +=09struct virtio_snd *snd; +=09unsigned int i; +=09int rc; + +=09snd =3D devm_kzalloc(&vdev->dev, sizeof(*snd), GFP_KERNEL); +=09if (!snd) +=09=09return -ENOMEM; + +=09snd->vdev =3D vdev; +=09INIT_WORK(&snd->reset_work, virtsnd_reset_fn); + +=09vdev->priv =3D snd; + +=09for (i =3D 0; i < VIRTIO_SND_VQ_MAX; ++i) +=09=09spin_lock_init(&snd->queues[i].lock); + +=09rc =3D virtsnd_find_vqs(snd); +=09if (rc) +=09=09goto on_failure; + +=09virtio_device_ready(vdev); + +=09rc =3D virtsnd_build_devs(snd); +=09if (rc) +=09=09goto on_failure; + +=09virtsnd_enable_event_vq(snd); + +on_failure: +=09if (rc) +=09=09virtsnd_remove(vdev); + +=09return rc; +} + +/** + * virtsnd_remove() - Remove VirtIO and ALSA devices. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + */ +static void virtsnd_remove(struct virtio_device *vdev) +{ +=09struct virtio_snd *snd =3D vdev->priv; + +=09if (!snd) +=09=09return; + +=09/* +=09 * Make sure no one is accessing the virtqueues and sending synchronous +=09 * requests to the device. This can happen if we got here because the +=09 * device needs to be reset. +=09 */ +=09virtsnd_disable_vqs(snd); + +=09if (snd->card) +=09=09snd_card_free(snd->card); + +=09vdev->config->reset(vdev); +=09vdev->config->del_vqs(vdev); + +=09devm_kfree(&vdev->dev, snd); + +=09vdev->priv =3D NULL; +} + +/** + * virtsnd_config_changed() - Handle configuration change notification. + * @vdev: VirtIO parent device. + * + * This callback function is called upon a configuration change interrupt + * request from the device. Currently only used to handle NEEDS_RESET devi= ce + * status. + * + * Context: Interrupt context. + */ +static void virtsnd_config_changed(struct virtio_device *vdev) +{ +=09struct virtio_snd *snd =3D vdev->priv; +=09unsigned int status =3D vdev->config->get_status(vdev); + +=09if (status & VIRTIO_CONFIG_S_NEEDS_RESET) +=09=09schedule_work(&snd->reset_work); +=09else +=09=09dev_warn(&vdev->dev, +=09=09=09 "sound device configuration was changed\n"); +} + +static const struct virtio_device_id id_table[] =3D { +=09{ VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, +=09{ 0 }, +}; + +static struct virtio_driver virtsnd_driver =3D { +=09.driver.name =3D KBUILD_MODNAME, +=09.driver.owner =3D THIS_MODULE, +=09.id_table =3D id_table, +=09.validate =3D virtsnd_validate, +=09.probe =3D virtsnd_probe, +=09.remove =3D virtsnd_remove, +=09.config_changed =3D virtsnd_config_changed, +}; + +static int __init init(void) +{ +=09return register_virtio_driver(&virtsnd_driver); +} +module_init(init); + +static void __exit fini(void) +{ +=09unregister_virtio_driver(&virtsnd_driver); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio sound card driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h new file mode 100644 index 000000000000..10084abaaf18 --- /dev/null +++ b/sound/virtio/virtio_card.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sound card driver for virtio + * Copyright (C) 2020 OpenSynergy GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ +#ifndef VIRTIO_SND_CARD_H +#define VIRTIO_SND_CARD_H + +#include +#include +#include + +/** + * struct virtio_snd_queue - Virtqueue wrapper structure. + * @lock: Used to synchronize access to a virtqueue. + * @vqueue: Underlying virtqueue. + */ +struct virtio_snd_queue { +=09spinlock_t lock; +=09struct virtqueue *vqueue; +}; + +/** + * struct virtio_snd - VirtIO sound card device. + * @vdev: Underlying virtio device. + * @queues: Virtqueue wrappers. + * @reset_work: Reset device work. + * @card: ALSA sound card. + * @event_msgs: Device events. + */ +struct virtio_snd { +=09struct virtio_device *vdev; +=09struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; +=09struct work_struct reset_work; +=09struct snd_card *card; +=09struct virtio_snd_event *event_msgs; +}; + +static inline struct virtio_snd_queue * +virtsnd_control_queue(struct virtio_snd *snd) +{ +=09return &snd->queues[VIRTIO_SND_VQ_CONTROL]; +} + +static inline struct virtio_snd_queue * +virtsnd_event_queue(struct virtio_snd *snd) +{ +=09return &snd->queues[VIRTIO_SND_VQ_EVENT]; +} + +static inline struct virtio_snd_queue * +virtsnd_tx_queue(struct virtio_snd *snd) +{ +=09return &snd->queues[VIRTIO_SND_VQ_TX]; +} + +static inline struct virtio_snd_queue * +virtsnd_rx_queue(struct virtio_snd *snd) +{ +=09return &snd->queues[VIRTIO_SND_VQ_RX]; +} + +#endif /* VIRTIO_SND_CARD_H */ --=20 2.30.0 --------------------------------------------------------------------- To unsubscribe, e-mail: virtio-dev-unsubscribe@lists.oasis-open.org For additional commands, e-mail: virtio-dev-help@lists.oasis-open.org