* [PATCH 0/3] soundwire: qcom: add multi-master support
@ 2026-09-11 8:21 Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 1/3] dt-bindings: soundwire: qcom: describe multi-master Srinivas Kandagatla
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Srinivas Kandagatla @ 2026-09-11 8:21 UTC (permalink / raw)
To: robh, vkoul
Cc: krzk+dt, conor+dt, srini, yung-chuan.liao, pierre-louis.bossart,
prasad.kumpatla, jingyi.wang, sibi.sankar, quic_srivasam,
linux-arm-msm, devicetree, linux-kernel, linux-sound,
Srinivas Kandagatla
This patch series adds support to the multi-master in existing soundwire
controller driver.
On Qualcomm Glymur SoC, both TX and RX soundwire controllers are wired
up in multi master mode connecting to an SDCA compliant Tambora codec.
One of the two controllers (tx,rx) act as primary controller in this
instance its tx which provides clock and control and some data lines and
other controller instance in this case rx act as dependent controller
providing extra data lanes to primary controller. This is called multi
master mode in Qualcomm Soundwire controller.
Multi Master mode allows synchronous transport across two independent
soundwire masters. This includes synchronized clock-stop, clock-resume,
clock scaling and bank switch between both masters, the Primary master
will ensure that.
From the slave's point of view there is still one SoundWire bus with
one clock and one control lane; the extra DATA lanes just come from
a different pad on the SoC.
Currently the bindings are added in a way that its possible to add more
than one dependent, however to keep things simple the driver only
supports single dependent for now.
Topology (2+2 lane example):
Primary IP Dependent IP
+-----------------+ +-----------------+
| MM_SYNC <=====+=================+=====> MM_SYNC |
| | | |
| SWR_CLK ---+ | | |
| CTRL ---+ | | |
| DATA0 ---+ | | DATA0 ---+ |
| DATA1 ---+ | | DATA1 ---+ |
+--------------|--+ +------------|----+
| |
v v
+-----------+-----------+ +---------+---------+
| SWR_CLK, CTRL, | | bus DATA2, |
| bus DATA0, DATA1 | | DATA3 |
+-----------+-----------+ +---------+---------+
| |
+----------------+----------------+
|
v
+--------------+
| Slave |
+--------------+
This series adds multi-master support.
Patch 1 adds two new DT properties qcom,multi-master-mode and
qcom,multi-master-peer for describing the pair.
Patch 2 wires up the primary and dependent roles in the driver.
Patch 3 programs SWRM_V3_MM_SYNC_CONFIG on both sides so their frame
generators lock to a single SWR_CLK.
Tested on Glymur (SC8480XP) CRD with WCD9378 in compute mode. HPH
playback and mic capture both work.
Srinivas Kandagatla (3):
dt-bindings: soundwire: qcom: describe multi-master
soundwire: qcom: add multi-master support
soundwire: qcom: program MM_SYNC for multi-master
.../bindings/soundwire/qcom,soundwire.yaml | 109 ++++++++-
drivers/soundwire/qcom.c | 218 ++++++++++++++++--
2 files changed, 303 insertions(+), 24 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/3] dt-bindings: soundwire: qcom: describe multi-master
2026-09-11 8:21 [PATCH 0/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
@ 2026-09-11 8:21 ` Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 2/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 3/3] soundwire: qcom: program MM_SYNC for multi-master Srinivas Kandagatla
2 siblings, 0 replies; 5+ messages in thread
From: Srinivas Kandagatla @ 2026-09-11 8:21 UTC (permalink / raw)
To: robh, vkoul
Cc: krzk+dt, conor+dt, srini, yung-chuan.liao, pierre-louis.bossart,
prasad.kumpatla, jingyi.wang, sibi.sankar, quic_srivasam,
linux-arm-msm, devicetree, linux-kernel, linux-sound,
Srinivas Kandagatla
v3.1.0+ Qualcomm SoundWire IPs can be paired to expose one logical
bus with more than two DATA lanes. Add the two properties that
describe such a pair:
qcom,multi-master-mode role in the pair ("primary" or "dependent")
qcom,multi-master-peer <phandle, bus-port-offset> to the other IP
Both nodes carry both properties.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
---
.../bindings/soundwire/qcom,soundwire.yaml | 109 ++++++++++++++++--
1 file changed, 100 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
index 8e6973fa229c..6cf3285ded92 100644
--- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
+++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
@@ -215,6 +215,36 @@ properties:
maximum: 4
- const: 0xff
+ qcom,multi-master-mode:
+ description:
+ Role in v3.1.0+ multi-master mode. Both nodes set this; the
+ primary owns SWR_CLK, the dependent contributes DATA lanes
+ only.
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ primary, dependent ]
+
+ qcom,multi-master-peer:
+ description:
+ Peers in multi-master mode. Each entry is a
+ <peer-phandle, bus-port-offset> pair. Peer N's DPn slots
+ 1, 2, 3, ... occupy bus ports (offset+1), (offset+2), ...
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ items:
+ - description: peer phandle
+ - description: bus port offset (last primary-local port)
+ minItems: 1
+
+ num-lanes:
+ description:
+ Number of DATA lanes this IP exposes locally. Required in
+ multi-master mode; on the primary it defines the boundary
+ between local bus lanes (0..num-lanes-1) and lanes contributed
+ by the dependent.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 1
+ maximum: 8
+
label:
maxItems: 1
@@ -224,21 +254,82 @@ required:
- interrupts
- clocks
- clock-names
- - '#sound-dai-cells'
- '#address-cells'
- '#size-cells'
- - qcom,ports-offset1
- - qcom,ports-offset2
-
-oneOf:
- - required:
- - qcom,ports-sinterval-low
- - required:
- - qcom,ports-sinterval
allOf:
- $ref: soundwire-controller.yaml#
+ # Single-master (no qcom,multi-master-mode): needs port config.
+ - if:
+ not:
+ required:
+ - qcom,multi-master-mode
+ then:
+ required:
+ - '#sound-dai-cells'
+ - qcom,ports-offset1
+ - qcom,ports-offset2
+ oneOf:
+ - required:
+ - qcom,ports-sinterval-low
+ - required:
+ - qcom,ports-sinterval
+
+ # Multi-master primary: drives the bus, needs port config + MM props.
+ - if:
+ properties:
+ qcom,multi-master-mode:
+ const: primary
+ required:
+ - qcom,multi-master-mode
+ then:
+ properties:
+ compatible:
+ contains:
+ const: qcom,soundwire-v3.1.0
+ required:
+ - '#sound-dai-cells'
+ - qcom,ports-offset1
+ - qcom,ports-offset2
+ - qcom,multi-master-peer
+ - num-lanes
+ oneOf:
+ - required:
+ - qcom,ports-sinterval-low
+ - required:
+ - qcom,ports-sinterval
+
+ # Multi-master dependent: contributes DATA lanes
+ - if:
+ properties:
+ qcom,multi-master-mode:
+ const: dependent
+ required:
+ - qcom,multi-master-mode
+ then:
+ properties:
+ compatible:
+ contains:
+ const: qcom,soundwire-v3.1.0
+ '#sound-dai-cells': false
+ qcom,din-ports: false
+ qcom,dout-ports: false
+ qcom,ports-word-length: false
+ qcom,ports-sinterval-low: false
+ qcom,ports-sinterval: false
+ qcom,ports-offset1: false
+ qcom,ports-offset2: false
+ qcom,ports-lane-control: false
+ qcom,ports-block-pack-mode: false
+ qcom,ports-hstart: false
+ qcom,ports-hstop: false
+ qcom,ports-block-group-count: false
+ qcom,ports-ch-mask: false
+ required:
+ - qcom,multi-master-peer
+ - num-lanes
+
unevaluatedProperties: false
examples:
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/3] soundwire: qcom: add multi-master support
2026-09-11 8:21 [PATCH 0/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 1/3] dt-bindings: soundwire: qcom: describe multi-master Srinivas Kandagatla
@ 2026-09-11 8:21 ` Srinivas Kandagatla
2026-09-14 17:21 ` Pierre-Louis Bossart
2026-09-11 8:21 ` [PATCH 3/3] soundwire: qcom: program MM_SYNC for multi-master Srinivas Kandagatla
2 siblings, 1 reply; 5+ messages in thread
From: Srinivas Kandagatla @ 2026-09-11 8:21 UTC (permalink / raw)
To: robh, vkoul
Cc: krzk+dt, conor+dt, srini, yung-chuan.liao, pierre-louis.bossart,
prasad.kumpatla, jingyi.wang, sibi.sankar, quic_srivasam,
linux-arm-msm, devicetree, linux-kernel, linux-sound,
Srinivas Kandagatla
v3.1.0+ Qualcomm SoundWire IPs can be paired to expose one logical
bus with more than two DATA lanes. One IP owns SWR_CLK and drives
the bus; the other contributes DATA lanes only.
Support this pairing on the driver side. A device link between the
two IPs enforces correct unbind ordering and keeps the peer resumed
while the primary is active.
Single-master deployments (no qcom,multi-master-mode in DT) are
unaffected.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
---
drivers/soundwire/qcom.c | 171 +++++++++++++++++++++++++++++++++++----
1 file changed, 156 insertions(+), 15 deletions(-)
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 35ffffd541bd..a5f39868161a 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -10,6 +10,7 @@
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_irq.h>
+#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
@@ -111,6 +112,7 @@
#define SWRM_DPn_PORT_HCTRL_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)
#define SWRM_DPn_BLOCK_CTRL3_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)
#define SWRM_DPn_SAMPLECTRL2_BANK(offset, n, m) (offset + 0x100 * (n - 1) + 0x40 * m)
+#define SWRM_DPn_SLOT_STRIDE 0x100
#define SWR_V1_3_MSTR_MAX_REG_ADDR 0x1740
#define SWR_V2_0_MSTR_MAX_REG_ADDR 0x50ac
@@ -192,6 +194,13 @@ struct qcom_swrm_ctrl {
const unsigned int *reg_layout;
void __iomem *mmio;
struct reset_control *audio_cgcr;
+ u8 num_lanes;
+ bool is_dependent;
+ struct qcom_swrm_ctrl *peer_ctrl;
+ u8 peer_first_lane;
+ u8 num_peer_lanes;
+ bool is_primary;
+ u32 peer_dpn_offset;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
@@ -1040,9 +1049,41 @@ static int qcom_swrm_pre_bank_switch(struct sdw_bus *bus)
u32p_replace_bits(&val, ctrl->cols_index, SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK);
u32p_replace_bits(&val, ctrl->rows_index, SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK);
+ if (ctrl->peer_ctrl)
+ ctrl->peer_ctrl->reg_write(ctrl->peer_ctrl, reg, val);
+
return ctrl->reg_write(ctrl, reg, val);
}
+static struct qcom_swrm_ctrl *
+qcom_swrm_port_target(struct qcom_swrm_ctrl *ctrl, u8 port_num, u32 *reg)
+{
+ u8 lane = ctrl->pconfig[port_num].lane_control;
+
+ if (ctrl->peer_ctrl && lane >= ctrl->peer_first_lane &&
+ lane < ctrl->peer_first_lane + ctrl->num_peer_lanes) {
+ *reg -= ctrl->peer_dpn_offset;
+ return ctrl->peer_ctrl;
+ }
+ return ctrl;
+}
+
+static int qcom_swrm_port_reg_write(struct qcom_swrm_ctrl *ctrl, u8 port_num,
+ u32 reg, u32 val)
+{
+ struct qcom_swrm_ctrl *tgt = qcom_swrm_port_target(ctrl, port_num, ®);
+
+ return tgt->reg_write(tgt, reg, val);
+}
+
+static int qcom_swrm_port_reg_read(struct qcom_swrm_ctrl *ctrl, u8 port_num,
+ u32 reg, u32 *val)
+{
+ struct qcom_swrm_ctrl *tgt = qcom_swrm_port_target(ctrl, port_num, ®);
+
+ return tgt->reg_read(tgt, reg, val);
+}
+
static int qcom_swrm_port_params(struct sdw_bus *bus,
struct sdw_port_params *p_params,
unsigned int bank)
@@ -1050,8 +1091,9 @@ static int qcom_swrm_port_params(struct sdw_bus *bus,
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
u32 offset = ctrl->reg_layout[SWRM_OFFSET_DP_BLOCK_CTRL_1];
- return ctrl->reg_write(ctrl, SWRM_DPn_BLOCK_CTRL_1(offset, p_params->num),
- p_params->bps - 1);
+ return qcom_swrm_port_reg_write(ctrl, p_params->num,
+ SWRM_DPn_BLOCK_CTRL_1(offset, p_params->num),
+ p_params->bps - 1);
}
static int qcom_swrm_transport_params(struct sdw_bus *bus,
@@ -1072,7 +1114,7 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
value |= pcfg->off2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT;
value |= pcfg->si & 0xff;
- ret = ctrl->reg_write(ctrl, reg, value);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
if (ret)
goto err;
@@ -1081,7 +1123,7 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
value = (pcfg->si >> 8) & 0xff;
reg = SWRM_DPn_SAMPLECTRL2_BANK(offset, params->port_num, bank);
- ret = ctrl->reg_write(ctrl, reg, value);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
if (ret)
goto err;
}
@@ -1091,7 +1133,10 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
reg = SWRM_DPn_PORT_CTRL_2_BANK(offset, params->port_num, bank);
value = pcfg->lane_control;
- ret = ctrl->reg_write(ctrl, reg, value);
+ if (ctrl->peer_ctrl && value >= ctrl->peer_first_lane &&
+ value < ctrl->peer_first_lane + ctrl->num_peer_lanes)
+ value -= ctrl->peer_first_lane;
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
if (ret)
goto err;
}
@@ -1102,7 +1147,7 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
reg = SWRM_DPn_BLOCK_CTRL2_BANK(offset, params->port_num, bank);
value = pcfg->blk_group_count;
- ret = ctrl->reg_write(ctrl, reg, value);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
if (ret)
goto err;
}
@@ -1112,10 +1157,10 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
if (pcfg->hstart != SWR_INVALID_PARAM && pcfg->hstop != SWR_INVALID_PARAM) {
value = (pcfg->hstop << 4) | pcfg->hstart;
- ret = ctrl->reg_write(ctrl, reg, value);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
} else {
value = (SWR_HSTOP_MAX_VAL << 4) | SWR_HSTART_MIN_VAL;
- ret = ctrl->reg_write(ctrl, reg, value);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg, value);
}
if (ret)
@@ -1124,7 +1169,8 @@ static int qcom_swrm_transport_params(struct sdw_bus *bus,
if (pcfg->bp_mode != SWR_INVALID_PARAM) {
offset = ctrl->reg_layout[SWRM_OFFSET_DP_BLOCK_CTRL3_BANK];
reg = SWRM_DPn_BLOCK_CTRL3_BANK(offset, params->port_num, bank);
- ret = ctrl->reg_write(ctrl, reg, pcfg->bp_mode);
+ ret = qcom_swrm_port_reg_write(ctrl, params->port_num, reg,
+ pcfg->bp_mode);
}
err:
@@ -1142,14 +1188,14 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus,
reg = SWRM_DPn_PORT_CTRL_BANK(offset, enable_ch->port_num, bank);
- ctrl->reg_read(ctrl, reg, &val);
+ qcom_swrm_port_reg_read(ctrl, enable_ch->port_num, reg, &val);
if (enable_ch->enable)
val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
else
val &= ~(0xff << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
- return ctrl->reg_write(ctrl, reg, val);
+ return qcom_swrm_port_reg_write(ctrl, enable_ch->port_num, reg, val);
}
static const struct sdw_master_port_ops qcom_swrm_port_ops = {
@@ -1562,6 +1608,68 @@ static int swrm_reg_show(struct seq_file *s_file, void *data)
DEFINE_SHOW_ATTRIBUTE(swrm_reg);
#endif
+static int qcom_swrm_multi_master_setup(struct qcom_swrm_ctrl *ctrl)
+{
+ struct of_phandle_args args;
+ struct qcom_swrm_ctrl *peer;
+ struct platform_device *pdev;
+ u32 offset;
+ int ret;
+
+ ret = of_parse_phandle_with_fixed_args(ctrl->dev->of_node,
+ "qcom,multi-master-peer",
+ 1, 0, &args);
+ if (ret)
+ return -EINVAL;
+
+ pdev = of_find_device_by_node(args.np);
+ of_node_put(args.np);
+ if (!pdev)
+ return -EPROBE_DEFER;
+
+ if (pdev->dev.driver != ctrl->dev->driver) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ peer = platform_get_drvdata(pdev);
+ if (!peer || !peer->is_dependent) {
+ ret = -EPROBE_DEFER;
+ goto out;
+ }
+
+ if (!ctrl->num_lanes || !peer->num_lanes ||
+ ctrl->num_lanes + peer->num_lanes > SDW_MAX_LANES) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ offset = args.args[0];
+ if (offset < ctrl->num_lanes) {
+ dev_err(ctrl->dev, "qcom,multi-master-peer: offset %u < num-lanes %u\n",
+ offset, ctrl->num_lanes);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!device_link_add(ctrl->dev, &pdev->dev,
+ DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_PM_RUNTIME)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ctrl->peer_ctrl = peer;
+ peer->peer_ctrl = ctrl;
+ ctrl->peer_first_lane = ctrl->num_lanes;
+ ctrl->num_peer_lanes = peer->num_lanes;
+ ctrl->peer_dpn_offset = offset * SWRM_DPn_SLOT_STRIDE;
+
+ ret = 0;
+out:
+ put_device(&pdev->dev);
+ return ret;
+}
+
static int qcom_swrm_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1576,6 +1684,9 @@ static int qcom_swrm_probe(struct platform_device *pdev)
if (!ctrl)
return -ENOMEM;
+ if (!of_property_read_u32(dev->of_node, "num-lanes", &val))
+ ctrl->num_lanes = val;
+
memset(ctrl->page1_cache, 0xff, sizeof(ctrl->page1_cache));
memset(ctrl->page2_cache, 0xff, sizeof(ctrl->page2_cache));
@@ -1636,9 +1747,19 @@ static int qcom_swrm_probe(struct platform_device *pdev)
ctrl->bus.compute_params = &qcom_swrm_compute_params;
ctrl->bus.clk_stop_timeout = 300;
- ret = qcom_swrm_get_port_config(ctrl);
- if (ret)
- goto err_clk;
+ if (of_property_match_string(dev->of_node, "qcom,multi-master-mode",
+ "dependent") < 0) {
+ ret = qcom_swrm_get_port_config(ctrl);
+ if (ret)
+ goto err_clk;
+ }
+
+ if (of_property_match_string(dev->of_node, "qcom,multi-master-mode", "primary") >= 0) {
+ ret = qcom_swrm_multi_master_setup(ctrl);
+ if (ret)
+ goto err_clk;
+ ctrl->is_primary = true;
+ }
params = &ctrl->bus.params;
params->max_dr_freq = DEFAULT_CLK_FREQ;
@@ -1689,6 +1810,16 @@ static int qcom_swrm_probe(struct platform_device *pdev)
ctrl->bus.controller_id = val;
}
+ if (of_property_match_string(dev->of_node, "qcom,multi-master-mode", "dependent") >= 0) {
+ ctrl->is_dependent = true;
+ dev_dbg(dev,
+ "Qualcomm SoundWire multi-master dependent v%x.%x.%x registered (%u lanes)\n",
+ (ctrl->version >> 24) & 0xff,
+ (ctrl->version >> 16) & 0xff,
+ ctrl->version & 0xffff, ctrl->num_lanes);
+ return 0;
+ }
+
ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
if (ret) {
dev_err(dev, "Failed to register Soundwire controller (%d)\n",
@@ -1696,7 +1827,11 @@ static int qcom_swrm_probe(struct platform_device *pdev)
goto err_clk;
}
+ if (ctrl->is_primary && ctrl->peer_ctrl)
+ qcom_swrm_init(ctrl->peer_ctrl);
+
qcom_swrm_init(ctrl);
+
wait_for_completion_timeout(&ctrl->enumeration,
msecs_to_jiffies(TIMEOUT_MS));
ret = qcom_swrm_register_dais(ctrl);
@@ -1733,7 +1868,13 @@ static void qcom_swrm_remove(struct platform_device *pdev)
{
struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
- sdw_bus_master_delete(&ctrl->bus);
+ if (ctrl->is_primary && ctrl->peer_ctrl) {
+ ctrl->peer_ctrl->peer_ctrl = NULL;
+ ctrl->peer_ctrl = NULL;
+ }
+
+ if (!ctrl->is_dependent)
+ sdw_bus_master_delete(&ctrl->bus);
clk_disable_unprepare(ctrl->hclk);
}
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 3/3] soundwire: qcom: program MM_SYNC for multi-master
2026-09-11 8:21 [PATCH 0/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 1/3] dt-bindings: soundwire: qcom: describe multi-master Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 2/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
@ 2026-09-11 8:21 ` Srinivas Kandagatla
2 siblings, 0 replies; 5+ messages in thread
From: Srinivas Kandagatla @ 2026-09-11 8:21 UTC (permalink / raw)
To: robh, vkoul
Cc: krzk+dt, conor+dt, srini, yung-chuan.liao, pierre-louis.bossart,
prasad.kumpatla, jingyi.wang, sibi.sankar, quic_srivasam,
linux-arm-msm, devicetree, linux-kernel, linux-sound,
Srinivas Kandagatla
The primary and dependent frame generators must lock to a single
SWR_CLK for the shared bus to run. Program SWRM_V3_MM_SYNC_CONFIG
on both sides at init, and re-apply on resume as the register does
not survive a clock stop.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
---
drivers/soundwire/qcom.c | 47 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index a5f39868161a..3b9b1a53b082 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -46,6 +46,18 @@
#define SWRM_V3_COMP_PARAMS_RD_FIFO_DEPTH GENMASK(23, 18)
#define SWRM_COMP_MASTER_ID 0x104
+
+#define SWRM_V3_MM_SYNC_CONFIG 0x108
+#define SWRM_V3_MM_SYNC_CONNECTED_MASTER_MASK GENMASK(15, 0)
+#define SWRM_V3_MM_SYNC_IS_DEPENDENT_MASTER BIT(16)
+#define SWRM_V3_MM_SYNC_MASK_CONTROL_BITS BIT(17)
+
+#define SWRM_V3_COMP_FEATURE_CFG 0x10
+#define SWRM_V3_COMP_FEATURE_CFG_BASE 0x00E
+#define SWRM_V3_COMP_FEATURE_CFG_FORCE_MODE_EN BIT(9)
+#define SWRM_V3_COMP_FEATURE_CFG_CLK_PIN_AVAIL BIT(10)
+#define SWRM_V3_COMP_FEATURE_CFG_CLK_STOP_EXEC_ON_CMD_IGNORE BIT(12)
+
#define SWRM_V1_3_INTERRUPT_STATUS 0x200
#define SWRM_V2_0_INTERRUPT_STATUS 0x5000
#define SWRM_INTERRUPT_STATUS_RMSK GENMASK(16, 0)
@@ -892,6 +904,37 @@ static bool swrm_wait_for_frame_gen_enabled(struct qcom_swrm_ctrl *ctrl)
return false;
}
+static void qcom_swrm_program_mm_sync(struct qcom_swrm_ctrl *ctrl)
+{
+ struct qcom_swrm_ctrl *peer = ctrl->peer_ctrl;
+ u32 conn_mask;
+
+ if (!peer)
+ return;
+
+ conn_mask = BIT(peer->bus.controller_id - 1) &
+ SWRM_V3_MM_SYNC_CONNECTED_MASTER_MASK;
+
+ if (ctrl->is_primary) {
+ ctrl->reg_write(ctrl, SWRM_V3_MM_SYNC_CONFIG, conn_mask);
+ ctrl->reg_write(ctrl, SWRM_V3_COMP_FEATURE_CFG,
+ SWRM_V3_COMP_FEATURE_CFG_BASE |
+ SWRM_V3_COMP_FEATURE_CFG_FORCE_MODE_EN |
+ SWRM_V3_COMP_FEATURE_CFG_CLK_PIN_AVAIL |
+ SWRM_V3_COMP_FEATURE_CFG_CLK_STOP_EXEC_ON_CMD_IGNORE);
+ return;
+ }
+
+ ctrl->reg_write(ctrl, SWRM_V3_MM_SYNC_CONFIG,
+ SWRM_V3_MM_SYNC_IS_DEPENDENT_MASTER |
+ SWRM_V3_MM_SYNC_MASK_CONTROL_BITS | conn_mask);
+
+ ctrl->reg_write(ctrl, SWRM_V3_COMP_FEATURE_CFG,
+ SWRM_V3_COMP_FEATURE_CFG_BASE |
+ SWRM_V3_COMP_FEATURE_CFG_FORCE_MODE_EN |
+ SWRM_V3_COMP_FEATURE_CFG_CLK_STOP_EXEC_ON_CMD_IGNORE);
+}
+
static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
{
u32 val;
@@ -918,6 +961,8 @@ static int qcom_swrm_init(struct qcom_swrm_ctrl *ctrl)
u32p_replace_bits(&val, SWRM_DEF_CMD_NO_PINGS, SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK);
ctrl->reg_write(ctrl, SWRM_MCP_CFG_ADDR, val);
+ qcom_swrm_program_mm_sync(ctrl);
+
if (ctrl->version == SWRM_VERSION_1_7_0) {
ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU);
ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL,
@@ -1909,6 +1954,8 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev)
} else {
reset_control_reset(ctrl->audio_cgcr);
+ qcom_swrm_program_mm_sync(ctrl);
+
if (ctrl->version == SWRM_VERSION_1_7_0) {
ctrl->reg_write(ctrl, SWRM_LINK_MANAGER_EE, SWRM_EE_CPU);
ctrl->reg_write(ctrl, SWRM_MCP_BUS_CTRL,
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/3] soundwire: qcom: add multi-master support
2026-09-11 8:21 ` [PATCH 2/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
@ 2026-09-14 17:21 ` Pierre-Louis Bossart
0 siblings, 0 replies; 5+ messages in thread
From: Pierre-Louis Bossart @ 2026-09-14 17:21 UTC (permalink / raw)
To: Srinivas Kandagatla, robh, vkoul
Cc: krzk+dt, conor+dt, srini, yung-chuan.liao, prasad.kumpatla,
jingyi.wang, sibi.sankar, quic_srivasam, linux-arm-msm,
devicetree, linux-kernel, linux-sound
On 9/11/26 10:21, Srinivas Kandagatla wrote:
> v3.1.0+ Qualcomm SoundWire IPs can be paired to expose one logical
> bus with more than two DATA lanes. One IP owns SWR_CLK and drives
> the bus; the other contributes DATA lanes only.
>
> Support this pairing on the driver side. A device link between the
> two IPs enforces correct unbind ordering and keeps the peer resumed
> while the primary is active.
>
> Single-master deployments (no qcom,multi-master-mode in DT) are
> unaffected.
It'd be good to clarify how the command protocol is handled. Each IP is
probably capable of handling PING/READ/WRITE commands in Column0, but in
the paired option the primary IP is the only one that deals with Column0
and all other DATA lanes are just data?
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-09-14 18:29 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 8:21 [PATCH 0/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 1/3] dt-bindings: soundwire: qcom: describe multi-master Srinivas Kandagatla
2026-09-11 8:21 ` [PATCH 2/3] soundwire: qcom: add multi-master support Srinivas Kandagatla
2026-09-14 17:21 ` Pierre-Louis Bossart
2026-09-11 8:21 ` [PATCH 3/3] soundwire: qcom: program MM_SYNC for multi-master Srinivas Kandagatla
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®