From e138d314d5b61b093814d59c5c49f14dae8cad18 Mon Sep 17 00:00:00 2001 From: kellermanrivero Date: Fri, 6 Nov 2026 13:46:08 +0000 Subject: [PATCH] ASoC: qcom: lpass macro / soundwire: retry deferred clock acquisition On Qualcomm X1E/X1P laptops the LPASS WSA/RX/TX macro codecs take their "fsgen" clock from the VA macro, and their "mclk"/"macro"/"dcodec" clocks from the q6prm clock controller, which lives under the ADSP's GPR service node. These providers can register a few seconds after the consumers probe, because the ADSP itself boots asynchronously (remoteproc + PD mapper). Clock-provider registration does not trigger deferred-probe retries, so a macro driver that loses this order race stays deferred forever: on the Asus Vivobook 16 (X1P-42-100) the whole audio chain (SoundWire masters, WSA/RX/TX macros, sound card) freezes on roughly every other boot, depending on ADSP bring-up timing versus driver probe. Fix by giving the devm_clk_get() calls in these drivers a bounded retry (60 x 500 ms) instead of returning -EPROBE_DEFER into a queue that is never revisited. 30 s covers the observed ADSP boot latency with ample margin, and probe context can sleep. Also applies the same treatment to the "iface" clock of the Qualcomm SoundWire controller, which depends on the same macro clock providers. Tested on Asus Vivobook 16 X1607QA (Snapdragon X X1P-42-100): audio chain now completes probe on every boot instead of intermittently. Signed-off-by: kellermanrivero --- drivers/soundwire/qcom.c | 37 +++++++++++++++++++++++- sound/soc/codecs/lpass-rx-macro.c | 45 ++++++++++++++++++++++++++---- sound/soc/codecs/lpass-tx-macro.c | 45 ++++++++++++++++++++++++++---- sound/soc/codecs/lpass-va-macro.c | 43 +++++++++++++++++++++++++--- sound/soc/codecs/lpass-wsa-macro.c | 45 ++++++++++++++++++++++++++---- 5 files changed, 195 insertions(+), 20 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index 6e0c5fb10efad..93ec0fb872539 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -21,6 +21,41 @@ #include #include #include "bus.h" +#include + +/* + * Local helper: bounded retry for deferred clock providers. + * Clock-provider registration does not trigger deferred-probe retries, + * so a consumer that loses the probe-order race stays deferred forever. + * Wait up to 30s for providers that are registered milliseconds later. + */ +static struct clk *clk_get_retry(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} + +static struct clk *clk_get_retry_optional(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get_optional(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} #define SWRM_COMP_SW_RESET 0x008 #define SWRM_COMP_STATUS 0x014 @@ -1669,7 +1704,7 @@ static int qcom_swrm_probe(struct platform_device *pdev) goto err_init; } - ctrl->hclk = devm_clk_get(dev, "iface"); + ctrl->hclk = clk_get_retry(dev, "iface"); if (IS_ERR(ctrl->hclk)) { ret = dev_err_probe(dev, PTR_ERR(ctrl->hclk), "unable to get iface clock\n"); goto err_init; diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c index c41eb8de61609..3198ba6412833 100644 --- a/sound/soc/codecs/lpass-rx-macro.c +++ b/sound/soc/codecs/lpass-rx-macro.c @@ -18,6 +18,41 @@ #include #include "lpass-macro-common.h" +#include + +/* + * Local helper: bounded retry for deferred clock providers. + * Clock-provider registration does not trigger deferred-probe retries, + * so a consumer that loses the probe-order race stays deferred forever. + * Wait up to 30s for providers that are registered milliseconds later. + */ +static struct clk *clk_get_retry(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} + +static struct clk *clk_get_retry_optional(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get_optional(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} #define CDC_RX_TOP_TOP_CFG0 (0x0000) #define CDC_RX_TOP_SWR_CTRL (0x0008) @@ -3780,25 +3815,25 @@ static int rx_macro_probe(struct platform_device *pdev) if (!rx) return -ENOMEM; - rx->macro = devm_clk_get_optional(dev, "macro"); + rx->macro = clk_get_retry_optional(dev, "macro"); if (IS_ERR(rx->macro)) return dev_err_probe(dev, PTR_ERR(rx->macro), "unable to get macro clock\n"); - rx->dcodec = devm_clk_get_optional(dev, "dcodec"); + rx->dcodec = clk_get_retry_optional(dev, "dcodec"); if (IS_ERR(rx->dcodec)) return dev_err_probe(dev, PTR_ERR(rx->dcodec), "unable to get dcodec clock\n"); - rx->mclk = devm_clk_get(dev, "mclk"); + rx->mclk = clk_get_retry(dev, "mclk"); if (IS_ERR(rx->mclk)) return dev_err_probe(dev, PTR_ERR(rx->mclk), "unable to get mclk clock\n"); if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { - rx->npl = devm_clk_get(dev, "npl"); + rx->npl = clk_get_retry(dev, "npl"); if (IS_ERR(rx->npl)) return dev_err_probe(dev, PTR_ERR(rx->npl), "unable to get npl clock\n"); } - rx->fsgen = devm_clk_get(dev, "fsgen"); + rx->fsgen = clk_get_retry(dev, "fsgen"); if (IS_ERR(rx->fsgen)) return dev_err_probe(dev, PTR_ERR(rx->fsgen), "unable to get fsgen clock\n"); diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c index 6deaaabe43afa..f3a6c5d9fa17a 100644 --- a/sound/soc/codecs/lpass-tx-macro.c +++ b/sound/soc/codecs/lpass-tx-macro.c @@ -16,6 +16,41 @@ #include #include "lpass-macro-common.h" +#include + +/* + * Local helper: bounded retry for deferred clock providers. + * Clock-provider registration does not trigger deferred-probe retries, + * so a consumer that loses the probe-order race stays deferred forever. + * Wait up to 30s for providers that are registered milliseconds later. + */ +static struct clk *clk_get_retry(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} + +static struct clk *clk_get_retry_optional(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get_optional(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} #define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000) #define CDC_TX_MCLK_EN_MASK BIT(0) @@ -2258,25 +2293,25 @@ static int tx_macro_probe(struct platform_device *pdev) tx->data = device_get_match_data(dev); - tx->macro = devm_clk_get_optional(dev, "macro"); + tx->macro = clk_get_retry_optional(dev, "macro"); if (IS_ERR(tx->macro)) return dev_err_probe(dev, PTR_ERR(tx->macro), "unable to get macro clock\n"); - tx->dcodec = devm_clk_get_optional(dev, "dcodec"); + tx->dcodec = clk_get_retry_optional(dev, "dcodec"); if (IS_ERR(tx->dcodec)) return dev_err_probe(dev, PTR_ERR(tx->dcodec), "unable to get dcodec clock\n"); - tx->mclk = devm_clk_get(dev, "mclk"); + tx->mclk = clk_get_retry(dev, "mclk"); if (IS_ERR(tx->mclk)) return dev_err_probe(dev, PTR_ERR(tx->mclk), "unable to get mclk clock\n"); if (tx->data->flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { - tx->npl = devm_clk_get(dev, "npl"); + tx->npl = clk_get_retry(dev, "npl"); if (IS_ERR(tx->npl)) return dev_err_probe(dev, PTR_ERR(tx->npl), "unable to get npl clock\n"); } - tx->fsgen = devm_clk_get(dev, "fsgen"); + tx->fsgen = clk_get_retry(dev, "fsgen"); if (IS_ERR(tx->fsgen)) return dev_err_probe(dev, PTR_ERR(tx->fsgen), "unable to get fsgen clock\n"); diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 03d6434a4a126..61f9f6cf676be 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -19,6 +19,41 @@ #include #include "lpass-macro-common.h" +#include + +/* + * Local helper: bounded retry for deferred clock providers. + * Clock-provider registration does not trigger deferred-probe retries, + * so a consumer that loses the probe-order race stays deferred forever. + * Wait up to 30s for providers that are registered milliseconds later. + */ +static struct clk *clk_get_retry(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} + +static struct clk *clk_get_retry_optional(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get_optional(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} /* VA macro registers */ #define CDC_VA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) @@ -1557,15 +1592,15 @@ static int va_macro_probe(struct platform_device *pdev) va->dev = dev; - va->macro = devm_clk_get_optional(dev, "macro"); + va->macro = clk_get_retry_optional(dev, "macro"); if (IS_ERR(va->macro)) return dev_err_probe(dev, PTR_ERR(va->macro), "unable to get macro clock\n"); - va->dcodec = devm_clk_get_optional(dev, "dcodec"); + va->dcodec = clk_get_retry_optional(dev, "dcodec"); if (IS_ERR(va->dcodec)) return dev_err_probe(dev, PTR_ERR(va->dcodec), "unable to get dcodec clock\n"); - va->mclk = devm_clk_get(dev, "mclk"); + va->mclk = clk_get_retry(dev, "mclk"); if (IS_ERR(va->mclk)) return dev_err_probe(dev, PTR_ERR(va->mclk), "unable to get mclk clock\n"); @@ -1610,7 +1645,7 @@ static int va_macro_probe(struct platform_device *pdev) goto err; if (va->has_npl_clk) { - va->npl = devm_clk_get(dev, "npl"); + va->npl = clk_get_retry(dev, "npl"); if (IS_ERR(va->npl)) { ret = PTR_ERR(va->npl); goto err; diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c index 899c8bee4645f..314e83cc1bf92 100644 --- a/sound/soc/codecs/lpass-wsa-macro.c +++ b/sound/soc/codecs/lpass-wsa-macro.c @@ -19,6 +19,41 @@ #include "lpass-macro-common.h" #include "lpass-wsa-macro.h" +#include + +/* + * Local helper: bounded retry for deferred clock providers. + * Clock-provider registration does not trigger deferred-probe retries, + * so a consumer that loses the probe-order race stays deferred forever. + * Wait up to 30s for providers that are registered milliseconds later. + */ +static struct clk *clk_get_retry(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} + +static struct clk *clk_get_retry_optional(struct device *dev, const char *id) +{ + struct clk *c; + int i; + + for (i = 0; i < 60; i++) { + c = devm_clk_get_optional(dev, id); + if (!IS_ERR(c) || PTR_ERR(c) != -EPROBE_DEFER) + return c; + msleep(500); + } + return c; +} #define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000) #define CDC_WSA_MCLK_EN_MASK BIT(0) @@ -2701,25 +2736,25 @@ static int wsa_macro_probe(struct platform_device *pdev) if (!wsa) return -ENOMEM; - wsa->macro = devm_clk_get_optional(dev, "macro"); + wsa->macro = clk_get_retry_optional(dev, "macro"); if (IS_ERR(wsa->macro)) return dev_err_probe(dev, PTR_ERR(wsa->macro), "unable to get macro clock\n"); - wsa->dcodec = devm_clk_get_optional(dev, "dcodec"); + wsa->dcodec = clk_get_retry_optional(dev, "dcodec"); if (IS_ERR(wsa->dcodec)) return dev_err_probe(dev, PTR_ERR(wsa->dcodec), "unable to get dcodec clock\n"); - wsa->mclk = devm_clk_get(dev, "mclk"); + wsa->mclk = clk_get_retry(dev, "mclk"); if (IS_ERR(wsa->mclk)) return dev_err_probe(dev, PTR_ERR(wsa->mclk), "unable to get mclk clock\n"); if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) { - wsa->npl = devm_clk_get(dev, "npl"); + wsa->npl = clk_get_retry(dev, "npl"); if (IS_ERR(wsa->npl)) return dev_err_probe(dev, PTR_ERR(wsa->npl), "unable to get npl clock\n"); } - wsa->fsgen = devm_clk_get(dev, "fsgen"); + wsa->fsgen = clk_get_retry(dev, "fsgen"); if (IS_ERR(wsa->fsgen)) return dev_err_probe(dev, PTR_ERR(wsa->fsgen), "unable to get fsgen clock\n");