From patchwork Sat Feb 27 08:59:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 388167 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.7 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_BLOCKED, 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 D3D41C433E0 for ; Sat, 27 Feb 2021 09:02:06 +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 CB4DB64EDB for ; Sat, 27 Feb 2021 09:02:05 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org CB4DB64EDB 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 5447916A1; Sat, 27 Feb 2021 10:01:14 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5447916A1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614416524; bh=ynYxj0YE6Zj4GR6AwZfMhhKycdTyQsOJhTolatQ+on0=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=X6gdihwy3SAps1eqoEv/U0BJt7U7U2lqcoHGP1Ne1aAHeJUFdlAQyTHCiH/LBkAoU tSzc9ZvriLECTxcEOypL+zd6xGAguKD9mHmHFqi9X6IaQ8W+AUW+5OHtck+aLLQ2Ay JoJG8gTo2ctaW5UFxlsrIAesGApbnVm0l4+7ATy8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 13A66F8016A; Sat, 27 Feb 2021 10:00:46 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9DF74F80245; Sat, 27 Feb 2021 10:00:21 +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 17B3BF80169 for ; Sat, 27 Feb 2021 10:00:13 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 17B3BF80169 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="lx2Ug7ay" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 1142FA123F; Sat, 27 Feb 2021 10:00:13 +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=H/IqaTbkOtxU zfKUTf6SoEir9ZqTvVT/Ba9mrKSHQWE=; b=lx2Ug7ay6rfrNixN+QUPwHbATjpu UbkwNOBaf49EX3Df1yDld4pBmpkkNQQaR/XVKKSL+/7p3ai4LtmeJWMLb+1qZobI WQ5PDUT8Ao11tlxFS5qfRLb071j6BNoEKb3iR+UwfMEPqvKOcHLGNP952YjW5ukH uRhvNcKXxW6G69R05PkKF8sWFjO18/yrUppS6TZBZ49jy7sNyZSxuAIUY6FwR3Ki M3gQ6AusjCBdjolPvn0aXE5CVaNalRfetpAK28QW0v95pwpHCWHu/I4t/xZ/fGqd hnVvQuRp5YHg3IJ6Hhs5btq7FVrHC0b5tHsUAUlRVkVAL8EIzXBb7TmTWw== From: Anton Yakovlev To: , , Subject: [PATCH v6 1/9] uapi: virtio_ids: add a sound device type ID from OASIS spec Date: Sat, 27 Feb 2021 09:59:48 +0100 Message-ID: <20210227085956.1700687-2-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.1 In-Reply-To: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> References: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-01.open-synergy.com (10.26.10.21) 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" The OASIS virtio spec defines a sound device type ID that is not present in the header yet. Signed-off-by: Anton Yakovlev --- include/uapi/linux/virtio_ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h index bc1c0621f5ed..029a2e07a7f9 100644 --- a/include/uapi/linux/virtio_ids.h +++ b/include/uapi/linux/virtio_ids.h @@ -51,6 +51,7 @@ #define VIRTIO_ID_PSTORE 22 /* virtio pstore device */ #define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */ #define VIRTIO_ID_MEM 24 /* virtio mem */ +#define VIRTIO_ID_SOUND 25 /* virtio sound */ #define VIRTIO_ID_FS 26 /* virtio filesystem */ #define VIRTIO_ID_PMEM 27 /* virtio pmem */ #define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */ From patchwork Sat Feb 27 08:59:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 388166 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,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 0C5F3C433DB for ; Sat, 27 Feb 2021 09:02:51 +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 CDE4E64EC0 for ; Sat, 27 Feb 2021 09:02:49 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org CDE4E64EC0 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 F19B416B0; Sat, 27 Feb 2021 10:01:57 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz F19B416B0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614416568; bh=apYjw+SM5a1XpuDeaEidyfe/LBuoxIM6IoT67Anowjs=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=NE3ccRqeODlIFZw4xaKpO+NONUuXoCiaep4dh2oADthoSaONw4txCJAufXbaFMjvq M2a8Z89cqQwx8ykZALAvl3nVgbtnMy5dd0Rd35ALherfF0b/Y6Ey8NJ4bRVacF2AoZ 4kZ/TpqVyJYMIOrDQCAv2/oA8DvCt43e6m8NK4hk= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id ABD9FF802DF; Sat, 27 Feb 2021 10:00:47 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 44BF7F8025C; Sat, 27 Feb 2021 10:00:27 +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 D6BE8F80159 for ; Sat, 27 Feb 2021 10:00:18 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz D6BE8F80159 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="BRMKjVyE" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 770B5A12FF; Sat, 27 Feb 2021 10:00:18 +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=Ut/hhJVa2BHa H15+mJQEpmPOhB+KovkMiJXUvE/dqG4=; b=BRMKjVyE9rshSEa078PRQhaREr0g tckMJRDRZyPkZeE9Gb3mN+23IWggKfOxIEBsE7EUor0al3hFSfUyJ635/VXwu11M xaeGq97LIZmaczWvrZVf2LpJpiow+R9kMvFzk0JOrzN9HP7BawDZMEN2eIuy9YHA COoVQRdQ+J9D14m7wgrcSSKyYpyim5h2MZildEe0WCZ7thRqdBrEanrJX4tIm9bS fkLC6ukqLZCJsE+Sbs/LXoNnIPm3mBZvNg4aHTQaIqLIvsB9Ly2XxcGQxEiLpajQ /r0yRhHVdjxZgls5shyD1vzfvijJIg0WtZiYyd5gqoEJWNP5txHCu2XnoA== From: Anton Yakovlev To: , , Subject: [PATCH v6 3/9] ALSA: virtio: handling control messages Date: Sat, 27 Feb 2021 09:59:50 +0100 Message-ID: <20210227085956.1700687-4-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.1 In-Reply-To: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> References: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-01.open-synergy.com (10.26.10.21) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, 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" The control queue can be used by different parts of the driver to send commands to the device. Control messages can be either synchronous or asynchronous. The lifetime of a message is controlled by a reference count. Introduce a module parameter to set the message completion timeout: msg_timeout_ms [=1000] Signed-off-by: Anton Yakovlev --- sound/virtio/Makefile | 3 +- sound/virtio/virtio_card.c | 13 ++ sound/virtio/virtio_card.h | 7 + sound/virtio/virtio_ctl_msg.c | 310 ++++++++++++++++++++++++++++++++++ sound/virtio/virtio_ctl_msg.h | 78 +++++++++ 5 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 sound/virtio/virtio_ctl_msg.c create mode 100644 sound/virtio/virtio_ctl_msg.h diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile index 8c87ebb9982b..dc551e637441 100644 --- a/sound/virtio/Makefile +++ b/sound/virtio/Makefile @@ -3,5 +3,6 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o virtio_snd-objs := \ - virtio_card.o + virtio_card.o \ + virtio_ctl_msg.o diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index 697cb5f16e4d..196cb97087b0 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -11,6 +11,10 @@ #include "virtio_card.h" +int msg_timeout_ms = MSEC_PER_SEC; +module_param(msg_timeout_ms, int, 0644); +MODULE_PARM_DESC(msg_timeout_ms, "Message completion timeout in milliseconds"); + static void virtsnd_remove(struct virtio_device *vdev); /** @@ -82,6 +86,7 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) { struct virtio_device *vdev = snd->vdev; static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { + [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb, [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb }; static const char *names[VIRTIO_SND_VQ_MAX] = { @@ -193,6 +198,11 @@ static int virtsnd_validate(struct virtio_device *vdev) return -EINVAL; } + if (!msg_timeout_ms) { + dev_err(&vdev->dev, "msg_timeout_ms value cannot be zero\n"); + return -EINVAL; + } + return 0; } @@ -214,6 +224,7 @@ static int virtsnd_probe(struct virtio_device *vdev) return -ENOMEM; snd->vdev = vdev; + INIT_LIST_HEAD(&snd->ctl_msgs); vdev->priv = snd; @@ -249,6 +260,8 @@ static void virtsnd_remove(struct virtio_device *vdev) { struct virtio_snd *snd = vdev->priv; + virtsnd_ctl_msg_cancel_all(snd); + if (snd->card) snd_card_free(snd->card); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index b903b1b12e90..349311a30199 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -11,6 +11,8 @@ #include #include +#include "virtio_ctl_msg.h" + #define VIRTIO_SND_CARD_DRIVER "virtio-snd" #define VIRTIO_SND_CARD_NAME "VirtIO SoundCard" @@ -29,15 +31,20 @@ struct virtio_snd_queue { * @vdev: Underlying virtio device. * @queues: Virtqueue wrappers. * @card: ALSA sound card. + * @ctl_msgs: Pending control request list. * @event_msgs: Device events. */ struct virtio_snd { struct virtio_device *vdev; struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; struct snd_card *card; + struct list_head ctl_msgs; struct virtio_snd_event *event_msgs; }; +/* Message completion timeout in milliseconds (module parameter). */ +extern int msg_timeout_ms; + static inline struct virtio_snd_queue * virtsnd_control_queue(struct virtio_snd *snd) { diff --git a/sound/virtio/virtio_ctl_msg.c b/sound/virtio/virtio_ctl_msg.c new file mode 100644 index 000000000000..67a5a0301ba3 --- /dev/null +++ b/sound/virtio/virtio_ctl_msg.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#include +#include + +#include "virtio_card.h" + +/** + * struct virtio_snd_msg - Control message. + * @sg_request: Scattergather list containing a device request (header). + * @sg_response: Scattergather list containing a device response (status). + * @list: Pending message list entry. + * @notify: Request completed notification. + * @ref_count: Reference count used to manage a message lifetime. + */ +struct virtio_snd_msg { + struct scatterlist sg_request; + struct scatterlist sg_response; + struct list_head list; + struct completion notify; + refcount_t ref_count; +}; + +/** + * virtsnd_ctl_msg_ref() - Increment reference counter for the message. + * @msg: Control message. + * + * Context: Any context. + */ +void virtsnd_ctl_msg_ref(struct virtio_snd_msg *msg) +{ + refcount_inc(&msg->ref_count); +} + +/** + * virtsnd_ctl_msg_unref() - Decrement reference counter for the message. + * @msg: Control message. + * + * The message will be freed when the ref_count value is 0. + * + * Context: Any context. + */ +void virtsnd_ctl_msg_unref(struct virtio_snd_msg *msg) +{ + if (refcount_dec_and_test(&msg->ref_count)) + kfree(msg); +} + +/** + * virtsnd_ctl_msg_request() - Get a pointer to the request header. + * @msg: Control message. + * + * Context: Any context. + */ +void *virtsnd_ctl_msg_request(struct virtio_snd_msg *msg) +{ + return sg_virt(&msg->sg_request); +} + +/** + * virtsnd_ctl_msg_request() - Get a pointer to the response header. + * @msg: Control message. + * + * Context: Any context. + */ +void *virtsnd_ctl_msg_response(struct virtio_snd_msg *msg) +{ + return sg_virt(&msg->sg_response); +} + +/** + * virtsnd_ctl_msg_alloc() - Allocate and initialize a control message. + * @request_size: Size of request header. + * @response_size: Size of response header. + * @gfp: Kernel flags for memory allocation. + * + * The message will be automatically freed when the ref_count value is 0. + * + * Context: Any context. May sleep if @gfp flags permit. + * Return: Allocated message on success, NULL on failure. + */ +struct virtio_snd_msg *virtsnd_ctl_msg_alloc(size_t request_size, + size_t response_size, gfp_t gfp) +{ + struct virtio_snd_msg *msg; + + if (!request_size || !response_size) + return NULL; + + msg = kzalloc(sizeof(*msg) + request_size + response_size, gfp); + if (!msg) + return NULL; + + sg_init_one(&msg->sg_request, (u8 *)msg + sizeof(*msg), request_size); + sg_init_one(&msg->sg_response, (u8 *)msg + sizeof(*msg) + request_size, + response_size); + + INIT_LIST_HEAD(&msg->list); + init_completion(&msg->notify); + /* This reference is dropped in virtsnd_ctl_msg_complete(). */ + refcount_set(&msg->ref_count, 1); + + return msg; +} + +/** + * virtsnd_ctl_msg_send() - Send a control message. + * @snd: VirtIO sound device. + * @msg: Control message. + * @out_sgs: Additional sg-list to attach to the request header (may be NULL). + * @in_sgs: Additional sg-list to attach to the response header (may be NULL). + * @nowait: Flag indicating whether to wait for completion. + * + * Context: Any context. Takes and releases the control queue spinlock. + * May sleep if @nowait is false. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_ctl_msg_send(struct virtio_snd *snd, struct virtio_snd_msg *msg, + struct scatterlist *out_sgs, + struct scatterlist *in_sgs, bool nowait) +{ + struct virtio_device *vdev = snd->vdev; + struct virtio_snd_queue *queue = virtsnd_control_queue(snd); + unsigned int js = msecs_to_jiffies(msg_timeout_ms); + struct virtio_snd_hdr *request = virtsnd_ctl_msg_request(msg); + struct virtio_snd_hdr *response = virtsnd_ctl_msg_response(msg); + unsigned int nouts = 0; + unsigned int nins = 0; + struct scatterlist *psgs[4]; + bool notify = false; + unsigned long flags; + int rc; + + virtsnd_ctl_msg_ref(msg); + + /* Set the default status in case the message was canceled. */ + response->code = cpu_to_le32(VIRTIO_SND_S_IO_ERR); + + psgs[nouts++] = &msg->sg_request; + if (out_sgs) + psgs[nouts++] = out_sgs; + + psgs[nouts + nins++] = &msg->sg_response; + if (in_sgs) + psgs[nouts + nins++] = in_sgs; + + spin_lock_irqsave(&queue->lock, flags); + rc = virtqueue_add_sgs(queue->vqueue, psgs, nouts, nins, msg, + GFP_ATOMIC); + if (!rc) { + notify = virtqueue_kick_prepare(queue->vqueue); + + list_add_tail(&msg->list, &snd->ctl_msgs); + } + spin_unlock_irqrestore(&queue->lock, flags); + + if (rc) { + dev_err(&vdev->dev, "failed to send control message (0x%08x)\n", + le32_to_cpu(request->code)); + + /* + * Since in this case virtsnd_ctl_msg_complete() will not be + * called, it is necessary to decrement the reference count. + */ + virtsnd_ctl_msg_unref(msg); + + goto on_exit; + } + + if (notify) + virtqueue_notify(queue->vqueue); + + if (nowait) + goto on_exit; + + rc = wait_for_completion_interruptible_timeout(&msg->notify, js); + if (rc <= 0) { + if (!rc) { + dev_err(&vdev->dev, + "control message (0x%08x) timeout\n", + le32_to_cpu(request->code)); + rc = -ETIMEDOUT; + } + + goto on_exit; + } + + switch (le32_to_cpu(response->code)) { + case VIRTIO_SND_S_OK: + rc = 0; + break; + case VIRTIO_SND_S_NOT_SUPP: + rc = -EOPNOTSUPP; + break; + case VIRTIO_SND_S_IO_ERR: + rc = -EIO; + break; + default: + rc = -EINVAL; + break; + } + +on_exit: + virtsnd_ctl_msg_unref(msg); + + return rc; +} + +/** + * virtsnd_ctl_msg_complete() - Complete a control message. + * @msg: Control message. + * + * Context: Any context. Expects the control queue spinlock to be held by + * caller. + */ +void virtsnd_ctl_msg_complete(struct virtio_snd_msg *msg) +{ + list_del(&msg->list); + complete(&msg->notify); + + virtsnd_ctl_msg_unref(msg); +} + +/** + * virtsnd_ctl_msg_cancel_all() - Cancel all pending control messages. + * @snd: VirtIO sound device. + * + * Context: Any context. + */ +void virtsnd_ctl_msg_cancel_all(struct virtio_snd *snd) +{ + struct virtio_snd_queue *queue = virtsnd_control_queue(snd); + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (!list_empty(&snd->ctl_msgs)) { + struct virtio_snd_msg *msg = + list_first_entry(&snd->ctl_msgs, struct virtio_snd_msg, + list); + + virtsnd_ctl_msg_complete(msg); + } + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_ctl_query_info() - Query the item configuration from the device. + * @snd: VirtIO sound device. + * @command: Control request code (VIRTIO_SND_R_XXX_INFO). + * @start_id: Item start identifier. + * @count: Item count to query. + * @size: Item information size in bytes. + * @info: Buffer for storing item information. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_ctl_query_info(struct virtio_snd *snd, int command, int start_id, + int count, size_t size, void *info) +{ + struct virtio_snd_msg *msg; + struct virtio_snd_query_info *query; + struct scatterlist sg; + + msg = virtsnd_ctl_msg_alloc(sizeof(*query), + sizeof(struct virtio_snd_hdr), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + query = virtsnd_ctl_msg_request(msg); + query->hdr.code = cpu_to_le32(command); + query->start_id = cpu_to_le32(start_id); + query->count = cpu_to_le32(count); + query->size = cpu_to_le32(size); + + sg_init_one(&sg, info, count * size); + + return virtsnd_ctl_msg_send(snd, msg, NULL, &sg, false); +} + +/** + * virtsnd_ctl_notify_cb() - Process all completed control messages. + * @vqueue: Underlying control virtqueue. + * + * This callback function is called upon a vring interrupt request from the + * device. + * + * Context: Interrupt context. Takes and releases the control queue spinlock. + */ +void virtsnd_ctl_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + struct virtio_snd_queue *queue = virtsnd_control_queue(snd); + struct virtio_snd_msg *msg; + u32 length; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + do { + virtqueue_disable_cb(vqueue); + while ((msg = virtqueue_get_buf(vqueue, &length))) + virtsnd_ctl_msg_complete(msg); + if (unlikely(virtqueue_is_broken(vqueue))) + break; + } while (!virtqueue_enable_cb(vqueue)); + spin_unlock_irqrestore(&queue->lock, flags); +} diff --git a/sound/virtio/virtio_ctl_msg.h b/sound/virtio/virtio_ctl_msg.h new file mode 100644 index 000000000000..7f4db044f28e --- /dev/null +++ b/sound/virtio/virtio_ctl_msg.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#ifndef VIRTIO_SND_MSG_H +#define VIRTIO_SND_MSG_H + +#include +#include + +struct virtio_snd; +struct virtio_snd_msg; + +void virtsnd_ctl_msg_ref(struct virtio_snd_msg *msg); + +void virtsnd_ctl_msg_unref(struct virtio_snd_msg *msg); + +void *virtsnd_ctl_msg_request(struct virtio_snd_msg *msg); + +void *virtsnd_ctl_msg_response(struct virtio_snd_msg *msg); + +struct virtio_snd_msg *virtsnd_ctl_msg_alloc(size_t request_size, + size_t response_size, gfp_t gfp); + +int virtsnd_ctl_msg_send(struct virtio_snd *snd, struct virtio_snd_msg *msg, + struct scatterlist *out_sgs, + struct scatterlist *in_sgs, bool nowait); + +/** + * virtsnd_ctl_msg_send_sync() - Simplified sending of synchronous message. + * @snd: VirtIO sound device. + * @msg: Control message. + * + * After returning from this function, the message will be deleted. If message + * content is still needed, the caller must additionally to + * virtsnd_ctl_msg_ref/unref() it. + * + * The msg_timeout_ms module parameter defines the message completion timeout. + * If the message is not completed within this time, the function will return an + * error. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + * + * The return value is a message status code (VIRTIO_SND_S_XXX) converted to an + * appropriate -errno value. + */ +static inline int virtsnd_ctl_msg_send_sync(struct virtio_snd *snd, + struct virtio_snd_msg *msg) +{ + return virtsnd_ctl_msg_send(snd, msg, NULL, NULL, false); +} + +/** + * virtsnd_ctl_msg_send_async() - Simplified sending of asynchronous message. + * @snd: VirtIO sound device. + * @msg: Control message. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static inline int virtsnd_ctl_msg_send_async(struct virtio_snd *snd, + struct virtio_snd_msg *msg) +{ + return virtsnd_ctl_msg_send(snd, msg, NULL, NULL, true); +} + +void virtsnd_ctl_msg_cancel_all(struct virtio_snd *snd); + +void virtsnd_ctl_msg_complete(struct virtio_snd_msg *msg); + +int virtsnd_ctl_query_info(struct virtio_snd *snd, int command, int start_id, + int count, size_t size, void *info); + +void virtsnd_ctl_notify_cb(struct virtqueue *vqueue); + +#endif /* VIRTIO_SND_MSG_H */ From patchwork Sat Feb 27 08:59:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 388165 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,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 6EB55C433E0 for ; Sat, 27 Feb 2021 09:03:37 +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 AB66C64DB2 for ; Sat, 27 Feb 2021 09:03:36 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org AB66C64DB2 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 3A39A16A8; Sat, 27 Feb 2021 10:02:45 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 3A39A16A8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614416615; bh=67pMHauNAFE3Fhkp/KJaGbyfc7Q3bIkfDbTkINmhdsk=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=bV9n62s9K/CIpIOD5hdiBv0kI3n96jNeuCg2y17ein4TtUahK8TMxcLsDrGQ3o3vQ 22nkV2cKXn+19TBEVm/ZqJDjzagPEvLeVYHLvB5OQkv3w3ddUbrfqhMenMp1dMfjtA S9CHtb9uHxnXKc+VoqQLpedkZrRxmIZQhBylB2iA= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id C358AF80423; Sat, 27 Feb 2021 10:00:49 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 7739BF8016A; Sat, 27 Feb 2021 10:00:29 +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 479FCF80159 for ; Sat, 27 Feb 2021 10:00:26 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 479FCF80159 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="Cd1hhxtf" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id DC2CEA1308; Sat, 27 Feb 2021 10:00:25 +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=MzrBekZG7OVA YN/3j7da/xWETsJmI/5nojTe9SXWTIE=; b=Cd1hhxtfkDWjjVPD6bq8KZYSlAYA 2vgepsmaWxYAeTa4xgLbX48DrAOjku6biof8vWjR5/9qY3BXJMSg+Dy5ugZU0g3U jE6wJ4i4ecBhimBJ6jT6/ddCbyDQU8eeJP6JJPm4E3Z0ena5VEPvyBDddpibzCze 9tH2oO2DAEde2O/EbwN/Gxh6SlIRwNMaKBtJqGOeCC4udr5GO647Q/Ke4QhT/NGR YIdRcGDSXI4mxKqCAIUY8QQ+uAQcJ6C/v3QlE/mJNdmWrdhUKWCtsVBnm1OXyIwl Wqf/84Mj90e8p8NOFQEhO2p8862AJwEb0KrsUYc7ydPsZUHpJ3EeD87PMQ== From: Anton Yakovlev To: , , Subject: [PATCH v6 5/9] ALSA: virtio: handling control and I/O messages for the PCM device Date: Sat, 27 Feb 2021 09:59:52 +0100 Message-ID: <20210227085956.1700687-6-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.1 In-Reply-To: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> References: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-01.open-synergy.com (10.26.10.21) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, 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" The driver implements a message-based transport for I/O substream operations. Before the start of the substream, the hardware buffer is sliced into I/O messages, the number of which is equal to the current number of periods. The size of each message is equal to the current size of one period. I/O messages are organized in an ordered queue. The completion of the I/O message indicates an elapsed period (the only exception is the end of the stream for the capture substream). Upon completion, the message is automatically re-added to the end of the queue. Signed-off-by: Anton Yakovlev --- sound/virtio/Makefile | 3 +- sound/virtio/virtio_card.c | 18 +- sound/virtio/virtio_card.h | 9 + sound/virtio/virtio_pcm.c | 32 +++ sound/virtio/virtio_pcm.h | 40 ++++ sound/virtio/virtio_pcm_msg.c | 417 ++++++++++++++++++++++++++++++++++ 6 files changed, 516 insertions(+), 3 deletions(-) create mode 100644 sound/virtio/virtio_pcm_msg.c diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile index 69162a545a41..626af3cc3ed7 100644 --- a/sound/virtio/Makefile +++ b/sound/virtio/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o virtio_snd-objs := \ virtio_card.o \ virtio_ctl_msg.o \ - virtio_pcm.o + virtio_pcm.o \ + virtio_pcm_msg.o diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index c5e9ceaaa8a0..9f74261b234c 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -65,8 +65,16 @@ static void virtsnd_event_notify_cb(struct virtqueue *vqueue) spin_lock_irqsave(&queue->lock, flags); do { virtqueue_disable_cb(vqueue); - while ((event = virtqueue_get_buf(vqueue, &length))) + while ((event = virtqueue_get_buf(vqueue, &length))) { + switch (le32_to_cpu(event->hdr.code)) { + case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: + case VIRTIO_SND_EVT_PCM_XRUN: + virtsnd_pcm_event(snd, event); + break; + } + virtsnd_event_send(vqueue, event, true, GFP_ATOMIC); + } if (unlikely(virtqueue_is_broken(vqueue))) break; } while (!virtqueue_enable_cb(vqueue)); @@ -87,7 +95,9 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) struct virtio_device *vdev = snd->vdev; static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb, - [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb + [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb, + [VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb, + [VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb }; static const char *names[VIRTIO_SND_VQ_MAX] = { [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", @@ -273,6 +283,7 @@ static int virtsnd_probe(struct virtio_device *vdev) static void virtsnd_remove(struct virtio_device *vdev) { struct virtio_snd *snd = vdev->priv; + unsigned int i; virtsnd_ctl_msg_cancel_all(snd); @@ -282,6 +293,9 @@ static void virtsnd_remove(struct virtio_device *vdev) vdev->config->del_vqs(vdev); vdev->config->reset(vdev); + for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) + virtsnd_pcm_msg_free(&snd->substreams[i]); + kfree(snd->event_msgs); } diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index 4431cc8f0445..7e50f3835ab1 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -79,4 +79,13 @@ virtsnd_rx_queue(struct virtio_snd *snd) return &snd->queues[VIRTIO_SND_VQ_RX]; } +static inline struct virtio_snd_queue * +virtsnd_pcm_queue(struct virtio_pcm_substream *vss) +{ + if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK) + return virtsnd_tx_queue(vss->snd); + else + return virtsnd_rx_queue(vss->snd); +} + #endif /* VIRTIO_SND_CARD_H */ diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c index 4348ead63c14..3cfd3520a9c0 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -337,6 +337,8 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd) vss->snd = snd; vss->sid = i; + init_waitqueue_head(&vss->msg_empty); + spin_lock_init(&vss->lock); rc = virtsnd_pcm_build_hw(vss, &info[i]); if (rc) @@ -461,3 +463,33 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd) return 0; } + +/** + * virtsnd_pcm_event() - Handle the PCM device event notification. + * @snd: VirtIO sound device. + * @event: VirtIO sound event. + * + * Context: Interrupt context. + */ +void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event) +{ + struct virtio_pcm_substream *vss; + u32 sid = le32_to_cpu(event->data); + + if (sid >= snd->nsubstreams) + return; + + vss = &snd->substreams[sid]; + + switch (le32_to_cpu(event->hdr.code)) { + case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: + /* TODO: deal with shmem elapsed period */ + break; + case VIRTIO_SND_EVT_PCM_XRUN: + spin_lock(&vss->lock); + if (vss->xfer_enabled) + vss->xfer_xrun = true; + spin_unlock(&vss->lock); + break; + } +} diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h index a2aadc44dce9..7af41cad4e8f 100644 --- a/sound/virtio/virtio_pcm.h +++ b/sound/virtio/virtio_pcm.h @@ -22,6 +22,17 @@ struct virtio_pcm_msg; * @features: Stream VirtIO feature bit map (1 << VIRTIO_SND_PCM_F_XXX). * @substream: Kernel ALSA substream. * @hw: Kernel ALSA substream hardware descriptor. + * @lock: Spinlock that protects fields shared by interrupt handlers and + * substream operators. + * @buffer_bytes: Current buffer size in bytes. + * @hw_ptr: Substream hardware pointer value in bytes [0 ... buffer_bytes). + * @xfer_enabled: Data transfer state (0 - off, 1 - on). + * @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun). + * @msgs: Allocated I/O messages. + * @nmsgs: Number of allocated I/O messages. + * @msg_last_enqueued: Index of the last I/O message added to the virtqueue. + * @msg_count: Number of pending I/O messages in the virtqueue. + * @msg_empty: Notify when msg_count is zero. */ struct virtio_pcm_substream { struct virtio_snd *snd; @@ -31,6 +42,16 @@ struct virtio_pcm_substream { u32 features; struct snd_pcm_substream *substream; struct snd_pcm_hardware hw; + spinlock_t lock; + size_t buffer_bytes; + size_t hw_ptr; + bool xfer_enabled; + bool xfer_xrun; + struct virtio_pcm_msg **msgs; + unsigned int nmsgs; + int msg_last_enqueued; + unsigned int msg_count; + wait_queue_head_t msg_empty; }; /** @@ -63,8 +84,27 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd); int virtsnd_pcm_build_devs(struct virtio_snd *snd); +void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event); + +void virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue); + +void virtsnd_pcm_rx_notify_cb(struct virtqueue *vqueue); + struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid); struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid); +struct virtio_snd_msg * +virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *vss, + unsigned int command, gfp_t gfp); + +int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss, + unsigned int periods, unsigned int period_bytes); + +void virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss); + +int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss); + +unsigned int virtsnd_pcm_msg_pending_num(struct virtio_pcm_substream *vss); + #endif /* VIRTIO_SND_PCM_H */ diff --git a/sound/virtio/virtio_pcm_msg.c b/sound/virtio/virtio_pcm_msg.c new file mode 100644 index 000000000000..549bf8deb14c --- /dev/null +++ b/sound/virtio/virtio_pcm_msg.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#include + +#include "virtio_card.h" + +/** + * struct virtio_pcm_msg - VirtIO I/O message. + * @substream: VirtIO PCM substream. + * @xfer: Request header payload. + * @status: Response header payload. + * @sgs: Payload scatter-gather table. + */ +struct virtio_pcm_msg { + struct virtio_pcm_substream *substream; + struct virtio_snd_pcm_xfer xfer; + struct virtio_snd_pcm_status status; + struct scatterlist sgs[0]; +}; + +/** + * enum pcm_msg_sg_index - Index values for the virtio_pcm_msg->sgs field in + * an I/O message. + * @PCM_MSG_SG_XFER: Element containing a virtio_snd_pcm_xfer structure. + * @PCM_MSG_SG_STATUS: Element containing a virtio_snd_pcm_status structure. + * @PCM_MSG_SG_DATA: The first element containing a data buffer. + */ +enum pcm_msg_sg_index { + PCM_MSG_SG_XFER = 0, + PCM_MSG_SG_STATUS, + PCM_MSG_SG_DATA +}; + +/** + * virtsnd_pcm_sg_num() - Count the number of sg-elements required to represent + * vmalloc'ed buffer. + * @data: Pointer to vmalloc'ed buffer. + * @length: Buffer size. + * + * Context: Any context. + * Return: Number of physically contiguous parts in the @data. + */ +static int virtsnd_pcm_sg_num(u8 *data, unsigned int length) +{ + phys_addr_t sg_address; + unsigned int sg_length; + int num = 0; + + while (length) { + struct page *pg = vmalloc_to_page(data); + phys_addr_t pg_address = page_to_phys(pg); + size_t pg_length; + + pg_length = PAGE_SIZE - offset_in_page(data); + if (pg_length > length) + pg_length = length; + + if (!num || sg_address + sg_length != pg_address) { + sg_address = pg_address; + sg_length = pg_length; + num++; + } else { + sg_length += pg_length; + } + + data += pg_length; + length -= pg_length; + } + + return num; +} + +/** + * virtsnd_pcm_sg_from() - Build sg-list from vmalloc'ed buffer. + * @sgs: Preallocated sg-list to populate. + * @nsgs: The maximum number of elements in the @sgs. + * @data: Pointer to vmalloc'ed buffer. + * @length: Buffer size. + * + * Splits the buffer into physically contiguous parts and makes an sg-list of + * such parts. + * + * Context: Any context. + */ +static void virtsnd_pcm_sg_from(struct scatterlist *sgs, int nsgs, u8 *data, + unsigned int length) +{ + int idx = -1; + + while (length) { + struct page *pg = vmalloc_to_page(data); + size_t pg_length; + + pg_length = PAGE_SIZE - offset_in_page(data); + if (pg_length > length) + pg_length = length; + + if (idx == -1 || + sg_phys(&sgs[idx]) + sgs[idx].length != page_to_phys(pg)) { + if (idx + 1 == nsgs) + break; + sg_set_page(&sgs[++idx], pg, pg_length, + offset_in_page(data)); + } else { + sgs[idx].length += pg_length; + } + + data += pg_length; + length -= pg_length; + } + + sg_mark_end(&sgs[idx]); +} + +/** + * virtsnd_pcm_msg_alloc() - Allocate I/O messages. + * @vss: VirtIO PCM substream. + * @periods: Current number of periods. + * @period_bytes: Current period size in bytes. + * + * The function slices the buffer into @periods parts (each with the size of + * @period_bytes), and creates @periods corresponding I/O messages. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -ENOMEM on failure. + */ +int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss, + unsigned int periods, unsigned int period_bytes) +{ + struct snd_pcm_runtime *runtime = vss->substream->runtime; + unsigned int i; + + vss->msgs = kcalloc(periods, sizeof(*vss->msgs), GFP_KERNEL); + if (!vss->msgs) + return -ENOMEM; + + vss->nmsgs = periods; + + for (i = 0; i < periods; ++i) { + u8 *data = runtime->dma_area + period_bytes * i; + int sg_num = virtsnd_pcm_sg_num(data, period_bytes); + struct virtio_pcm_msg *msg; + + msg = kzalloc(sizeof(*msg) + sizeof(*msg->sgs) * (sg_num + 2), + GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->substream = vss; + sg_init_one(&msg->sgs[PCM_MSG_SG_XFER], &msg->xfer, + sizeof(msg->xfer)); + sg_init_one(&msg->sgs[PCM_MSG_SG_STATUS], &msg->status, + sizeof(msg->status)); + virtsnd_pcm_sg_from(&msg->sgs[PCM_MSG_SG_DATA], sg_num, data, + period_bytes); + + vss->msgs[i] = msg; + } + + return 0; +} + +/** + * virtsnd_pcm_msg_free() - Free all allocated I/O messages. + * @vss: VirtIO PCM substream. + * + * Context: Any context. + */ +void virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss) +{ + unsigned int i; + + for (i = 0; vss->msgs && i < vss->nmsgs; ++i) + kfree(vss->msgs[i]); + kfree(vss->msgs); + + vss->msgs = NULL; + vss->nmsgs = 0; +} + +/** + * virtsnd_pcm_msg_send() - Send asynchronous I/O messages. + * @vss: VirtIO PCM substream. + * + * All messages are organized in an ordered circular list. Each time the + * function is called, all currently non-enqueued messages are added to the + * virtqueue. For this, the function keeps track of two values: + * + * msg_last_enqueued = index of the last enqueued message, + * msg_count = # of pending messages in the virtqueue. + * + * Context: Any context. Expects the tx/rx queue and the VirtIO substream + * spinlocks to be held by caller. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss) +{ + struct snd_pcm_runtime *runtime = vss->substream->runtime; + struct virtio_snd *snd = vss->snd; + struct virtio_device *vdev = snd->vdev; + struct virtqueue *vqueue = virtsnd_pcm_queue(vss)->vqueue; + int i; + int n; + bool notify = false; + + i = (vss->msg_last_enqueued + 1) % runtime->periods; + n = runtime->periods - vss->msg_count; + + for (; n; --n, i = (i + 1) % runtime->periods) { + struct virtio_pcm_msg *msg = vss->msgs[i]; + struct scatterlist *psgs[] = { + &msg->sgs[PCM_MSG_SG_XFER], + &msg->sgs[PCM_MSG_SG_DATA], + &msg->sgs[PCM_MSG_SG_STATUS] + }; + int rc; + + msg->xfer.stream_id = cpu_to_le32(vss->sid); + memset(&msg->status, 0, sizeof(msg->status)); + + if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK) + rc = virtqueue_add_sgs(vqueue, psgs, 2, 1, msg, + GFP_ATOMIC); + else + rc = virtqueue_add_sgs(vqueue, psgs, 1, 2, msg, + GFP_ATOMIC); + + if (rc) { + dev_err(&vdev->dev, + "SID %u: failed to send I/O message\n", + vss->sid); + return rc; + } + + vss->msg_last_enqueued = i; + vss->msg_count++; + } + + if (!(vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING))) + notify = virtqueue_kick_prepare(vqueue); + + if (notify) + virtqueue_notify(vqueue); + + return 0; +} + +/** + * virtsnd_pcm_msg_pending_num() - Returns the number of pending I/O messages. + * @vss: VirtIO substream. + * + * Context: Any context. + * Return: Number of messages. + */ +unsigned int virtsnd_pcm_msg_pending_num(struct virtio_pcm_substream *vss) +{ + unsigned int num; + unsigned long flags; + + spin_lock_irqsave(&vss->lock, flags); + num = vss->msg_count; + spin_unlock_irqrestore(&vss->lock, flags); + + return num; +} + +/** + * virtsnd_pcm_msg_complete() - Complete an I/O message. + * @msg: I/O message. + * @written_bytes: Number of bytes written to the message. + * + * Completion of the message means the elapsed period. If transmission is + * allowed, then each completed message is immediately placed back at the end + * of the queue. + * + * For the playback substream, @written_bytes is equal to sizeof(msg->status). + * + * For the capture substream, @written_bytes is equal to sizeof(msg->status) + * plus the number of captured bytes. + * + * Context: Interrupt context. Takes and releases the VirtIO substream spinlock. + */ +static void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg, + size_t written_bytes) +{ + struct virtio_pcm_substream *vss = msg->substream; + + /* + * hw_ptr always indicates the buffer position of the first I/O message + * in the virtqueue. Therefore, on each completion of an I/O message, + * the hw_ptr value is unconditionally advanced. + */ + spin_lock(&vss->lock); + /* + * If the capture substream returned an incorrect status, then just + * increase the hw_ptr by the message size. + */ + if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK || + written_bytes <= sizeof(msg->status)) { + struct scatterlist *sg; + + for (sg = &msg->sgs[PCM_MSG_SG_DATA]; sg; sg = sg_next(sg)) + vss->hw_ptr += sg->length; + } else { + vss->hw_ptr += written_bytes - sizeof(msg->status); + } + + if (vss->hw_ptr >= vss->buffer_bytes) + vss->hw_ptr -= vss->buffer_bytes; + + vss->xfer_xrun = false; + vss->msg_count--; + + if (vss->xfer_enabled) { + struct snd_pcm_runtime *runtime = vss->substream->runtime; + + runtime->delay = + bytes_to_frames(runtime, + le32_to_cpu(msg->status.latency_bytes)); + + spin_unlock(&vss->lock); + snd_pcm_period_elapsed(vss->substream); + spin_lock(&vss->lock); + + virtsnd_pcm_msg_send(vss); + } else if (!vss->msg_count) { + wake_up_all(&vss->msg_empty); + } + spin_unlock(&vss->lock); +} + +/** + * virtsnd_pcm_notify_cb() - Process all completed I/O messages. + * @queue: Underlying tx/rx virtqueue. + * + * Context: Interrupt context. Takes and releases the tx/rx queue spinlock. + */ +static inline void virtsnd_pcm_notify_cb(struct virtio_snd_queue *queue) +{ + struct virtio_pcm_msg *msg; + u32 written_bytes; + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + do { + virtqueue_disable_cb(queue->vqueue); + while ((msg = virtqueue_get_buf(queue->vqueue, &written_bytes))) + virtsnd_pcm_msg_complete(msg, written_bytes); + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + } while (!virtqueue_enable_cb(queue->vqueue)); + spin_unlock_irqrestore(&queue->lock, flags); +} + +/** + * virtsnd_pcm_tx_notify_cb() - Process all completed TX messages. + * @vqueue: Underlying tx virtqueue. + * + * Context: Interrupt context. + */ +void virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + + virtsnd_pcm_notify_cb(virtsnd_tx_queue(snd)); +} + +/** + * virtsnd_pcm_rx_notify_cb() - Process all completed RX messages. + * @vqueue: Underlying rx virtqueue. + * + * Context: Interrupt context. + */ +void virtsnd_pcm_rx_notify_cb(struct virtqueue *vqueue) +{ + struct virtio_snd *snd = vqueue->vdev->priv; + + virtsnd_pcm_notify_cb(virtsnd_rx_queue(snd)); +} + +/** + * virtsnd_pcm_ctl_msg_alloc() - Allocate and initialize the PCM device control + * message for the specified substream. + * @vss: VirtIO PCM substream. + * @command: Control request code (VIRTIO_SND_R_PCM_XXX). + * @gfp: Kernel flags for memory allocation. + * + * Context: Any context. May sleep if @gfp flags permit. + * Return: Allocated message on success, NULL on failure. + */ +struct virtio_snd_msg * +virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *vss, + unsigned int command, gfp_t gfp) +{ + size_t request_size = sizeof(struct virtio_snd_pcm_hdr); + size_t response_size = sizeof(struct virtio_snd_hdr); + struct virtio_snd_msg *msg; + + switch (command) { + case VIRTIO_SND_R_PCM_SET_PARAMS: + request_size = sizeof(struct virtio_snd_pcm_set_params); + break; + } + + msg = virtsnd_ctl_msg_alloc(request_size, response_size, gfp); + if (msg) { + struct virtio_snd_pcm_hdr *hdr = virtsnd_ctl_msg_request(msg); + + hdr->hdr.code = cpu_to_le32(command); + hdr->stream_id = cpu_to_le32(vss->sid); + } + + return msg; +} From patchwork Sat Feb 27 08:59:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 388164 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.7 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_BLOCKED, 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 5EDDDC433E0 for ; Sat, 27 Feb 2021 09:04:20 +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 C9EBF64DB2 for ; Sat, 27 Feb 2021 09:04:19 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org C9EBF64DB2 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 53CE2167C; Sat, 27 Feb 2021 10:03:28 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 53CE2167C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614416658; bh=jsc/2aw/yOLTfBYO0dRHuyZ5le+fSUGKC2MOucbzxhY=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=ApNLFugf6yOQgqPRQULSi7Z6qkebmihAf9rps/juaqVFu7wmI3WUTLlH6eah5aq/j GuhqviQR2kXwZjpjqsJ0AIZlCMRt/Px9w2JMRKMP9snoMoTpO0TP5XfDNxzmLB1Ho7 AS4HXxY6Ifx17U6GRTzRGGsfo3agDM3J5LjByNRc= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id EF512F80482; Sat, 27 Feb 2021 10:00:52 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 0540FF8016A; Sat, 27 Feb 2021 10:00:36 +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 446A8F80159 for ; Sat, 27 Feb 2021 10:00:33 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 446A8F80159 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="XaR0VYnu" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id D7D9DA163C; Sat, 27 Feb 2021 10:00:32 +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=Yv5/bt9+VnoM LxNxABkBrsWiKVqDMIwgsE8xb/OoNw8=; b=XaR0VYnu7PidlT94/LOky2fI+j3P eqy+lCjB8ONcbhGGRml21idFQ9eKAFu57AqsHg86Mc3cK1jZHgy3fw5buMCQ5com DzRfeAJa+Ghzjn6YL+4ci0xnyi+Cp6Dzu3BL9ta8v3axjsnGx6St66xjkPNbf3BQ VCUp8F2aGTl62b/KeExX4nP+iUx7nA0ii6FJP3u1ouKfo05EuRr/3xuHyWUhC9r8 4f9ibvdDmV5MQm3eBgRSquAIfZ5+7MACnEAbAm4rpi54xeM5ws5MKBSYT1ZzBIpu FeB1ZlEXb0dPo0aR1DJ/F8NB8q0AGEDTMrYIIBwFCr63V/94DL+OwLRJFQ== From: Anton Yakovlev To: , , Subject: [PATCH v6 7/9] ALSA: virtio: introduce jack support Date: Sat, 27 Feb 2021 09:59:54 +0100 Message-ID: <20210227085956.1700687-8-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.1 In-Reply-To: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> References: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-01.open-synergy.com (10.26.10.21) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, 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" Enumerate all available jacks and create ALSA controls. At the moment jacks have a simple implementation and can only be used to receive notifications about a plugged in/out device. Signed-off-by: Anton Yakovlev --- sound/virtio/Makefile | 1 + sound/virtio/virtio_card.c | 14 +++ sound/virtio/virtio_card.h | 12 ++ sound/virtio/virtio_jack.c | 233 +++++++++++++++++++++++++++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 sound/virtio/virtio_jack.c diff --git a/sound/virtio/Makefile b/sound/virtio/Makefile index 34493226793f..09f485291285 100644 --- a/sound/virtio/Makefile +++ b/sound/virtio/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o virtio_snd-objs := \ virtio_card.o \ virtio_ctl_msg.o \ + virtio_jack.o \ virtio_pcm.o \ virtio_pcm_msg.o \ virtio_pcm_ops.o diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index 9f74261b234c..c852afda5712 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -67,6 +67,10 @@ static void virtsnd_event_notify_cb(struct virtqueue *vqueue) virtqueue_disable_cb(vqueue); while ((event = virtqueue_get_buf(vqueue, &length))) { switch (le32_to_cpu(event->hdr.code)) { + case VIRTIO_SND_EVT_JACK_CONNECTED: + case VIRTIO_SND_EVT_JACK_DISCONNECTED: + virtsnd_jack_event(snd, event); + break; case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED: case VIRTIO_SND_EVT_PCM_XRUN: virtsnd_pcm_event(snd, event); @@ -185,10 +189,20 @@ static int virtsnd_build_devs(struct virtio_snd *snd) VIRTIO_SND_CARD_NAME " at %s/%s", dev_name(dev->parent), dev_name(dev)); + rc = virtsnd_jack_parse_cfg(snd); + if (rc) + return rc; + rc = virtsnd_pcm_parse_cfg(snd); if (rc) return rc; + if (snd->njacks) { + rc = virtsnd_jack_build_devs(snd); + if (rc) + return rc; + } + if (snd->nsubstreams) { rc = virtsnd_pcm_build_devs(snd); if (rc) diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index 7e50f3835ab1..2092da297a9d 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -18,6 +18,7 @@ #define VIRTIO_SND_CARD_NAME "VirtIO SoundCard" #define VIRTIO_SND_PCM_NAME "VirtIO PCM" +struct virtio_jack; struct virtio_pcm_substream; /** @@ -38,6 +39,8 @@ struct virtio_snd_queue { * @ctl_msgs: Pending control request list. * @event_msgs: Device events. * @pcm_list: VirtIO PCM device list. + * @jacks: VirtIO jacks. + * @njacks: Number of jacks. * @substreams: VirtIO PCM substreams. * @nsubstreams: Number of PCM substreams. */ @@ -48,6 +51,8 @@ struct virtio_snd { struct list_head ctl_msgs; struct virtio_snd_event *event_msgs; struct list_head pcm_list; + struct virtio_jack *jacks; + u32 njacks; struct virtio_pcm_substream *substreams; u32 nsubstreams; }; @@ -88,4 +93,11 @@ virtsnd_pcm_queue(struct virtio_pcm_substream *vss) return virtsnd_rx_queue(vss->snd); } +int virtsnd_jack_parse_cfg(struct virtio_snd *snd); + +int virtsnd_jack_build_devs(struct virtio_snd *snd); + +void virtsnd_jack_event(struct virtio_snd *snd, + struct virtio_snd_event *event); + #endif /* VIRTIO_SND_CARD_H */ diff --git a/sound/virtio/virtio_jack.c b/sound/virtio/virtio_jack.c new file mode 100644 index 000000000000..c69f1dcdcc84 --- /dev/null +++ b/sound/virtio/virtio_jack.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * virtio-snd: Virtio sound device + * Copyright (C) 2021 OpenSynergy GmbH + */ +#include +#include +#include + +#include "virtio_card.h" + +/** + * DOC: Implementation Status + * + * At the moment jacks have a simple implementation and can only be used to + * receive notifications about a plugged in/out device. + * + * VIRTIO_SND_R_JACK_REMAP + * is not supported + */ + +/** + * struct virtio_jack - VirtIO jack. + * @jack: Kernel jack control. + * @nid: Functional group node identifier. + * @features: Jack virtio feature bit map (1 << VIRTIO_SND_JACK_F_XXX). + * @defconf: Pin default configuration value. + * @caps: Pin capabilities value. + * @connected: Current jack connection status. + * @type: Kernel jack type (SND_JACK_XXX). + */ +struct virtio_jack { + struct snd_jack *jack; + u32 nid; + u32 features; + u32 defconf; + u32 caps; + bool connected; + int type; +}; + +/** + * virtsnd_jack_get_label() - Get the name string for the jack. + * @vjack: VirtIO jack. + * + * Returns the jack name based on the default pin configuration value (see HDA + * specification). + * + * Context: Any context. + * Return: Name string. + */ +static const char *virtsnd_jack_get_label(struct virtio_jack *vjack) +{ + unsigned int defconf = vjack->defconf; + unsigned int device = + (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT; + unsigned int location = + (defconf & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; + + switch (device) { + case AC_JACK_LINE_OUT: + return "Line Out"; + case AC_JACK_SPEAKER: + return "Speaker"; + case AC_JACK_HP_OUT: + return "Headphone"; + case AC_JACK_CD: + return "CD"; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + if (location == AC_JACK_LOC_HDMI) + return "HDMI Out"; + else + return "SPDIF Out"; + case AC_JACK_LINE_IN: + return "Line"; + case AC_JACK_AUX: + return "Aux"; + case AC_JACK_MIC_IN: + return "Mic"; + case AC_JACK_SPDIF_IN: + return "SPDIF In"; + case AC_JACK_DIG_OTHER_IN: + return "Digital In"; + default: + return "Misc"; + } +} + +/** + * virtsnd_jack_get_type() - Get the type for the jack. + * @vjack: VirtIO jack. + * + * Returns the jack type based on the default pin configuration value (see HDA + * specification). + * + * Context: Any context. + * Return: SND_JACK_XXX value. + */ +static int virtsnd_jack_get_type(struct virtio_jack *vjack) +{ + unsigned int defconf = vjack->defconf; + unsigned int device = + (defconf & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT; + + switch (device) { + case AC_JACK_LINE_OUT: + case AC_JACK_SPEAKER: + return SND_JACK_LINEOUT; + case AC_JACK_HP_OUT: + return SND_JACK_HEADPHONE; + case AC_JACK_SPDIF_OUT: + case AC_JACK_DIG_OTHER_OUT: + return SND_JACK_AVOUT; + case AC_JACK_MIC_IN: + return SND_JACK_MICROPHONE; + default: + return SND_JACK_LINEIN; + } +} + +/** + * virtsnd_jack_parse_cfg() - Parse the jack configuration. + * @snd: VirtIO sound device. + * + * This function is called during initial device initialization. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_jack_parse_cfg(struct virtio_snd *snd) +{ + struct virtio_device *vdev = snd->vdev; + struct virtio_snd_jack_info *info; + u32 i; + int rc; + + virtio_cread_le(vdev, struct virtio_snd_config, jacks, &snd->njacks); + if (!snd->njacks) + return 0; + + snd->jacks = devm_kcalloc(&vdev->dev, snd->njacks, sizeof(*snd->jacks), + GFP_KERNEL); + if (!snd->jacks) + return -ENOMEM; + + info = kcalloc(snd->njacks, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + rc = virtsnd_ctl_query_info(snd, VIRTIO_SND_R_JACK_INFO, 0, snd->njacks, + sizeof(*info), info); + if (rc) + goto on_exit; + + for (i = 0; i < snd->njacks; ++i) { + struct virtio_jack *vjack = &snd->jacks[i]; + + vjack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid); + vjack->features = le32_to_cpu(info[i].features); + vjack->defconf = le32_to_cpu(info[i].hda_reg_defconf); + vjack->caps = le32_to_cpu(info[i].hda_reg_caps); + vjack->connected = info[i].connected; + } + +on_exit: + kfree(info); + + return rc; +} + +/** + * virtsnd_jack_build_devs() - Build ALSA controls for jacks. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_jack_build_devs(struct virtio_snd *snd) +{ + u32 i; + int rc; + + for (i = 0; i < snd->njacks; ++i) { + struct virtio_jack *vjack = &snd->jacks[i]; + + vjack->type = virtsnd_jack_get_type(vjack); + + rc = snd_jack_new(snd->card, virtsnd_jack_get_label(vjack), + vjack->type, &vjack->jack, true, true); + if (rc) + return rc; + + if (vjack->jack) + vjack->jack->private_data = vjack; + + snd_jack_report(vjack->jack, + vjack->connected ? vjack->type : 0); + } + + return 0; +} + +/** + * virtsnd_jack_event() - Handle the jack event notification. + * @snd: VirtIO sound device. + * @event: VirtIO sound event. + * + * Context: Interrupt context. + */ +void virtsnd_jack_event(struct virtio_snd *snd, struct virtio_snd_event *event) +{ + u32 jack_id = le32_to_cpu(event->data); + struct virtio_jack *vjack; + + if (jack_id >= snd->njacks) + return; + + vjack = &snd->jacks[jack_id]; + + switch (le32_to_cpu(event->hdr.code)) { + case VIRTIO_SND_EVT_JACK_CONNECTED: + vjack->connected = true; + break; + case VIRTIO_SND_EVT_JACK_DISCONNECTED: + vjack->connected = false; + break; + default: + return; + } + + snd_jack_report(vjack->jack, vjack->connected ? vjack->type : 0); +} From patchwork Sat Feb 27 08:59:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 388163 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,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 B2E7DC433DB for ; Sat, 27 Feb 2021 09:05:30 +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 2C82E60C40 for ; Sat, 27 Feb 2021 09:05:30 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2C82E60C40 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 B77F316BF; Sat, 27 Feb 2021 10:04:38 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B77F316BF DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614416728; bh=u/Q0K1mwSL+ZSIlh8M0CngEFW23v0RV6Q1Q+XXqyfE4=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=sKPitMY2DVmVWXQ4XtMr4b9ddyWRPT9+580b27BsfD1bXEzzUUf9PhxwBkSqlwALS N/66H/VFKOoqf0NvKz2t36Mi83HKmyBSSd25LovS3QWYxJt1+uHWZUwjLmji1d3TG5 B/HhtXhuPi8w8lM6/i7/jdKszRut7CUjVvEwEzuo= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 0A0C4F804FC; Sat, 27 Feb 2021 10:01:06 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 52FEAF80430; Sat, 27 Feb 2021 10:00:51 +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 B8337F80159 for ; Sat, 27 Feb 2021 10:00:44 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz B8337F80159 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="Xlopr/eW" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 547D9A1765; Sat, 27 Feb 2021 10:00:44 +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=zLcadKSiDEB2 5euoNy23WAsizIICsggTnRBQxLzaEyk=; b=Xlopr/eWd8YAOS0Eq0Z5K60FlzOz bU5/TBb1pB5ZKQL4nQdWmOfb4CP8BVBaKMiRUTizHmrAdIZ/j41753zNj8Ya8IFN +aNd+oaKAF1PoeDzX3i1Ybxhqrxcqnx+4ig6A65sDmwh819Yui1/WE9CNDLlv+oI cUYRdXgwk9edgYAj6XgRyHQ43LYBBSDVh1yzPewBV+Ck0ImSleEZM11HYhOajP25 EST9S6ndRwNVuws6+ZYtl4+oyZv/5ydv/L0Dzhic+/oeQ2vah+3kGSG5LISIUC98 XOMN/S9asSSsEUf+plIANnLVAOny61yBt0NwquFfJ5AERcOVGZh13JH1xQ== From: Anton Yakovlev To: , , Subject: [PATCH v6 9/9] ALSA: virtio: introduce device suspend/resume support Date: Sat, 27 Feb 2021 09:59:56 +0100 Message-ID: <20210227085956.1700687-10-anton.yakovlev@opensynergy.com> X-Mailer: git-send-email 2.30.1 In-Reply-To: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> References: <20210227085956.1700687-1-anton.yakovlev@opensynergy.com> MIME-Version: 1.0 X-ClientProxiedBy: SR-MAIL-01.open-synergy.com (10.26.10.21) To SR-MAIL-01.open-synergy.com (10.26.10.21) Cc: linux-kernel@vger.kernel.org, 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" All running PCM substreams are stopped on device suspend and restarted on device resume. Signed-off-by: Anton Yakovlev --- sound/virtio/virtio_card.c | 56 +++++++++++++++++++++++++++++++++++ sound/virtio/virtio_pcm.c | 1 + sound/virtio/virtio_pcm_ops.c | 41 ++++++++++++++++++++----- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index 59455a562018..c7ae8801991d 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -323,6 +323,58 @@ static void virtsnd_remove(struct virtio_device *vdev) kfree(snd->event_msgs); } +#ifdef CONFIG_PM_SLEEP +/** + * virtsnd_freeze() - Suspend device. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_freeze(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + virtsnd_ctl_msg_cancel_all(snd); + + vdev->config->del_vqs(vdev); + vdev->config->reset(vdev); + + kfree(snd->event_msgs); + + /* + * If the virtsnd_restore() fails before re-allocating events, then we + * get a dangling pointer here. + */ + snd->event_msgs = NULL; + + return 0; +} + +/** + * virtsnd_restore() - Resume device. + * @vdev: VirtIO parent device. + * + * Context: Any context. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_restore(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + int rc; + + rc = virtsnd_find_vqs(snd); + if (rc) + return rc; + + virtio_device_ready(vdev); + + virtsnd_enable_event_vq(snd); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + static const struct virtio_device_id id_table[] = { { VIRTIO_ID_SOUND, VIRTIO_DEV_ANY_ID }, { 0 }, @@ -335,6 +387,10 @@ static struct virtio_driver virtsnd_driver = { .validate = virtsnd_validate, .probe = virtsnd_probe, .remove = virtsnd_remove, +#ifdef CONFIG_PM_SLEEP + .freeze = virtsnd_freeze, + .restore = virtsnd_restore, +#endif }; static int __init init(void) diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c index 3605151860f2..4a4a6583b002 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -109,6 +109,7 @@ static int virtsnd_pcm_build_hw(struct virtio_pcm_substream *vss, SNDRV_PCM_INFO_BATCH | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE; if (!info->channels_min || info->channels_min > info->channels_max) { diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c index d02d79bd94f3..03a7e2666cfb 100644 --- a/sound/virtio/virtio_pcm_ops.c +++ b/sound/virtio/virtio_pcm_ops.c @@ -167,7 +167,8 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream, int vrate = -1; int rc; - if (virtsnd_pcm_msg_pending_num(vss)) { + if (runtime->status->state != SNDRV_PCM_STATE_SUSPENDED && + virtsnd_pcm_msg_pending_num(vss)) { dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", vss->sid); return -EBADFD; @@ -231,6 +232,10 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream, if (rc) return rc; + /* If messages have already been allocated before, do nothing. */ + if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) + return 0; + /* Free previously allocated messages (if any). */ virtsnd_pcm_msg_free(vss); @@ -267,20 +272,24 @@ static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream) */ static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct virtio_pcm_substream *vss = snd_pcm_substream_chip(substream); struct virtio_device *vdev = vss->snd->vdev; struct virtio_snd_msg *msg; - if (virtsnd_pcm_msg_pending_num(vss)) { - dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", - vss->sid); - return -EBADFD; + if (runtime->status->state != SNDRV_PCM_STATE_SUSPENDED) { + if (virtsnd_pcm_msg_pending_num(vss)) { + dev_err(&vdev->dev, "SID %u: invalid I/O queue state\n", + vss->sid); + return -EBADFD; + } + + vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream); + vss->hw_ptr = 0; + vss->msg_last_enqueued = -1; } - vss->buffer_bytes = snd_pcm_lib_buffer_bytes(substream); - vss->hw_ptr = 0; vss->xfer_xrun = false; - vss->msg_last_enqueued = -1; vss->msg_count = 0; msg = virtsnd_pcm_ctl_msg_alloc(vss, VIRTIO_SND_R_PCM_PREPARE, @@ -309,6 +318,21 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) int rc; switch (command) { + case SNDRV_PCM_TRIGGER_RESUME: { + /* + * We restart the substream by executing the standard command + * sequence. + */ + rc = virtsnd_pcm_hw_params(substream, NULL); + if (rc) + return rc; + + rc = virtsnd_pcm_prepare(substream); + if (rc) + return rc; + + fallthrough; + } case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: { struct virtio_snd_queue *queue = virtsnd_pcm_queue(vss); @@ -335,6 +359,7 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) return virtsnd_ctl_msg_send_sync(snd, msg); } + case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: { spin_lock_irqsave(&vss->lock, flags);