[RFC PATCH 05/23] ASoC: codecs: wsa884x: add VI sense capture DAI

From: Srinivas Kandagatla

Date: Fri Oct 23 2026 - 13:22:12 EST


From: Ravi Hothi <ravi.hothi@xxxxxxxxxxxxxxxx>

The VI sense port carries the voltage and current sense data that DSP
speaker protection needs, but it is described as a sink port next to
the playback ports and has no DAI, so nothing can capture from it.

Make it a source port with its own capture DAI, ops and stream runtime,
and leave it out of the playback port list. sdw_get_slave_dpn_prop()
searches the sink port properties by count, so pack that array densely
now that VI sense is no longer in it.

Signed-off-by: Ravi Hothi <ravi.hothi@xxxxxxxxxxxxxxxx>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxxxxxxxx>
---
sound/soc/codecs/wsa884x.c | 90 +++++++++++++++++++++++++++++++-------
1 file changed, 74 insertions(+), 16 deletions(-)

diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index acf32af5aae3..fbb9c5773aa5 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -732,8 +732,11 @@ struct wsa884x_priv {
struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM];
struct sdw_slave *slave;
struct sdw_stream_config sconfig;
+ struct sdw_stream_config vi_sconfig;
struct sdw_stream_runtime *sruntime;
+ struct sdw_stream_runtime *vi_sruntime;
struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS];
+ struct sdw_port_config vi_port_config;
struct gpio_desc *sd_n;
struct reset_control *sd_reset;
bool port_prepared[WSA884X_MAX_SWR_PORTS];
@@ -822,8 +825,8 @@ enum wsa884x_mode {
static const struct soc_enum wsa884x_dev_mode_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text);

-static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
- [WSA884X_PORT_DAC] = {
+static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS - 1] = {
+ {
.num = WSA884X_PORT_DAC + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
@@ -831,7 +834,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
- [WSA884X_PORT_COMP] = {
+ {
.num = WSA884X_PORT_COMP + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
@@ -839,7 +842,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
- [WSA884X_PORT_BOOST] = {
+ {
.num = WSA884X_PORT_BOOST + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
@@ -847,7 +850,7 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
- [WSA884X_PORT_PBR] = {
+ {
.num = WSA884X_PORT_PBR + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
@@ -855,22 +858,25 @@ static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = {
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
},
- [WSA884X_PORT_VISENSE] = {
- .num = WSA884X_PORT_VISENSE + 1,
+ {
+ .num = WSA884X_PORT_CPS + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
- .max_ch = 2,
+ .max_ch = 1,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
- },
- [WSA884X_PORT_CPS] = {
- .num = WSA884X_PORT_CPS + 1,
+ }
+};
+
+static struct sdw_dpn_prop wsa884x_src_dpn_prop[] = {
+ {
+ .num = WSA884X_PORT_VISENSE + 1,
.type = SDW_DPN_SIMPLE,
.min_ch = 1,
- .max_ch = 1,
+ .max_ch = 2,
.simple_ch_prep_sm = true,
.read_only_wordlength = true,
- }
+ },
};

static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
@@ -892,6 +898,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
},
[WSA884X_PORT_VISENSE] = {
.num = WSA884X_PORT_VISENSE + 1,
+ /* V and I sense pair */
.ch_mask = 0x3,
},
[WSA884X_PORT_CPS] = {
@@ -1767,6 +1774,27 @@ static const struct snd_soc_component_driver wsa884x_component_drv = {
.num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map),
};

+static int wsa884x_vi_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);
+
+ if (!wsa884x->port_enable[WSA884X_PORT_VISENSE])
+ return 0;
+
+ wsa884x->vi_port_config = wsa884x_pconfig[WSA884X_PORT_VISENSE];
+ wsa884x->vi_sconfig.frame_rate = params_rate(params);
+ wsa884x->vi_sconfig.ch_count = 2;
+ wsa884x->vi_sconfig.bps = 1;
+ wsa884x->vi_sconfig.type = SDW_STREAM_PDM;
+ wsa884x->vi_sconfig.direction = SDW_DATA_DIR_TX;
+
+ return sdw_stream_add_slave(wsa884x->slave, &wsa884x->vi_sconfig,
+ &wsa884x->vi_port_config, 1,
+ wsa884x->vi_sruntime);
+}
+
static int wsa884x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -1776,6 +1804,9 @@ static int wsa884x_hw_params(struct snd_pcm_substream *substream,

wsa884x->active_ports = 0;
for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) {
+ /* VISENSE is driven by the separate VI capture DAI */
+ if (i == WSA884X_PORT_VISENSE)
+ continue;
if (!wsa884x->port_enable[i])
continue;

@@ -1795,7 +1826,10 @@ static int wsa884x_hw_free(struct snd_pcm_substream *substream,
{
struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);

- sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ sdw_stream_remove_slave(wsa884x->slave, wsa884x->vi_sruntime);
+ else
+ sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime);

return 0;
}
@@ -1829,11 +1863,20 @@ static int wsa884x_set_stream(struct snd_soc_dai *dai,
{
struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev);

- wsa884x->sruntime = stream;
+ if (direction == SNDRV_PCM_STREAM_CAPTURE)
+ wsa884x->vi_sruntime = stream;
+ else
+ wsa884x->sruntime = stream;

return 0;
}

+static const struct snd_soc_dai_ops wsa884x_vi_dai_ops = {
+ .hw_params = wsa884x_vi_hw_params,
+ .hw_free = wsa884x_hw_free,
+ .set_stream = wsa884x_set_stream,
+};
+
static const struct snd_soc_dai_ops wsa884x_dai_ops = {
.hw_params = wsa884x_hw_params,
.hw_free = wsa884x_hw_free,
@@ -1856,6 +1899,19 @@ static struct snd_soc_dai_driver wsa884x_dais[] = {
},
.ops = &wsa884x_dai_ops,
},
+ {
+ .name = "VI",
+ .capture = {
+ .stream_name = "VI Capture",
+ .rates = WSA884X_RATES | WSA884X_FRAC_RATES,
+ .formats = WSA884X_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &wsa884x_vi_dai_ops,
+ },
};

static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
@@ -2091,9 +2147,11 @@ static int wsa884x_probe(struct sdw_slave *pdev,
WSA884X_MAX_SWR_PORTS))
dev_dbg(dev, "Static Port mapping not specified\n");

- pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS - 1, 0) & ~BIT(WSA884X_PORT_VISENSE);
+ pdev->prop.source_ports = BIT(WSA884X_PORT_VISENSE);
pdev->prop.simple_clk_stop_capable = true;
pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop;
+ pdev->prop.src_dpn_prop = wsa884x_src_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;

wsa884x_reset_deassert(wsa884x);
--
2.47.3