[PATCH v2 3/3] soundwire: qcom: program MM_SYNC for multi-master
From: Srinivas Kandagatla
Date: Thu Oct 01 2026 - 06:31:34 EST
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@xxxxxxxxxxxxxxxx>
---
drivers/soundwire/qcom.c | 56 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 767147a6635d..92e1644623c5 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,43 @@ 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;
+
+ /*
+ * peer_ctrl is only ever set on qcom,soundwire-v3.1.0 IPs (the
+ * multi-master binding requires that compatible), so the V3-only
+ * MM_SYNC / COMP_FEATURE_CFG registers written below are safe to
+ * touch here.
+ */
+ if (!peer || peer->bus.controller_id < 1)
+ 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;
@@ -920,6 +969,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,
@@ -1935,6 +1986,11 @@ static int __maybe_unused swrm_runtime_resume(struct device *dev)
} else {
reset_control_reset(ctrl->audio_cgcr);
+ qcom_swrm_program_mm_sync(ctrl);
+ /* dependent has no PM-runtime of its own */
+ if (ctrl->is_primary && ctrl->peer_ctrl)
+ qcom_swrm_program_mm_sync(ctrl->peer_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