From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DDB973769E6 for ; Mon, 24 Aug 2026 03:42:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787542947; cv=none; b=PvwHPSceCUbjlMp4cRKob6x1/fJUDlsnil/ClZj8dWlAqvfBmVsoT4Xc5E6vKebPTE4QtlNuzAwgry1OicDQSePtFc3nOHL0gZKFDf1Qe2xAyLxCT/qxyvK4hr0rPl0T+ILET30j8vmSMDu4BHwUqAmpqvC6GKVK2JK3R21BrUs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787542947; c=relaxed/simple; bh=hVvWSR+4y9hh/YdFNcmxA7cJJldpDGkxTr0G5bzOUlc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cpnQ9bHkdOkhOSxOJWYCZ7Ps6ofQvv+U6v6Csze7sj2Gi8g122npJnqk9dkvNPicVASKdJ/by4TvZUjnccuP6BoRiJT4G8aSY/ulm6AEEDeAzbWLM3YEDvAe+rccL9sdTCTb8mrmQPW+V854vgKvLzaYgLsyHkJhwymNndMj0Ro= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=carbonforge.ai; spf=pass smtp.mailfrom=carbonforge.ai; dkim=pass (2048-bit key) header.d=carbonforge.ai header.i=@carbonforge.ai header.b=QGt7zjEO; arc=none smtp.client-ip=209.85.215.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=carbonforge.ai Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=carbonforge.ai Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=carbonforge.ai header.i=@carbonforge.ai header.b="QGt7zjEO" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-cbb8b54fcf8so2876419a12.0 for ; Sun, 23 Aug 2026 20:42:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=carbonforge.ai; s=google; t=1787542945; x=1788147745; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=4p3op5z6ERoIS4WKnwW2MZ7Z2ZW1xOrWk04xWTMa0VU=; b=QGt7zjEOPHw0YYtxjR54t2ukOchDslwmeJJkG9pO5Gdy9XtdfPz+CHGlFuLf35J2E8 SRl5wOZKKutQ+zPmJyWyl0j0XVmTi3cjz/8kV41iThnZ+Jh3UbmkJ8rAmSK46QpwysfF P5L7DW3balPzx551W6HQfY1S7MQOJv10xLSmM5WlPSUcZAC7nzMX0KGyHKi4ppC8s6FU So/gkQWKocQpT/eXapy2hph8QOHjVS4qtVfeEszvsKlddXwDSAQcff4zf8KzJPN2PjaJ JhxEwsqv44+W2Vb1irBSx1+7N6TSXpt4G5soPRdTmXlvFRAPkanCThMPAZ7+E4zrESg8 7pPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787542945; x=1788147745; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=4p3op5z6ERoIS4WKnwW2MZ7Z2ZW1xOrWk04xWTMa0VU=; b=E4UcBt6wVpHt4Qk2WCqK2Alf3+gnl6qB3Oc3klowwI9l0ItSnHCvlaLKZrgyIcx1FI UpNtcaqph9sd5GyQZo7Dx3vfzFf8oGZOXwFjqLGECcpS4A5PDaxCqyQ7mr6fyarz2iYk 4iJoJp1i2bxTWEERJ/UqbMTvBNFiLpaCQst7z6Aqk01tqmsLXWcTr20p8KfVkAn3Zgkb xrtC/D3avClb7XG6wdpPSlQiRP9KE7K99kwCjBzaWyXFvBQVJVJYlRy1oPQdBshvngxB JqNcX/VdXsXuw2R98KgQwRRAlZrQQfIuNoz9Bbd+AmZ+bnj30P24rwbS07Mhz0j8qKh6 Zacw== X-Forwarded-Encrypted: i=1; AHgh+RpSMBvz5odWMlWUiOV7lPY3U/IjPDL6+DGXQGfAumOoGmoKBPTvk7+4cT5pUHKQ2SH7bUdlRwOA6+6qxQ==@vger.kernel.org X-Gm-Message-State: AFuF++nTdg9w67oblHLx4R5HTEfWxzVtFav3XLLCRDmu3o3xqiij+Z4i vCpGL+J2JpNMVtDrbo6KLQfS/mxR0RCEzjkftNiwPOzZ6KsiuzA3LfPqGyIRVKe5Nrer X-Gm-Gg: AR+sD12r+7Xi77gSeCotCYY+3x8hN/hOAezP7LGo9AhLV0z9DLlAJT3XA4Ihvpc6mTJ 9Rc0/BGDYd4VxGrmj4UJRk7rQ4Hm7WsaQHirGaJ9pCiuvA2LVzBHrWbDfye+SRjrF91c1OdxaC5 Nd1WL+RaTeadHitwdgF1EydWmPFCwCZxUMsVRzufdaiTuPm3dAOFVS5p5CFiQkz+bbSMjmrtJkn g+nuFNzEARXsA24Ujp1IgTLfF5m3mgA5hrdIq2DJOqIVP6mixdIe/BNvnRT1fHLlrwTrINkSd9j BxBxUmkYIKnavIAhWRGW7swxfxalFZWNmHOaYEiR5LdLG1e5ahTzMWZ7NrsTvWTXgWA8i+ceiRK d3tqqU0Eof0qgGhjyCAOI8Ofe4vMLdTuCnv/vz5o9kFR0sxomQcsX0kJ2AOxrWOcQdbrO2G48pV tklu1lN+yyjhE0Ue0kVcC1fBaWUs/4LF5fWpvRtu1rBEV0Sb2BOeRsMID35PcHEaK8Y8YOSUr8a 1ZzOfeHbkckg7EPb2NiFwXBjZ2QFlKFHLZuGJ3mV/eSYiLycWsK9qalTdIkRnnNuyg0FPUjaP0w GeIeuEi8IfyHlGXoTd1Twqw61JXMPk6FKgoUHUX3GwnRKW8jKTOEmdWu6ZqY/KZOulSqpA/xhYY xh2ut1tB50noz47JLlAU0Lv9ShsICf+Gl2Y7ZlQ== X-Received: by 2002:a05:6a21:7d02:b0:3c3:a3fd:db0a with SMTP id adf61e73a8af0-3cd3013e240mr48114696637.16.1787542945012; Sun, 23 Aug 2026 20:42:25 -0700 (PDT) Received: from jml-HP-OmniBook-X-Flip-Laptop-14-kc0xxx.tail8972ac.ts.net (45-19-222-18.lightspeed.sntcca.sbcglobal.net. [45.19.222.18]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-327f90e22f8sm19080206eec.13.2026.08.23.20.42.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 23 Aug 2026 20:42:24 -0700 (PDT) From: jml To: Vijendar.Mukunda@amd.com, shenghao-ding@ti.com, broonie@kernel.org Cc: venkataprasad.potturu@amd.com, kevin-lu@ti.com, baojun.xu@ti.com, sen@ti.com, lgirdwood@gmail.com, perex@perex.cz, tiwai@suse.com, linux-sound@vger.kernel.org, jml Subject: [PATCH v2 3/3] ASoC: tas2783: re-initialise amplifiers from .startup after system sleep Date: Sun, 23 Aug 2026 20:41:53 -0700 Message-ID: <20260824034153.50193-4-jml@carbonforge.ai> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260824034153.50193-1-jml@carbonforge.ai> References: <20260824034153.50193-1-jml@carbonforge.ai> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit After system sleep the amplifiers come back silent. The firmware is in fact re-downloaded correctly when the peripheral re-enumerates - the download reports success and both fw_dl_success and hw_init are set - but the part still produces no output. Only a firmware download performed shortly before playback restores sound. Doing that from .hw_params is not safe when the parts are used as an aggregated pair on a single SoundWire stream, as they are on the HP OmniBook X Flip 14-kc0xxx: by then the first amplifier's port configuration is already in place, and resetting the second one knocks the first out. Exactly one of the two speakers then works, and which one varies from resume to resume. Do the re-initialisation from .startup instead. ASoC calls .startup for every codec DAI of a link before any of them reaches .hw_params, so both amplifiers can be re-initialised while the stream is still unconfigured. Gate it on a flag set from a PM notifier rather than on fw_dl_success: whether a given peripheral re-enumerates before or after the notifier runs is a race, so fw_dl_success is not a reliable indication that the DSP still holds its program. Signed-off-by: jml --- sound/soc/codecs/tas2783-sdw.c | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 2ca634a6e..4c850a857 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,13 @@ struct tas2783_prv { bool fw_dl_success; /* use fallback fw name */ bool fw_use_fallback; + /* set across system sleep, cleared once the part has been re-inited */ + bool force_reinit; + struct notifier_block pm_nb; }; +static s32 tas_io_init(struct device *dev, struct sdw_slave *slave); + static const struct reg_default tas2783_reg_default[] = { {TAS2783_AMP_LEVEL, 0x28}, {TASDEV_REG_SDW(0, 0, 0x04), 0x21}, @@ -1051,7 +1057,47 @@ static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream, TAS2783_SDCA_POW_STATE_OFF); } +/* + * The amplifier loses its DSP program across system sleep and only a + * firmware download performed shortly before playback makes it produce + * sound again. Doing that from .hw_params is not safe when the parts are + * used as an aggregated pair on one SoundWire stream: by then the first + * amplifier's port configuration is already in place, and resetting the + * second one knocks the first out, so exactly one of the two speakers + * works and which one varies from resume to resume. + * + * .startup runs for every codec DAI of the link before any of them + * reaches .hw_params, so both amplifiers can be re-initialised while the + * stream is still unconfigured. + */ +static int tas_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2783_prv *tas_dev = + snd_soc_component_get_drvdata(component); + s32 ret; + + if (!tas_dev->force_reinit && tas_dev->fw_dl_success) + return 0; + + tas_dev->hw_init = false; + regcache_cache_only(tas_dev->regmap, false); + + ret = tas_io_init(tas_dev->dev, tas_dev->sdw_peripheral); + if (ret || !tas_dev->fw_dl_success) { + dev_err(tas_dev->dev, "re-init before playback failed, err=%d\n", + ret); + return -EIO; + } + + tas_dev->force_reinit = false; + + return 0; +} + static const struct snd_soc_dai_ops tas_dai_ops = { + .startup = tas_sdw_startup, .hw_params = tas_sdw_hw_params, .hw_free = tas_sdw_pcm_hw_free, .set_stream = tas_set_sdw_stream, @@ -1391,6 +1437,32 @@ static void tas_remove(struct tas2783_prv *tas_dev) snd_soc_unregister_component(tas_dev->dev); } +/* + * fw_dl_success on its own is not a reliable indication that the DSP still + * holds its program: whether a given peripheral re-enumerates before or + * after this notifier runs is a race, and a download performed while the + * bus is still settling does not stick. + */ +static int tas2783_pm_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct tas2783_prv *tas_dev = + container_of(nb, struct tas2783_prv, pm_nb); + + switch (action) { + case PM_SUSPEND_PREPARE: + case PM_HIBERNATION_PREPARE: + case PM_POST_SUSPEND: + case PM_POST_HIBERNATION: + tas_dev->force_reinit = true; + break; + default: + break; + } + + return NOTIFY_DONE; +} + static s32 tas_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id) { @@ -1462,6 +1534,11 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, /* keep in cache until the device is fully initialized */ regcache_cache_only(regmap, true); tas_dev->regmap = regmap; + + tas_dev->pm_nb.notifier_call = tas2783_pm_notify; + if (register_pm_notifier(&tas_dev->pm_nb)) + dev_warn(dev, "pm notifier registration failed"); + return tas_init(tas_dev); } @@ -1469,6 +1546,7 @@ static void tas_sdw_remove(struct sdw_slave *peripheral) { struct tas2783_prv *tas_dev = dev_get_drvdata(&peripheral->dev); + unregister_pm_notifier(&tas_dev->pm_nb); pm_runtime_disable(tas_dev->dev); tas_remove(tas_dev); mutex_destroy(&tas_dev->calib_lock); -- 2.53.0