Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jorijn van der Graaf" <jorijnvdgraaf@catcrafts.net>
Cc: robh@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH 3/3] ASoC: codecs: wcd9378: add TX/capture codec driver
Date: Mon, 06 Jul 2026 19:38:26 +0000	[thread overview]
Message-ID: <20260706193827.D90641F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260706192229.144137-4-jorijnvdgraaf@catcrafts.net>

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] Modifying a static global array during probe prevents multiple device instances from working correctly.
- [High] `pm_runtime_enable()` is called after `component_master_add_with_match()`, causing component probe to fail synchronously.
- [High] Missing bounds check in `wcd9378_tx_mode_put()` allows out-of-bounds enum values to be written and retrieved.
- [High] Inconsistent SoundWire bus clock scaling calculation between `wcd9378_bus_config()` and `wcd9378_swr_clk_indicate()`.
- [High] Destroying `micb_lock` in the component unbind callback leads to a use-after-destroy upon component re-bind.
- [Medium] Off-by-one error when calculating the SoundWire channel count causes incorrect bandwidth allocation.
- [Medium] Missing High-Pass Filter (HPF) init pulse configuration for ADC2 when routed via SmartJACK.
- [Medium] Concurrent ALSA control puts cause read-modify-write data races on shared driver channel maps.
- [Medium] Using asynchronous `pm_runtime_put()` in `wcd9378_unbind()` causes a race condition where the TX SoundWire slave device may not be properly suspended before removal.
--

commit 336c5ad4fab99f2ac788ce17fc541b077bfc53c9
Author: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Subject: ASoC: codecs: wcd9378: add TX/capture codec driver

This patch adds the TX/capture driver for the Qualcomm WCD9378 audio codec
over SoundWire. It introduces analog core configuration and SDCA-style 
function blocks for SmartMIC and SmartJACK sequencers.

> diff --git a/sound/soc/codecs/wcd9378-sdw.c b/sound/soc/codecs/wcd9378-sdw.c
> --- a/sound/soc/codecs/wcd9378-sdw.c
> +++ b/sound/soc/codecs/wcd9378-sdw.c
[ ... ]
> +int wcd9378_sdw_hw_params(struct wcd9378_sdw_priv *wcd,
> +			  struct snd_pcm_substream *substream,
> +			  struct snd_pcm_hw_params *params,
> +			  struct snd_soc_dai *dai)
> +{
> +	struct sdw_port_config port_config[WCD9378_MAX_SWR_PORTS];
> +	unsigned long ch_mask;
> +	int i, j;
> +
> +	wcd->sconfig.ch_count = 1;

[Severity: Medium]
Does initializing the channel count to 1 here cause an off-by-one error?
It looks like if there are two active channels, the count will become 3,
which might lead to overallocating SoundWire bus bandwidth.

[ ... ]
> +static int wcd9378_bus_config(struct sdw_slave *slave,
> +			      struct sdw_bus_params *params)
> +{
> +	u8 scale;
> +
> +	switch (params->curr_dr_freq) {
> +	case 4800000:
> +		scale = WCD9378_SWRS_CLK_SCALE_DIV4;
> +		break;
> +	case 9600000:
> +	default:
> +		scale = WCD9378_SWRS_CLK_SCALE_DIV2;
> +		break;
> +	}

[Severity: High]
Is there a calculation mismatch between wcd9378_bus_config() and 
wcd9378_swr_clk_indicate()?

Here, a 9.6MHz double-rate frequency gets WCD9378_SWRS_CLK_SCALE_DIV2.
However, in wcd9378_swr_clk_indicate() in wcd9378.c, the bus clock is 
calculated by dividing the DR frequency by 2, resulting in 4.8MHz. That 
function then evaluates 4800000 < 9600000 and assigns 
WCD9378_SWRS_CLK_SCALE_DIV4 instead. 

Could this stall the sequencer if the configurations conflict?

[ ... ]
> +static int wcd9378_sdw_probe(struct sdw_slave *pdev,
> +			     const struct sdw_device_id *id)
> +{
[ ... ]
> +	if (wcd->is_tx) {
> +		pdev->prop.source_ports = GENMASK(WCD9378_MAX_TX_SWR_PORTS, 1);
> +		pdev->prop.src_dpn_prop = wcd9378_dpn_prop;
> +		wcd->ch_info = &wcd9378_sdw_tx_ch_info[0];
> +
> +		for (i = 0; i < master_ch_mask_size; i++)
> +			wcd->ch_info[i].master_ch_mask = WCD9378_SWRM_CH_MASK(master_ch_mask[i]);

[Severity: High]
Will this loop permanently modify the global static array wcd9378_sdw_tx_ch_info?
Since the master_ch_mask is written directly to the shared array, probing a 
second instance of this codec could overwrite the configuration for all active
devices.

> diff --git a/sound/soc/codecs/wcd9378.c b/sound/soc/codecs/wcd9378.c
> --- a/sound/soc/codecs/wcd9378.c
> +++ b/sound/soc/codecs/wcd9378.c
[ ... ]
> +static const struct wcd9378_smp_fn wcd9378_smp_jack_adc2 = {
> +	.usage_reg = WCD9378_SMP_JACK_IT31_USAGE,
> +	.micb_reg = WCD9378_SMP_JACK_IT31_MICB,
> +	.req_reg = WCD9378_SMP_JACK_PDE34_REQ_PS,
> +	.act_reg = WCD9378_SMP_JACK_PDE34_ACT_PS,
> +};

[Severity: Medium]
Should this struct also include the hpf_reg and hpf_mask fields?
When ADC2 is fed from AMIC2 via SmartJACK, the missing filter configuration
means the sequencer enable path will read zeroes, skipping the
WCD9378_ANA_TX_CH2_HPF2_INIT pulse and potentially leaving a DC offset.

[ ... ]
> +static int wcd9378_connect_port(struct wcd9378_sdw_priv *wcd, u8 port_idx,
> +				u8 ch_id, bool enable)
> +{
[ ... ]
> +	if (enable) {
> +		port_config->ch_mask |= ch_mask;
> +		wcd->master_channel_map[mstr_port_num] |= mstr_ch_mask;
> +	} else {
> +		port_config->ch_mask &= ~ch_mask;
> +		wcd->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
> +	}

[Severity: Medium]
Can concurrent ALSA control operations corrupt the channel masks?
Since wcd9378_set_swr_port() is reachable via ALSA put callbacks, multiple
userspace threads toggling switches might cause a read-modify-write data
race on port_config->ch_mask and wcd->master_channel_map.

[ ... ]
> +static int wcd9378_tx_mode_put(struct snd_kcontrol *kcontrol,
> +			       struct snd_ctl_elem_value *ucontrol)
> +{
[ ... ]
> +	u32 mode_val = ucontrol->value.enumerated.item[0];
> +
> +	if (mode_val == wcd9378->tx_mode[adc])
> +		return 0;
> +
> +	wcd9378->tx_mode[adc] = mode_val;

[Severity: High]
Could this store an out-of-bounds enum value?
Without a check verifying that mode_val is less than ARRAY_SIZE(tx_mode_mux_text),
userspace can write an invalid integer here. This value is later exposed via
wcd9378_tx_mode_get(), which might crash userspace audio servers when used as
an array index.

[ ... ]
> +static int wcd9378_soc_codec_probe(struct snd_soc_component *component)
> +{
[ ... ]
> +	ret = pm_runtime_resume_and_get(dev);
> +	if (ret < 0)
> +		return ret;

[Severity: High]
Will this pm_runtime_resume_and_get() call fail with -EACCES?
It looks like component_master_add_with_match() in wcd9378_probe() triggers
the component bind synchronously if all slaves are present, which in turn
calls wcd9378_soc_codec_probe() here. Because pm_runtime_enable() hasn't
been called yet in the master probe, the disable depth is still > 0,
causing the resume to abort audio initialization.

[ ... ]
> +static void wcd9378_unbind(struct device *dev)
> +{
> +	struct wcd9378_priv *wcd9378 = dev_get_drvdata(dev);
> +
> +	snd_soc_unregister_component(dev);
> +	pm_runtime_put(wcd9378->txdev);

[Severity: Medium]
Should this be pm_runtime_put_sync() instead?
Using the asynchronous pm_runtime_put() queues a suspend operation that might
be cancelled immediately when the driver core calls pm_runtime_disable() upon
device removal, leaving the TX SoundWire slave device powered on.

> +	device_link_remove(dev, wcd9378->txdev);
> +	device_link_remove(dev, wcd9378->rxdev);
> +	device_link_remove(wcd9378->rxdev, wcd9378->txdev);
> +	component_unbind_all(dev, wcd9378);
> +	mutex_destroy(&wcd9378->micb_lock);

[Severity: High]
Does destroying micb_lock here prevent a successful re-bind?
The component framework allows unbinding and rebinding without fully removing
the master platform device. Since mutex_init() is only called in wcd9378_probe(),
a subsequent wcd9378_bind() will leave the destroyed mutex uninitialized, leading
to undefined behavior or a crash when wcd9378_micbias_control() tries to lock it.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260706192229.144137-1-jorijnvdgraaf@catcrafts.net?part=3

  reply	other threads:[~2026-07-06 19:38 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-06 19:22 [PATCH 0/3] ASoC: codecs: add the Qualcomm WCD9378 audio codec Jorijn van der Graaf
2026-07-06 19:22 ` [PATCH 1/3] ASoC: dt-bindings: qcom,wcd93xx-common: don't require micbias4 Jorijn van der Graaf
2026-07-06 19:22 ` [PATCH 2/3] ASoC: dt-bindings: add the Qualcomm WCD9378 audio codec Jorijn van der Graaf
2026-07-06 21:17   ` Srinivas Kandagatla
2026-07-06 22:22     ` Jorijn van der Graaf
2026-07-06 19:22 ` [PATCH 3/3] ASoC: codecs: wcd9378: add TX/capture codec driver Jorijn van der Graaf
2026-07-06 19:38   ` sashiko-bot [this message]
2026-07-06 20:35     ` Jorijn van der Graaf
2026-07-06 21:41   ` Srinivas Kandagatla
2026-07-06 22:22     ` Jorijn van der Graaf

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=20260706193827.D90641F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jorijnvdgraaf@catcrafts.net \
    --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