Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: joakim.zhang@cixtech.com
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v4 4/4] ASoC: cdns: Add Cadence I2S-MC controller driver
Date: Mon, 19 Oct 2026 12:35:07 +0000	[thread overview]
Message-ID: <sashiko-outbox-177072@kernel.org> (raw)
In-Reply-To: <20261019122048.1107179-5-joakim.zhang@cixtech.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] ASoC: cdns: shared clk_i2s reparented in cdns_i2s_mc_hw_params() while active
- [Medium] ASoC: cdns: clock and runtime PM leak in cdns_i2s_mc_probe() and on remove
- [Medium] ASoC: cdns: devm_pm_runtime_enable() ordered before IRQ in cdns_i2s_mc_probe()
--

commit b995dbd0c500d9df940a91311e64f2712a2548e6
Author: Joakim Zhang <joakim.zhang@cixtech.com>

ASoC: cdns: Add Cadence I2S-MC controller driver

This commit adds ASoC driver support for the Cadence I2S-MC multi-channel
controller found in the CIX SKY1 audio subsystem. It exposes playback and
capture DAIs using dmaengine PCM, manages audio reference clock parenting,
and handles runtime and system power management.

> diff --git a/sound/soc/cdns/cdns-i2s-mc.c b/sound/soc/cdns/cdns-i2s-mc.c
> new file mode 100644
> index 0000000000000..defbb93adfe76
> --- /dev/null
> +++ b/sound/soc/cdns/cdns-i2s-mc.c
> @@ -0,0 +1,1128 @@

[ ... ]

> +static int cdns_i2s_mc_hw_params(struct snd_pcm_substream *substream,
> +				 struct snd_pcm_hw_params *params,
> +				 struct snd_soc_dai *cpu_dai)
> +{
> +	struct cdns_i2s_mc_priv *i2s_mc_priv = snd_soc_dai_get_drvdata(cpu_dai);
> +	struct device *dev = i2s_mc_priv->dev;
> +	u32 rate, sample_rate = 0;
> +	u32 slots, slot_width, resolution, ctrl;
> +	unsigned long i2s_clk_rate;
> +	u8 pin_tx_num, pin_rx_num;
> +	struct clk *clk_parent;
> +	bool is_master_mode;
> +	int ret;
> +
> +	rate = params_rate(params);
> +	slot_width = i2s_mc_priv->devtype_data->data_width;
> +	dev_dbg(dev, "rate = %d, slot_width = %d\n", rate, slot_width);
> +
> +	/* switch clk mux to select the appropriate clk parent */
> +	if (rate % 8000 == 0) {
> +		/* Sampling rate is a multiple of 8KHz, select "audio-clk0" */
> +		clk_parent = i2s_mc_priv->clks[AUDIO_CLK0];
> +
> +		ret = clk_set_parent(i2s_mc_priv->clk_i2s, clk_parent);
> +	} else if (rate % 11025 == 0) {
> +		/* Sampling rate is a multiple of 11.025KHz, select "audio-clk2" */
> +		clk_parent = i2s_mc_priv->clks[AUDIO_CLK2];
> +
> +		ret = clk_set_parent(i2s_mc_priv->clk_i2s, clk_parent);
> +	} else {
> +		dev_err(i2s_mc_priv->dev, "Invalid sample rate\n");
> +		return -EINVAL;
> +	}

[Severity: Medium]
Can switching the parent of i2s_mc_priv->clk_i2s here corrupt an already
active stream on the other DAI?

The driver exposes two separate DAIs in cdns_i2s_mc_dai[] (i2s-mc-aif1 for
playback and i2s-mc-aif2 for capture), which can be opened and configured
independently. Both DAIs share the single functional clock
i2s_mc_priv->clk_i2s.

If one DAI is running at a multiple of 8 kHz (using clks[AUDIO_CLK0]) and
userspace calls snd_pcm_hw_params() -> cdns_i2s_mc_hw_params() on the second
DAI with a multiple of 11.025 kHz (using clks[AUDIO_CLK2]), clk_set_parent()
will change the parent of clk_i2s underneath the active stream. That changes
i2s_clk_rate while the first stream is active, invalidating its programmed
I2S_SRR divider (I2S_SRR_TSAMPLE_RATE or I2S_SRR_RSAMPLE_RATE) and
potentially violating I2S_CLK_RATIO_MIN_MASTER or I2S_CLK_RATIO_MIN_SLAVE.

Should cdns_i2s_mc_hw_params() check whether the other stream is already
configured with an incompatible clock parent, or constrain the two DAIs to
the same rate family when both are active?

[ ... ]

> +static int cdns_i2s_mc_probe(struct platform_device *pdev)
> +{

[ ... ]

> +	pm_runtime_get_noresume(&pdev->dev);
> +	pm_runtime_set_active(&pdev->dev);
> +	ret = devm_pm_runtime_enable(&pdev->dev);
> +	if (ret) {
> +		pm_runtime_put_noidle(&pdev->dev);
> +		return dev_err_probe(&pdev->dev, ret, "Failed to enable runtime PM\n");
> +	}
> +
> +	ret = cdns_i2s_mc_clks_enable(i2s_mc_priv);
> +	if (ret) {
> +		pm_runtime_put_noidle(&pdev->dev);
> +		return dev_err_probe(&pdev->dev, ret, "Failed to enable clocks\n");
> +	}
> +
> +	cdns_i2s_mc_rst(i2s_mc_priv);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		ret = irq;
> +		goto err_disable_clks;
> +	}
> +	i2s_mc_priv->irq = irq;
> +
> +	ret = devm_request_irq(&pdev->dev, irq, cdns_i2s_mc_isr, 0,
> +			       dev_name(&pdev->dev), i2s_mc_priv);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
> +		goto err_disable_clks;
> +	}

[Severity: Medium]
Is the devres registration order between devm_pm_runtime_enable() and
devm_request_irq() safe during teardown?

In cdns_i2s_mc_probe(), devm_pm_runtime_enable() is registered before
i2s_mc_priv->irq is assigned and before devm_request_irq() is called.
Because devres unwinds in reverse order, on driver unbind or if
devm_snd_soc_register_component() fails, devm_irq_release() calls free_irq()
before pm_runtime_disable_action() disables runtime PM.

Since cdns_i2s_mc_runtime_suspend() calls disable_irq(i2s_mc_priv->irq) and
cdns_i2s_mc_runtime_resume() calls enable_irq(i2s_mc_priv->irq), a runtime
PM transition occurring before runtime PM is disabled can call enable_irq()
or disable_irq() on an already-freed IRQ. It also leaves the IRQ disabled
when devm_irq_release() frees it.

Should platform_get_irq() and devm_request_irq() be moved before
devm_pm_runtime_enable()?

> +
> +	/*
> +	 * Let pm_runtime_put_sync() disable the clocks, and take regcache effect.
> +	 * If CONFIG_PM is not enabled, the clock will stay powered.
> +	 */
> +	pm_runtime_put_sync(&pdev->dev);
> +
> +	ret = devm_snd_soc_register_component(&pdev->dev, &cdns_i2s_mc_component,
> +					      cdns_i2s_mc_dai, ARRAY_SIZE(cdns_i2s_mc_dai));
> +	if (ret)
> +		return dev_err_probe(&pdev->dev, ret, "Failed to register soc component\n");
> +
> +	return 0;
> +
> +err_disable_clks:
> +	cdns_i2s_mc_clks_disable(i2s_mc_priv);
> +	pm_runtime_put_noidle(&pdev->dev);
> +	return ret;
> +}

[Severity: Medium]
Does cdns_i2s_mc_probe() leak the prepare/enable counts on clk_hst and
clk_i2s when devm_snd_soc_register_component() fails or when the driver is
unbound?

In cdns_i2s_mc_probe(), clk_hst and clk_i2s are enabled via
cdns_i2s_mc_clks_enable(), and the driver relies on pm_runtime_put_sync()
running cdns_i2s_mc_runtime_suspend() to disable them. There is no devm
action or .remove callback in cdns_i2s_mc_driver to disable the clocks.

When CONFIG_PM is disabled (where pm_runtime_put_sync() is a no-op and the
clocks stay powered) or when runtime suspend is prevented (for example via
sysfs power/control), if devm_snd_soc_register_component() fails,
cdns_i2s_mc_probe() returns without calling cdns_i2s_mc_clks_disable().
The same leak occurs on driver unbind because devm_pm_runtime_enable() only
calls pm_runtime_disable() and does not invoke cdns_i2s_mc_runtime_suspend()
when the device is still runtime-active.

Also, on the error paths after pm_runtime_set_active(&pdev->dev) (when
devm_pm_runtime_enable(), cdns_i2s_mc_clks_enable(), platform_get_irq(), or
devm_request_irq() fails), pm_runtime_set_suspended(&pdev->dev) is not
called, leaving the runtime PM status marked active while the clocks are
disabled.

[ ... ]

> +static struct platform_driver cdns_i2s_mc_driver = {
> +	.probe = cdns_i2s_mc_probe,
> +	.driver = {
> +		.name = DRV_NAME,
> +		.pm = pm_ptr(&cdns_i2s_mc_pm_ops),
> +		.of_match_table = cdns_i2s_mc_of_match,
> +	},
> +};

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019122048.1107179-1-joakim.zhang@cixtech.com?part=4

      reply	other threads:[~2026-10-19 12:35 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 12:20 [PATCH v4 0/4] ASoC: cdns: Add Cadence I2S-SC and I2S-MC controller support joakim.zhang
2026-10-19 12:20 ` [PATCH v4 1/4] ASoC: dt-bindings: Add Cadence I2S-SC controller joakim.zhang
2026-10-20 18:08   ` Rob Herring (Arm)
2026-10-19 12:20 ` [PATCH v4 2/4] ASoC: cdns: Add Cadence I2S-SC controller driver joakim.zhang
2026-10-19 12:37   ` sashiko-bot
2026-10-19 12:20 ` [PATCH v4 3/4] ASoC: dt-bindings: Add Cadence I2S-MC controller joakim.zhang
2026-10-20 18:08   ` Rob Herring (Arm)
2026-10-19 12:20 ` [PATCH v4 4/4] ASoC: cdns: Add Cadence I2S-MC controller driver joakim.zhang
2026-10-19 12:35   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-177072@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=joakim.zhang@cixtech.com \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox