From patchwork Sun Jan 24 16:54:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 369980 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 D1D7EC433E0 for ; Sun, 24 Jan 2021 16:55:45 +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 4807F22CA2 for ; Sun, 24 Jan 2021 16:55:45 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 4807F22CA2 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 C9DA2188E; Sun, 24 Jan 2021 17:54:53 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz C9DA2188E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507343; bh=0Qgei7YJkVy6mCd+hxSbfqhpewpplHmYDQl9yQoOG1w=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=WalSg4ar/Q2fSziJdNkRIF2vBW2gK3w9V0Dg818g/f/OwFr573a1MS/lxRYskS3rO BRPsKxfgG3zFK2OMh4GKoY73ZtYlk+nMgA/d3Eo5sCYNSV+bfHscec2a9wsyokM7OS S7DRum+3PIOeKJ6dh2JSpDuH3uGQp8ryq27tCNwY= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 0B5B0F804C2; Sun, 24 Jan 2021 17:54:34 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 162DAF80217; Sun, 24 Jan 2021 17:54:30 +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 AF230F801D8 for ; Sun, 24 Jan 2021 17:54:23 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz AF230F801D8 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="BJBvQT2N" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 4FB44A1476; Sun, 24 Jan 2021 17:54:23 +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=JMWVu00sJKNa UzNO4ZqMeD8FYjdkJxYXTlplUlw+228=; b=BJBvQT2Nm7ZBi1ltugxw+KKsQk0e i9pn/XfRRC2yiXe8aiGrls+9OsS87qj3d7iEKdsM6i+2RsVhvPa7VTbV69xML+Um w+2VbGhfZEk3N5z0aOuc0x0iZ/e7lIb7z+EqLqUeSz2Rq7Mhu8MaZQVtUoQgkOgC FxE5ZFEsG8x7V+Qcbpz+vmSQLreT4W5Ky+PuLAoz2OyieJJ3GRibrgPVuwrX9j7v 7pCPL+3sy6qIex9z7xg+JENq8u8ecR5bYfTSnXY5eQf98+NazO7pWztGYXXdQNyb QhTT7uILHYlI3NAczNw4EKJoGQGth5t8/qxI98dw2ROwLQFp4qXDf21rig== From: Anton Yakovlev To: , , Subject: [PATCH v2 1/9] uapi: virtio_ids: add a sound device type ID from OASIS spec Date: Sun, 24 Jan 2021 17:54:00 +0100 Message-ID: <20210124165408.1122868-2-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, 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 Sun Jan 24 16:54:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 369979 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 9BFE1C433E0 for ; Sun, 24 Jan 2021 16:56:42 +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 B5E1E22CA2 for ; Sun, 24 Jan 2021 16:56:41 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org B5E1E22CA2 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 5F91A187A; Sun, 24 Jan 2021 17:55:50 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5F91A187A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507400; bh=nT2Nl5PU6zq1U2WwtbmyYlmYdKeHAZcIL4x5R6dmgxM=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=aES4zduKoh5JMf2cnEH1YaKezzNAQdqTD42h45U5icXqdlC2jvmjmTmoBcSOyg9AV w8XVINftUHbju3f+Zxnj5mEs1xBQRDplKrTykH7ua/S4iT4sEZ+WlOMMZWPbJGfUmJ f6hVnDFoSJ4n3NqM8pVfKF893IEWc1hmwN9YFhRg= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 14AB7F804E4; Sun, 24 Jan 2021 17:54:39 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 07457F804E4; Sun, 24 Jan 2021 17:54:37 +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 0EEDFF80254 for ; Sun, 24 Jan 2021 17:54:31 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 0EEDFF80254 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="UXyx9IYV" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 9D642A147A; Sun, 24 Jan 2021 17:54:30 +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=6cp1PusUrPMO ANk2zBZ7csScNQZZrGUKyiBxqbE4Seo=; b=UXyx9IYVhYqCleBxePPcF8oxuIMS Mz0ENLTlb4Ocn00e3HnyInLP0G4pFeZKSNEivdGhiVj63oLOHggG4TI7CB+Jv1Rv A+ZwlqrnqkBZ2vGUbJP7UWPj1U8OpEepfCdpo2hkPXTi/6NRQxUJHD6paMQX0ei5 tYpXsvehHAY/Za1XuDEpzOgaddPBHTJNKngm2aWURjlltZtO57e2tkPlXYUqnkxI 500YJlwqK+VPplvEQKjjXCqsmcfkZEKwV+sTOdcnEyG/wvcxzrz9wr4JxYadwli1 ++BS/Go6A4tcG7MjGw+YS6MuuANISAj2hZfbWppZ1qjyLa10I1UcQrCHFw== From: Anton Yakovlev To: , , Subject: [PATCH v2 3/9] ALSA: virtio: handling control messages Date: Sun, 24 Jan 2021 17:54:02 +0100 Message-ID: <20210124165408.1122868-4-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, 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 | 20 +++ sound/virtio/virtio_card.h | 7 + sound/virtio/virtio_ctl_msg.c | 293 ++++++++++++++++++++++++++++++++++ sound/virtio/virtio_ctl_msg.h | 122 ++++++++++++++ 5 files changed, 444 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 532d823fdf6f..955eadc2d858 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -24,6 +24,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); /** @@ -125,6 +129,7 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) unsigned int n = 0; int rc; + callbacks[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb; callbacks[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb; rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, @@ -193,6 +198,15 @@ static void virtsnd_disable_vqs(struct virtio_snd *snd) if (queue->vqueue) virtqueue_disable_cb(queue->vqueue); queue->vqueue = NULL; + /* Cancel all pending requests for the control queue */ + if (i == VIRTIO_SND_VQ_CONTROL) { + struct virtio_snd_msg *msg; + struct virtio_snd_msg *next; + + list_for_each_entry_safe(msg, next, &snd->ctl_msgs, + list) + virtsnd_ctl_msg_complete(snd, msg); + } spin_unlock_irqrestore(&queue->lock, flags); } @@ -283,6 +297,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; } @@ -305,6 +324,7 @@ static int virtsnd_probe(struct virtio_device *vdev) snd->vdev = vdev; INIT_WORK(&snd->reset_work, virtsnd_reset_fn); + INIT_LIST_HEAD(&snd->ctl_msgs); vdev->priv = snd; diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index 10084abaaf18..37b734a92134 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -23,6 +23,8 @@ #include #include +#include "virtio_ctl_msg.h" + /** * struct virtio_snd_queue - Virtqueue wrapper structure. * @lock: Used to synchronize access to a virtqueue. @@ -39,6 +41,7 @@ struct virtio_snd_queue { * @queues: Virtqueue wrappers. * @reset_work: Reset device work. * @card: ALSA sound card. + * @ctl_msgs: Pending control request list. * @event_msgs: Device events. */ struct virtio_snd { @@ -46,9 +49,13 @@ struct virtio_snd { struct virtio_snd_queue queues[VIRTIO_SND_VQ_MAX]; struct work_struct reset_work; 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..c1701756bc32 --- /dev/null +++ b/sound/virtio/virtio_ctl_msg.c @@ -0,0 +1,293 @@ +// 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 "virtio_card.h" +#include "virtio_ctl_msg.h" + +/** + * virtsnd_ctl_msg_alloc_ext() - Allocate and initialize a control message. + * @vdev: VirtIO parent device. + * @request_size: Size of request header (pointed to by sg_request field). + * @response_size: Size of response header (pointed to by sg_response field). + * @sgs: Additional data to attach to the message (may be NULL). + * @out_sgs: Number of scattergather elements to attach to the request header. + * @in_sgs: Number of scattergather elements to attach to the 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, ERR_PTR(-errno) on failure. + */ +struct virtio_snd_msg *virtsnd_ctl_msg_alloc_ext(struct virtio_device *vdev, + size_t request_size, + size_t response_size, + struct scatterlist *sgs, + unsigned int out_sgs, + unsigned int in_sgs, gfp_t gfp) +{ + struct virtio_snd_msg *msg; + size_t msg_size = + sizeof(*msg) + (1 + out_sgs + 1 + in_sgs) * sizeof(*msg->sgs); + unsigned int i; + + msg = devm_kzalloc(&vdev->dev, msg_size + request_size + response_size, + gfp); + if (!msg) + return ERR_PTR(-ENOMEM); + + sg_init_one(&msg->sg_request, (u8 *)msg + msg_size, request_size); + sg_init_one(&msg->sg_response, (u8 *)msg + msg_size + request_size, + response_size); + + INIT_LIST_HEAD(&msg->list); + init_completion(&msg->notify); + atomic_set(&msg->ref_count, 1); + + msg->sgs[msg->out_sgs++] = &msg->sg_request; + if (sgs) + for (i = 0; i < out_sgs; ++i) + msg->sgs[msg->out_sgs++] = &sgs[i]; + + msg->sgs[msg->out_sgs + msg->in_sgs++] = &msg->sg_response; + if (sgs) + for (i = out_sgs; i < out_sgs + in_sgs; ++i) + msg->sgs[msg->out_sgs + msg->in_sgs++] = &sgs[i]; + + return msg; +} + +/** + * virtsnd_ctl_msg_send() - Send an (asynchronous) control message. + * @snd: VirtIO sound device. + * @msg: Control message. + * + * If a message is failed to be enqueued, it will be deleted. If message content + * is still needed, the caller must additionally to virtsnd_ctl_msg_ref/unref() + * it. + * + * Context: Any context. Takes and releases the control queue spinlock. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_ctl_msg_send(struct virtio_snd *snd, struct virtio_snd_msg *msg) +{ + struct virtio_device *vdev = snd->vdev; + struct virtio_snd_queue *queue = virtsnd_control_queue(snd); + struct virtio_snd_hdr *response = sg_virt(&msg->sg_response); + bool notify = false; + unsigned long flags; + int rc = -EIO; + + /* Set the default status in case the message was not sent or was + * canceled. + */ + response->code = cpu_to_virtio32(vdev, VIRTIO_SND_S_IO_ERR); + + spin_lock_irqsave(&queue->lock, flags); + if (queue->vqueue) { + rc = virtqueue_add_sgs(queue->vqueue, msg->sgs, msg->out_sgs, + msg->in_sgs, 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) { + if (!notify || virtqueue_notify(queue->vqueue)) + return 0; + + spin_lock_irqsave(&queue->lock, flags); + list_del(&msg->list); + spin_unlock_irqrestore(&queue->lock, flags); + } + + virtsnd_ctl_msg_unref(snd->vdev, msg); + + return -EIO; +} + +/** + * virtsnd_ctl_msg_send_sync() - Send a (synchronous) control 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. Takes and releases the control queue spinlock. + * 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. + */ +int virtsnd_ctl_msg_send_sync(struct virtio_snd *snd, + struct virtio_snd_msg *msg) +{ + struct virtio_device *vdev = snd->vdev; + unsigned int js = msecs_to_jiffies(msg_timeout_ms); + struct virtio_snd_hdr *response; + int rc; + + virtsnd_ctl_msg_ref(vdev, msg); + + rc = virtsnd_ctl_msg_send(snd, msg); + if (rc) + goto on_failure; + + rc = wait_for_completion_interruptible_timeout(&msg->notify, js); + if (rc <= 0) { + if (!rc) { + struct virtio_snd_hdr *request = + sg_virt(&msg->sg_request); + + dev_err(&vdev->dev, + "control message (0x%08x) timeout\n", + le32_to_cpu(request->code)); + rc = -EIO; + } + + goto on_failure; + } + + response = sg_virt(&msg->sg_response); + + switch (le32_to_cpu(response->code)) { + case VIRTIO_SND_S_OK: + rc = 0; + break; + case VIRTIO_SND_S_BAD_MSG: + rc = -EINVAL; + break; + case VIRTIO_SND_S_NOT_SUPP: + rc = -EOPNOTSUPP; + break; + case VIRTIO_SND_S_IO_ERR: + rc = -EIO; + break; + default: + rc = -EPERM; + break; + } + +on_failure: + virtsnd_ctl_msg_unref(vdev, msg); + + return rc; +} + +/** + * virtsnd_ctl_msg_complete() - Complete a control message. + * @snd: VirtIO sound device. + * @msg: Control message. + * + * Context: Any context. + */ +void virtsnd_ctl_msg_complete(struct virtio_snd *snd, + struct virtio_snd_msg *msg) +{ + list_del(&msg->list); + complete(&msg->notify); + + virtsnd_ctl_msg_unref(snd->vdev, msg); +} + +/** + * 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_device *vdev = snd->vdev; + struct virtio_snd_msg *msg; + struct virtio_snd_query_info *query; + struct scatterlist sg; + + sg_init_one(&sg, info, count * size); + + msg = virtsnd_ctl_msg_alloc_ext(vdev, sizeof(*query), + sizeof(struct virtio_snd_hdr), &sg, 0, + 1, GFP_KERNEL); + if (IS_ERR(msg)) + return PTR_ERR(msg); + + query = sg_virt(&msg->sg_request); + query->hdr.code = cpu_to_virtio32(vdev, command); + query->start_id = cpu_to_virtio32(vdev, start_id); + query->count = cpu_to_virtio32(vdev, count); + query->size = cpu_to_virtio32(vdev, size); + + return virtsnd_ctl_msg_send_sync(snd, msg); +} + +/** + * 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); + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (queue->vqueue) { + virtqueue_disable_cb(queue->vqueue); + + for (;;) { + struct virtio_snd_msg *msg; + u32 length; + + msg = virtqueue_get_buf(queue->vqueue, &length); + if (!msg) + break; + + virtsnd_ctl_msg_complete(snd, msg); + } + + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + + if (virtqueue_enable_cb(queue->vqueue)) + break; + } + 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..0f8de8f2fd2d --- /dev/null +++ b/sound/virtio/virtio_ctl_msg.h @@ -0,0 +1,122 @@ +/* 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_MSG_H +#define VIRTIO_SND_MSG_H + +#include +#include + +struct virtio_snd; + +/** + * struct virtio_snd_msg - Control message. + * @sg_request: Scattergather element containing a device request (header). + * @sg_response: Scattergather element containing a device response (status). + * @list: Pending message list entry. + * @notify: Request completed notification. + * @ref_count: Reference count used to manage a message lifetime. + * @out_sgs: Number of read-only sg elements in the sgs array. + * @in_sgs: Number of write-only sg elements in the sgs array. + * @sgs: Array of sg elements to add to the control virtqueue. + */ +struct virtio_snd_msg { +/* public: */ + struct scatterlist sg_request; + struct scatterlist sg_response; +/* private: internal use only */ + struct list_head list; + struct completion notify; + atomic_t ref_count; + unsigned int out_sgs; + unsigned int in_sgs; + struct scatterlist *sgs[0]; +}; + +/** + * virtsnd_ctl_msg_ref() - Increment reference counter for the message. + * @vdev: VirtIO parent device. + * @msg: Control message. + * + * Context: Any context. + */ +static inline void virtsnd_ctl_msg_ref(struct virtio_device *vdev, + struct virtio_snd_msg *msg) +{ + atomic_inc(&msg->ref_count); +} + +/** + * virtsnd_ctl_msg_unref() - Decrement reference counter for the message. + * @vdev: VirtIO parent device. + * @msg: Control message. + * + * The message will be freed when the ref_count value is 0. + * + * Context: Any context. + */ +static inline void virtsnd_ctl_msg_unref(struct virtio_device *vdev, + struct virtio_snd_msg *msg) +{ + if (!atomic_dec_return(&msg->ref_count)) + devm_kfree(&vdev->dev, msg); +} + +struct virtio_snd_msg *virtsnd_ctl_msg_alloc_ext(struct virtio_device *vdev, + size_t request_size, + size_t response_size, + struct scatterlist *sgs, + unsigned int out_sgs, + unsigned int in_sgs, + gfp_t gfp); + +/** + * virtsnd_ctl_msg_alloc() - Simplified control message allocation. + * @vdev: VirtIO parent device. + * @request_size: Size of request header (pointed to by sg_request field). + * @response_size: Size of response header (pointed to by sg_response field). + * @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, ERR_PTR(-errno) on failure. + */ +static inline +struct virtio_snd_msg *virtsnd_ctl_msg_alloc(struct virtio_device *vdev, + size_t request_size, + size_t response_size, gfp_t gfp) +{ + return virtsnd_ctl_msg_alloc_ext(vdev, request_size, response_size, + NULL, 0, 0, gfp); +} + +int virtsnd_ctl_msg_send(struct virtio_snd *snd, struct virtio_snd_msg *msg); + +int virtsnd_ctl_msg_send_sync(struct virtio_snd *snd, + struct virtio_snd_msg *msg); + +void virtsnd_ctl_msg_complete(struct virtio_snd *snd, + 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 Sun Jan 24 16:54:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 369978 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 125E6C433DB for ; Sun, 24 Jan 2021 16:57:41 +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 76CC922CA2 for ; Sun, 24 Jan 2021 16:57:40 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 76CC922CA2 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 0F0D61873; Sun, 24 Jan 2021 17:56:49 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 0F0D61873 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507459; bh=Vzl2BJA4Gy9uznRzwwtqRFnSNsDnF0V4+q8XcjkRdQ4=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=sszPW/wgraU333GGWNmsje+mVz6NtfVu02bZXCG5hwmnpdZz5fN1eVmDBLWo7qSmK C/cIPM5vmOFRH5caSl8kyGSbF9qb5JvOTiYTtJl1H3E7F5Jjj1gj8QvPgM0kZvmM9H masiPGF+ObZ9hKFTlLiLYl6eO0hQ5HMxVdIroBIU= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 4A958F804FD; Sun, 24 Jan 2021 17:54:47 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 4C67FF804FD; Sun, 24 Jan 2021 17:54:45 +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 132D4F804FB for ; Sun, 24 Jan 2021 17:54:42 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 132D4F804FB Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="r21UG6M/" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id A3DDFA146E; Sun, 24 Jan 2021 17:54:41 +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=VOjdMlszU3u7 iXPzdQ1k8GoYyH1Sujlr//n1EymoP+8=; b=r21UG6M/H9rUafvy/q+F33avwnKP 7FSRx4IR2RjdwacLomKOh1J88GOsGOYiCUTKy9ypDP6CQsy28VkLsao4ADMIkddQ +jTlpK8JKoDFf91AOft6iI6p9mmbkEVpyaFWN/AtEZsbXLiJvG96DuWtTGF3vk6B xXYjjgpZ10fj07tsxIG4OD1r+FJjSs0bCaxyFTxkb/oWFDm9DzN2v6bghE5jNHLp YJDbZegXFZSFTbUuSPjDHGxIn+p9UUD2p47CYGe4+Ou4Ud/YkBfQwu+/mW6ks/l3 Z1KDQzZpU2U99iGSTDRIAaiMVLMQT+YOp9FOpnCKtzicvULdeoxXVIAjRA== From: Anton Yakovlev To: , , Subject: [PATCH v2 5/9] ALSA: virtio: handling control and I/O messages for the PCM device Date: Sun, 24 Jan 2021 17:54:04 +0100 Message-ID: <20210124165408.1122868-6-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, 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 | 10 ++ sound/virtio/virtio_card.h | 9 + sound/virtio/virtio_pcm.c | 3 + sound/virtio/virtio_pcm.h | 31 ++++ sound/virtio/virtio_pcm_msg.c | 325 ++++++++++++++++++++++++++++++++++ 6 files changed, 380 insertions(+), 1 deletion(-) 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 39fe13b43dd1..11d025ee77c2 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -143,6 +143,12 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) callbacks[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb; callbacks[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb; + virtio_cread(vdev, struct virtio_snd_config, streams, &n); + if (n) { + callbacks[VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb; + callbacks[VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb; + } + rc = virtio_find_vqs(vdev, VIRTIO_SND_VQ_MAX, vqs, callbacks, names, NULL); if (rc) { @@ -177,6 +183,10 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) * virtsnd_enable_event_vq() - Enable the event virtqueue. * @snd: VirtIO sound device. * + * The tx queue is enabled only if the device supports playback stream(s). + * + * The rx queue is enabled only if the device supports capture stream(s). + * * Context: Any context. */ static void virtsnd_enable_event_vq(struct virtio_snd *snd) diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index be6651a6aaf8..b11c09984882 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -89,4 +89,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 *substream) +{ + if (substream->direction == SNDRV_PCM_STREAM_PLAYBACK) + return virtsnd_tx_queue(substream->snd); + else + return virtsnd_rx_queue(substream->snd); +} + #endif /* VIRTIO_SND_CARD_H */ diff --git a/sound/virtio/virtio_pcm.c b/sound/virtio/virtio_pcm.c index 036990b7b78a..1ab50dcc88c8 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -376,6 +376,7 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd) substream->snd = snd; substream->sid = i; + init_waitqueue_head(&substream->msg_empty); rc = virtsnd_pcm_build_hw(substream, &info[i]); if (rc) @@ -530,6 +531,8 @@ void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event) break; } case VIRTIO_SND_EVT_PCM_XRUN: { + if (atomic_read(&substream->xfer_enabled)) + atomic_set(&substream->xfer_xrun, 1); break; } } diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h index 73fb4d9dc524..d011b7e1d18d 100644 --- a/sound/virtio/virtio_pcm.h +++ b/sound/virtio/virtio_pcm.h @@ -24,6 +24,7 @@ #include struct virtio_pcm; +struct virtio_pcm_msg; /** * struct virtio_pcm_substream - VirtIO PCM substream. @@ -34,6 +35,16 @@ struct virtio_pcm; * @features: Stream VirtIO feature bit map (1 << VIRTIO_SND_PCM_F_XXX). * @substream: Kernel ALSA substream. * @hw: Kernel ALSA substream hardware descriptor. + * @frame_bytes: Current frame size in bytes. + * @period_size: Current period size in frames. + * @buffer_size: Current buffer size in frames. + * @hw_ptr: Substream hardware pointer value in frames [0 ... buffer_size). + * @xfer_enabled: Data transfer state (0 - off, 1 - on). + * @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun). + * @msgs: 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; @@ -43,6 +54,16 @@ struct virtio_pcm_substream { u32 features; struct snd_pcm_substream *substream; struct snd_pcm_hardware hw; + unsigned int frame_bytes; + snd_pcm_uframes_t period_size; + snd_pcm_uframes_t buffer_size; + atomic_t hw_ptr; + atomic_t xfer_enabled; + atomic_t xfer_xrun; + struct virtio_pcm_msg *msgs; + int msg_last_enqueued; + atomic_t msg_count; + wait_queue_head_t msg_empty; }; /** @@ -86,4 +107,14 @@ struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, unsigned int nid); struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, unsigned int nid); +struct virtio_snd_msg * +virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *substream, + unsigned int command, gfp_t gfp); + +int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *substream, + unsigned int nmsg, u8 *dma_area, + unsigned int period_bytes); + +int virtsnd_pcm_msg_send(struct virtio_pcm_substream *substream); + #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..d524e7cbe43f --- /dev/null +++ b/sound/virtio/virtio_pcm_msg.c @@ -0,0 +1,325 @@ +// 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 "virtio_card.h" + +/** + * enum pcm_msg_sg_index - Scatter-gather element indexes for an I/O message. + * @PCM_MSG_SG_XFER: Element containing a virtio_snd_pcm_xfer structure. + * @PCM_MSG_SG_DATA: Element containing a data buffer. + * @PCM_MSG_SG_STATUS: Element containing a virtio_snd_pcm_status structure. + * @PCM_MSG_SG_MAX: The maximum number of elements in the scatter-gather table. + * + * These values are used as the index of the payload scatter-gather table. + */ +enum pcm_msg_sg_index { + PCM_MSG_SG_XFER = 0, + PCM_MSG_SG_DATA, + PCM_MSG_SG_STATUS, + PCM_MSG_SG_MAX +}; + +/** + * 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[PCM_MSG_SG_MAX]; +}; + +/** + * virtsnd_pcm_msg_alloc() - Allocate I/O messages. + * @substream: VirtIO PCM substream. + * @nmsg: Number of messages (equal to the number of periods). + * @dma_area: Pointer to used audio buffer. + * @period_bytes: Period (message payload) size. + * + * The function slices the buffer into nmsg parts (each with the size of + * period_bytes), and creates nmsg 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 *substream, + unsigned int nmsg, u8 *dma_area, + unsigned int period_bytes) +{ + struct virtio_device *vdev = substream->snd->vdev; + unsigned int i; + + if (substream->msgs) + devm_kfree(&vdev->dev, substream->msgs); + + substream->msgs = devm_kcalloc(&vdev->dev, nmsg, + sizeof(*substream->msgs), GFP_KERNEL); + if (!substream->msgs) + return -ENOMEM; + + for (i = 0; i < nmsg; ++i) { + struct virtio_pcm_msg *msg = &substream->msgs[i]; + + msg->substream = substream; + + sg_init_table(msg->sgs, PCM_MSG_SG_MAX); + sg_init_one(&msg->sgs[PCM_MSG_SG_XFER], &msg->xfer, + sizeof(msg->xfer)); + sg_init_one(&msg->sgs[PCM_MSG_SG_DATA], + dma_area + period_bytes * i, period_bytes); + sg_init_one(&msg->sgs[PCM_MSG_SG_STATUS], &msg->status, + sizeof(msg->status)); + } + + return 0; +} + +/** + * virtsnd_pcm_msg_send() - Send asynchronous I/O messages. + * @substream: 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. + * Return: 0 on success, -EIO on failure. + */ +int virtsnd_pcm_msg_send(struct virtio_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->substream->runtime; + struct virtio_snd *snd = substream->snd; + struct virtio_device *vdev = snd->vdev; + struct virtqueue *vqueue = virtsnd_pcm_queue(substream)->vqueue; + int i; + int n; + bool notify = false; + + if (!vqueue) + return -EIO; + + i = (substream->msg_last_enqueued + 1) % runtime->periods; + n = runtime->periods - atomic_read(&substream->msg_count); + + for (; n; --n, i = (i + 1) % runtime->periods) { + struct virtio_pcm_msg *msg = &substream->msgs[i]; + struct scatterlist *psgs[PCM_MSG_SG_MAX] = { + [PCM_MSG_SG_XFER] = &msg->sgs[PCM_MSG_SG_XFER], + [PCM_MSG_SG_DATA] = &msg->sgs[PCM_MSG_SG_DATA], + [PCM_MSG_SG_STATUS] = &msg->sgs[PCM_MSG_SG_STATUS] + }; + int rc; + + msg->xfer.stream_id = cpu_to_virtio32(vdev, substream->sid); + memset(&msg->status, 0, sizeof(msg->status)); + + atomic_inc(&substream->msg_count); + + if (substream->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) { + atomic_dec(&substream->msg_count); + return -EIO; + } + + substream->msg_last_enqueued = i; + } + + if (!(substream->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING))) + notify = virtqueue_kick_prepare(vqueue); + + if (notify) + if (!virtqueue_notify(vqueue)) + return -EIO; + + return 0; +} + +/** + * virtsnd_pcm_msg_complete() - Complete an I/O message. + * @msg: I/O message. + * @size: Number of bytes written. + * + * Completion of the message means the elapsed period. + * + * The interrupt handler modifies three fields of the substream structure + * (hw_ptr, xfer_xrun, msg_count) that are used in operator functions. These + * values are atomic to avoid frequent interlocks with the interrupt handler. + * This becomes especially important in the case of multiple running substreams + * that share both the virtqueue and interrupt handler. + * + * Context: Interrupt context. + */ +static void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg, size_t size) +{ + struct virtio_pcm_substream *substream = msg->substream; + snd_pcm_uframes_t hw_ptr; + unsigned int msg_count; + + /* + * 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. + */ + hw_ptr = (snd_pcm_uframes_t)atomic_read(&substream->hw_ptr); + + /* + * If the capture substream returned an incorrect status, then just + * increase the hw_ptr by the period size. + */ + if (substream->direction == SNDRV_PCM_STREAM_PLAYBACK || + size <= sizeof(msg->status)) { + hw_ptr += substream->period_size; + } else { + size -= sizeof(msg->status); + hw_ptr += size / substream->frame_bytes; + } + + atomic_set(&substream->hw_ptr, (u32)(hw_ptr % substream->buffer_size)); + atomic_set(&substream->xfer_xrun, 0); + + msg_count = atomic_dec_return(&substream->msg_count); + + if (atomic_read(&substream->xfer_enabled)) { + struct snd_pcm_runtime *runtime = substream->substream->runtime; + + runtime->delay = + bytes_to_frames(runtime, + le32_to_cpu(msg->status.latency_bytes)); + + snd_pcm_period_elapsed(substream->substream); + + virtsnd_pcm_msg_send(substream); + } else if (!msg_count) { + wake_up_all(&substream->msg_empty); + } +} + +/** + * virtsnd_pcm_notify_cb() - Process all completed I/O messages. + * @vqueue: Underlying tx/rx virtqueue. + * + * If transmission is allowed, then each completed message is immediately placed + * back at the end of the queue. + * + * Context: Interrupt context. Takes and releases the tx/rx queue spinlock. + */ +static inline void virtsnd_pcm_notify_cb(struct virtio_snd_queue *queue) +{ + unsigned long flags; + + spin_lock_irqsave(&queue->lock, flags); + while (queue->vqueue) { + virtqueue_disable_cb(queue->vqueue); + + for (;;) { + struct virtio_pcm_msg *msg; + u32 length; + + msg = virtqueue_get_buf(queue->vqueue, &length); + if (!msg) + break; + + virtsnd_pcm_msg_complete(msg, length); + } + + if (unlikely(virtqueue_is_broken(queue->vqueue))) + break; + + if (virtqueue_enable_cb(queue->vqueue)) + break; + } + 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. + * @substream: 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, ERR_PTR(-errno) on failure. + */ +struct virtio_snd_msg * +virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *substream, + unsigned int command, gfp_t gfp) +{ + struct virtio_device *vdev = substream->snd->vdev; + 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(vdev, request_size, response_size, gfp); + if (!IS_ERR(msg)) { + struct virtio_snd_pcm_hdr *hdr = sg_virt(&msg->sg_request); + + hdr->hdr.code = cpu_to_virtio32(vdev, command); + hdr->stream_id = cpu_to_virtio32(vdev, substream->sid); + } + + return msg; +} From patchwork Sun Jan 24 16:54:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 369977 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 A5C95C433E6 for ; Sun, 24 Jan 2021 16:58:36 +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 218E62168B for ; Sun, 24 Jan 2021 16:58:36 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 218E62168B 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 B0DBB18AD; Sun, 24 Jan 2021 17:57:44 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B0DBB18AD DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507514; bh=AopjAcb1HwbrtQVyZT34o7HsQYoW+Rz6LRAMNGRbqto=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=o7QwfF7Xdb8jV+aVaala5fNPiqQp1RvBFeuZIzCUto2vFLTpGdqtj9FKG5Rk8CtNh QjtjtK61dWVMePjdg7+Chmsgns9P65YUyWLZmZbpHVexdFAn5E4lZJzQAwxCsuEVul qXyc7xFFFJIxuw+KALoMJM99oUXEXKC9iJCcTWOk= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 7891BF80517; Sun, 24 Jan 2021 17:55:06 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 10DAAF80518; Sun, 24 Jan 2021 17:55:04 +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 99C48F8014E for ; Sun, 24 Jan 2021 17:54:58 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 99C48F8014E Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="zmQRUL+j" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 36CCAA1483; Sun, 24 Jan 2021 17:54:58 +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=cEpVbC9oDPt4 Vd7QhGHWvpz2WjKYYV1jqvN179ZWVh4=; b=zmQRUL+jfqA//fj3u+dcmYLi0opJ gcPuWnKTft2M081VaTmBckU8UwlZ38kxfN+bVQg0e6daLCW64NIwDo3KxVJ2XHi1 vuuAuTjX0bjodjI8jhoPZFKo2QLqvt80yA/r5ath45lnxxhPMchxYouzc2bYxCJH vEaDs7ojAJK+XhiJ1yJ9ydgUV6s13akfEX3/woY3pDyN1bjilx/YDPmJQ9/96fWp dVtg6M3xC9vQU+WdYg+HitqQ0759gDq+oes8e++PFWxjTBdfOP8KMV4ZaMk1xzyv o4nMhBjfcEIgshaLN7wYFKg6dleRS7ofS4SIz512GTbEfcBmrMA8AHLmIQ== From: Anton Yakovlev To: , , Subject: [PATCH v2 7/9] ALSA: virtio: introduce jack support Date: Sun, 24 Jan 2021 17:54:06 +0100 Message-ID: <20210124165408.1122868-8-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, 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 | 18 +++ sound/virtio/virtio_card.h | 12 ++ sound/virtio/virtio_jack.c | 255 +++++++++++++++++++++++++++++++++++++ 4 files changed, 286 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 11d025ee77c2..1dd709437208 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -93,6 +93,11 @@ static void virtsnd_event_notify_cb(struct virtqueue *vqueue) break; 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); @@ -295,10 +300,20 @@ static int virtsnd_build_devs(struct virtio_snd *snd) strscpy(snd->card->longname, "VirtIO Sound Card", sizeof(snd->card->longname)); + 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) @@ -428,6 +443,9 @@ static void virtsnd_remove(struct virtio_device *vdev) devm_kfree(&vdev->dev, pcm); } + if (snd->jacks) + devm_kfree(&vdev->dev, snd->jacks); + if (snd->substreams) devm_kfree(&vdev->dev, snd->substreams); diff --git a/sound/virtio/virtio_card.h b/sound/virtio/virtio_card.h index b11c09984882..df4b0696e8c4 100644 --- a/sound/virtio/virtio_card.h +++ b/sound/virtio/virtio_card.h @@ -26,6 +26,7 @@ #include "virtio_ctl_msg.h" #include "virtio_pcm.h" +struct virtio_jack; struct virtio_pcm_substream; /** @@ -47,6 +48,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. */ @@ -58,6 +61,8 @@ struct virtio_snd { struct list_head ctl_msgs; struct virtio_snd_event *event_msgs; struct list_head pcm_list; + struct virtio_jack *jacks; + unsigned int njacks; struct virtio_pcm_substream *substreams; unsigned int nsubstreams; }; @@ -98,4 +103,11 @@ virtsnd_pcm_queue(struct virtio_pcm_substream *substream) return virtsnd_rx_queue(substream->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..83593c59f6bf --- /dev/null +++ b/sound/virtio/virtio_jack.c @@ -0,0 +1,255 @@ +// 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 "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; + unsigned int nid; + unsigned int features; + unsigned int defconf; + unsigned int caps; + bool connected; + int type; +}; + +/** + * virtsnd_jack_get_label() - Get the name string for the jack. + * @jack: 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 *jack) +{ + unsigned int defconf = jack->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. + * @jack: 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 *jack) +{ + unsigned int defconf = jack->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; + unsigned int i; + int rc; + + virtio_cread(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 = devm_kcalloc(&vdev->dev, 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) + return rc; + + for (i = 0; i < snd->njacks; ++i) { + struct virtio_jack *jack = &snd->jacks[i]; + struct virtio_pcm *pcm; + + jack->nid = le32_to_cpu(info[i].hdr.hda_fn_nid); + jack->features = le32_to_cpu(info[i].features); + jack->defconf = le32_to_cpu(info[i].hda_reg_defconf); + jack->caps = le32_to_cpu(info[i].hda_reg_caps); + jack->connected = info[i].connected; + + pcm = virtsnd_pcm_find_or_create(snd, jack->nid); + if (IS_ERR(pcm)) + return PTR_ERR(pcm); + } + + devm_kfree(&vdev->dev, info); + + return 0; +} + +/** + * 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) +{ + unsigned int i; + int rc; + + for (i = 0; i < snd->njacks; ++i) { + struct virtio_jack *jack = &snd->jacks[i]; + + jack->type = virtsnd_jack_get_type(jack); + + rc = snd_jack_new(snd->card, virtsnd_jack_get_label(jack), + jack->type, &jack->jack, true, true); + if (rc) + return rc; + + if (!jack->jack) + continue; + + jack->jack->private_data = jack; + + snd_jack_report(jack->jack, + jack->connected ? jack->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) +{ + unsigned int jack_id = le32_to_cpu(event->data); + struct virtio_jack *jack; + + if (jack_id >= snd->njacks) + return; + + jack = &snd->jacks[jack_id]; + + switch (le32_to_cpu(event->hdr.code)) { + case VIRTIO_SND_EVT_JACK_CONNECTED: { + jack->connected = true; + break; + } + case VIRTIO_SND_EVT_JACK_DISCONNECTED: { + jack->connected = false; + break; + } + default: { + return; + } + } + + snd_jack_report(jack->jack, jack->connected ? jack->type : 0); +} From patchwork Sun Jan 24 16:54:08 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Yakovlev X-Patchwork-Id: 369976 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 10E78C433DB for ; Sun, 24 Jan 2021 16:59:19 +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 8537122CA2 for ; Sun, 24 Jan 2021 16:59:18 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8537122CA2 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 21FB4190C; Sun, 24 Jan 2021 17:58:27 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 21FB4190C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1611507557; bh=rULIMTPFuk1jke58EOOeVKCk3AbCxpitKS3AJ2GJ080=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=hlYv0xKZTCbaKNmakvaktbu3YKoXhJweTdweT0NCWjdokHxpkiT0T2JSu6ixnrwuw LPUdqtm4HRrmlsyt86XHP0CiAqIHYeqLaauQuGEQ6ZrbRIL5MdomAjGqwmUyF7j2bG yNTzRWG2VMTb3dMpxg0KJKj21cD4N8jHx7aTwffc= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 18337F8051F; Sun, 24 Jan 2021 17:55:17 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 00DC3F80524; Sun, 24 Jan 2021 17:55:15 +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 BDBA4F8051F for ; Sun, 24 Jan 2021 17:55:12 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz BDBA4F8051F Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=opensynergy.com header.i=@opensynergy.com header.b="ddEXikMV" Received: from SR-MAILGATE-02.opensynergy.com (localhost.localdomain [127.0.0.1]) by mx1.opensynergy.com (Proxmox) with ESMTP id 4D4B8A1484; Sun, 24 Jan 2021 17:55:12 +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=vX3CRhwxWMNn x+gi+0QcsHwpBgsOeqVz7H3Ivu36FTw=; b=ddEXikMVBpleWeXCRUnX+9KDnWYS n85tz7BW/70508N0Dxkx0U39Ib4IEaz8Ce1xcFd4lTHa+/JaJg/pQJ5qTJIw+Q9e W89lv3h0EGtseKg9oMK1I84U1AXQaMYPDOEYgxQ+Fainai5IoHhNU9ThCnwttdBo rL3bqGjynR3ROXEDiZRoL+z540EngE89YJuYITXtZsNrS+GrhVzgxR1MHgWsbY9e Eiy4qnk/R91zCFoER3JV2M4A2YVItb5asjmIiMdYdpqAty502aJ8Vf2NRsWZHKyD /zpVDoJezotnm5py8ra6Q/2wHROZZfH8QN5/78cIPP7c+Wc8saZPhwoAXA== From: Anton Yakovlev To: , , Subject: [PATCH v2 9/9] ALSA: virtio: introduce device suspend/resume support Date: Sun, 24 Jan 2021 17:54:08 +0100 Message-ID: <20210124165408.1122868-10-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, 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 | 54 ++++++++++++++++++++ sound/virtio/virtio_pcm.c | 40 +++++++++++++++ sound/virtio/virtio_pcm.h | 6 +++ sound/virtio/virtio_pcm_ops.c | 93 ++++++++++++++++++++--------------- 4 files changed, 154 insertions(+), 39 deletions(-) diff --git a/sound/virtio/virtio_card.c b/sound/virtio/virtio_card.c index fabf91fc1c9c..90dadf18d9b0 100644 --- a/sound/virtio/virtio_card.c +++ b/sound/virtio/virtio_card.c @@ -491,6 +491,56 @@ static void virtsnd_config_changed(struct virtio_device *vdev) "sound device configuration was changed\n"); } +#ifdef CONFIG_PM_SLEEP +/** + * virtsnd_freeze() - Suspend device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +static int virtsnd_freeze(struct virtio_device *vdev) +{ + struct virtio_snd *snd = vdev->priv; + + virtsnd_disable_vqs(snd); + + vdev->config->reset(vdev); + vdev->config->del_vqs(vdev); + + return 0; +} + +/** + * virtsnd_restore() - Resume device. + * @vdev: VirtIO parent device. + * + * Context: Any context that permits to sleep. + * 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); + + if (snd->nsubstreams) { + rc = virtsnd_pcm_restore(snd); + if (rc) + return rc; + } + + 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 }, @@ -504,6 +554,10 @@ static struct virtio_driver virtsnd_driver = { .probe = virtsnd_probe, .remove = virtsnd_remove, .config_changed = virtsnd_config_changed, +#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 6a1ca6b2c3ca..68d9c6dee13a 100644 --- a/sound/virtio/virtio_pcm.c +++ b/sound/virtio/virtio_pcm.c @@ -122,6 +122,7 @@ static int virtsnd_pcm_build_hw(struct virtio_pcm_substream *substream, 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) { @@ -511,6 +512,45 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd) return 0; } +#ifdef CONFIG_PM_SLEEP +/** + * virtsnd_pcm_restore() - Resume PCM substreams. + * @snd: VirtIO sound device. + * + * Context: Any context that permits to sleep. + * Return: 0 on success, -errno on failure. + */ +int virtsnd_pcm_restore(struct virtio_snd *snd) +{ + unsigned int i; + + for (i = 0; i < snd->nsubstreams; ++i) { + struct virtio_pcm_substream *substream = &snd->substreams[i]; + struct snd_pcm_substream *ksubstream = substream->substream; + int rc; + + if (!substream->suspended) + continue; + + /* + * We restart the substream by executing the standard command + * sequence. The START command will be sent from a subsequent + * call to the trigger() callback function after the device has + * been resumed. + */ + rc = ksubstream->ops->hw_params(ksubstream, NULL); + if (rc) + return rc; + + rc = ksubstream->ops->prepare(ksubstream); + if (rc) + return rc; + } + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + /** * virtsnd_pcm_event() - Handle the PCM device event notification. * @snd: VirtIO sound device. diff --git a/sound/virtio/virtio_pcm.h b/sound/virtio/virtio_pcm.h index a326b921b947..23d0fdd57225 100644 --- a/sound/virtio/virtio_pcm.h +++ b/sound/virtio/virtio_pcm.h @@ -41,6 +41,7 @@ struct virtio_pcm_msg; * @hw_ptr: Substream hardware pointer value in frames [0 ... buffer_size). * @xfer_enabled: Data transfer state (0 - off, 1 - on). * @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun). + * @suspended: Kernel ALSA substream is suspended. * @msgs: 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. @@ -60,6 +61,7 @@ struct virtio_pcm_substream { atomic_t hw_ptr; atomic_t xfer_enabled; atomic_t xfer_xrun; + bool suspended; struct virtio_pcm_msg *msgs; int msg_last_enqueued; atomic_t msg_count; @@ -102,6 +104,10 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd); int virtsnd_pcm_build_devs(struct virtio_snd *snd); +#ifdef CONFIG_PM_SLEEP +int virtsnd_pcm_restore(struct virtio_snd *snd); +#endif /* CONFIG_PM_SLEEP */ + void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event); void virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue); diff --git a/sound/virtio/virtio_pcm_ops.c b/sound/virtio/virtio_pcm_ops.c index 19882777fcd6..0b3c66802325 100644 --- a/sound/virtio/virtio_pcm_ops.c +++ b/sound/virtio/virtio_pcm_ops.c @@ -187,6 +187,8 @@ static int virtsnd_pcm_open(struct snd_pcm_substream *substream) if (!ss) return -EBADFD; + ss->suspended = false; + substream->runtime->hw = ss->hw; substream->private_data = ss; @@ -241,18 +243,20 @@ static int virtsnd_pcm_hw_params(struct snd_pcm_substream *substream, int vrate = -1; int rc; - /* - * If we got here after ops->trigger() was called, the queue may - * still contain messages. In this case, we need to release the - * substream first. - */ - if (atomic_read(&ss->msg_count)) { - rc = virtsnd_pcm_release(ss); - if (rc) { - dev_err(&vdev->dev, - "SID %u: invalid I/O queue state\n", - ss->sid); - return rc; + if (!ss->suspended) { + /* + * If we got here after ops->trigger() was called, the queue may + * still contain messages. In this case, we need to release the + * substream first. + */ + if (atomic_read(&ss->msg_count)) { + rc = virtsnd_pcm_release(ss); + if (rc) { + dev_err(&vdev->dev, + "SID %u: invalid I/O queue state\n", + ss->sid); + return rc; + } } } @@ -383,37 +387,41 @@ static int virtsnd_pcm_hw_free(struct snd_pcm_substream *substream) static int virtsnd_pcm_prepare(struct snd_pcm_substream *substream) { struct virtio_pcm_substream *ss = snd_pcm_substream_chip(substream); - struct virtio_snd_queue *queue = virtsnd_pcm_queue(ss); struct virtio_snd_msg *msg; unsigned long flags; int rc; - /* - * If we got here after ops->trigger() was called, the queue may - * still contain messages. In this case, we need to reset the - * substream first. - */ - if (atomic_read(&ss->msg_count)) { - rc = virtsnd_pcm_hw_params(substream, NULL); - if (rc) - return rc; - } - - spin_lock_irqsave(&queue->lock, flags); - ss->msg_last_enqueued = -1; - spin_unlock_irqrestore(&queue->lock, flags); + if (!ss->suspended) { + struct virtio_snd_queue *queue = virtsnd_pcm_queue(ss); + + /* + * If we got here after ops->trigger() was called, the queue may + * still contain messages. In this case, we need to reset the + * substream first. + */ + if (atomic_read(&ss->msg_count)) { + rc = virtsnd_pcm_hw_params(substream, NULL); + if (rc) + return rc; + } - /* - * Since I/O messages are asynchronous, they can be completed - * when the runtime structure no longer exists. Since each - * completion implies incrementing the hw_ptr, we cache all the - * current values needed to compute the new hw_ptr value. - */ - ss->frame_bytes = substream->runtime->frame_bits >> 3; - ss->period_size = substream->runtime->period_size; - ss->buffer_size = substream->runtime->buffer_size; + spin_lock_irqsave(&queue->lock, flags); + ss->msg_last_enqueued = -1; + spin_unlock_irqrestore(&queue->lock, flags); + + /* + * Since I/O messages are asynchronous, they can be completed + * when the runtime structure no longer exists. Since each + * completion implies incrementing the hw_ptr, we cache all the + * current values needed to compute the new hw_ptr value. + */ + ss->frame_bytes = substream->runtime->frame_bits >> 3; + ss->period_size = substream->runtime->period_size; + ss->buffer_size = substream->runtime->buffer_size; + + atomic_set(&ss->hw_ptr, 0); + } - atomic_set(&ss->hw_ptr, 0); atomic_set(&ss->xfer_xrun, 0); atomic_set(&ss->msg_count, 0); @@ -446,9 +454,12 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) switch (command) { case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: { int rc; + ss->suspended = false; + spin_lock(&queue->lock); rc = virtsnd_pcm_msg_send(ss); spin_unlock(&queue->lock); @@ -465,9 +476,13 @@ static int virtsnd_pcm_trigger(struct snd_pcm_substream *substream, int command) return virtsnd_ctl_msg_send(snd, msg); } case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: { atomic_set(&ss->xfer_enabled, 0); + if (command == SNDRV_PCM_TRIGGER_SUSPEND) + ss->suspended = true; + msg = virtsnd_pcm_ctl_msg_alloc(ss, VIRTIO_SND_R_PCM_STOP, GFP_ATOMIC); if (IS_ERR(msg))