* [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control
@ 2026-07-06 13:20 Mohammad Rafi Shaik
2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Mohammad Rafi Shaik @ 2026-07-06 13:20 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai
Cc: Krzysztof Kozlowski, linux-arm-msm, linux-sound, devicetree,
linux-kernel
Add support for MI2S clock control within q6apm-lpass DAIs, including
handling of MCLK, BCLK, and ECLK via the DAI .set_sysclk callback.
Each MI2S port now retrieves its clock handles from the device tree,
allowing per-port clock configuration and proper enable/disable during
startup and shutdown.
On platforms such as Monaco and Lemans, third-party codecs are
hardware-wired to the SoC and do not always have an in-tree codec
driver to manage their clocks. For these designs, clock line
enablement must be driven from the platform side, and this
series provides the necessary support for that.
On QAIF-based platforms such as Shikra and Hawi, responsibility
for voting I2S MCLK and bit-clock has moved from the DSP to the
kernel. This series introduces the required device tree binding
support to represent and vote for these clocks from the kernel.
Enhances the sc8280xp machine driver to set the boards spacific
configurations.
---
Changes in v3:
- Addressed all review comments from Mark Brown.
- Fixed OF node reference handling, clock configuration, and sample-rate
handling issues as suggested by Mark Brown.
- Added proper error checking for DAI configuration APIs as suggested by Mark Brown.
- Added SENARY DAI support alongside MI2S DAIs as suggested by Val Packett.
- Link to v2: https://lore.kernel.org/all/20260608023011.942228-1-mohammad.rafi.shaik@oss.qualcomm.com/
Changes in v2:
- Added a detailed commit description to clearly explain the need for this change.
- Improved the machine driver based on Neil’s feedback.
- Link to v1: https://lore.kernel.org/all/20260309111300.2484262-1-mohammad.rafi.shaik@oss.qualcomm.com/
---
Mohammad Rafi Shaik (3):
ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode
ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control
ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for
board-specific config
.../bindings/sound/qcom,q6apm-lpass-dais.yaml | 56 ++++
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 154 ++++++++++-
sound/soc/qcom/qdsp6/q6prm.h | 4 +
sound/soc/qcom/sc8280xp.c | 245 ++++++++++++++++--
4 files changed, 437 insertions(+), 22 deletions(-)
base-commit: 2b763db0c2763d6bf73d7d3e69665222d1f377cf
--
2.34.1
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode 2026-07-06 13:20 [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control Mohammad Rafi Shaik @ 2026-07-06 13:20 ` Mohammad Rafi Shaik 2026-07-06 13:30 ` sashiko-bot 2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:20 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config Mohammad Rafi Shaik 2 siblings, 1 reply; 8+ messages in thread From: Mohammad Rafi Shaik @ 2026-07-06 13:20 UTC (permalink / raw) To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai Cc: Krzysztof Kozlowski, linux-arm-msm, linux-sound, devicetree, linux-kernel, Srinivas Kandagatla, Krzysztof Kozlowski, Neil Armstrong Extend the qcom,q6apm-lpass-dais device tree binding to explicitly describe Digital Audio Interface (DAI) child nodes. Add #address-cells and #size-cells to allow representation of multiple DAI instances as child nodes, and define a dai@<id> pattern to document per-DAI properties such as the interface ID and associated clocks. On platforms such as Monaco and Lemans, third-party codecs are hardware wired to the SoC and do not always have an in-tree codec driver to manage their clocks. For these designs, clock line enablement must be driven from the platform side, and this series provides the necessary support for that. On QAIF-based platforms such as Shikra and Hawi, responsibility for voting I2S MCLK and BCLK has moved from the DSP to the kernel. This series introduces the required device tree binding support to represent and vote for these clocks from the kernel. Co-developed-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> Tested-by: Neil Armstrong <neil.armstrong@linaro.org> Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> --- .../bindings/sound/qcom,q6apm-lpass-dais.yaml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml index 2fb95544db8b..42d05334bd39 100644 --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml @@ -21,6 +21,47 @@ properties: '#sound-dai-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +# Digital Audio Interfaces +patternProperties: + '^dai@[0-9a-f]+$': + type: object + description: + Q6DSP Digital Audio Interfaces. + + properties: + reg: + maxItems: 1 + description: + Digital Audio Interface ID + + clocks: + minItems: 1 + items: + - description: MI2S master clock + - description: MI2S bit clock + - description: MI2S external bit clock + + clock-names: + minItems: 1 + items: + - const: mclk + - const: bclk + - const: eclk + + dependencies: + clocks: [clock-names] + + required: + - reg + + additionalProperties: false + required: - compatible - '#sound-dai-cells' @@ -29,7 +70,22 @@ unevaluatedProperties: false examples: - | + #include <dt-bindings/sound/qcom,q6afe.h> + dais { compatible = "qcom,q6apm-lpass-dais"; #sound-dai-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + + dai@16 { + reg = <PRIMARY_MI2S_RX>; + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6prmcc LPASS_CLK_ID_PRI_MI2S_EBIT + LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "mclk", "bclk", "eclk"; + }; }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode 2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik @ 2026-07-06 13:30 ` sashiko-bot 0 siblings, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-07-06 13:30 UTC (permalink / raw) To: Mohammad Rafi Shaik; +Cc: devicetree, conor+dt, robh Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] The device tree example uses an incorrect unit address `dai@16` for `reg = <PRIMARY_MI2S_RX>`, which will cause a `unit_address_vs_reg` compiler warning. -- commit 34242d1a4dde8cbcb03c8b665b181f4cb4658671 Author: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode This commit extends the qcom,q6apm-lpass-dais device tree binding to explicitly describe Digital Audio Interface (DAI) child nodes. It adds #address-cells, #size-cells, and a dai@<id> pattern to document per-DAI properties like the interface ID and associated clocks. This enables clock line enablement from the platform side for codecs hardware wired to the SoC. > diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml > index 2fb95544db8b5..42d05334bd391 100644 > --- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml > +++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml [ ... ] > @@ -29,7 +70,22 @@ unevaluatedProperties: false > > examples: > - | > + #include <dt-bindings/sound/qcom,q6afe.h> > + > dais { > compatible = "qcom,q6apm-lpass-dais"; > #sound-dai-cells = <1>; > + #address-cells = <1>; > + #size-cells = <0>; > + > + dai@16 { > + reg = <PRIMARY_MI2S_RX>; [Severity: Low] Does this node name result in a unit_address_vs_reg warning during make dt_binding_check? Since PRIMARY_MI2S_RX evaluates to 16 in decimal (0x10 in hex) and unit addresses in Device Tree node names are expected to be hexadecimal, should this be named dai@10 instead of dai@16? > + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 > + LPASS_CLK_ATTRIBUTE_COUPLE_NO>, [ ... ] -- Sashiko AI review · https://sashiko.dev/#/patchset/20260706132009.1496321-1-mohammad.rafi.shaik@oss.qualcomm.com?part=1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control 2026-07-06 13:20 [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik @ 2026-07-06 13:20 ` Mohammad Rafi Shaik 2026-07-06 13:33 ` sashiko-bot 2026-07-06 18:18 ` Mark Brown 2026-07-06 13:20 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config Mohammad Rafi Shaik 2 siblings, 2 replies; 8+ messages in thread From: Mohammad Rafi Shaik @ 2026-07-06 13:20 UTC (permalink / raw) To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai Cc: Krzysztof Kozlowski, linux-arm-msm, linux-sound, devicetree, linux-kernel, Srinivas Kandagatla, Neil Armstrong Add support for MI2S clock control within q6apm-lpass DAIs, including handling of MCLK, BCLK, and ECLK via the DAI .set_sysclk callback. Each MI2S port now retrieves its clock handles from the device tree, allowing per-port clock configuration and proper enable/disable during startup and shutdown. Co-developed-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> Tested-by: Neil Armstrong <neil.armstrong@linaro.org> Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 154 +++++++++++++++++++++++- sound/soc/qcom/qdsp6/q6prm.h | 4 + 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9..461e65526506 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -2,10 +2,12 @@ // Copyright (c) 2021, Linaro Limited #include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h> +#include <linux/clk.h> #include <linux/err.h> #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <sound/pcm.h> @@ -15,13 +17,22 @@ #include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" +#include "q6prm.h" #define AUDIOREACH_BE_PCM_BASE 16 +struct q6apm_dai_priv_data { + struct clk *mclk; + struct clk *bclk; + struct clk *eclk; + bool mclk_enabled, bclk_enabled, eclk_enabled; +}; + struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -251,6 +262,73 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return q6apm_lpass_dai_startup(substream, dai); +} + +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (dai_data->priv[dai->id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].mclk); + dai_data->priv[dai->id].mclk_enabled = false; + } + + if (dai_data->priv[dai->id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].bclk); + dai_data->priv[dai->id].bclk_enabled = false; + } + + if (dai_data->priv[dai->id].eclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].eclk); + dai_data->priv[dai->id].eclk_enabled = false; + } + q6apm_lpass_dai_shutdown(substream, dai); +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + case LPAIF_MI2S_ECLK: + sysclk = dai_data->priv[dai->id].eclk; + enabled = &dai_data->priv[dai->id].eclk_enabled; + break; + default: + return -EINVAL; + } + + if (sysclk) { + if (*enabled) + return 0; + + clk_set_rate(sysclk, freq); + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -272,11 +350,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6apm_lpass_dai_shutdown, + .startup = q6i2s_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -297,6 +376,73 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) + return dev_err_probe(dev, PTR_ERR(priv->mclk), + "unable to get mi2s mclk\n"); + priv->mclk = NULL; + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) { + if (priv->mclk) { + clk_put(priv->mclk); + priv->mclk = NULL; + } + return dev_err_probe(dev, PTR_ERR(priv->bclk), + "unable to get mi2s bclk\n"); + } + priv->bclk = NULL; + } + + priv->eclk = of_clk_get_by_name(node, "eclk"); + if (IS_ERR(priv->eclk)) { + if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) { + if (priv->mclk) { + clk_put(priv->mclk); + priv->mclk = NULL; + } + if (priv->bclk) { + clk_put(priv->bclk); + priv->bclk = NULL; + } + return dev_err_probe(dev, PTR_ERR(priv->eclk), + "unable to get mi2s eclk\n"); + } + priv->eclk = NULL; + } + break; + default: + break; + } + } + + return 0; +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -304,12 +450,16 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; + int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); + ret = of_q6apm_parse_dai_data(dev, dai_data); + if (ret) + return ret; memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a988a32086fe..bbbe6d368a41 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,6 +3,10 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ +#define LPAIF_MI2S_MCLK 1 +#define LPAIF_MI2S_BCLK 2 +#define LPAIF_MI2S_ECLK 3 + /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */ -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control 2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik @ 2026-07-06 13:33 ` sashiko-bot 2026-07-06 18:18 ` Mark Brown 1 sibling, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-07-06 13:33 UTC (permalink / raw) To: Mohammad Rafi Shaik; +Cc: conor+dt, robh, devicetree Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] Clock references acquired via of_clk_get_by_name() are leaked on early probe error returns, driver component registration failure, and driver unbind. - [High] q6i2s_set_sysclk() silently ignores frequency updates if the clock is already enabled, preventing ALSA stream reconfiguration. - [High] MI2S clocks are disabled before the DSP graph is safely stopped during DAI shutdown, which can cause hardware lockups. -- commit 194ae39f86b1dc7d38cb5e3de67fcbfe435cfeea Author: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control This commit adds support for MI2S clock control within the q6apm-lpass DAIs, handling MCLK, BCLK, and ECLK via the DAI .set_sysclk callback. It allows per-port clock configuration and proper enable/disable during startup and shutdown. > diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c > index 006b283484d9e..461e655265060 100644 > --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c > +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c [ ... ] > @@ -251,6 +262,73 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s > return 0; > } > > +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) > +{ > + return q6apm_lpass_dai_startup(substream, dai); > +} > + > +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) > +{ > + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); > + > + if (dai_data->priv[dai->id].mclk_enabled) { > + clk_disable_unprepare(dai_data->priv[dai->id].mclk); > + dai_data->priv[dai->id].mclk_enabled = false; > + } > + > + if (dai_data->priv[dai->id].bclk_enabled) { > + clk_disable_unprepare(dai_data->priv[dai->id].bclk); > + dai_data->priv[dai->id].bclk_enabled = false; > + } > + > + if (dai_data->priv[dai->id].eclk_enabled) { > + clk_disable_unprepare(dai_data->priv[dai->id].eclk); > + dai_data->priv[dai->id].eclk_enabled = false; > + } > + q6apm_lpass_dai_shutdown(substream, dai); [Severity: High] Does this code disable the physical MI2S clocks before stopping the DSP graph? Since q6apm_lpass_dai_shutdown() sends the stop command to the DSP, disabling the clocks first could violate hardware teardown requirements and cause hardware lockups while the DSP is still actively streaming. Should the clocks be disabled only after the DSP graph has been completely stopped? > +} > + > +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) > +{ > + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); > + struct clk *sysclk = NULL; > + bool *enabled = NULL; > + int ret = 0; > + > + switch (clk_id) { > + case LPAIF_MI2S_MCLK: > + sysclk = dai_data->priv[dai->id].mclk; > + enabled = &dai_data->priv[dai->id].mclk_enabled; > + break; > + case LPAIF_MI2S_BCLK: > + sysclk = dai_data->priv[dai->id].bclk; > + enabled = &dai_data->priv[dai->id].bclk_enabled; > + break; > + case LPAIF_MI2S_ECLK: > + sysclk = dai_data->priv[dai->id].eclk; > + enabled = &dai_data->priv[dai->id].eclk_enabled; > + break; > + default: > + return -EINVAL; > + } > + > + if (sysclk) { > + if (*enabled) > + return 0; [Severity: High] Can this prevent ALSA from reconfiguring the sample rate? The ALSA framework can call set_sysclk() multiple times for a stream, such as to dynamically reconfigure the rate. Because this early return ignores frequency updates if the clock is already enabled, the physical clock may remain at its initial rate and cause audio distortion. > + > + clk_set_rate(sysclk, freq); > + ret = clk_prepare_enable(sysclk); > + if (ret) { > + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); > + return ret; > + } [ ... ] > @@ -297,6 +376,73 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { > .remove_order = SND_SOC_COMP_ORDER_FIRST, > }; > > +static int of_q6apm_parse_dai_data(struct device *dev, > + struct q6apm_lpass_dai_data *data) > +{ > + int ret; > + > + for_each_child_of_node_scoped(dev->of_node, node) { > + struct q6apm_dai_priv_data *priv; > + int id; > + > + ret = of_property_read_u32(node, "reg", &id); > + if (ret || id < 0 || id >= APM_PORT_MAX) { > + dev_err(dev, "valid dai id not found:%d\n", ret); > + continue; > + } > + > + switch (id) { > + /* MI2S specific properties */ > + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: > + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: > + case SENARY_MI2S_RX ... SENARY_MI2S_TX: > + priv = &data->priv[id]; > + priv->mclk = of_clk_get_by_name(node, "mclk"); > + if (IS_ERR(priv->mclk)) { > + if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) > + return dev_err_probe(dev, PTR_ERR(priv->mclk), > + "unable to get mi2s mclk\n"); > + priv->mclk = NULL; > + } > + > + priv->bclk = of_clk_get_by_name(node, "bclk"); > + if (IS_ERR(priv->bclk)) { > + if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) { > + if (priv->mclk) { > + clk_put(priv->mclk); > + priv->mclk = NULL; > + } > + return dev_err_probe(dev, PTR_ERR(priv->bclk), > + "unable to get mi2s bclk\n"); > + } > + priv->bclk = NULL; > + } > + > + priv->eclk = of_clk_get_by_name(node, "eclk"); > + if (IS_ERR(priv->eclk)) { > + if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) { > + if (priv->mclk) { > + clk_put(priv->mclk); > + priv->mclk = NULL; > + } > + if (priv->bclk) { > + clk_put(priv->bclk); > + priv->bclk = NULL; > + } > + return dev_err_probe(dev, PTR_ERR(priv->eclk), > + "unable to get mi2s eclk\n"); [Severity: High] Does this leak clock references acquired in previous iterations of the loop? When a clock lookup returns -EPROBE_DEFER, the current code properly cleans up clocks for the current node, but it returns without cleaning up clocks acquired for any previous child nodes. Since probe deferrals can occur multiple times, this could accumulate leaks over time. > + } > + priv->eclk = NULL; > + } > + break; > + default: > + break; > + } > + } > + > + return 0; > +} > + > static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) > { > struct q6dsp_audio_port_dai_driver_config cfg; > @@ -304,12 +450,16 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) > struct snd_soc_dai_driver *dais; > struct device *dev = &pdev->dev; > int num_dais; > + int ret; > > dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); > if (!dai_data) > return -ENOMEM; > > dev_set_drvdata(dev, dai_data); > + ret = of_q6apm_parse_dai_data(dev, dai_data); > + if (ret) > + return ret; > > memset(&cfg, 0, sizeof(cfg)); > cfg.q6i2s_ops = &q6i2s_ops; [Severity: High] If devm_snd_soc_register_component() fails later in this function, are the manually acquired clocks leaked? There seems to be no error handling path or devres action to call clk_put() for the clocks acquired in of_q6apm_parse_dai_data(), leaving them permanently leaked on registration failure. Additionally, since q6apm_lpass_dai_platform_driver lacks a .remove callback, does this also guarantee that all acquired clocks leak during device unbind? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260706132009.1496321-1-mohammad.rafi.shaik@oss.qualcomm.com?part=2 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control 2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:33 ` sashiko-bot @ 2026-07-06 18:18 ` Mark Brown 1 sibling, 0 replies; 8+ messages in thread From: Mark Brown @ 2026-07-06 18:18 UTC (permalink / raw) To: Mohammad Rafi Shaik Cc: Srinivas Kandagatla, Liam Girdwood, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai, Krzysztof Kozlowski, linux-arm-msm, linux-sound, devicetree, linux-kernel, Srinivas Kandagatla, Neil Armstrong [-- Attachment #1: Type: text/plain, Size: 531 bytes --] On Mon, Jul 06, 2026 at 06:50:08PM +0530, Mohammad Rafi Shaik wrote: > Add support for MI2S clock control within q6apm-lpass DAIs, including > handling of MCLK, BCLK, and ECLK via the DAI .set_sysclk callback. > +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) > +{ > + clk_set_rate(sysclk, freq); > + ret = clk_prepare_enable(sysclk); > + if (ret) { > + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); > + return ret; > + } clk_set_rate() can fail too. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config 2026-07-06 13:20 [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik 2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik @ 2026-07-06 13:20 ` Mohammad Rafi Shaik 2026-07-06 13:34 ` sashiko-bot 2 siblings, 1 reply; 8+ messages in thread From: Mohammad Rafi Shaik @ 2026-07-06 13:20 UTC (permalink / raw) To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai Cc: Krzysztof Kozlowski, linux-arm-msm, linux-sound, devicetree, linux-kernel, Neil Armstrong The sc8280xp machine driver is currently written with a largely SoC-centric view and assumes a uniform audio topology across all boards. In practice, multiple products based on the same SoC use different board designs and external audio components, which require board-specific configuration to function correctly. Several Qualcomm platforms like talos integrate third-party audio codecs or use different external audio paths. These designs often require additional configuration such as explicit MI2S MCLK settings for audio to work. This change enhances the sc8280xp machine driver to support board-specific configuration such as allowing each board variant to provide its own DAPM widgets and routes, reflecting the actual audio components and connectors present and enabling MI2S MCLK programming for boards that use external codecs requiring a stable master clock. Tested-by: Neil Armstrong <neil.armstrong@linaro.org> Signed-off-by: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> --- sound/soc/qcom/sc8280xp.c | 245 ++++++++++++++++++++++++++++++++++---- 1 file changed, 225 insertions(+), 20 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 7925aa3f63ba..b13f39dc5afc 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -12,17 +12,78 @@ #include <sound/jack.h> #include <linux/input-event-codes.h> #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" +#include "qdsp6/q6prm.h" #include "common.h" #include "sdw.h" +#define I2S_MCLKFS 256 + +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), +}; + +struct snd_soc_common { + const char *driver_name; + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + const struct snd_kcontrol_new *controls; + int num_controls; + unsigned int codec_dai_fmt; + bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; +}; + struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; + struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; +static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) +{ + int rate = params_rate(params); + + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} + +static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) +{ + return I2S_BIT_RATE(params_rate(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); +} + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -32,10 +93,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; switch (cpu_dai->id) { - case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX...QUINARY_MI2S_TX: - snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - break; case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* @@ -64,7 +121,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + if (data->snd_soc_common_priv->wcd_jack) + return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); + + return 0; } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -96,6 +156,63 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + int mclk_freq = sc8280xp_get_mclk_freq(params); + int bclk_freq = sc8280xp_get_bclk_freq(params); + int ret; + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret && ret != -ENOTSUPP) + return ret; + + if (data->snd_soc_common_priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + data->snd_soc_common_priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } + + if (data->snd_soc_common_priv->mi2s_mclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->snd_soc_common_priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } + + if (data->snd_soc_common_priv->codec_sysclk_set) { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret) + return ret; + } + break; + default: + break; + } + + return 0; +} + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -117,6 +234,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sc8280xp_be_ops = { .startup = qcom_snd_sdw_startup, .shutdown = qcom_snd_sdw_shutdown, + .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, }; @@ -127,7 +245,7 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card) int i; for_each_card_prelinks(card, i, link) { - if (link->no_pcm == 1) { + if (link->no_pcm == 1 || link->num_codecs > 0) { link->init = sc8280xp_snd_init; link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; link->ops = &sc8280xp_be_ops; @@ -145,37 +263,124 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->owner = THIS_MODULE; + /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); + if (!data->snd_soc_common_priv) + return -ENODEV; + + card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); + card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; + card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; + card->dapm_routes = data->snd_soc_common_priv->dapm_routes; + card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; + card->controls = data->snd_soc_common_priv->controls; + card->num_controls = data->snd_soc_common_priv->num_controls; + ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = of_device_get_match_data(dev); + card->driver_name = data->snd_soc_common_priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } +static struct snd_soc_common kaanapali_priv_data = { + .driver_name = "kaanapali", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common qcs615_priv_data = { + .driver_name = "qcs615", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .mi2s_mclk_enable = true, +}; + +static struct snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sc8280xp_priv_data = { + .driver_name = "sc8280xp", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common sm8450_priv_data = { + .driver_name = "sm8450", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common sm8550_priv_data = { + .driver_name = "sm8550", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common sm8650_priv_data = { + .driver_name = "sm8650", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static struct snd_soc_common sm8750_priv_data = { + .driver_name = "sm8750", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, - {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, - {.compatible = "qcom,qcs615-sndcard", "qcs615"}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, - {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, - {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, - {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, - {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, - {.compatible = "qcom,sm8450-sndcard", "sm8450"}, - {.compatible = "qcom,sm8550-sndcard", "sm8550"}, - {.compatible = "qcom,sm8650-sndcard", "sm8650"}, - {.compatible = "qcom,sm8750-sndcard", "sm8750"}, + {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data}, + {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data}, + {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data}, + {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data}, + {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data}, + {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data}, + {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data}, + {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data}, + {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data}, + {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data}, {} }; -- 2.34.1 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config 2026-07-06 13:20 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config Mohammad Rafi Shaik @ 2026-07-06 13:34 ` sashiko-bot 0 siblings, 0 replies; 8+ messages in thread From: sashiko-bot @ 2026-07-06 13:34 UTC (permalink / raw) To: Mohammad Rafi Shaik; +Cc: devicetree, robh, conor+dt Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] Backend DAI operations are erroneously attached to DPCM Frontend links due to a flawed `link->num_codecs > 0` condition. - [Low] Board-specific `snd_soc_common` match data structures are missing the `const` qualifier. -- commit a4f95b828cd79a0224347fb7462baf2aac9d444d Author: Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com> Subject: ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config The sc8280xp machine driver is currently written with a largely SoC-centric view and assumes a uniform audio topology across all boards. This change enhances the driver to support board-specific configuration such as allowing each board variant to provide its own DAPM widgets and routes. > diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c > index 7925aa3f63ba0..b13f39dc5afc2 100644 > --- a/sound/soc/qcom/sc8280xp.c > +++ b/sound/soc/qcom/sc8280xp.c [ ... ] > @@ -127,7 +245,7 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card) > int i; > > for_each_card_prelinks(card, i, link) { > - if (link->no_pcm == 1) { > + if (link->no_pcm == 1 || link->num_codecs > 0) { [Severity: High] Does this unintentionally match DPCM frontend links as well? Looking at qcom_snd_parse_of(), it explicitly assigns a dummy codec and sets link->num_codecs = 1 for all DPCM frontend links: sound/soc/qcom/common.c:qcom_snd_parse_of() { ... if (codec) { ... } else { /* DPCM frontend */ link->codecs = &snd_soc_dummy_dlc; link->num_codecs = 1; link->dynamic = 1; } ... } This means link->num_codecs > 0 will evaluate to true for every link on the card, attaching sc8280xp_be_ops and sc8280xp_snd_init to frontend links. If sc8280xp_snd_prepare() is then invoked for frontend links, it will index the data->stream_prepared array using frontend CPU DAI IDs. Could this result in state corruption or out-of-bounds memory accesses if these IDs exceed AFE_PORT_MAX? Should this condition check !link->dynamic to safely exclude frontend links? > link->init = sc8280xp_snd_init; > link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; > link->ops = &sc8280xp_be_ops; [ ... ] > @@ -145,37 +263,124 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) > card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); > if (!card) > return -ENOMEM; > - card->owner = THIS_MODULE; > + > /* Allocate the private data */ > data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > if (!data) > return -ENOMEM; > > + data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); [Severity: Low] This isn't a bug, but can we avoid this explicit cast by declaring the private data structures as const? The of_device_get_match_data() function returns a const void *. Since the matched data structures are not modified at runtime, could we declare them as static const and update the snd_soc_common_priv pointer in sc8280xp_snd_data to be const as well? > + if (!data->snd_soc_common_priv) > + return -ENODEV; > + > + card->owner = THIS_MODULE; [ ... ] > +static struct snd_soc_common kaanapali_priv_data = { [Severity: Low] This isn't a bug, but should this and the other snd_soc_common match data structures introduced below be declared as static const to place them in the read-only .rodata section? > + .driver_name = "kaanapali", > + .dapm_widgets = sc8280xp_dapm_widgets, > + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), > + .wcd_jack = true, > +}; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260706132009.1496321-1-mohammad.rafi.shaik@oss.qualcomm.com?part=3 ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-06 18:18 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-06 13:20 [PATCH v3 0/3] ASoC: qcom: qdsp6: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:20 ` [PATCH v3 1/3] ASoC: dt-bindings: qcom,q6apm-lpass-dais: Document DAI subnode Mohammad Rafi Shaik 2026-07-06 13:30 ` sashiko-bot 2026-07-06 13:20 ` [PATCH v3 2/3] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Mohammad Rafi Shaik 2026-07-06 13:33 ` sashiko-bot 2026-07-06 18:18 ` Mark Brown 2026-07-06 13:20 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config Mohammad Rafi Shaik 2026-07-06 13:34 ` sashiko-bot
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox