From patchwork Fri Mar 20 15:40:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193227 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A111BC4332B for ; Fri, 20 Mar 2020 15:42:25 +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 2AC6F2070A for ; Fri, 20 Mar 2020 15:42:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="WHcloZJV" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2AC6F2070A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 822151660; Fri, 20 Mar 2020 16:41:33 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 822151660 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584718943; bh=YS3NSO6q+i2WiQkBUEZ0/rtLhHtgYNrOeRRY3eC4bGU=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=WHcloZJVw92mqvpPQgu0atceVD9k95CUdg2b1ZBaLbx/TeSgZy3seuGdseddJNLjw QVg1uRsPfUnOhyfL7pdj90dEaRQX3ZVyr+RTPTJxIkykDWuGdfX/1pr+w6rK3bLqby pr2QTpE4UPtA/CqcecWgS9MMgBb/WV3FgH/xcftA= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id D29FFF802A0; Fri, 20 Mar 2020 16:40:41 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id A3B2AF8028C; Fri, 20 Mar 2020 16:40:36 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id D3EC3F800C0; Fri, 20 Mar 2020 16:40:29 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz D3EC3F800C0 IronPort-SDR: TMKazTZU/rLYt8a0dNBido/F/HOn6RgEUvEKVIHMxtKKWfQZwVCaDw3HAA1qmRisxj+79L0FQm 9wrB/rY89T8A== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:25 -0700 IronPort-SDR: M6/L5so0ACwj3NG3ef1LhPgWkzea1mcea7cD1dI26Kd4jEertV6YA0EEZ3wyTmmDTZpV/M1lTq jbY1Q/3lFahA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257374" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:24 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 01/12] ASoC: add function parameters to enable forced path pruning Date: Fri, 20 Mar 2020 16:40:10 +0100 Message-Id: <20200320154021.5964-2-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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" This is a preparation for the host part of a virtualised VirtIO audio host-guest driver pair. It adds a "mode" parameter to soc_dpcm_runtime_update() to allow it to be used when stopping streaming in a virtual machine, which requires forced DPCM audio path pruning. For audio virtualisation the host side driver will be using the vhost API, i.e. it will run completely in the kernel. When a guest begins to stream audio, the vhost calls snd_soc_runtime_activate() and soc_dpcm_runtime_update() to activate an audio path and update audio routing. When streaming is stopped, the vhost driver calls soc_dpcm_runtime_update() and snd_soc_runtime_deactivate(). The latter doesn't work at the moment, because the DPCM doesn't recognise the path as inactive. We address this by adding a "mode" parameter to soc_dpcm_runtime_update(). If virtualisation isn't used, the current behaviour isn't affected. Signed-off-by: Guennadi Liakhovetski --- include/sound/soc-dpcm.h | 28 ++++++++++++++++---- sound/soc/soc-compress.c | 2 +- sound/soc/soc-dapm.c | 8 +++--- sound/soc/soc-pcm.c | 67 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 74 insertions(+), 31 deletions(-) diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 0f6c50b..b961c06 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -61,6 +61,23 @@ enum snd_soc_dpcm_trigger { SND_SOC_DPCM_TRIGGER_BESPOKE, }; +/** + * enum snd_soc_dpcm_update_mode - mode for calling soc_dpcm_runtime_update() + * + * @SND_SOC_DPCM_UPDATE_FULL: default mode, used for mux, mixer, and + * volume widgets + * @SND_SOC_DPCM_UPDATE_NEW_ONLY: a pipeline is starting. Skip checking + * for old paths. + * @SND_SOC_DPCM_UPDATE_OLD_ONLY: a pipeline is shutting down. Skip + * checking for new paths, force old path + * pruning. + */ +enum snd_soc_dpcm_update_mode { + SND_SOC_DPCM_UPDATE_FULL, + SND_SOC_DPCM_UPDATE_NEW_ONLY, + SND_SOC_DPCM_UPDATE_OLD_ONLY, +}; + /* * Dynamic PCM link * This links together a FE and BE DAI at runtime and stores the link @@ -133,7 +150,8 @@ struct snd_pcm_substream * snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream); /* update audio routing between PCMs and any DAI links */ -int snd_soc_dpcm_runtime_update(struct snd_soc_card *card); +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card, + enum snd_soc_dpcm_update_mode mode); #ifdef CONFIG_DEBUG_FS void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); @@ -143,11 +161,11 @@ static inline void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) } #endif -int dpcm_path_get(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list_); +int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list_); void dpcm_path_put(struct snd_soc_dapm_widget_list **list); -int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list, int new); +int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, + struct snd_soc_dapm_widget_list **list, bool new, bool force_prune); int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream); void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream); diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 50062eb..66d7e78 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -162,7 +162,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) dev_dbg(fe->dev, "Compress ASoC: %s no valid %s route\n", fe->dai_link->name, stream ? "capture" : "playback"); /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); + dpcm_process_paths(fe, stream, &list, true, false); fe->dpcm[stream].runtime = fe_substream->runtime; fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d5eb52f..b3c71d1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2291,7 +2291,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - snd_soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card, SND_SOC_DPCM_UPDATE_FULL); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2356,7 +2356,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - snd_soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card, SND_SOC_DPCM_UPDATE_FULL); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); @@ -3396,7 +3396,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - snd_soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card, SND_SOC_DPCM_UPDATE_FULL); return change; } @@ -3501,7 +3501,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - snd_soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card, SND_SOC_DPCM_UPDATE_FULL); return change; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2b915f4..6a2502e 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1660,14 +1660,14 @@ static bool dpcm_be_is_active(struct snd_soc_dpcm *dpcm, int stream, } static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, - struct snd_soc_dapm_widget_list **list_) + struct snd_soc_dapm_widget_list **list_, bool force) { struct snd_soc_dpcm *dpcm; int prune = 0; /* Destroy any old FE <--> BE connections */ for_each_dpcm_be(fe, stream, dpcm) { - if (dpcm_be_is_active(dpcm, stream, *list_)) + if (!force && dpcm_be_is_active(dpcm, stream, *list_)) continue; dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n", @@ -1742,12 +1742,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, * FE substream. */ int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, - int stream, struct snd_soc_dapm_widget_list **list, int new) + int stream, struct snd_soc_dapm_widget_list **list, + bool new, bool force_prune) { if (new) return dpcm_add_paths(fe, stream, list); else - return dpcm_prune_paths(fe, stream, list); + return dpcm_prune_paths(fe, stream, list, force_prune); } void dpcm_clear_pending_state(struct snd_soc_pcm_runtime *fe, int stream) @@ -2772,11 +2773,13 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream) return ret; } -static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) +static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream, + bool force) { struct snd_pcm_substream *substream = snd_soc_dpcm_get_substream(fe, stream); enum snd_soc_dpcm_trigger trigger = fe->dai_link->trigger[stream]; + int event = force ? SND_SOC_DAPM_STREAM_STOP : SND_SOC_DAPM_STREAM_NOP; int err; dev_dbg(fe->dev, "ASoC: runtime %s close on FE %s\n", @@ -2808,7 +2811,7 @@ static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream) dev_err(fe->dev,"ASoC: shutdown FE failed %d\n", err); /* run the stream event for each BE */ - dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); + dpcm_dapm_stream_event(fe, stream, event); return 0; } @@ -2901,7 +2904,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) return ret; } -static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) +static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, bool new, + bool force_prune) { struct snd_soc_dapm_widget_list *list; int stream; @@ -2947,13 +2951,13 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) } /* update any playback/capture paths */ - count = dpcm_process_paths(fe, stream, &list, new); + count = dpcm_process_paths(fe, stream, &list, new, force_prune); if (count) { dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE); if (new) ret = dpcm_run_update_startup(fe, stream); else - ret = dpcm_run_update_shutdown(fe, stream); + ret = dpcm_run_update_shutdown(fe, stream, force_prune); if (ret < 0) dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n"); dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO); @@ -2971,25 +2975,46 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card, + enum snd_soc_dpcm_update_mode mode) { struct snd_soc_pcm_runtime *fe; int ret = 0; mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME); + /* shutdown all old paths first */ - for_each_card_rtds(card, fe) { - ret = soc_dpcm_fe_runtime_update(fe, 0); - if (ret) - goto out; - } + if (mode != SND_SOC_DPCM_UPDATE_NEW_ONLY) + /* + * This is entered if mode == FULL or OLD_ONLY. In both cases we + * have to call soc_dpcm_fe_runtime_update() but only in the + * OLD_ONLY case we have to set the "force" (last) parameter to + * "true." + */ + for_each_card_rtds(card, fe) { + /* + * check "old" paths (new = false), only force for + * shutting down. + */ + ret = soc_dpcm_fe_runtime_update(fe, false, + mode == SND_SOC_DPCM_UPDATE_OLD_ONLY); + if (ret) + goto out; + } /* bring new paths up */ - for_each_card_rtds(card, fe) { - ret = soc_dpcm_fe_runtime_update(fe, 1); - if (ret) - goto out; - } + if (mode != SND_SOC_DPCM_UPDATE_OLD_ONLY) + /* + * This is entered if mode == FULL or NEW_ONLY. In both cases we + * have to call soc_dpcm_fe_runtime_update() with the "force" + * (last) parameter set to "false" + */ + for_each_card_rtds(card, fe) { + /* check "new" paths (new = true), no forcing */ + ret = soc_dpcm_fe_runtime_update(fe, true, false); + if (ret) + goto out; + } out: mutex_unlock(&card->mutex); @@ -3045,7 +3070,7 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) } /* calculate valid and active FE <-> BE dpcms */ - dpcm_process_paths(fe, stream, &list, 1); + dpcm_process_paths(fe, stream, &list, true, false); ret = dpcm_fe_dai_startup(fe_substream); if (ret < 0) From patchwork Fri Mar 20 15:40:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193225 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D3C7C4332D for ; Fri, 20 Mar 2020 15:45:05 +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 0553A2070A for ; Fri, 20 Mar 2020 15:45:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="bS+m52PS" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 0553A2070A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 4C349823; Fri, 20 Mar 2020 16:44:13 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 4C349823 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584719103; bh=HndsIeMbUnlgvKj9VoGwly/yM9fjQj+xriuxL6qj6vU=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=bS+m52PS8Wy+HkL6/E92dTz4W8qKDtQmPCRgK/qgK5brHrJjJMt8sByLaQZR+gSJp fbmRwByjDeLp/6qxk2zS0crVlzD/x2ntrJTcR2TQICS19MN6yITZzaTzbiZfajm8Cu JJVhB5JZXNdy3tkIziShu6u/L74cpaMqS+0haD2U= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id E441AF802F8; Fri, 20 Mar 2020 16:41:02 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9CDB0F802A2; Fri, 20 Mar 2020 16:40:44 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 34CCBF80139; Fri, 20 Mar 2020 16:40:30 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 34CCBF80139 IronPort-SDR: tiJklcEqnn62QltgynqKMbPuLB6l3XfIug8DI+Kgk7OtZJHbaPAV9Q7bv1lOsAZh+HihSF2lX7 NAVyRvcjg2SA== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:27 -0700 IronPort-SDR: Hc4o6CWf2Dab14ToCsqDC32jDZL+7MK9TChYzdEhAiowAh+vDbK9+CZqkzx4iPf/XCkAe6RKjg TV0HIXC5Vi6w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257385" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:26 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 03/12] ASoC: SOF: VirtIO: make a function global Date: Fri, 20 Mar 2020 16:40:12 +0100 Message-Id: <20200320154021.5964-4-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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" sof_ipc_tx_message_unlocked() will be needed for VirtIO code, make it global. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/ipc.c | 7 ++++--- sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 1c67949..cf57085 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -231,9 +231,9 @@ static int tx_wait_done(struct snd_sof_ipc *ipc, struct snd_sof_ipc_msg *msg, } /* send IPC message from host to DSP */ -static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, - void *msg_data, size_t msg_bytes, - void *reply_data, size_t reply_bytes) +int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes) { struct snd_sof_dev *sdev = ipc->sdev; struct snd_sof_ipc_msg *msg; @@ -284,6 +284,7 @@ static int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, return ret; } +EXPORT_SYMBOL(sof_ipc_tx_message_unlocked); /* send IPC message from host to DSP */ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a4b297c..922b671 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -493,6 +493,9 @@ int sof_ipc_tx_message(struct snd_sof_ipc *ipc, u32 header, int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, u32 header, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes); +int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, + void *msg_data, size_t msg_bytes, + void *reply_data, size_t reply_bytes); /* * Trace/debug From patchwork Fri Mar 20 15:40:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193224 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 89399C4332D for ; Fri, 20 Mar 2020 15:46:24 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 131D42072C for ; Fri, 20 Mar 2020 15:46:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="A0jbSt/R" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 131D42072C Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 572E311C; Fri, 20 Mar 2020 16:45:32 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 572E311C DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584719182; bh=CzDuwL0kjtKcimHd1XbGl/22092bJljLNeREghNsiYI=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=A0jbSt/R4LsUnpfbzEFxbVPXsnTXLu4hVWLhNyxKAjhmmuOyL2cJsxfu+NYjUfNsl ouRrnJFtN4dSyJKb/yZAjy2AUBneqzAfEHIvUE0+hUDJG6tJsifnn3v/NK8O2nrPl7 HwX+iwm/VW/nJayS/b83HB2qq+GTq2QM84aBdRGw= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id CF15FF80321; Fri, 20 Mar 2020 16:41:11 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 4CB98F802A7; Fri, 20 Mar 2020 16:40:45 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id E7F7EF80162; Fri, 20 Mar 2020 16:40:32 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz E7F7EF80162 IronPort-SDR: yGvEo8tMRsHcdshW+66R7twUcgf742gzv4hI8oZxwUi6NfyOgJy2PvJ1gM/VNFHq11X1bATjhl q7Xl5a0SmXIg== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:28 -0700 IronPort-SDR: p8F0MvKARWqqYO01osbo5cJFBOSxR3FXo0RnszBfJlwvNvxCBMmLe2nYyYvsre3pU9Q6HBUPS1 Da218k9qi4LA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257388" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:27 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 04/12] vhost: convert VHOST_VSOCK_SET_RUNNING to a generic ioctl Date: Fri, 20 Mar 2020 16:40:13 +0100 Message-Id: <20200320154021.5964-5-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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" VHOST_VSOCK_SET_RUNNING is used by the vhost vsock driver to perform crucial VirtQueue initialisation, like assigning .private fields and calling vhost_vq_init_access(), and clean up. However, this ioctl is actually extremely useful for any vhost driver, that doesn't have a side channel to inform it of a status change, e.g. upon a guest reboot. This patch makes that ioctl generic, while preserving its numeric value and also keeping the original alias. Signed-off-by: Guennadi Liakhovetski --- include/uapi/linux/vhost.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h index 40d028ee..c628103 100644 --- a/include/uapi/linux/vhost.h +++ b/include/uapi/linux/vhost.h @@ -93,6 +93,8 @@ #define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64) #define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64) +#define VHOST_SET_RUNNING _IOW(VHOST_VIRTIO, 0x61, int) + /* VHOST_NET specific defines */ /* Attach virtio net ring to a raw socket, or tap device. @@ -114,6 +116,6 @@ /* VHOST_VSOCK specific defines */ #define VHOST_VSOCK_SET_GUEST_CID _IOW(VHOST_VIRTIO, 0x60, __u64) -#define VHOST_VSOCK_SET_RUNNING _IOW(VHOST_VIRTIO, 0x61, int) +#define VHOST_VSOCK_SET_RUNNING VHOST_SET_RUNNING #endif From patchwork Fri Mar 20 15:40:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193226 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9CD92C4332D for ; Fri, 20 Mar 2020 15:44:01 +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 236C52070A for ; Fri, 20 Mar 2020 15:44:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="X1gKHNZy" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 236C52070A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 702AE1607; Fri, 20 Mar 2020 16:43:09 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 702AE1607 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584719039; bh=c7SIsHAAXjU1YLHnq0zVFulXi1hfunmviwHY2Bjo2p0=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=X1gKHNZylWsGE53Q0ssqnBHwN1G0Et8hUPAdz6cnvLRbcRYRrPaQKzNnfctjjndFe ZqlAtKzBMAQJV3Do12iEJha1tXdjJi27Yj41PrC3N1AAWU2qMgYv7ebunFdXiDio7U Wnv5B5zTVYe8cSizDiwBziNdOo9JHlQumaUmRXKQ= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id 35042F802DD; Fri, 20 Mar 2020 16:40:54 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id 9D827F80212; Fri, 20 Mar 2020 16:40:43 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id C50B8F80212; Fri, 20 Mar 2020 16:40:33 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz C50B8F80212 IronPort-SDR: uMO4h0bQGMkj/rrBF0alviayyNgTfZhZV5GB8G9bou8cEoHd4WO4VN6pHd+g2E+R0HggXEDWFV lkMypyk7onEg== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:29 -0700 IronPort-SDR: //q3uznxU8M/QSmlnIb7/VfWNZHw1mP0Lx2ucSpcXrE9+qEzBiBfSuvbImUDFbQ+5dcx2f5zPm pT6aV+71d4Jg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257390" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:28 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 05/12] ASoC: SOF: support IPC with immediate response Date: Fri, 20 Mar 2020 16:40:14 +0100 Message-Id: <20200320154021.5964-6-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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" Usually when an IPC message is sent, we have to wait for a reply from the DSP or from the host in the VirtIO case. However, sometimes in the VirtIO case a response is available immediately. Skip sleeping in such cases. Signed-off-by: Guennadi Liakhovetski --- sound/soc/sof/ipc.c | 9 +++++---- sound/soc/sof/ops.h | 10 +++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index cf57085..6fa501c1 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -262,6 +262,10 @@ int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, sdev->msg = msg; + /* + * If snd_sof_dsp_send_msg() returns a positive number it means, that a + * response is already available, no need to sleep waiting for it + */ ret = snd_sof_dsp_send_msg(sdev, msg); /* Next reply that we receive will be related to this message */ if (!ret) @@ -279,10 +283,7 @@ int sof_ipc_tx_message_unlocked(struct snd_sof_ipc *ipc, u32 header, ipc_log_header(sdev->dev, "ipc tx", msg->header); /* now wait for completion */ - if (!ret) - ret = tx_wait_done(ipc, msg, reply_data); - - return ret; + return tx_wait_done(ipc, msg, reply_data); } EXPORT_SYMBOL(sof_ipc_tx_message_unlocked); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index a771500..43bcfbf 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -274,7 +274,15 @@ static inline void snd_sof_dsp_block_write(struct snd_sof_dev *sdev, u32 bar, sof_ops(sdev)->block_write(sdev, bar, offset, src, bytes); } -/* ipc */ +/** + * snd_sof_dsp_send_msg - call sdev ops to send a message + * @sdev: sdev context + * @msg: message to send + * + * Returns < 0 - an error code + * 0 - the message has been sent, wait for a reply + * > 0 - the message has been sent, a reply is already available + */ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { From patchwork Fri Mar 20 15:40:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193222 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3899DC4332D for ; Fri, 20 Mar 2020 15:48:55 +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 68E012070A for ; Fri, 20 Mar 2020 15:48:54 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="McqDinQw" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 68E012070A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 AC55086F; Fri, 20 Mar 2020 16:48:02 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz AC55086F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584719332; bh=9d0lkmKPuy87+KM2q6St0uGYaHt2neT1JXaLetRlng4=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=McqDinQwwzDOHDG+Bdz+aAfFLCWfa4tzIPKDowCVqFSuy+TsIxDDqLQiefFLEvCrc GBOM12yct7x5+oQBSX1H2n3XVAnZzKUY2LO9kW0FQJlU1BBeLSnDiUQmdZoK4qIUVt 4CYJ7XPISC50T7x9V5A5Bg6E1NEteon3jn00mxQY= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id BAA13F8035F; Fri, 20 Mar 2020 16:41:22 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id B1168F802EA; Fri, 20 Mar 2020 16:41:00 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 900C4F8028B; Fri, 20 Mar 2020 16:40:37 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 900C4F8028B IronPort-SDR: NWy1JnRq8u8srVxM42oISwZ/5Z4FV47NXj5iro0JfqBj/X233dJb2AYrNLpJQTt1sfrvn1oz3s ZxdSwawmRMFA== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:34 -0700 IronPort-SDR: uO52HjQca/Gf8eaFMJot38ETcPIuiXTp1v630epdZ7QjHEFgpPIXD8HB7zozUInXpPhNdYj60y KSBQnxVPva3w== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257404" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:32 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 09/12] ASoC: SOF: add a vhost driver: sound part Date: Fri, 20 Mar 2020 16:40:18 +0100 Message-Id: <20200320154021.5964-10-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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 SOF VirtIO driver uses a vhost driver as a counterpart to communicate with the DSP. This patch adds a sound interface of the vhost driver. Signed-off-by: Guennadi Liakhovetski --- include/sound/soc-topology.h | 3 + include/sound/sof/virtio.h | 73 +++ include/uapi/linux/vhost.h | 5 + include/uapi/linux/vhost_types.h | 7 + sound/soc/soc-pcm.c | 31 +- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 4 + sound/soc/sof/core.c | 6 + sound/soc/sof/ipc.c | 5 + sound/soc/sof/pcm.c | 6 +- sound/soc/sof/pm.c | 4 + sound/soc/sof/sof-audio.c | 9 + sound/soc/sof/sof-audio.h | 17 + sound/soc/sof/sof-priv.h | 18 + sound/soc/sof/topology.c | 55 +- sound/soc/sof/vhost-be.c | 1093 ++++++++++++++++++++++++++++++++++++++ 16 files changed, 1324 insertions(+), 13 deletions(-) create mode 100644 sound/soc/sof/vhost-be.c diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 5223896..ea0c2a6 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -34,6 +34,9 @@ /* object scan be loaded and unloaded in groups with identfying indexes */ #define SND_SOC_TPLG_INDEX_ALL 0 /* ID that matches all FW objects */ +#define SOC_VIRT_DAI_PLAYBACK "VM FE Playback" +#define SOC_VIRT_DAI_CAPTURE "VM FE Capture" + /* dynamic object type */ enum snd_soc_dobj_type { SND_SOC_DOBJ_NONE = 0, /* object is not dynamic */ diff --git a/include/sound/sof/virtio.h b/include/sound/sof/virtio.h index d7d94da..fc98664 100644 --- a/include/sound/sof/virtio.h +++ b/include/sound/sof/virtio.h @@ -11,6 +11,8 @@ #ifndef _SOF_VIRTIO_H #define _SOF_VIRTIO_H +#include + #include /* @@ -126,4 +128,75 @@ struct dsp_sof_data_resp { #define HDR_SIZE_REQ offsetof(struct dsp_sof_data_req, data) #define HDR_SIZE_RESP offsetof(struct dsp_sof_data_resp, data) +struct snd_sof_dev; +struct sof_ipc_stream_posn; + +#if IS_ENABLED(CONFIG_VHOST_SOF) +struct firmware; + +struct vhost_dsp; +struct sof_vhost_ops { + int (*update_posn)(struct vhost_dsp *dsp, + struct sof_ipc_stream_posn *posn); +}; + +struct dsp_sof_client { + const struct firmware *fw; + struct snd_sof_dev *sdev; + /* List of guest endpoints, connecting to the host mixer or demux */ + struct list_head pipe_conn; + /* List of vhost instances on a DSP */ + struct list_head list; + + /* Component ID range index in the bitmap */ + unsigned int id; + + /* the comp_ids for this vm audio */ + int comp_id_begin; + int comp_id_end; + + unsigned int reset_count; + + struct vhost_dsp *vhost; +}; + +/* The below functions are only referenced when VHOST_SOF is selected */ +struct device; +void dsp_sof_client_release(struct dsp_sof_client *client); +struct dsp_sof_client *dsp_sof_client_add(struct snd_sof_dev *sdev, + struct vhost_dsp *dsp); +struct device *dsp_sof_dev_init(const struct sof_vhost_ops *ops); +struct vhost_dsp_topology; +int dsp_sof_set_tplg(struct dsp_sof_client *client, + const struct vhost_dsp_topology *tplg); +/* Copy audio data between DMA and VirtQueue */ +int dsp_sof_stream_data(struct dsp_sof_client *client, + struct dsp_sof_data_req *req, + struct dsp_sof_data_resp *reply); +/* Forward an IPC message from a guest to the DSP */ +int dsp_sof_ipc_fwd(struct dsp_sof_client *client, int vq_idx, + void *ipc_buf, void *reply_buf, + size_t count, size_t reply_sz); + +/* The below functions are always referenced, they need dummy counterparts */ +int dsp_sof_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn); +void dsp_sof_suspend(struct snd_sof_dev *sdev); +void dsp_sof_dev_set(struct snd_sof_dev *sdev); +#else +static inline int dsp_sof_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn) +{ + return 0; +} + +static inline void dsp_sof_suspend(struct snd_sof_dev *sdev) +{ +} + +static inline void dsp_sof_dev_set(struct snd_sof_dev *sdev) +{ +} +#endif + #endif diff --git a/include/uapi/linux/vhost.h b/include/uapi/linux/vhost.h index c628103..a9b0f99a 100644 --- a/include/uapi/linux/vhost.h +++ b/include/uapi/linux/vhost.h @@ -118,4 +118,9 @@ #define VHOST_VSOCK_SET_GUEST_CID _IOW(VHOST_VIRTIO, 0x60, __u64) #define VHOST_VSOCK_SET_RUNNING VHOST_SET_RUNNING +/* VHOST_DSP specific defines */ + +#define VHOST_DSP_SET_GUEST_TPLG _IOW(VHOST_VIRTIO, 0x80, \ + struct vhost_dsp_topology) + #endif diff --git a/include/uapi/linux/vhost_types.h b/include/uapi/linux/vhost_types.h index c907290..898660d 100644 --- a/include/uapi/linux/vhost_types.h +++ b/include/uapi/linux/vhost_types.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -119,6 +120,12 @@ struct vhost_scsi_target { unsigned short reserved; }; +/* VHOST_DSP */ + +struct vhost_dsp_topology { + char name[NAME_MAX + 1]; +}; + /* Feature bits */ /* Log all write descriptors. Can be changed while device is active. */ #define VHOST_F_LOG_ALL 26 diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6a2502e..950c817 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #define DPCM_MAX_BE_USERS 8 @@ -1589,10 +1590,15 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, int stream; /* adjust dir to stream */ - if (dir == SND_SOC_DAPM_DIR_OUT) + if (dir == SND_SOC_DAPM_DIR_OUT) { + if (!strcmp(widget->sname, SOC_VIRT_DAI_PLAYBACK)) + return false; stream = SNDRV_PCM_STREAM_PLAYBACK; - else + } else { + if (!strcmp(widget->sname, SOC_VIRT_DAI_CAPTURE)) + return false; stream = SNDRV_PCM_STREAM_CAPTURE; + } rtd = dpcm_get_be(card, widget, stream); if (rtd) @@ -3177,14 +3183,6 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->pcm = pcm; pcm->private_data = rtd; - if (rtd->dai_link->no_pcm || rtd->dai_link->params) { - if (playback) - pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; - if (capture) - pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; - goto out; - } - /* ASoC PCM operations */ if (rtd->dai_link->dynamic) { rtd->ops.open = dpcm_fe_dai_open; @@ -3204,6 +3202,19 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.pointer = soc_pcm_pointer; } + if (rtd->dai_link->no_pcm || rtd->dai_link->params) { + /* + * Usually in this case we also don't need to assign .ops + * callbacks, but in case of a "no PCM" pipeline, used by a VM + * we use the .prepare() hook to configure the hardware. + */ + if (playback) + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd; + if (capture) + pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd; + goto out; + } + for_each_rtd_components(rtd, i, component) { const struct snd_soc_component_driver *drv = component->driver; diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index e96d8ff..90c772b 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -195,6 +195,7 @@ config SND_SOC_SOF tristate select SND_SOC_TOPOLOGY select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_SUPPORT + select VHOST if VHOST_SOF help This option is not user-selectable but automagically handled by 'select' statements at a higher level diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index b728d09..d947afc 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -14,6 +14,10 @@ ifdef CONFIG_SND_SOC_SOF_VIRTIO_FE snd-sof-objs += virtio-fe.o endif +ifdef CONFIG_VHOST_SOF +snd-sof-objs += vhost-be.o +endif + obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d0bf082..09f01ea 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include "sof-audio.h" #include "sof-priv.h" #include "ops.h" #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) @@ -222,6 +224,9 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto dbg_err; } + /* enable the vhost driver on this device */ + dsp_sof_dev_set(sdev); + /* init the IPC */ sdev->ipc = snd_sof_ipc_init(sdev); if (!sdev->ipc) { @@ -333,6 +338,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->vbe_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 8f52de0..fa8d3e2 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -14,6 +14,8 @@ #include #include +#include + #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" @@ -451,6 +453,9 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) memcpy(&stream->posn, &posn, sizeof(posn)); + /* optionally update position for vBE */ + dsp_sof_update_guest_posn(sdev, &posn); + /* only inform ALSA for period_wakeup mode */ if (!stream->substream->runtime->no_period_wakeup) snd_sof_pcm_period_elapsed(stream->substream); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1c7ace6..a07ccd2 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -57,7 +57,7 @@ static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream /* * sof pcm period elapse work */ -static void sof_pcm_period_elapsed_work(struct work_struct *work) +void sof_pcm_period_elapsed_work(struct work_struct *work) { struct snd_sof_pcm_stream *sps = container_of(work, struct snd_sof_pcm_stream, @@ -91,7 +91,8 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) * To avoid sending IPC before the previous IPC is handled, we * schedule delayed work here to call the snd_pcm_period_elapsed(). */ - schedule_work(&spcm->stream[substream->stream].period_elapsed_work); + if (spcm->stream[substream->stream].substream) + schedule_work(&spcm->stream[substream->stream].period_elapsed_work); } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); @@ -750,6 +751,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) /* load the default topology */ sdev->component = component; + sdev->card = component->card; tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, "%s/%s", diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index c410822..0898d17 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -8,6 +8,8 @@ // Author: Liam Girdwood // +#include + #include "ops.h" #include "sof-priv.h" #include "sof-audio.h" @@ -247,6 +249,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) /* reset FW state */ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + dsp_sof_suspend(sdev); + return ret; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b4c5fe2..20413ec 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -151,6 +151,7 @@ int sof_restore_pipelines(struct device *dev) struct snd_sof_dai *dai; struct sof_ipc_comp_dai *comp_dai; struct sof_ipc_cmd_hdr *hdr; + struct sof_ipc_buffer *buffer; int ret; /* restore pipeline components */ @@ -182,6 +183,14 @@ int sof_restore_pipelines(struct device *dev) pipeline = swidget->private; ret = sof_load_pipeline_ipc(dev, pipeline, &r); break; + + case snd_soc_dapm_buffer: + + buffer = swidget->private; + if (!buffer->size) + break; + + /* Fall through */ default: hdr = swidget->private; ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index e9a6091..0216196 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -39,6 +39,7 @@ struct snd_sof_pcm_stream { * active or not while suspending the stream */ bool suspend_ignored; + size_t guest_offset; }; /* ALSA SOF PCM device */ @@ -104,6 +105,7 @@ struct snd_sof_dai { struct snd_soc_component *scomp; const char *name; const char *cpu_dai_name; + unsigned int pipeline_id; struct sof_ipc_comp_dai comp_dai; struct sof_ipc_dai_config *dai_config; @@ -213,4 +215,19 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); +#if IS_ENABLED(CONFIG_VHOST_SOF) +int dsp_sof_add_conn(struct snd_sof_dev *sdev, + struct snd_sof_widget *w_host, + struct snd_sof_widget *w_guest, + enum sof_ipc_stream_direction direction); +#else +static inline int dsp_sof_add_conn(struct snd_sof_dev *sdev, + struct snd_sof_widget *w_host, + struct snd_sof_widget *w_guest, + enum sof_ipc_stream_direction direction) +{ + return 0; +} +#endif + #endif diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 44780c2..665e464 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -57,6 +57,18 @@ /* The maximum number of components a virtio user vFE driver can use */ #define SOF_VIRTIO_MAX_UOS_COMPS 1000 +#define SOF_VIRTIO_COMP_ID_UNASSIGNED 0xffffffff + +/* + * in virtio iovec array: + * iovec[0]: the ipc message data between vFE and vBE + * iovec[1]: the ipc reply data between vFE and vBE + */ +#define SOF_VIRTIO_IPC_MSG 0 +#define SOF_VIRTIO_IPC_REPLY 1 + +/* Maximum supported number of VirtIO clients */ +#define SND_SOF_MAX_VFES BITS_PER_LONG /* DSP power state */ enum sof_dsp_power_states { @@ -368,6 +380,7 @@ struct snd_sof_dev { * can't use const */ struct snd_soc_component_driver plat_drv; + struct snd_soc_card *card; /* current DSP power state */ struct sof_dsp_power_state dsp_power_state; @@ -435,6 +448,9 @@ struct snd_sof_dev { /* VirtIO fields for host and guest */ atomic_t reset_count; + struct list_head vbe_list; + struct list_head connector_list; + unsigned long vfe_mask[DIV_ROUND_UP(SND_SOF_MAX_VFES, BITS_PER_LONG)]; /* DMA for Trace */ struct snd_dma_buffer dmatb; @@ -609,4 +625,6 @@ int intel_pcm_close(struct snd_sof_dev *sdev, int sof_machine_check(struct snd_sof_dev *sdev); +void sof_pcm_period_elapsed_work(struct work_struct *work); + #endif diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3fa0717..f8bba27 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1332,6 +1332,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, if (ret == 0 && dai) { dai->scomp = scomp; + dai->pipeline_id = swidget->pipeline_id; memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); } @@ -1378,6 +1379,13 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, swidget->private = buffer; + /* + * VirtIO dummy buffers between a dummy "aif_in" / "aif_out" widget and + * a mixer / demux respectively + */ + if (!buffer->size) + return 0; + ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer, sizeof(*buffer), r, sizeof(*r)); if (ret < 0) { @@ -1423,6 +1431,16 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, struct sof_ipc_comp_host *host; int ret; + /* + * For now just drop any virtual PCMs. Might need to use a more robust + * identification than the name + */ + if ((dir == SOF_IPC_STREAM_PLAYBACK && + !strcmp(SOC_VIRT_DAI_PLAYBACK, swidget->widget->sname)) || + (dir == SOF_IPC_STREAM_CAPTURE && + !strcmp(SOC_VIRT_DAI_CAPTURE, swidget->widget->sname))) + return 0; + host = kzalloc(sizeof(*host), GFP_KERNEL); if (!host) return -ENOMEM; @@ -3137,6 +3155,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, link->trigger[0] = SND_SOC_DPCM_TRIGGER_POST; link->trigger[1] = SND_SOC_DPCM_TRIGGER_POST; + /* + * set .no_pcm on VirtIO hosts for pseudo PCMs, used as anchors + * for guest pipeline linking + */ + if (link->stream_name && + (!strcmp(link->stream_name, "vm_fe_playback") || + !strcmp(link->stream_name, "vm_fe_capture"))) + link->no_pcm = true; + /* nothing more to do for FE dai links */ return 0; } @@ -3358,6 +3385,32 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, } /* + * In VirtIO case the host topology will contain a dummy PCM and a + * buffer at each location, where a partial guest topology will be + * attached. These dummy widgets shall not be sent to the DSP. We use + * them to identify and store VirtIO guest connection points. + */ + if (source_swidget->id == snd_soc_dapm_buffer) { + struct sof_ipc_buffer *buffer = source_swidget->private; + /* Is this a virtual playback buffer? */ + if (!buffer->size) { + ret = dsp_sof_add_conn(sdev, sink_swidget, + source_swidget, + SOF_IPC_STREAM_PLAYBACK); + goto err; + } + } else if (sink_swidget->id == snd_soc_dapm_buffer) { + struct sof_ipc_buffer *buffer = sink_swidget->private; + /* Is this a virtual capture buffer? */ + if (!buffer->size) { + ret = dsp_sof_add_conn(sdev, source_swidget, + sink_swidget, + SOF_IPC_STREAM_CAPTURE); + goto err; + } + } + + /* * Don't send routes whose sink widget is of type * output or out_drv to the DSP */ @@ -3624,7 +3677,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) /* VirtIO guests request topology from the host */ if (sdev->pdata->vfe) { fw = &vfe_fw; - ret = sof_ops(sdev)->request_topology(sdev, file, &vfe_fw); + ret = sof_ops(sdev)->request_topology(sdev, &vfe_fw); } else { ret = request_firmware(&fw, file, sdev->dev); } diff --git a/sound/soc/sof/vhost-be.c b/sound/soc/sof/vhost-be.c new file mode 100644 index 00000000..e1dede9 --- /dev/null +++ b/sound/soc/sof/vhost-be.c @@ -0,0 +1,1093 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * Copyright(c) 2017-2020 Intel Corporation. All rights reserved. + * + * Author: Libin Yang + * Luo Xionghu + * Liam Girdwood + * Guennadi Liakhovetski + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "sof-audio.h" +#include "sof-priv.h" +#include "ops.h" + +/* A connection of a guest pipeline into the host topology */ +struct dsp_pipeline_connect { + int host_pipeline_id; + int guest_pipeline_id; + int host_component_id; + int guest_component_id; + enum sof_ipc_stream_direction direction; + struct list_head list; +}; + +static const char dsp_pcm_name[] = "VHost PCM"; + +/* + * This function is used to find a BE substream. It uses the dai_link stream + * name for that. The current dai_link stream names are "vm_fe_playback" and + * "vm_fe_capture," which means only one Virtual Machine is supported and the VM + * only supports one playback pcm and one capture pcm. After we switch to the + * new topology, we can support multiple VMs and multiple PCM streams for each + * VM. This function may be abandoned after switching to the new topology. + * + * Note: if this function returns substream != NULL, then *rtd != NULL too (if + * rtd != NULL, of course). If it returns NULL, *rtd hasn't been changed. + */ +static struct snd_pcm_substream *dsp_sof_get_substream(struct snd_sof_dev *sdev, + struct snd_soc_pcm_runtime **rtd, int direction) +{ + struct snd_soc_card *card = sdev->card; + struct snd_soc_pcm_runtime *r; + + for_each_card_rtds(card, r) { + struct snd_pcm_substream *substream; + struct snd_pcm *pcm = r->pcm; + if (!pcm || !pcm->internal) + continue; + + /* Find a substream dedicated to the vFE. */ + substream = pcm->streams[direction].substream; + if (substream) { + struct snd_soc_dai_link *dai_link = r->dai_link; + + /* FIXME: replace hard-coded stream name */ + if (dai_link->stream_name && + (!strcmp(dai_link->stream_name, "vm_fe_playback") || + !strcmp(dai_link->stream_name, "vm_fe_capture"))) { + if (rtd) + *rtd = r; + return substream; + } + } + } + + return NULL; +} + +static struct snd_sof_pcm *dsp_sof_find_spcm_comp(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ + return snd_sof_find_spcm_comp(sdev->component, comp_id, direction); +} + +/* + * Prepare hardware parameters, required for buffer allocation and PCM + * configuration + */ +static int dsp_sof_assemble_params(struct sof_ipc_pcm_params *pcm, + struct snd_pcm_hw_params *params) +{ + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS)->min = + pcm->params.channels; + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE)->min = + pcm->params.rate; + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)->min = + pcm->params.host_period_bytes; + + hw_param_interval(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES)->min = + pcm->params.buffer.size; + + snd_mask_none(fmt); + switch (pcm->params.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S32); + break; + case SOF_IPC_FRAME_FLOAT: + snd_mask_set(fmt, SNDRV_PCM_FORMAT_FLOAT); + break; + default: + return -EINVAL; + } + return 0; +} + +/* Handle SOF_IPC_STREAM_PCM_PARAMS IPC */ +static int dsp_sof_stream_hw_params(struct snd_sof_dev *sdev, + struct sof_ipc_pcm_params *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + struct snd_pcm_hw_params params; + int direction = pcm->params.direction; + int ret; + + /* find the proper substream */ + substream = dsp_sof_get_substream(sdev, NULL, direction); + if (!substream) + return -ENODEV; + + runtime = substream->runtime; + if (!runtime) { + dev_err(sdev->dev, "no runtime is available for hw_params\n"); + return -ENODEV; + } + + /* TODO: codec hw_params */ + + /* Use different stream_tag from FE. This is the real tag */ + dsp_sof_assemble_params(pcm, ¶ms); + + /* Allocate a duplicate of the guest buffer */ + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(¶ms)); + if (ret < 0) { + dev_err(sdev->dev, + "error %d: could not allocate %d bytes for PCM \"%s\"\n", + ret, params_buffer_bytes(¶ms), substream->pcm->name); + return ret; + } + + return snd_sof_pcm_platform_hw_params(sdev, substream, ¶ms, + &pcm->params); +} + +/* Allocate a runtime object and buffer pages */ +static int dsp_sof_pcm_open(struct snd_sof_dev *sdev, void *ipc_data) +{ + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + struct sof_ipc_pcm_params *pcm = ipc_data; + struct snd_pcm_runtime *runtime; + struct snd_sof_pcm *spcm; + u32 comp_id = pcm->comp_id; + size_t size; + int direction, ret; + + spcm = dsp_sof_find_spcm_comp(sdev, comp_id, &direction); + if (!spcm) { + dev_err(sdev->dev, "%s(): no SPCM for comp %u\n", __func__, comp_id); + return -ENODEV; + } + + substream = dsp_sof_get_substream(sdev, &rtd, direction); + if (!substream) { + dev_err(sdev->dev, "%s(): no substream for comp %u\n", __func__, comp_id); + return -ENODEV; + } + if (substream->ref_count > 0) + return -EBUSY; + substream->ref_count++; /* set it used */ + + runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); + if (!runtime) + return -ENOMEM; + + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); + runtime->status = alloc_pages_exact(size, GFP_KERNEL); + if (!runtime->status) { + ret = -ENOMEM; + goto eruntime; + } + memset((void *)runtime->status, 0, size); + + size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); + runtime->control = alloc_pages_exact(size, GFP_KERNEL); + if (!runtime->control) { + dev_err(sdev->dev, "fail to alloc pages for runtime->control"); + ret = -ENOMEM; + goto estatus; + } + memset((void *)runtime->control, 0, size); + + init_waitqueue_head(&runtime->sleep); + init_waitqueue_head(&runtime->tsleep); + runtime->status->state = SNDRV_PCM_STATE_OPEN; + + substream->runtime = runtime; + substream->private_data = rtd; + rtd->dpcm[direction].runtime = runtime; + substream->stream = direction; + + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + substream->dma_buffer.dev.dev = sdev->dev; + + /* check with spcm exists or not */ + spcm->stream[direction].posn.host_posn = 0; + spcm->stream[direction].posn.dai_posn = 0; + spcm->stream[direction].substream = substream; + spcm->stream[direction].guest_offset = 0; + + /* TODO: codec open */ + + snd_sof_pcm_platform_open(sdev, substream); + + return 0; + +estatus: + free_pages_exact(runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); +eruntime: + kfree(runtime); + return ret; +} + +static void dsp_sof_stream_close(struct snd_sof_dev *sdev, int direction) +{ + struct snd_pcm_substream *substream = dsp_sof_get_substream(sdev, NULL, + direction); + if (!substream) + return; + + /* TODO: codec close */ + + substream->ref_count = 0; + if (substream->runtime) { + snd_sof_pcm_platform_close(sdev, substream); + + free_pages_exact(substream->runtime->status, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); + free_pages_exact(substream->runtime->control, + PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); + kfree(substream->runtime); + substream->runtime = NULL; + } +} + +/* Handle the SOF_IPC_STREAM_PCM_FREE IPC */ +static int dsp_sof_pcm_close(struct snd_sof_dev *sdev, void *ipc_data) +{ + struct snd_sof_pcm *spcm; + struct sof_ipc_stream *stream; + int direction; + + stream = (struct sof_ipc_stream *)ipc_data; + + spcm = dsp_sof_find_spcm_comp(sdev, stream->comp_id, &direction); + if (!spcm) + return 0; + + dsp_sof_stream_close(sdev, direction); + + return 0; +} + +/* Copy audio data from DMA buffers for capture */ +static int dsp_sof_stream_capture(struct snd_sof_pcm_stream *stream, + struct snd_pcm_runtime *runtime, + struct dsp_sof_data_req *req, + struct dsp_sof_data_resp *reply) +{ + size_t data_size = req->size; + int ret; + + stream->guest_offset = req->offset; + + if (req->offset + data_size > runtime->dma_bytes) { + reply->size = 0; + ret = -ENOBUFS; + } else { + stream->guest_offset += data_size; + + memcpy(reply->data, runtime->dma_area + req->offset, data_size); + reply->size = data_size; + ret = 0; + } + + reply->error = ret; + + return ret; +} + +/* Copy audio data to DMA buffers for playback */ +static int dsp_sof_stream_playback(struct snd_sof_pcm_stream *stream, + struct snd_pcm_runtime *runtime, + struct dsp_sof_data_req *req, + struct dsp_sof_data_resp *reply) +{ + size_t data_size = req->size; + int ret; + + stream->guest_offset = req->offset; + + if (req->offset + data_size > runtime->dma_bytes) { + ret = -ENOBUFS; + } else { + stream->guest_offset += data_size; + + memcpy(runtime->dma_area + req->offset, req->data, data_size); + ret = 0; + } + + reply->error = ret; + reply->size = 0; + + return ret; +} + +/* Send or receive audio data */ +int dsp_sof_stream_data(struct dsp_sof_client *client, + struct dsp_sof_data_req *req, + struct dsp_sof_data_resp *reply) +{ + int direction; + struct snd_sof_dev *sdev = client->sdev; + struct snd_sof_pcm *spcm = dsp_sof_find_spcm_comp(sdev, + req->comp_id, &direction); + struct snd_pcm_substream *substream = dsp_sof_get_substream(sdev, NULL, + direction); + + if (!spcm || !substream) { + reply->error = -ENODEV; + reply->size = 0; + return reply->error; + } + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + return dsp_sof_stream_playback(spcm->stream + direction, + substream->runtime, req, reply); + + return dsp_sof_stream_capture(spcm->stream + direction, + substream->runtime, req, reply); +} +EXPORT_SYMBOL_GPL(dsp_sof_stream_data); + +/* Handle the stream IPC */ +static int dsp_sof_ipc_stream(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr, void *reply_buf) +{ + struct sof_ipc_pcm_params *pcm; + struct sof_ipc_stream *stream; + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + int ret = 0, direction, comp_id; + u32 cmd = hdr->cmd & SOF_CMD_TYPE_MASK; + struct snd_soc_dpcm *dpcm; + + switch (cmd) { + case SOF_IPC_STREAM_PCM_PARAMS: + ret = dsp_sof_pcm_open(sdev, hdr); + if (ret < 0) + break; + pcm = container_of(hdr, struct sof_ipc_pcm_params, hdr); + ret = dsp_sof_stream_hw_params(sdev, pcm); + break; + case SOF_IPC_STREAM_TRIG_START: + stream = container_of(hdr, struct sof_ipc_stream, hdr); + comp_id = stream->comp_id; + if (!dsp_sof_find_spcm_comp(sdev, comp_id, &direction)) { + ret = -ENODEV; + break; + } + substream = dsp_sof_get_substream(sdev, &rtd, direction); + if (!substream) { + ret = -ENODEV; + break; + } + + /* Create an RTD, a CPU DAI when parsing aif_in */ + snd_soc_runtime_activate(rtd, direction); + snd_soc_dpcm_runtime_update(sdev->card, SND_SOC_DPCM_UPDATE_NEW_ONLY); + + dpcm = list_first_entry(&rtd->dpcm[direction].be_clients, + struct snd_soc_dpcm, list_be); + + if (list_empty(&rtd->dpcm[direction].be_clients)) + dev_warn(rtd->dev, "BE client list empty\n"); + else if (!dpcm->be) + dev_warn(rtd->dev, "No BE\n"); + else + dpcm->be->dpcm[direction].state = SND_SOC_DPCM_STATE_HW_PARAMS; + + ret = rtd->ops.prepare(substream); + if (ret < 0) + break; + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_START); + break; + case SOF_IPC_STREAM_PCM_FREE: + dsp_sof_pcm_close(sdev, hdr); + break; + } + + return ret; +} + +/* validate component IPC */ +static int dsp_sof_ipc_comp(struct dsp_sof_client *client, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_ipc_ctrl_data *cdata = container_of(hdr, + struct sof_ipc_ctrl_data, rhdr.hdr); + + return cdata->comp_id < client->comp_id_begin || + cdata->comp_id >= client->comp_id_end ? -EINVAL : 0; +} + +/* process PM IPC */ +static int dsp_sof_ipc_pm(struct dsp_sof_client *client, struct sof_ipc_cmd_hdr *hdr, + struct sof_vfe_ipc_power_resp *resp) +{ + struct snd_sof_dev *sdev = client->sdev; + u32 cmd = hdr->cmd & SOF_CMD_TYPE_MASK; + struct sof_vfe_ipc_power_req *rq; + unsigned int reset_count; + int ret; + + switch (cmd) { + case SOF_IPC_PM_VFE_POWER_STATUS: + rq = container_of(hdr, struct sof_vfe_ipc_power_req, hdr); + if (rq->power) { + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0) + return ret; + } + + /* + * The DSP is runtime-PM active now for IPC processing, so + * .reset_count won't change + */ + reset_count = atomic_read(&sdev->reset_count); + resp->reply.hdr.size = sizeof(*resp); + resp->reply.hdr.cmd = SOF_IPC_GLB_PM_MSG | + SOF_IPC_PM_VFE_POWER_STATUS; + resp->reply.error = 0; + resp->reset_status = reset_count == client->reset_count ? + SOF_VIRTIO_IPC_RESET_NONE : SOF_VIRTIO_IPC_RESET_DONE; + + if (!rq->power) { + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + } + return 1; + } + + return 0; +} + +static int dsp_sof_error_reply(struct sof_ipc_reply *rhdr, unsigned int cmd, + int err) +{ + rhdr->hdr.size = sizeof(*rhdr); + rhdr->hdr.cmd = cmd; + rhdr->error = err; + + return err; +} + +int dsp_sof_add_conn(struct snd_sof_dev *sdev, + struct snd_sof_widget *w_host, + struct snd_sof_widget *w_guest, + enum sof_ipc_stream_direction direction) +{ + struct dsp_pipeline_connect *conn; + + if (w_host->pipeline_id == w_guest->pipeline_id) + return 0; + + conn = devm_kmalloc(sdev->dev, sizeof(*conn), GFP_KERNEL); + if (!conn) + return -ENOMEM; + + /* + * We'll need this mapping twice: first to overwrite a sink or source ID + * for SOF_IPC_TPLG_COMP_CONNECT, then to overwrite the scheduling + * component ID for SOF_IPC_TPLG_PIPE_NEW + */ + conn->host_pipeline_id = w_host->pipeline_id; + conn->guest_pipeline_id = w_guest->pipeline_id; + conn->host_component_id = w_host->comp_id; + conn->direction = direction; + + list_add_tail(&conn->list, &sdev->connector_list); + + return 0; +} + +/* Handle some special cases of the "new component" IPC */ +static int dsp_sof_ipc_tplg_comp_new(struct dsp_sof_client *client, int vq_idx, + struct sof_ipc_cmd_hdr *hdr, struct sof_ipc_reply *rhdr) +{ + struct sof_ipc_comp *comp = container_of(hdr, struct sof_ipc_comp, hdr); + struct snd_sof_dev *sdev = client->sdev; + struct snd_sof_pcm *spcm, *last; + struct sof_ipc_comp_host *host; + struct dsp_pipeline_connect *conn; + + if (comp->id < client->comp_id_begin || + comp->id >= client->comp_id_end) + return -EINVAL; + + switch (comp->type) { + case SOF_COMP_VIRT_CON: + list_for_each_entry(conn, &sdev->connector_list, list) + if (conn->guest_pipeline_id == comp->pipeline_id) { + /* This ID will have to be overwritten */ + conn->guest_component_id = comp->id; + break; + } + + dsp_sof_error_reply(rhdr, hdr->cmd, 0); + + /* The firmware doesn't need this component */ + return 1; + case SOF_COMP_HOST: + /* + * TODO: below is a temporary solution. next step is + * to create a whole pcm stuff incluing substream + * based on Liam's suggestion. + */ + + /* + * let's create spcm in HOST ipc + * spcm should be created in pcm load, but there is no such ipc + * so we create it here. It is needed for the "period elapsed" + * IPC from the firmware, which will use the host ID to route + * the IPC back to the PCM. + */ + host = container_of(comp, struct sof_ipc_comp_host, comp); + spcm = kzalloc(sizeof(*spcm), GFP_KERNEL); + if (!spcm) + return -ENOMEM; + + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id = + SOF_VIRTIO_COMP_ID_UNASSIGNED; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id = + SOF_VIRTIO_COMP_ID_UNASSIGNED; + spcm->stream[host->direction].comp_id = host->comp.id; + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].posn.comp_id = + spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].comp_id; + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].posn.comp_id = + spcm->stream[SNDRV_PCM_STREAM_CAPTURE].comp_id; + INIT_WORK(&spcm->stream[host->direction].period_elapsed_work, + sof_pcm_period_elapsed_work); + last = list_last_entry(&sdev->pcm_list, struct snd_sof_pcm, list); + spcm->pcm.dai_id = last->pcm.dai_id + 1; + strncpy(spcm->pcm.pcm_name, dsp_pcm_name, + sizeof(spcm->pcm.pcm_name)); + list_add(&spcm->list, &sdev->pcm_list); + + client->reset_count = atomic_read(&sdev->reset_count); + break; + default: + break; + } + + return 0; +} + +/* Handle the "new pipeline" IPC: replace the scheduling sink ID */ +static int dsp_sof_ipc_tplg_pipe_new(struct dsp_sof_client *client, int vq_idx, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_ipc_pipe_new *pipeline = container_of(hdr, + struct sof_ipc_pipe_new, hdr); + struct snd_sof_dev *sdev = client->sdev; + struct dsp_pipeline_connect *conn; + + list_for_each_entry(conn, &sdev->connector_list, list) + if (pipeline->pipeline_id == conn->guest_pipeline_id) { + struct snd_sof_dai *dai; + + dai = snd_sof_find_dai_pipe(sdev, conn->host_pipeline_id); + if (!dai) { + dev_warn(sdev->dev, + "no DAI with pipe %u found\n", + conn->host_pipeline_id); + continue; + } + + /* Overwrite the scheduling sink ID with the DAI ID */ + pipeline->sched_id = dai->comp_dai.comp.id; + break; + } + + return 0; +} + +/* Handle the "connect components" IPC: replace the virtual component ID */ +static int dsp_sof_ipc_tplg_comp_connect(struct dsp_sof_client *client, + int vq_idx, struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_ipc_pipe_comp_connect *connect = container_of(hdr, + struct sof_ipc_pipe_comp_connect, hdr); + struct dsp_pipeline_connect *conn; + + list_for_each_entry(conn, &client->sdev->connector_list, list) { + if (conn->direction == SOF_IPC_STREAM_PLAYBACK && + connect->sink_id == conn->guest_component_id) { + /* Overwrite the sink ID with the actual mixer component ID */ + connect->sink_id = conn->host_component_id; + break; + } + + if (conn->direction == SOF_IPC_STREAM_CAPTURE && + connect->source_id == conn->guest_component_id) { + /* Overwrite the source ID with the actual demux component ID */ + connect->source_id = conn->host_component_id; + break; + } + } + + return 0; +} + +/* Read guest's topology file and send it back to the requester */ +static int dsp_sof_ipc_tplg_read(struct dsp_sof_client *client, + struct sof_ipc_cmd_hdr *hdr, + void *reply_buf, size_t reply_sz) +{ + struct sof_vfe_ipc_tplg_req *tplg = container_of(hdr, + struct sof_vfe_ipc_tplg_req, hdr); + struct sof_vfe_ipc_tplg_resp *partdata = reply_buf; + const struct firmware *fw = client->fw; + size_t to_copy, remainder; + + if (reply_sz <= sizeof(partdata->reply)) + return -ENOBUFS; + + if (!fw || fw->size <= tplg->offset) + return -EINVAL; + + remainder = fw->size - tplg->offset; + + partdata->reply.hdr.cmd = hdr->cmd; + /* + * Non-standard size use: it's the remaining firmware bytes, plus + * the header, that way the last part will contain a correct size + */ + partdata->reply.hdr.size = remainder + sizeof(partdata->reply); + + to_copy = min_t(size_t, reply_sz - sizeof(partdata->reply), + remainder); + + memcpy(partdata->data, fw->data + tplg->offset, to_copy); + + if (remainder == to_copy) { + release_firmware(fw); + client->fw = NULL; + } + + return 0; +} + +/* Send the next component ID to the guest */ +static int dsp_sof_ipc_tplg_comp_id(struct dsp_sof_client *client, + struct sof_ipc_cmd_hdr *hdr, + void *reply_buf, size_t reply_sz) +{ + struct sof_vfe_ipc_tplg_resp *partdata = reply_buf; + + client->comp_id_begin = client->sdev->next_comp_id + + client->id * SOF_VIRTIO_MAX_UOS_COMPS; + client->comp_id_end = client->comp_id_begin + SOF_VIRTIO_MAX_UOS_COMPS; + + partdata->reply.hdr.cmd = hdr->cmd; + partdata->reply.hdr.size = sizeof(partdata->reply) + sizeof(u32); + *(u32 *)partdata->data = client->comp_id_begin; + + return 0; +} + +/* Handle topology IPC */ +static int dsp_sof_ipc_tplg(struct dsp_sof_client *client, int vq_idx, + struct sof_ipc_cmd_hdr *hdr, + void *reply_buf, size_t reply_sz) +{ + u32 cmd = hdr->cmd & SOF_CMD_TYPE_MASK; + int ret; + + switch (cmd) { + case SOF_IPC_TPLG_COMP_NEW: + return dsp_sof_ipc_tplg_comp_new(client, vq_idx, hdr, + reply_buf); + case SOF_IPC_TPLG_PIPE_NEW: + return dsp_sof_ipc_tplg_pipe_new(client, vq_idx, hdr); + case SOF_IPC_TPLG_COMP_CONNECT: + return dsp_sof_ipc_tplg_comp_connect(client, vq_idx, hdr); + case SOF_IPC_TPLG_VFE_GET: + ret = dsp_sof_ipc_tplg_read(client, hdr, reply_buf, reply_sz); + return ret < 0 ? ret : 1; + case SOF_IPC_TPLG_VFE_COMP_ID: + ret = dsp_sof_ipc_tplg_comp_id(client, hdr, reply_buf, reply_sz); + return ret < 0 ? ret : 1; + } + + return 0; +} + +/* Call SOF core to send an IPC message to the DSP */ +static void sof_virtio_send_ipc(struct snd_sof_dev *sdev, void *ipc_data, + void *reply_buf, size_t count, + size_t reply_size) +{ + struct snd_sof_ipc *ipc = sdev->ipc; + struct sof_ipc_cmd_hdr *hdr = ipc_data; + struct sof_ipc_reply *rhdr = reply_buf; + int ret = sof_ipc_tx_message(ipc, hdr->cmd, ipc_data, count, + reply_buf, reply_size); + + if (ret < 0 && !rhdr->error) + rhdr->error = ret; +} + +/* Post-process SOF_IPC_STREAM_PCM_PARAMS */ +static int dsp_sof_ipc_stream_param_post(struct snd_sof_dev *sdev, + void *reply_buf) +{ + struct sof_ipc_pcm_params_reply *reply = reply_buf; + u32 comp_id = reply->comp_id; + int direction, ret; + struct snd_sof_pcm *spcm = dsp_sof_find_spcm_comp(sdev, comp_id, + &direction); + if (!spcm) + return -ENODEV; + + ret = snd_sof_ipc_pcm_params(sdev, spcm->stream[direction].substream, + reply); + if (ret < 0) + dev_err(sdev->dev, "error: got wrong reply for PCM %d\n", + spcm->pcm.pcm_id); + + return ret; +} + +/* Handle the stream IPC post-processing */ +static int dsp_sof_ipc_stream_codec(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_ipc_stream *stream = container_of(hdr, + struct sof_ipc_stream, hdr); + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + unsigned int i; + int direction; + + if (!dsp_sof_find_spcm_comp(sdev, stream->comp_id, &direction)) + return -ENODEV; + + substream = dsp_sof_get_substream(sdev, &rtd, direction); + if (!substream) + return -ENODEV; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + const struct snd_soc_dai_ops *ops = codec_dai->driver->ops; + + /* + * Now we are ready to trigger start. + * Let's unmute the codec firstly + */ + snd_soc_dai_digital_mute(codec_dai, 0, direction); + if (ops->trigger) { + int ret = ops->trigger(substream, + SNDRV_PCM_TRIGGER_START, + codec_dai); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int dsp_sof_ipc_stream_stop(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_ipc_stream *stream = container_of(hdr, + struct sof_ipc_stream, hdr); + struct snd_pcm_substream *substream; + struct snd_soc_pcm_runtime *rtd; + int direction, comp_id = stream->comp_id; + unsigned int i; + + if (!dsp_sof_find_spcm_comp(sdev, comp_id, &direction)) + return -ENODEV; + + substream = dsp_sof_get_substream(sdev, &rtd, direction); + if (!substream) + return -ENODEV; + + for (i = 0; i < rtd->num_codecs; i++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; + const struct snd_soc_dai_ops *ops = codec_dai->driver->ops; + + if (ops->trigger) { + int ret = ops->trigger(substream, + SNDRV_PCM_TRIGGER_STOP, + codec_dai); + if (ret < 0) { + dev_err(codec_dai->dev, + "trigger stop fails\n"); + return ret; + } + } + } + + snd_sof_pcm_platform_trigger(sdev, substream, + SNDRV_PCM_TRIGGER_STOP); + snd_soc_dpcm_runtime_update(sdev->card, + SND_SOC_DPCM_UPDATE_OLD_ONLY); + snd_soc_runtime_deactivate(rtd, direction); + + return 0; +} + +/* Handle an IPC reply */ +static int dsp_sof_ipc_post(struct snd_sof_dev *sdev, + struct sof_ipc_cmd_hdr *hdr, void *reply_buf) +{ + struct sof_ipc_reply *rhdr = reply_buf; + int ret; + + switch (hdr->cmd) { + case SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS: + if (rhdr->error < 0) + break; + return dsp_sof_ipc_stream_param_post(sdev, reply_buf); + case SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_TRIG_START: + if (rhdr->error < 0) + break; + /* setup the codec */ + return dsp_sof_ipc_stream_codec(sdev, hdr); + case SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_TRIG_STOP: + ret = dsp_sof_ipc_stream_stop(sdev, hdr); + return rhdr->error < 0 ? rhdr->error : ret; + } + + return rhdr->error; +} + +/* Forward an IPC message from a guest to the DSP */ +int dsp_sof_ipc_fwd(struct dsp_sof_client *client, int vq_idx, + void *ipc_buf, void *reply_buf, + size_t count, size_t reply_sz) +{ + struct snd_sof_dev *sdev = client->sdev; + struct sof_ipc_cmd_hdr *hdr = ipc_buf; + struct sof_ipc_reply *rhdr = reply_buf; + u32 type; + int ret; + + /* validate IPC */ + if (!count) { + dev_err(sdev->dev, "error: guest IPC size is 0\n"); + return -EINVAL; + } + + type = hdr->cmd & SOF_GLB_TYPE_MASK; + rhdr->error = 0; + + /* validate the ipc */ + switch (type) { + case SOF_IPC_GLB_COMP_MSG: + ret = dsp_sof_ipc_comp(client, hdr); + if (ret < 0) + goto err; + break; + case SOF_IPC_GLB_STREAM_MSG: + ret = dsp_sof_ipc_stream(sdev, hdr, reply_buf); + if (ret < 0) { + dev_err(sdev->dev, "STREAM IPC 0x%x failed %d!\n", + hdr->cmd, ret); + goto err; + } + break; + case SOF_IPC_GLB_PM_MSG: + ret = dsp_sof_ipc_pm(client, hdr, reply_buf); + if (!ret) + break; + if (ret < 0) + goto err; + return 0; + case SOF_IPC_GLB_DAI_MSG: + /* + * After we use the new topology solution for FE, + * we will not touch DAI anymore. + */ + break; + case SOF_IPC_GLB_TPLG_MSG: + ret = dsp_sof_ipc_tplg(client, vq_idx, hdr, reply_buf, + reply_sz); + if (!ret) + break; + if (ret < 0) + goto err; + return 0; + case SOF_IPC_GLB_TRACE_MSG: + /* Trace should be initialized in SOS, skip FE requirement */ + return 0; + default: + dev_warn(sdev->dev, "unhandled IPC 0x%x!\n", hdr->cmd); + break; + } + + /* now send the IPC */ + sof_virtio_send_ipc(sdev, ipc_buf, reply_buf, count, reply_sz); + + /* For some IPCs, the reply needs to be handled */ + ret = dsp_sof_ipc_post(sdev, hdr, reply_buf); + if (ret < 0) + dev_err(sdev->dev, "err: failed to send %u bytes virtio IPC 0x%x: %d\n", + hdr->size, hdr->cmd, ret); + + return ret; + +err: + return dsp_sof_error_reply(rhdr, hdr->cmd, ret); +} +EXPORT_SYMBOL_GPL(dsp_sof_ipc_fwd); + +int dsp_sof_set_tplg(struct dsp_sof_client *client, + const struct vhost_dsp_topology *tplg) +{ + struct snd_sof_dev *sdev = client->sdev; + struct snd_sof_pdata *plat_data = sdev->pdata; + char *path; + int ret; + + path = kasprintf(GFP_KERNEL, "%s/%s", plat_data->tplg_filename_prefix, + tplg->name); + if (!path) + return -ENOMEM; + + ret = request_firmware(&client->fw, path, sdev->dev); + if (ret < 0) + dev_err(sdev->dev, + "error: request VFE topology %s failed: %d\n", + tplg->name, ret); + kfree(path); + + return ret; +} +EXPORT_SYMBOL_GPL(dsp_sof_set_tplg); + +void dsp_sof_suspend(struct snd_sof_dev *sdev) +{ + struct snd_sof_pcm *spcm, *next; + + list_for_each_entry_safe(spcm, next, &sdev->pcm_list, list) + if (!strcmp(dsp_pcm_name, spcm->pcm.pcm_name)) { + list_del(&spcm->list); + dsp_sof_stream_close(sdev, SNDRV_PCM_STREAM_PLAYBACK); + dsp_sof_stream_close(sdev, SNDRV_PCM_STREAM_CAPTURE); + kfree(spcm); + } +} + +/* A VM instance has closed the miscdevice */ +void dsp_sof_client_release(struct dsp_sof_client *client) +{ + bitmap_release_region(client->sdev->vfe_mask, client->id, 0); + + list_del(&client->list); + + kfree(client); +} +EXPORT_SYMBOL_GPL(dsp_sof_client_release); + +/* A new VM instance has opened the miscdevice */ +struct dsp_sof_client *dsp_sof_client_add(struct snd_sof_dev *sdev, + struct vhost_dsp *dsp) +{ + int id = bitmap_find_free_region(sdev->vfe_mask, SND_SOF_MAX_VFES, 0); + struct dsp_sof_client *client; + + if (id < 0) + return NULL; + + client = kmalloc(sizeof(*client), GFP_KERNEL); + if (!client) { + bitmap_release_region(sdev->vfe_mask, id, 0); + return NULL; + } + + client->sdev = sdev; + client->id = id; + client->vhost = dsp; + + /* + * link to sdev->vbe_list + * Maybe virtio_miscdev managing the list is more reasonable. + * Let's use sdev to manage the FE audios now. + * FIXME: protect the list. + */ + list_add(&client->list, &sdev->vbe_list); + + return client; +} +EXPORT_SYMBOL_GPL(dsp_sof_client_add); + +/* The struct snd_sof_dev instance, that VirtIO guests will be using */ +static struct snd_sof_dev *vhost_sof_dev; +static const struct sof_vhost_ops *vhost_ops; + +/* Find a client by component ID */ +static struct dsp_sof_client *dsp_sof_comp_to_client(struct snd_sof_dev *sdev, + int comp_id) +{ + struct dsp_sof_client *client; + + list_for_each_entry(client, &sdev->vbe_list, list) + if (comp_id < client->comp_id_end && + comp_id >= client->comp_id_begin) + return client; + + return NULL; +} + +/* Called from the position update IRQ thread */ +int dsp_sof_update_guest_posn(struct snd_sof_dev *sdev, + struct sof_ipc_stream_posn *posn) +{ + struct dsp_sof_client *client = dsp_sof_comp_to_client(sdev, + posn->comp_id); + + if (!client || !vhost_ops) + return -ENODEV; + + return vhost_ops->update_posn(client->vhost, posn); +} + +/* The vhost driver is loaded */ +struct device *dsp_sof_dev_init(const struct sof_vhost_ops *ops) +{ + if (!vhost_sof_dev) + return NULL; + + bitmap_zero(vhost_sof_dev->vfe_mask, SND_SOF_MAX_VFES); + + vhost_ops = ops; + + return vhost_sof_dev->dev; +} +EXPORT_SYMBOL_GPL(dsp_sof_dev_init); + +/* This SOF device will be used for VirtIO */ +void dsp_sof_dev_set(struct snd_sof_dev *sdev) +{ + INIT_LIST_HEAD(&sdev->connector_list); + vhost_sof_dev = sdev; +} From patchwork Fri Mar 20 15:40:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guennadi Liakhovetski X-Patchwork-Id: 193223 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=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 87751C4332D for ; Fri, 20 Mar 2020 15:47:29 +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 108BF2070A for ; Fri, 20 Mar 2020 15:47:29 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=alsa-project.org header.i=@alsa-project.org header.b="vDrYH9nP" DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 108BF2070A Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.intel.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 6E1CF1616; Fri, 20 Mar 2020 16:46:37 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6E1CF1616 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1584719247; bh=9gxDrU3SPuO5ifFKv4Zf1xO0dXSjsuJAAyjC4CUMwME=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=vDrYH9nPBsU2GZJryTuENCBMCjdEmGJ1eIuXg+aPBpXhgtOIW9bSLFr4Kdd2pS2O+ qp/gGtFROrkqaoarHGvco6FU5T3xWAdSSC/t5ezYEjeoKbslUmsL3AgMygNxmBdt/M wh92GeT/aRCCEUHVt0c2d+Qo0XtGIqDDo0HQMVh8= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id E84D3F80336; Fri, 20 Mar 2020 16:41:13 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id DD1BFF802E3; Fri, 20 Mar 2020 16:40:53 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 7D09FF8029A; Fri, 20 Mar 2020 16:40:40 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 7D09FF8029A IronPort-SDR: I1YAQ/loOXJHGgpTXI5WN479zFF8cTfA47vs34TtfEDbcL+z4RsMueFlzx3apA55+hpn2mStXh a2s4zU20O6cw== X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Mar 2020 08:40:37 -0700 IronPort-SDR: D1Y7p70qfiqfQjUG/qgkVl0owLK4tyd7IPxxxqkE3nOwYCA/jOQh+6NWIfBM2ImG6/Bp3sFhq5 EaT3vm1yCM1Q== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.72,285,1580803200"; d="scan'208";a="237257408" Received: from gliakhov-mobl2.ger.corp.intel.com (HELO ubuntu.ger.corp.intel.com) ([10.252.57.199]) by fmsmga007.fm.intel.com with ESMTP; 20 Mar 2020 08:40:36 -0700 From: Guennadi Liakhovetski To: alsa-devel@alsa-project.org Subject: [PATCH 11/12] ASoC: SOF: VirtIO: free guest pipelines upon termination Date: Fri, 20 Mar 2020 16:40:20 +0100 Message-Id: <20200320154021.5964-12-guennadi.liakhovetski@linux.intel.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> References: <20200320154021.5964-1-guennadi.liakhovetski@linux.intel.com> MIME-Version: 1.0 Cc: sound-open-firmware@alsa-project.org 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" Currently the SOF driver switches off the DSP every time runtime or system-wide suspend is entered. After the DSP is turned off, next time it's turned on, the firmware and topology have to be re-sent to it. When a guest SOF instance restarts it sends its topology to the host, which then forwards it to the DSP. This is correct if the DSP was suspended during that time and lost the guest's topology. However, if the DSP stayed active during that entire time, sending duplicate components to it produces errors. To prevent this from happening this patch adds freeing of components during guest shut down. Signed-off-by: Guennadi Liakhovetski --- drivers/vhost/dsp.c | 3 + include/sound/sof/virtio.h | 4 ++ sound/soc/sof/vhost-be.c | 155 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) diff --git a/drivers/vhost/dsp.c b/drivers/vhost/dsp.c index 205ae8c..33e849a 100644 --- a/drivers/vhost/dsp.c +++ b/drivers/vhost/dsp.c @@ -114,6 +114,9 @@ static void vhost_dsp_deactivate(struct vhost_dsp *dsp) dsp->active = false; + /* If a VM reboots dsp_sof_client_release() isn't called */ + dsp_sof_topology_purge(dsp->snd); + spin_lock_irqsave(&dsp->posn_lock, flags); list_for_each_entry_safe(buf, next, &dsp->posn_buf_list, list) { list_del(&buf->list); diff --git a/include/sound/sof/virtio.h b/include/sound/sof/virtio.h index fc98664..80e174e 100644 --- a/include/sound/sof/virtio.h +++ b/include/sound/sof/virtio.h @@ -147,6 +147,9 @@ struct dsp_sof_client { struct list_head pipe_conn; /* List of vhost instances on a DSP */ struct list_head list; + /* List of widgets to free for tear-down */ + struct list_head comp_list; + struct list_head pipe_list; /* Component ID range index in the bitmap */ unsigned int id; @@ -177,6 +180,7 @@ int dsp_sof_stream_data(struct dsp_sof_client *client, int dsp_sof_ipc_fwd(struct dsp_sof_client *client, int vq_idx, void *ipc_buf, void *reply_buf, size_t count, size_t reply_sz); +void dsp_sof_topology_purge(struct dsp_sof_client *client); /* The below functions are always referenced, they need dummy counterparts */ int dsp_sof_update_guest_posn(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/vhost-be.c b/sound/soc/sof/vhost-be.c index e1dede9..c25512b 100644 --- a/sound/soc/sof/vhost-be.c +++ b/sound/soc/sof/vhost-be.c @@ -43,6 +43,18 @@ struct dsp_pipeline_connect { struct list_head list; }; +struct dsp_sof_comp_list { + struct list_head list; + uint32_t comp_id; + enum sof_comp_type comp_type; +}; + +struct dsp_sof_pipe_list { + struct list_head list; + uint32_t comp_id; + uint32_t pipe_id; +}; + static const char dsp_pcm_name[] = "VHost PCM"; /* @@ -446,6 +458,75 @@ static int dsp_sof_ipc_comp(struct dsp_sof_client *client, cdata->comp_id >= client->comp_id_end ? -EINVAL : 0; } +void dsp_sof_topology_purge(struct dsp_sof_client *client) +{ + struct snd_sof_dev *sdev = client->sdev; + struct sof_ipc_free fcomp = { + .hdr = { + .size = sizeof(fcomp), + }, + }; + struct sof_ipc_reply reply; + struct dsp_sof_comp_list *citem, *ctmp; + struct dsp_sof_pipe_list *pitem, *ptmp; + int ret; + + pm_runtime_get_sync(sdev->dev); + + /* First free all pipelines */ + list_for_each_entry_safe(pitem, ptmp, &client->pipe_list, list) { + fcomp.id = pitem->comp_id; + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | + SOF_IPC_TPLG_PIPE_FREE; + + dev_dbg(sdev->dev, "tplg: unload component ID: %d pipe %u\n", + fcomp.id, pitem->pipe_id); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + fcomp.hdr.cmd, &fcomp, sizeof(fcomp), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "error: %d unloading component %d\n", + ret, fcomp.id); + + list_del(&pitem->list); + kfree(pitem); + } + + /* Then free all individual components */ + list_for_each_entry_safe(citem, ctmp, &client->comp_list, list) { + fcomp.id = citem->comp_id; + switch (citem->comp_type) { + case SOF_COMP_BUFFER: + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | + SOF_IPC_TPLG_BUFFER_FREE; + break; + default: + fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | + SOF_IPC_TPLG_COMP_FREE; + } + + dev_dbg(sdev->dev, "tplg: unload component ID: %d type %u\n", + fcomp.id, citem->comp_type); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, + fcomp.hdr.cmd, &fcomp, sizeof(fcomp), + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "error: %d unloading component %d\n", + ret, fcomp.id); + + list_del(&citem->list); + kfree(citem); + } + + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); +} +EXPORT_SYMBOL_GPL(dsp_sof_topology_purge); + /* process PM IPC */ static int dsp_sof_ipc_pm(struct dsp_sof_client *client, struct sof_ipc_cmd_hdr *hdr, struct sof_vfe_ipc_power_resp *resp) @@ -526,6 +607,47 @@ int dsp_sof_add_conn(struct snd_sof_dev *sdev, return 0; } +static int dsp_sof_tplg_comp_add(struct dsp_sof_client *client, + struct sof_ipc_comp *comp) +{ + struct dsp_sof_comp_list *citem = kmalloc(sizeof(*citem), GFP_KERNEL); + if (!citem) + return -ENOMEM; + + citem->comp_id = comp->id; + citem->comp_type = comp->type; + + dev_dbg(client->sdev->dev, "%s(): adding %p ID %d type %x\n", + __func__, citem, comp->id, comp->type); + list_add_tail(&citem->list, &client->comp_list); + + return 0; +} + +static int dsp_sof_tplg_pipe_add(struct dsp_sof_client *client, + struct sof_ipc_pipe_new *pipe) +{ + struct dsp_sof_pipe_list *pitem = kmalloc(sizeof(*pitem), GFP_KERNEL); + if (!pitem) + return -ENOMEM; + + pitem->comp_id = pipe->comp_id; + pitem->pipe_id = pipe->pipeline_id; + + dev_dbg(client->sdev->dev, "%s(): adding %p ID %d pipe %x\n", + __func__, pitem, pipe->comp_id, pipe->pipeline_id); + list_add_tail(&pitem->list, &client->pipe_list); + + return 0; +} + +static int dsp_sof_ipc_tplg_buf_new(struct dsp_sof_client *client, + struct sof_ipc_cmd_hdr *hdr, struct sof_ipc_reply *rhdr) +{ + struct sof_ipc_comp *comp = container_of(hdr, struct sof_ipc_comp, hdr); + return dsp_sof_tplg_comp_add(client, comp); +} + /* Handle some special cases of the "new component" IPC */ static int dsp_sof_ipc_tplg_comp_new(struct dsp_sof_client *client, int vq_idx, struct sof_ipc_cmd_hdr *hdr, struct sof_ipc_reply *rhdr) @@ -535,6 +657,7 @@ static int dsp_sof_ipc_tplg_comp_new(struct dsp_sof_client *client, int vq_idx, struct snd_sof_pcm *spcm, *last; struct sof_ipc_comp_host *host; struct dsp_pipeline_connect *conn; + int ret; if (comp->id < client->comp_id_begin || comp->id >= client->comp_id_end) @@ -595,6 +718,10 @@ static int dsp_sof_ipc_tplg_comp_new(struct dsp_sof_client *client, int vq_idx, break; } + ret = dsp_sof_tplg_comp_add(client, comp); + if (ret < 0) + return ret; + return 0; } @@ -606,6 +733,9 @@ static int dsp_sof_ipc_tplg_pipe_new(struct dsp_sof_client *client, int vq_idx, struct sof_ipc_pipe_new, hdr); struct snd_sof_dev *sdev = client->sdev; struct dsp_pipeline_connect *conn; + int ret = dsp_sof_tplg_pipe_add(client, pipeline); + if (ret < 0) + return ret; list_for_each_entry(conn, &sdev->connector_list, list) if (pipeline->pipeline_id == conn->guest_pipeline_id) { @@ -723,6 +853,8 @@ static int dsp_sof_ipc_tplg(struct dsp_sof_client *client, int vq_idx, case SOF_IPC_TPLG_COMP_NEW: return dsp_sof_ipc_tplg_comp_new(client, vq_idx, hdr, reply_buf); + case SOF_IPC_TPLG_BUFFER_NEW: + return dsp_sof_ipc_tplg_buf_new(client, hdr, reply_buf); case SOF_IPC_TPLG_PIPE_NEW: return dsp_sof_ipc_tplg_pipe_new(client, vq_idx, hdr); case SOF_IPC_TPLG_COMP_CONNECT: @@ -987,6 +1119,23 @@ int dsp_sof_set_tplg(struct dsp_sof_client *client, void dsp_sof_suspend(struct snd_sof_dev *sdev) { struct snd_sof_pcm *spcm, *next; + struct dsp_sof_client *client; + + /* Upon resume we'll rebuild lists */ + list_for_each_entry(client, &sdev->vbe_list, list) { + struct dsp_sof_comp_list *citem, *ctmp; + struct dsp_sof_pipe_list *pitem, *ptmp; + + list_for_each_entry_safe(pitem, ptmp, &client->pipe_list, list) { + list_del(&pitem->list); + kfree(pitem); + } + + list_for_each_entry_safe(citem, ctmp, &client->comp_list, list) { + list_del(&citem->list); + kfree(citem); + } + } list_for_each_entry_safe(spcm, next, &sdev->pcm_list, list) if (!strcmp(dsp_pcm_name, spcm->pcm.pcm_name)) { @@ -1000,6 +1149,9 @@ void dsp_sof_suspend(struct snd_sof_dev *sdev) /* A VM instance has closed the miscdevice */ void dsp_sof_client_release(struct dsp_sof_client *client) { + /* If a VM crashes we don't get ioctl(VHOST_SET_RUNNING, 0) from QEMU */ + dsp_sof_topology_purge(client); + bitmap_release_region(client->sdev->vfe_mask, client->id, 0); list_del(&client->list); @@ -1024,6 +1176,9 @@ struct dsp_sof_client *dsp_sof_client_add(struct snd_sof_dev *sdev, return NULL; } + INIT_LIST_HEAD(&client->pipe_list); + INIT_LIST_HEAD(&client->comp_list); + client->sdev = sdev; client->id = id; client->vhost = dsp;