From patchwork Tue Apr 11 15:25:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207751 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id C31F1C76196 for ; Tue, 11 Apr 2023 15:27:33 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 45149F10; Tue, 11 Apr 2023 17:26:41 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 45149F10 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226851; bh=pBwFGn9ELKVEU+Rvm8MJHDCKQ1Ly3rABzADXMPiimPw=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=O2I4Acf9ZvTLHCxR54fq+QkENnTOkmrgpT1dzOnc5e2ybAmyq1+dKnmXb1iPYofPQ yzciZkyrFHSAEm3y0DkFRoJo/yt/8fTKdMbtZyQBWFAiYpQw+LJSOkTwJk7/AsMBVw FrbDRAro1besIeG0zn9de+i//v6nPgSctfcNZsnU= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 74306F80448; Tue, 11 Apr 2023 17:25:48 +0200 (CEST) To: Subject: [PATCH 1/6] ASoC: cs35l56: Remove quick-cancelling of dsp_work() Date: Tue, 11 Apr 2023 16:25:23 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122674746.26.16881587647873355224@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline Delete the 'removing' flag and don't kick init_completion to make a quick cancel of dsp_work(). Just let it timeout on the wait for the completion. Simplify the code to standard cancelling or flushing of the work. This avoids introducing corner cases from a layer of custom signalling. It also avoids potential race conditions when system-suspend handling is added. Unless the hardware is broken, the dsp_work() will already have started and passed the completion before the driver would want to cancel it. Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/cs35l56.c | 8 +------- sound/soc/codecs/cs35l56.h | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index d97b465f0d3c..675aad8e909f 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -867,7 +867,7 @@ static void cs35l56_dsp_work(struct work_struct *work) goto complete; } - if (!cs35l56->init_done || cs35l56->removing) + if (!cs35l56->init_done) goto complete; cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", @@ -917,9 +917,6 @@ static void cs35l56_dsp_work(struct work_struct *work) goto err; } - if (cs35l56->removing) - goto err; - mutex_lock(&cs35l56->irq_lock); init_completion(&cs35l56->init_completion); @@ -967,7 +964,6 @@ static int cs35l56_component_probe(struct snd_soc_component *component) BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values)); - cs35l56->removing = false; cs35l56->component = component; wm_adsp2_component_probe(&cs35l56->dsp, component); @@ -984,8 +980,6 @@ static void cs35l56_component_remove(struct snd_soc_component *component) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); - cs35l56->removing = true; - complete(&cs35l56->init_completion); cancel_work_sync(&cs35l56->dsp_work); } diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index efc4b99180f9..dc91cd7d877f 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -49,7 +49,6 @@ struct cs35l56_private { bool soft_resetting; bool init_done; bool sdw_attached; - bool removing; bool fw_patched; bool can_hibernate; struct completion init_completion; From patchwork Tue Apr 11 15:25:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207755 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 16887C76196 for ; Tue, 11 Apr 2023 15:28:39 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id B0984F06; Tue, 11 Apr 2023 17:27:46 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz B0984F06 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226916; bh=CGz6dOryt6i+9MyE/ROX0UldLs/32MeJGRQXpIrQOPs=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=MfYF21zfmipcgRZqf07tmyPPz/NnSwAhAGB59Xd0pgjLYcMZjVU8DzQXxFt2U8+y+ 4DIkrqlkIqSrPaV8Imm03B9rdkAczLHbCK8z4KPqv7sMIQ/rkANUy+QqS2Ox8xp6GN QQSm2jNGt/gkr8OfX5O2YJAEIKZtHqZ61uJZNQKo= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id E3E31F80563; Tue, 11 Apr 2023 17:26:48 +0200 (CEST) To: Subject: [PATCH 2/6] ASoC: cs35l56: Use DAPM widget for firmware PLAY/PAUSE Date: Tue, 11 Apr 2023 16:25:24 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122680811.26.11649985628420343041@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline If we use a DAPM widget instead of mute_stream() to send the PLAY command we can issue the plays to multiple amps in parallel. With mute_stream each codec driver instance is called one at a time so we get N * PS0 delay time. DAPM does each stage on every widget in a card before moving to the next stage. So all amps will do the PRE_PMU then all will do the POST_PMU. The PLAY is sent in the PRE_PMU so that they all power-up in parallel. After the PS0 wait in the first POST_PMU all the other amps will also be ready so there won't be any extra delay, or it will be negligible. There's also no point waiting for the MBOX ack in the PRE_PMU. We won't see a PS0 state in POST_PMU if it didn't ack the PLAY command. So we can save a little extra time. Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/cs35l56.c | 105 +++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 675aad8e909f..997a5c5acaab 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -32,6 +32,23 @@ static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); +static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command) +{ + unsigned int val; + int ret; + + regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); + ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + val, (val == 0), + CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); + if (ret) { + dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret); + return ret; + } + + return 0; +} + static int cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56) { int ret; @@ -182,10 +199,45 @@ static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx6_enum, static const struct snd_kcontrol_new sdw1_tx6_mux = SOC_DAPM_ENUM("SDW1TX6 SRC", cs35l56_sdw1tx6_enum); +static int cs35l56_play_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); + unsigned int val; + int ret; + + dev_dbg(cs35l56->dev, "play: %d\n", event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Don't wait for ACK, we check in POST_PMU that it completed */ + return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, + CS35L56_MBOX_CMD_AUDIO_PLAY); + case SND_SOC_DAPM_POST_PMU: + /* Wait for firmware to enter PS0 power state */ + ret = regmap_read_poll_timeout(cs35l56->regmap, + CS35L56_TRANSDUCER_ACTUAL_PS, + val, (val == CS35L56_PS0), + CS35L56_PS0_POLL_US, + CS35L56_PS0_TIMEOUT_US); + if (ret) + dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret); + return ret; + case SND_SOC_DAPM_POST_PMD: + return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE); + default: + return 0; + } +} + static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = { SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0), SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0), + SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("SPK"), @@ -252,6 +304,9 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { { "AMP", NULL, "VDD_B" }, { "AMP", NULL, "VDD_AMP" }, + { "ASP1 Playback", NULL, "PLAY" }, + { "SDW1 Playback", NULL, "PLAY" }, + { "ASP1RX1", NULL, "ASP1 Playback" }, { "ASP1RX2", NULL, "ASP1 Playback" }, { "DSP1", NULL, "ASP1RX1" }, @@ -288,23 +343,6 @@ static const struct snd_soc_dapm_route cs35l56_audio_map[] = { { "SDW1 Capture", NULL, "SDW1 TX6 Source" }, }; -static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command) -{ - unsigned int val; - int ret; - - regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command); - ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, - val, (val == 0), - CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US); - if (ret) { - dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret); - return ret; - } - - return 0; -} - static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -611,43 +649,11 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai, return 0; } -static int cs35l56_mute_stream(struct snd_soc_dai *dai, int mute, int stream) -{ - struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component); - unsigned int val; - int ret; - - dev_dbg(cs35l56->dev, "%s: %d %s\n", __func__, stream, mute ? "mute" : "unmute"); - - if (stream != SNDRV_PCM_STREAM_PLAYBACK) - return 0; - - if (mute) { - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE); - } else { - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PLAY); - if (ret == 0) { - /* Wait for firmware to enter PS0 power state */ - ret = regmap_read_poll_timeout(cs35l56->regmap, - CS35L56_TRANSDUCER_ACTUAL_PS, - val, (val == CS35L56_PS0), - CS35L56_PS0_POLL_US, - CS35L56_PS0_TIMEOUT_US); - if (ret) - dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret); - ret = 0; - } - } - - return ret; -} - static const struct snd_soc_dai_ops cs35l56_ops = { .set_fmt = cs35l56_asp_dai_set_fmt, .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot, .hw_params = cs35l56_asp_dai_hw_params, .set_sysclk = cs35l56_asp_dai_set_sysclk, - .mute_stream = cs35l56_mute_stream, }; static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream, @@ -749,7 +755,6 @@ static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = { .shutdown = cs35l56_sdw_dai_shutdown, .hw_params = cs35l56_sdw_dai_hw_params, .hw_free = cs35l56_sdw_dai_hw_free, - .mute_stream = cs35l56_mute_stream, .set_stream = cs35l56_sdw_dai_set_stream, }; From patchwork Tue Apr 11 15:25:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207753 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id BD6ABC77B70 for ; Tue, 11 Apr 2023 15:28:12 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 6EAAFF11; Tue, 11 Apr 2023 17:27:20 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6EAAFF11 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226890; bh=Y51UQ2ugAy2nOw3puPqR97e5FQ4fHG7M3xmAcy9F10A=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=UMX0uS8uNX+5PkmRuexzw7Sq+9dqGyrAoWApo8NyeXFsOAGNaYX1iseLpXpC9repe a+UaXHxpdLx6jPt6SWYB9JX+2UFquXI6cmclIWs6aUVKDLY91686oMDpYV9RL5CK56 EpydKsO+MXffu8mCg9qZvORVbPjfKGDoL+uuM7ps= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id BE1DFF80552; Tue, 11 Apr 2023 17:25:52 +0200 (CEST) To: Subject: [PATCH 3/6] ASoC: cs35l56: Skip first init_completion wait in dsp_work if init_done Date: Tue, 11 Apr 2023 16:25:25 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122675139.26.1346332199626475851@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline At the start of dsp_work() only wait for init_completion if !init_done. This allows system suspend to re-queue dsp_work() without having to do a dummy complete() of init_completion. A dummy completion in system suspend would have to be conditional on init_done. But that would create a possible race condition between our system resume and cs35l56_init() in the corner case that we suspend right after the SoundWire core has enumerated and reported ATTACHED. It is safer and simpler to have cs35l56_init() as the only place that init_completion is completed, and dsp_work() as the only place that it is consumed. Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/cs35l56.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 997a5c5acaab..62c44276c121 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -866,7 +866,8 @@ static void cs35l56_dsp_work(struct work_struct *work) unsigned int val; int ret = 0; - if (!wait_for_completion_timeout(&cs35l56->init_completion, + if (!cs35l56->init_done && + !wait_for_completion_timeout(&cs35l56->init_completion, msecs_to_jiffies(5000))) { dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__); goto complete; From patchwork Tue Apr 11 15:25:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207752 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id C702DC76196 for ; Tue, 11 Apr 2023 15:27:51 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id A3885F15; Tue, 11 Apr 2023 17:26:59 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz A3885F15 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226869; bh=Mqw8ivp0XKOZsZETtkKr4zcTpabQvV/v/6kvdtrM03w=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=CJQISNBfMh4sId3nRIhxg4Ehh8lIIFeKNjSZZZnigrrY5Z1q+2CAcvcynA7OyLiAH 8GokFkoDdI5BjaSmHms3Q34rHovZMeyTbeug3skzS4Na07zZNtaLyJhZNq+ZqgLEPD CgYatW1fGGGMgmQjsuAlyr3ztwDBKninscHZbV/A= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 0ADD1F8053D; Tue, 11 Apr 2023 17:25:50 +0200 (CEST) To: Subject: [PATCH 4/6] ASoC: cs35l56: Always wait for firmware boot in runtime-resume Date: Tue, 11 Apr 2023 16:25:26 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122674948.26.205996534753979275@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline When we are resuming from a system suspend the CS35L56 has probably been hard reset (usually a power-on reset). So we must wait for the firmware to boot. On SoundWire we also need it to re-initialize before we can read the registers to check the CS35L56 state. The simplest way to handle this is for runtime-resume to always wait for firmware boot. If the firmware is already booted the overhead is only one register read. The system-resume will have to runtime-resume the driver anyway before attempting any register access. So this will automatically include the wait for initialization on SoundWire. Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/cs35l56.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 62c44276c121..74abcf1c604a 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -1102,10 +1102,8 @@ int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) unsigned int val; int ret; - if (!cs35l56->can_hibernate) { - regcache_cache_only(cs35l56->regmap, false); + if (!cs35l56->can_hibernate) goto out_sync; - } if (!cs35l56->sdw_peripheral) { /* @@ -1120,19 +1118,19 @@ int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) CS35L56_CONTROL_PORT_READY_US + 400); } - regcache_cache_only(cs35l56->regmap, false); - - ret = cs35l56_wait_for_firmware_boot(cs35l56); - if (ret) { - dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret); - goto err; - } - - ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); - if (ret) - goto err; - out_sync: + regcache_cache_only(cs35l56->regmap, false); + + ret = cs35l56_wait_for_firmware_boot(cs35l56); + if (ret) { + dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret); + goto err; + } + + ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE); + if (ret) + goto err; + /* BOOT_DONE will be 1 if the amp reset */ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val); if (val & CS35L56_OTP_BOOT_DONE_MASK) { From patchwork Tue Apr 11 15:25:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207754 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id 5CD61C77B6F for ; Tue, 11 Apr 2023 15:28:26 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 50B24F0A; Tue, 11 Apr 2023 17:27:33 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 50B24F0A DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226903; bh=YXzYx6RD1cn41fVnW0DD+Z1KLIBev2KwlwkFKC7mWwk=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=naEuAMsHtLzGdxRPaOTX+SyI1edwx3VNlnUmnx376HY8aCBz7OL1u9ZIXVCs/gFvd gwZkxN5KwgPlzChp4NRkSFBDUw5FSGAhUbzEAL7z8a7chrz5JB7x5maPSppw6KW2hz 0owJ7Fw8B5yAai9MHApQW9ND4nZlQRkHzq5y6u7s= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 9943AF80529; Tue, 11 Apr 2023 17:26:45 +0200 (CEST) To: Subject: [PATCH 5/6] ASoC: cs35l56: Add basic system suspend handling Date: Tue, 11 Apr 2023 16:25:27 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122680485.26.15149703897978505351@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline This adds the main handling for system suspend but does not handle re-patching the firmware after system resume. This is a multi-stage suspend and resume because if there is a RESET line it is almost certain that it will be shared by all the amps. So every amp must have done its suspend before we can assert RESET. Likewise we must de-assert RESET before the amps can resume. It's preferable to assert RESET before we turning off regulators, and while they power up. The actual suspend and resume is done by using the pair pm_runtime_force_suspend() and pm_runtime_force_resume() to re-use our runtime suspend/resume sequences. pm_runtime_force_suspend() will disable our pm_runtime. If we were runtime-resumed it calls our runtime_suspend(). pm_runtime_force_resume() re-enables pm_runtime and if we were originally runtime-resumed before the pm_runtime_force_suspend() it calls our runtime_resume(). Otherwise it leaves us runtime-suspended. The general process is therefore: suspend() -> finish dsp_work and then run our runtime_suspend suspend_late() -> assert RESET and turn off supplies resume_early() -> enable supplies and de-assert RESET resume() -> pm_runtime_force_resume() In addition, to prevent the IRQ handler running in the period between pm_runtime_force_suspend() and pm_runtime_force_resume() the parent IRQ is temporarily disabled: - from suspend until suspend_noirq - from resume_noirq until resume Signed-off-by: Richard Fitzgerald --- sound/soc/codecs/cs35l56-sdw.c | 26 +++++++ sound/soc/codecs/cs35l56.c | 126 +++++++++++++++++++++++++++++++++ sound/soc/codecs/cs35l56.h | 6 ++ 3 files changed, 158 insertions(+) diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 448ef3609f4c..947d4e5f4dc9 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -450,6 +450,29 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) return 0; } +static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + if (!cs35l56->init_done) + return 0; + + /* + * Disable SoundWire interrupts. + * Flush - don't cancel because that could leave an unbalanced pm_runtime_get. + */ + cs35l56->sdw_irq_no_unmask = true; + flush_work(&cs35l56->sdw_irq_work); + + /* Mask interrupts and flush in case sdw_irq_work was queued again */ + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + flush_work(&cs35l56->sdw_irq_work); + + return cs35l56_system_suspend(dev); +} + static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { struct device *dev = &peripheral->dev; @@ -499,6 +522,9 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) static const struct dev_pm_ops cs35l56_sdw_pm = { SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ }; static const struct sdw_device_id cs35l56_sdw_id[] = { diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 74abcf1c604a..eb85c27ab087 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -1154,6 +1156,127 @@ int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) } EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE); +int cs35l56_system_suspend(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend\n"); + + if (cs35l56->component) + flush_work(&cs35l56->dsp_work); + + /* + * The interrupt line is normally shared, but after we start suspending + * we can't check if our device is the source of an interrupt, and can't + * clear it. Prevent this race by temporarily disabling the parent irq + * until we reach _no_irq. + */ + if (cs35l56->irq) + disable_irq(cs35l56->irq); + + return pm_runtime_force_suspend(dev); +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend); + +int cs35l56_system_suspend_late(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend_late\n"); + + /* + * Assert RESET before removing supplies. + * RESET is usually shared by all amps so it must not be asserted until + * all driver instances have done their suspend() stage. + */ + if (cs35l56->reset_gpio) { + gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + usleep_range(CS35L56_RESET_PULSE_MIN_US, CS35L56_RESET_PULSE_MIN_US + 400); + } + + regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late); + +int cs35l56_system_suspend_no_irq(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_suspend_no_irq\n"); + + /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */ + if (cs35l56->irq) + enable_irq(cs35l56->irq); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq); + +int cs35l56_system_resume_no_irq(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + dev_dbg(dev, "system_resume_no_irq\n"); + + /* + * WAKE interrupts unmask if the CS35L56 hibernates, which can cause + * spurious interrupts, and the interrupt line is normally shared. + * We can't check if our device is the source of an interrupt, and can't + * clear it, until it has fully resumed. Prevent this race by temporarily + * disabling the parent irq until we complete resume(). + */ + if (cs35l56->irq) + disable_irq(cs35l56->irq); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq); + +int cs35l56_system_resume_early(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "system_resume_early\n"); + + /* Ensure a spec-compliant RESET pulse. */ + if (cs35l56->reset_gpio) { + gpiod_set_value_cansleep(cs35l56->reset_gpio, 0); + usleep_range(CS35L56_RESET_PULSE_MIN_US, CS35L56_RESET_PULSE_MIN_US + 400); + } + + /* Enable supplies before releasing RESET. */ + ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); + if (ret) { + dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret); + return ret; + } + + /* Release shared RESET before drivers start resume(). */ + gpiod_set_value_cansleep(cs35l56->reset_gpio, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume_early); + +int cs35l56_system_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; + + dev_dbg(dev, "system_resume\n"); + + /* Undo pm_runtime_force_suspend() before re-enabling the irq */ + ret = pm_runtime_force_resume(dev); + if (cs35l56->irq) + enable_irq(cs35l56->irq); + + return ret; +} +EXPORT_SYMBOL_GPL(cs35l56_system_resume); + static int cs35l56_dsp_init(struct cs35l56_private *cs35l56) { struct wm_adsp *dsp; @@ -1451,6 +1574,9 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE); const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = { SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL) + SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume) + LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) + NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq) }; EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index dc91cd7d877f..50278dafc9ca 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -67,6 +67,12 @@ extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; int cs35l56_runtime_suspend(struct device *dev); int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56); +int cs35l56_system_suspend(struct device *dev); +int cs35l56_system_suspend_late(struct device *dev); +int cs35l56_system_suspend_no_irq(struct device *dev); +int cs35l56_system_resume_no_irq(struct device *dev); +int cs35l56_system_resume_early(struct device *dev); +int cs35l56_system_resume(struct device *dev); irqreturn_t cs35l56_irq(int irq, void *data); int cs35l56_irq_request(struct cs35l56_private *cs35l56); int cs35l56_common_probe(struct cs35l56_private *cs35l56); From patchwork Tue Apr 11 15:25:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Richard Fitzgerald X-Patchwork-Id: 13207750 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org 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 smtp.lore.kernel.org (Postfix) with ESMTPS id CBD82C77B6F for ; Tue, 11 Apr 2023 15:27:26 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 52E7EF05; Tue, 11 Apr 2023 17:26:34 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 52E7EF05 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681226844; bh=wVtoPMluT+HbD+tfrKpVbIYt3gzLS28HERMO8KCFL0o=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=fn63ihpgeEuHa68pXxIkGjLpRoeacapHWKWZ1jgV0WtjMHvX968q0K0B6778jA44V lKQCdU/1RT+Zbgz1uv60XrhdaIfJgrcEDMOBnCgAsqPJXABpPEmkaxuOmERSGLjC9T ZL/2yAcv0CU8BHc8lmXQbp8mUlaN2VstcEYW/hIs= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id C2B32F8052D; Tue, 11 Apr 2023 17:25:46 +0200 (CEST) To: Subject: [PATCH 6/6] ASoC: cs35l56: Re-patch firmware after system suspend Date: Tue, 11 Apr 2023 16:25:28 +0100 In-Reply-To: <20230411152528.329803-1-rf@opensource.cirrus.com> References: <20230411152528.329803-1-rf@opensource.cirrus.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168122674550.26.8545058503709956172@mailman-core.alsa-project.org> X-Patchwork-Original-From: Richard Fitzgerald via Alsa-devel From: Richard Fitzgerald Reply-To: Richard Fitzgerald Cc: alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, patches@opensource.cirrus.com, Richard Fitzgerald Content-Disposition: inline Check during cs35l56_system_resume() whether the firmware patch must be applied again. The FIRMWARE_MISSING flag in the PROTECTION_STATUS register indicates whether the firmware has been patched. In non-secure mode the FIRMWARE_MISSING flag is cleared at the end of dsp_work(). If it is set after system-resume we know that dsp_work() must be run again. In secure mode the pre-OS loader will have done the secure patching and cleared the FIRMWARE_MISSING flag. So this flag does not tell us whether firmware memory was lost. But the driver could only be downloading non-secure tunings, which is always safe to do. If the driver has control of RESET we will have asserted it during suspend so the firmware patch will have been lost. The driver would only have control of RESET in non-secure mode. Signed-off-by: Richard Fitzgerald --- include/sound/cs35l56.h | 4 ++ sound/soc/codecs/cs35l56-sdw.c | 12 +++++- sound/soc/codecs/cs35l56.c | 67 +++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/include/sound/cs35l56.h b/include/sound/cs35l56.h index 5f8ea2dfaa21..b3300bce74f4 100644 --- a/include/sound/cs35l56.h +++ b/include/sound/cs35l56.h @@ -95,6 +95,7 @@ #define CS35L56_MAIN_RENDER_USER_MUTE 0x3400024 #define CS35L56_MAIN_RENDER_USER_VOLUME 0x340002C #define CS35L56_MAIN_POSTURE_NUMBER 0x3400094 +#define CS35L56_PROTECTION_STATUS 0x34000D8 #define CS35L56_TRANSDUCER_ACTUAL_PS 0x3400150 #define CS35L56_DSP1_YMEM_UNPACKED24_6141 0x3405FF4 #define CS35L56_DSP1_PMEM_0 0x3800000 @@ -216,6 +217,9 @@ #define CS35L56_MAIN_POSTURE_MAX 255 #define CS35L56_MAIN_POSTURE_MASK CS35L56_MAIN_POSTURE_MAX +/* CS35L56_PROTECTION_STATUS */ +#define CS35L56_FIRMWARE_MISSING BIT(0) + /* Software Values */ #define CS35L56_HALO_STATE_SHUTDOWN 1 #define CS35L56_HALO_STATE_BOOT_DONE 2 diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 947d4e5f4dc9..e759347423cf 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -473,6 +473,16 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) return cs35l56_system_suspend(dev); } +static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev) +{ + struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + + cs35l56->sdw_irq_no_unmask = false; + /* runtime_resume re-enables the interrupt */ + + return cs35l56_system_resume(dev); +} + static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { struct device *dev = &peripheral->dev; @@ -522,7 +532,7 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) static const struct dev_pm_ops cs35l56_sdw_pm = { SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL) - SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_system_resume) + SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume) LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early) /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */ }; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index eb85c27ab087..18e341744839 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -946,6 +946,7 @@ static void cs35l56_dsp_work(struct work_struct *work) goto err_unlock; } + regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING); cs35l56->fw_patched = true; err_unlock: @@ -1026,6 +1027,8 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = { .num_controls = ARRAY_SIZE(cs35l56_controls), .set_bias_level = cs35l56_set_bias_level, + + .suspend_bias_off = 1, /* see cs35l56_system_resume() */ }; static const struct reg_sequence cs35l56_hibernate_seq[] = { @@ -1156,6 +1159,47 @@ int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56) } EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE); +static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56) +{ + unsigned int val; + int ret; + + /* Nothing to re-patch if we haven't done any patching yet. */ + if (!cs35l56->fw_patched) + return false; + + /* + * If we have control of RESET we will have asserted it so the firmware + * will need re-patching. + */ + if (cs35l56->reset_gpio) + return true; + + /* + * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so + * can't be used here to test for memory retention. + * Assume that tuning must be re-loaded. + */ + if (cs35l56->secured) + return true; + + ret = pm_runtime_resume_and_get(cs35l56->dev); + if (ret) { + dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret); + return ret; + } + + ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val); + if (ret) + dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret); + else + ret = !!(val & CS35L56_FIRMWARE_MISSING); + + pm_runtime_put_autosuspend(cs35l56->dev); + + return ret; +} + int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); @@ -1273,7 +1317,28 @@ int cs35l56_system_resume(struct device *dev) if (cs35l56->irq) enable_irq(cs35l56->irq); - return ret; + if (ret) + return ret; + + /* Firmware won't have been loaded if the component hasn't probed */ + if (!cs35l56->component) + return 0; + + ret = cs35l56_is_fw_reload_needed(cs35l56); + dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret); + if (ret < 1) + return ret; + + cs35l56->fw_patched = false; + init_completion(&cs35l56->dsp_ready_completion); + queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work); + + /* + * suspend_bias_off ensures we are now in BIAS_OFF so there will be + * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching. + */ + + return 0; } EXPORT_SYMBOL_GPL(cs35l56_system_resume);