From 99203e8d8968def973e82bb712ea959c6a46c16e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 27 Oct 2026 16:46:55 +0200 Subject: [PATCH] ASoC: hdac_hda: Keep runtime PM state in sync across codec remove/probe After the codec has been removed, its runtime PM status is RPM_SUSPENDED while hdac_hda_codec_probe() powers the codec up via snd_hda_codec_device_new(). The pm_runtime_put()/snd_hda_power_down() calls on probe and registration then see an already suspended device, the codec suspend callback is not called and the codec, its HDA link and the codec_powered bit stay set, preventing the controller from runtime suspending. The remove has a similar problem with the codec link accounting: hdac_hda_codec_remove() drops the link reference taken in probe without looking at the runtime PM state of the codec. If the codec was already runtime suspended, the link reference has been dropped by the codec suspend callback and the remove drops it a second time. Resume the codec in hdac_hda_codec_remove() before disabling runtime PM so that the link reference and the codec_powered bit are always in the same state as after the probe, and mark the device active on probe once the codec has been powered up. Finally mark the codec device suspended at the end of the remove. Its runtime PM is disabled at this point and if only the ASoC card is unbound (e.g. by unbinding the machine driver), a following system suspend would otherwise call the runtime suspend callback on the already cleaned up codec via pm_runtime_force_suspend(). This is a followup patch to address Sashiko reports. Link: https://lore.kernel.org/linux-sound/20261027083840.28813-4-peter.ujfalusi@linux.intel.com/ Fixes: c3ec8ac82105 ("ASoC: hdac_hda: fix memleak on module unload") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi Link: https://patch.msgid.link/20261027144655.31988-1-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hda.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index bd4b113401a8..48eb95973b7a 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -514,6 +514,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, false); + /* The codec is in D0, the status is RPM_SUSPENDED after a previous unbind */ + pm_runtime_set_active(&hdev->dev); + /* match for forbid call in snd_hda_codec_device_new() */ pm_runtime_allow(&hdev->dev); @@ -554,6 +557,12 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) return; } + /* + * Resume the codec to have its link reference (dropped below) and + * codec_powered bit in the same state as after the probe. + */ + pm_runtime_get_sync(&hdev->dev); + /* prevent runtime PM callbacks while the link and the codec are torn down */ pm_runtime_disable(&hdev->dev); snd_hdac_ext_bus_link_put(hdev->bus, hlink); @@ -565,6 +574,9 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component) /* Balances the disable above, cleanup_for_unbind keeps runtime PM disabled */ pm_runtime_enable(&hdev->dev); + pm_runtime_put_noidle(&hdev->dev); + /* The codec is cleaned up, system suspend must skip it from now on */ + pm_runtime_set_suspended(&hdev->dev); } static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = {