* [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files
@ 2026-11-07 16:12 Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 01/11] soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro Vijendar Mukunda
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
This is v3 of the AMD SoundWire manager driver refactor series. It
incorporates review feedback from v2 and extends the series with two
new patches that guard DAI and clock-stop paths by acp_rev.
Changes since v2:
- Add patch 10: guard DAI PCM caps by acp_rev in register_dais();
replaces bare stream->rates and stream->formats assignments with an
explicit switch/case returning -EINVAL for unrecognised revisions.
- Add patch 11: guard clock-stop register access by acp_rev in
amd_sdw_clock_stop(); replaces open-coded accesses with an explicit
switch/case returning -EINVAL for unrecognised revisions.
- Patch 4 extended: also rename pad control helpers introduced by the
fix series (amd_sdw_pad_ctrl_supported to
amd_acp63_sdw_pad_ctrl_supported, __amd_sdw_oe_update to
__amd_acp63_sdw_oe_update) to the amd_acp63_* prefix.
- Patch 8 updated: rename amd_enable_sdw_pads() to
amd_acp63_enable_sdw_pads() in amd_init.c; the CLK_STOP_MODE path
in amd_acp63_sdw_suspend() now gains the same interrupt-gate and
work-drain sequence present in the POWER_OFF_MODE paths;
amd_sdw_host_wake_enable_by_rev() is called in CLK_STOP_MODE to
disable wake signalling before clock stop.
- Patch 9: commit message reformatted as a bullet list for clarity;
amd_sdw_hw_init_ops() dispatch wrapper documented explicitly.
- Rebased on top of the AMD SoundWire fix series v5 (25 patches).
-Link to v2:
https://lore.kernel.org/all/20261013100306.2921771-2-Vijendar.Mukunda@amd.com/
Changes since v1:
- Patch 4 subject updated: "rename hardware backend symbols" (covers
functions, op-table instances, and the register-offset constant)
- Patch 6: removed the forward reference to "subsequent patches" from
the commit message; dropped WARN_ONCE guards from dispatch helpers
- Patch 7: commit message corrected; #include <linux/workqueue.h>
addition dropped (available transitively)
- Patch 8: amd_sdw_hw_init_ops() now uses a static const
struct amd_sdw_hw_ops instead of devm_kzalloc; amd_sdw_hw_init_ops()
moved after amd_sdw_manager_remove() with a forward declaration before
probe to resolve ordering
- Patch 9 (new): splits amd_manager.c into amd_manager_common.c
(shared revision-independent functions, analogous to cadence_master.c)
and renames what remains to amd_acp63_manager.c (ACP6.3/7.x register-
level backend, analogous to intel_auxdevice.c)
Series overview:
Patches 1-3 are preparatory: rename the manager-count macro to carry
the ACP6.3 scope, convert the pdevinfo/sdw_pdata and pdev arrays in
sdw_amd_probe_controller() from fixed-size stack arrays to heap
allocations sized by the ACP revision.
Patch 4 renames revision-specific hardware-access functions, op-table
instances, the manager register-offset constant, and pad control
helpers to the amd_acp63_*/ACP63_* prefix.
Patch 5 removes an unused define.
Patches 6-8 introduce and wire up the hw_ops dispatch framework:
patch 6 defines struct amd_sdw_hw_ops and the inline acp_*() dispatch
helpers; patch 7 converts the irq/work handlers to dispatch per
acp_rev to their ACP6.3-family implementations; patch 8 extracts PM
callbacks into hw_ops slots, renames amd_enable_sdw_pads() to
amd_acp63_enable_sdw_pads(), populates a static const
amd_acp63_hw_ops table, and converts all remaining direct
amd_acp63_*() call sites to use the acp_*() wrappers.
Patch 9 splits amd_manager.c: shared revision-independent functions
move to amd_manager_common.c and the ACP6.3/7.0/7.1/7.2 register-
level backend is renamed to amd_acp63_manager.c.
Patches 10-11 close two open-coded revision assumptions left after the
split: register_dais() and amd_sdw_clock_stop() replace bare accesses
with explicit switch/case blocks on acp_rev.
Dependencies:
This series applies on top of the AMD SoundWire fix series (v5,
25 patches) posted at:
https://lore.kernel.org/linux-sound/20261107154659.3358223-1-Vijendar.Mukunda@amd.com
Vijendar Mukunda (11):
soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro
soundwire: amd: allocate pdevinfo and sdw_pdata by ACP revision in
probe
soundwire: amd: allocate sdw_amd_ctx pdev array dynamically
soundwire: amd: rename hardware backend symbols to amd_acp63_*()
prefix
soundwire: amd: remove unused AMD_SDW_MAX_FREQ_NUM define
soundwire: amd: introduce struct amd_sdw_hw_ops dispatch framework
soundwire: amd: dispatch irq/work handlers to per-revision functions
soundwire: amd: populate hw_ops and wire all call sites through
acp_*() helpers
soundwire: amd: extract shared functions into amd_manager_common.c
soundwire: amd: guard DAI PCM caps by acp_rev in register_dais()
soundwire: amd: guard clock-stop register access by acp_rev
drivers/soundwire/Makefile | 2 +-
.../{amd_manager.c => amd_acp63_manager.c} | 940 ++++--------------
drivers/soundwire/amd_init.c | 54 +-
drivers/soundwire/amd_manager.h | 255 +++--
drivers/soundwire/amd_manager_common.c | 822 +++++++++++++++
include/linux/soundwire/sdw_amd.h | 11 +-
6 files changed, 1249 insertions(+), 835 deletions(-)
rename drivers/soundwire/{amd_manager.c => amd_acp63_manager.c} (52%)
create mode 100644 drivers/soundwire/amd_manager_common.c
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 01/11] soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 02/11] soundwire: amd: allocate pdevinfo and sdw_pdata by ACP revision in probe Vijendar Mukunda
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Rename AMD_SDW_MAX_MANAGER_COUNT to AMD_ACP63_SDW_MAX_MANAGER_COUNT and
add a comment clarifying that ACP6.3/7.0/7.1/7.2 all expose two
SoundWire manager instances. The rename makes the platform scope
explicit and matches the acp63 prefix convention used for the shared
ACP6.3-base hardware throughout the driver.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 2 +-
drivers/soundwire/amd_manager.h | 2 +-
include/linux/soundwire/sdw_amd.h | 5 +++--
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 2c52fd181e3c..dd39968a7770 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -604,7 +604,7 @@ static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime
int port_bo, hstart, hstop, sample_int;
unsigned int rate, bps, channels;
unsigned int stream_slot_size, max_slots;
- static unsigned int next_offset[AMD_SDW_MAX_MANAGER_COUNT] = {1};
+ static unsigned int next_offset[AMD_ACP63_SDW_MAX_MANAGER_COUNT] = {1};
unsigned int inst_id = amd_manager->instance;
port_bo = 0;
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index 98c814d5d7cd..ff49e5d08f68 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -256,7 +256,7 @@ static struct sdw_manager_dp_reg acp70_sdw_dp_reg[AMD_ACP70_SDW_MAX_DAI] = {
ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
};
-static u32 sdw_manager_reg_mask_array[AMD_SDW_MAX_MANAGER_COUNT] = {
+static u32 sdw_manager_reg_mask_array[AMD_ACP63_SDW_MAX_MANAGER_COUNT] = {
AMD_SDW0_EXT_INTR_MASK,
AMD_SDW1_EXT_INTR_MASK
};
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 04b95379a724..b67ae581fa17 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -26,7 +26,8 @@
#define AMD_SDW_POWER_OFF_MODE 2
#define ACP_SDW0 0
#define ACP_SDW1 1
-#define AMD_SDW_MAX_MANAGER_COUNT 2
+/* ACP6.3/7.0/7.1/7.2 all expose two SoundWire manager instances */
+#define AMD_ACP63_SDW_MAX_MANAGER_COUNT 2
#define ACP63_PCI_REV_ID 0x63
#define ACP70_PCI_REV_ID 0x70
#define ACP71_PCI_REV_ID 0x71
@@ -149,7 +150,7 @@ struct sdw_amd_acpi_info {
struct sdw_amd_ctx {
int count;
u32 link_mask;
- struct platform_device *pdev[AMD_SDW_MAX_MANAGER_COUNT];
+ struct platform_device *pdev[AMD_ACP63_SDW_MAX_MANAGER_COUNT];
struct sdw_peripherals *peripherals;
};
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 02/11] soundwire: amd: allocate pdevinfo and sdw_pdata by ACP revision in probe
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 01/11] soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 03/11] soundwire: amd: allocate sdw_amd_ctx pdev array dynamically Vijendar Mukunda
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Determine the SoundWire manager count from the ACP revision and allocate
the pdevinfo and sdw_pdata arrays dynamically instead of using fixed-size
stack arrays.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_init.c | 37 ++++++++++++++++++++++++++++++++----
1 file changed, 33 insertions(+), 4 deletions(-)
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c
index e5e6c7415dc6..92586185865a 100644
--- a/drivers/soundwire/amd_init.c
+++ b/drivers/soundwire/amd_init.c
@@ -78,12 +78,12 @@ static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx)
static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
{
+ struct platform_device_info *pdevinfo;
+ struct acp_sdw_pdata *sdw_pdata;
struct sdw_amd_ctx *ctx;
struct acpi_device *adev;
- struct acp_sdw_pdata sdw_pdata[2];
- struct platform_device_info pdevinfo[2];
u32 link_mask;
- int count, index;
+ int count, index, max_manager_count;
int ret;
if (!res)
@@ -98,6 +98,18 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
count = res->count;
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
+ switch (res->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ max_manager_count = AMD_ACP63_SDW_MAX_MANAGER_COUNT;
+ break;
+ default:
+ dev_err(&adev->dev, "unsupported ACP revision: 0x%x\n", res->acp_rev);
+ return NULL;
+ }
+
ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->acp_rev, res->parent);
if (ret)
return NULL;
@@ -122,7 +134,20 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
sdw_res->flags = IORESOURCE_MEM;
sdw_res->start = res->addr;
sdw_res->end = res->addr + res->reg_range;
- memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ sdw_pdata = kcalloc(max_manager_count, sizeof(*sdw_pdata), GFP_KERNEL);
+ if (!sdw_pdata) {
+ kfree(ctx);
+ return NULL;
+ }
+
+ pdevinfo = kcalloc(max_manager_count, sizeof(*pdevinfo), GFP_KERNEL);
+ if (!pdevinfo) {
+ kfree(sdw_pdata);
+ kfree(ctx);
+ return NULL;
+ }
+
link_mask = ctx->link_mask;
for (index = 0; index < count; index++) {
if (!(link_mask & BIT(index)))
@@ -143,6 +168,8 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
if (IS_ERR(ctx->pdev[index]))
goto err;
}
+ kfree(pdevinfo);
+ kfree(sdw_pdata);
return ctx;
err:
while (index--) {
@@ -152,6 +179,8 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
platform_device_unregister(ctx->pdev[index]);
}
+ kfree(pdevinfo);
+ kfree(sdw_pdata);
kfree(ctx);
return NULL;
}
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 03/11] soundwire: amd: allocate sdw_amd_ctx pdev array dynamically
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 01/11] soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 02/11] soundwire: amd: allocate pdevinfo and sdw_pdata by ACP revision in probe Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 04/11] soundwire: amd: rename hardware backend symbols to amd_acp63_*() prefix Vijendar Mukunda
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Replace the fixed-size pdev[AMD_ACP63_SDW_MAX_MANAGER_COUNT] member of
struct sdw_amd_ctx with a dynamically allocated pointer array. The array
is sized by max_manager_count via kcalloc() in sdw_amd_probe_controller()
and freed on all error paths and in sdw_amd_cleanup().
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_init.c | 12 ++++++++++++
include/linux/soundwire/sdw_amd.h | 4 ++--
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c
index 92586185865a..2d3b88cdd484 100644
--- a/drivers/soundwire/amd_init.c
+++ b/drivers/soundwire/amd_init.c
@@ -72,6 +72,7 @@ static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx)
continue;
platform_device_unregister(ctx->pdev[i]);
}
+ kfree(ctx->pdev);
return 0;
}
@@ -126,8 +127,16 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
ctx->count = count;
ctx->link_mask = res->link_mask;
+
+ ctx->pdev = kcalloc(max_manager_count, sizeof(*ctx->pdev), GFP_KERNEL);
+ if (!ctx->pdev) {
+ kfree(ctx);
+ return NULL;
+ }
+
struct resource *sdw_res __free(kfree) = kzalloc_obj(*sdw_res);
if (!sdw_res) {
+ kfree(ctx->pdev);
kfree(ctx);
return NULL;
}
@@ -137,6 +146,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
sdw_pdata = kcalloc(max_manager_count, sizeof(*sdw_pdata), GFP_KERNEL);
if (!sdw_pdata) {
+ kfree(ctx->pdev);
kfree(ctx);
return NULL;
}
@@ -144,6 +154,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
pdevinfo = kcalloc(max_manager_count, sizeof(*pdevinfo), GFP_KERNEL);
if (!pdevinfo) {
kfree(sdw_pdata);
+ kfree(ctx->pdev);
kfree(ctx);
return NULL;
}
@@ -181,6 +192,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
kfree(pdevinfo);
kfree(sdw_pdata);
+ kfree(ctx->pdev);
kfree(ctx);
return NULL;
}
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index b67ae581fa17..6174e888b433 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -144,13 +144,13 @@ struct sdw_amd_acpi_info {
* @count: link count
* @link_mask: bit-wise mask listing SoundWire links reported by the
* Controller
- * @pdev: platform device structure
+ * @pdev: dynamically allocated array of platform device pointers, freed by sdw_amd_cleanup()
* @peripherals: array representing Peripherals exposed across all enabled links
*/
struct sdw_amd_ctx {
int count;
u32 link_mask;
- struct platform_device *pdev[AMD_ACP63_SDW_MAX_MANAGER_COUNT];
+ struct platform_device **pdev;
struct sdw_peripherals *peripherals;
};
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 04/11] soundwire: amd: rename hardware backend symbols to amd_acp63_*() prefix
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (2 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 03/11] soundwire: amd: allocate sdw_amd_ctx pdev array dynamically Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 05/11] soundwire: amd: remove unused AMD_SDW_MAX_FREQ_NUM define Vijendar Mukunda
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
In preparation for the hw_ops dispatch framework, rename the
revision-specific hardware-access functions, op-table instances and
the register-offset constant in amd_manager.c / amd_manager.h to the
amd_acp63_*() / ACP63_* prefix. The amd_acp63_ prefix denotes ACP6.3
as the base revision of this shared IP block, not ACP6.3-only support.
Generic SoundWire protocol helpers (amd_sdw_xfer_msg(), amd_prop_read(),
amd_sdw_clock_stop() and related bus helpers) and the top-level
work_struct handlers (amd_sdw_irq_thread(),
amd_sdw_update_slave_status_work()) intentionally keep the amd_sdw_*()
prefix; inline comments mark them as revision-independent. All other
amd_sdw_*() and amd_*() functions in the hardware path are renamed,
including amd_acp63_sdw_process_wake_event().
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 198 +++++++++++++++++---------------
drivers/soundwire/amd_manager.h | 7 +-
2 files changed, 112 insertions(+), 93 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index dd39968a7770..52801059b87a 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -27,7 +27,7 @@
#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)
-static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
+static int amd_acp63_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
{
struct sdw_bus *bus = &amd_manager->bus;
struct sdw_master_prop *prop = &bus->prop;
@@ -57,7 +57,7 @@ static int amd_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
return 0;
}
-static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager)
+static int amd_acp63_init_sdw_manager(struct amd_sdw_manager *amd_manager)
{
u32 val;
int ret;
@@ -89,7 +89,7 @@ static int amd_init_sdw_manager(struct amd_sdw_manager *amd_manager)
AMD_SDW_TIMEOUT);
}
-static int amd_enable_sdw_manager(struct amd_sdw_manager *amd_manager)
+static int amd_acp63_enable_sdw_manager(struct amd_sdw_manager *amd_manager)
{
u32 val;
@@ -98,7 +98,7 @@ static int amd_enable_sdw_manager(struct amd_sdw_manager *amd_manager)
AMD_SDW_TIMEOUT);
}
-static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager)
+static int amd_acp63_disable_sdw_manager(struct amd_sdw_manager *amd_manager)
{
u32 val;
@@ -115,7 +115,7 @@ static int amd_disable_sdw_manager(struct amd_sdw_manager *amd_manager)
AMD_SDW_TIMEOUT);
}
-static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
+static void amd_acp63_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
u32 val;
@@ -132,7 +132,7 @@ static void amd_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
amd_manager->interrupt_enabled = true;
}
-static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
+static void amd_acp63_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
{
u32 irq_mask;
@@ -156,14 +156,14 @@ static void amd_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK);
}
-static void __amd_sdw_oe_update(struct amd_sdw_manager *amd_manager,
- u32 mask, bool enable)
+static void __amd_acp63_sdw_oe_update(struct amd_sdw_manager *amd_manager,
+ u32 mask, bool enable)
{
amd_updatel(amd_manager->acp_mmio, ACP_PAD_DISABLE_OE_CTRL,
mask, enable ? 0 : mask);
}
-static bool amd_sdw_pad_ctrl_supported(struct amd_sdw_manager *amd_manager)
+static bool amd_acp63_sdw_pad_ctrl_supported(struct amd_sdw_manager *amd_manager)
{
switch (amd_manager->acp_rev) {
case ACP70_PCI_REV_ID:
@@ -179,11 +179,11 @@ static bool amd_sdw_pad_ctrl_supported(struct amd_sdw_manager *amd_manager)
}
}
-static void amd_sdw_oe_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
+static void amd_acp63_sdw_oe_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
{
u32 mask;
- if (!amd_sdw_pad_ctrl_supported(amd_manager))
+ if (!amd_acp63_sdw_pad_ctrl_supported(amd_manager))
return;
switch (amd_manager->instance) {
@@ -200,29 +200,29 @@ static void amd_sdw_oe_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
}
mutex_lock(amd_manager->acp_sdw_lock);
- __amd_sdw_oe_update(amd_manager, mask, enable);
+ __amd_acp63_sdw_oe_update(amd_manager, mask, enable);
mutex_unlock(amd_manager->acp_sdw_lock);
}
-static void amd_sdw_pad_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
+static void amd_acp63_sdw_pad_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
{
u32 keeper_mask, pulldown_mask, schmen_mask, oe_mask;
- if (!amd_sdw_pad_ctrl_supported(amd_manager))
+ if (!amd_acp63_sdw_pad_ctrl_supported(amd_manager))
return;
switch (amd_manager->instance) {
case ACP_SDW0:
- keeper_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
+ keeper_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
pulldown_mask = AMD_SDW0_PAD_PULLDOWN_MASK;
- schmen_mask = AMD_SDW0_PAD_SCHMEN_MASK;
- oe_mask = AMD_SDW0_PAD_OE_CTRL_MASK;
+ schmen_mask = AMD_SDW0_PAD_SCHMEN_MASK;
+ oe_mask = AMD_SDW0_PAD_OE_CTRL_MASK;
break;
case ACP_SDW1:
- keeper_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
+ keeper_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
pulldown_mask = AMD_SDW1_PAD_PULLDOWN_MASK;
- schmen_mask = AMD_SDW1_PAD_SCHMEN_MASK;
- oe_mask = AMD_SDW1_PAD_OE_CTRL_MASK;
+ schmen_mask = AMD_SDW1_PAD_SCHMEN_MASK;
+ oe_mask = AMD_SDW1_PAD_OE_CTRL_MASK;
break;
default:
dev_err(amd_manager->dev, "invalid SDW instance %d\n",
@@ -250,11 +250,11 @@ static void amd_sdw_pad_ctrl(struct amd_sdw_manager *amd_manager, bool enable)
}
/* enable OE on D0 (clear disable bits), disable OE on D3 (set them) */
- __amd_sdw_oe_update(amd_manager, oe_mask, enable);
+ __amd_acp63_sdw_oe_update(amd_manager, oe_mask, enable);
mutex_unlock(amd_manager->acp_sdw_lock);
}
-static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager)
+static void amd_acp63_sdw_set_frameshape(struct amd_sdw_manager *amd_manager)
{
u32 frame_size;
@@ -262,7 +262,7 @@ static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager)
writel(frame_size, amd_manager->mmio + ACP_SW_FRAMESIZE);
}
-static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
+static void amd_acp63_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
{
u32 wake_ctrl;
@@ -275,7 +275,8 @@ static void amd_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable
writel(wake_ctrl, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
}
-static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 target_device_state)
+static int amd_acp63_sdw_set_device_state(struct amd_sdw_manager *amd_manager,
+ u32 target_device_state)
{
u32 sdw_dev_state;
@@ -301,7 +302,7 @@ static int amd_sdw_set_device_state(struct amd_sdw_manager *amd_manager, u32 tar
return 0;
}
-static int amd_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
+static int amd_acp63_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
{
u32 intr_cntl1;
u32 sdw_host_wake_irq_mask;
@@ -357,8 +358,8 @@ static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_m
*lower_word = lower_data;
}
-static int amd_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,
- u32 upper_data, u64 *response)
+static int amd_acp63_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,
+ u32 upper_data, u64 *response)
{
u32 lower_resp, upper_resp;
u32 sts;
@@ -421,7 +422,8 @@ amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
scp_msg.buf = &msg->addr_page1;
scp_msg.flags = SDW_MSG_FLAG_WRITE;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[0])) {
+ if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data,
+ &response_buf[0])) {
dev_err_ratelimited(amd_manager->dev,
"SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -429,7 +431,8 @@ amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
scp_msg.addr = SDW_SCP_ADDRPAGE2;
scp_msg.buf = &msg->addr_page2;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[1])) {
+ if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data,
+ &response_buf[1])) {
dev_err_ratelimited(amd_manager->dev,
"SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -494,6 +497,7 @@ static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *a
return SDW_CMD_OK;
}
+/* AMD SoundWire bus helper shared across all ACP revisions. */
static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg,
int cmd_offset)
{
@@ -501,7 +505,7 @@ static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struc
u32 upper_data = 0, lower_data = 0;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);
- if (amd_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
+ if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -509,6 +513,7 @@ static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struc
return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);
}
+/* AMD SoundWire bus helper shared across all ACP revisions. */
static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
@@ -525,6 +530,7 @@ static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sd
return SDW_CMD_OK;
}
+/* AMD SoundWire bus helper shared across all ACP revisions. */
static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status)
{
switch (status) {
@@ -539,6 +545,7 @@ static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 i
}
}
+/* AMD SoundWire bus helper shared across all ACP revisions. */
static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager)
{
u64 slave_stat;
@@ -556,12 +563,12 @@ static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *am
}
}
-static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager)
+static void amd_acp63_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager)
{
u64 response;
mutex_lock(&amd_manager->bus.msg_lock);
- if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
mutex_unlock(&amd_manager->bus.msg_lock);
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
@@ -574,14 +581,14 @@ static void amd_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_man
amd_sdw_process_ping_status(response, amd_manager);
}
-static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
+static u32 amd_acp63_sdw_read_ping_status(struct sdw_bus *bus)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u64 response;
u32 slave_stat;
lockdep_assert_held(&bus->msg_lock);
- if (amd_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
return amd_manager->ping_slave_stat == U32_MAX ? 0 : amd_manager->ping_slave_stat;
@@ -594,7 +601,7 @@ static u32 amd_sdw_read_ping_status(struct sdw_bus *bus)
return slave_stat;
}
-static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
+static int amd_acp63_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
struct sdw_transport_data t_data = {0};
@@ -680,8 +687,8 @@ static int amd_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime
return 0;
}
-static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
- unsigned int bank)
+static int amd_acp63_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
+ unsigned int bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 frame_fmt_reg, dpn_frame_fmt;
@@ -717,9 +724,9 @@ static int amd_sdw_port_params(struct sdw_bus *bus, struct sdw_port_params *p_pa
return 0;
}
-static int amd_sdw_transport_params(struct sdw_bus *bus,
- struct sdw_transport_params *params,
- enum sdw_reg_bank bank)
+static int amd_acp63_sdw_transport_params(struct sdw_bus *bus,
+ struct sdw_transport_params *params,
+ enum sdw_reg_bank bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 dpn_frame_fmt;
@@ -794,9 +801,9 @@ static int amd_sdw_transport_params(struct sdw_bus *bus,
return 0;
}
-static int amd_sdw_port_enable(struct sdw_bus *bus,
- struct sdw_enable_ch *enable_ch,
- unsigned int bank)
+static int amd_acp63_sdw_port_enable(struct sdw_bus *bus,
+ struct sdw_enable_ch *enable_ch,
+ unsigned int bank)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
u32 dpn_ch_enable;
@@ -873,6 +880,7 @@ static int sdw_master_read_amd_prop(struct sdw_bus *bus)
return 0;
}
+/* AMD SoundWire bus helper shared across all ACP revisions. */
static int amd_prop_read(struct sdw_bus *bus)
{
sdw_master_read_prop(bus);
@@ -880,16 +888,16 @@ static int amd_prop_read(struct sdw_bus *bus)
return 0;
}
-static const struct sdw_master_port_ops amd_sdw_port_ops = {
- .dpn_set_port_params = amd_sdw_port_params,
- .dpn_set_port_transport_params = amd_sdw_transport_params,
- .dpn_port_enable_ch = amd_sdw_port_enable,
+static const struct sdw_master_port_ops amd_acp63_sdw_port_ops = {
+ .dpn_set_port_params = amd_acp63_sdw_port_params,
+ .dpn_set_port_transport_params = amd_acp63_sdw_transport_params,
+ .dpn_port_enable_ch = amd_acp63_sdw_port_enable,
};
-static const struct sdw_master_ops amd_sdw_ops = {
+static const struct sdw_master_ops amd_acp63_sdw_ops = {
.read_prop = amd_prop_read,
.xfer_msg = amd_sdw_xfer_msg,
- .read_ping_status = amd_sdw_read_ping_status,
+ .read_ping_status = amd_acp63_sdw_read_ping_status,
};
static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -1085,7 +1093,7 @@ static void amd_sdw_update_slave_status_work(struct work_struct *work)
ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);
writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio +
ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
- amd_sdw_read_and_process_ping_status(amd_manager);
+ amd_acp63_sdw_read_and_process_ping_status(amd_manager);
goto update_status;
} else {
dev_err_ratelimited(amd_manager->dev,
@@ -1119,7 +1127,7 @@ static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_chang
}
}
-static void amd_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
+static void amd_acp63_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
{
dev_dbg(amd_manager->dev, "SoundWire Wake event reported\n");
pm_request_resume(amd_manager->dev);
@@ -1142,12 +1150,12 @@ static void amd_sdw_irq_thread(struct work_struct *work)
amd_manager->instance, status_change_0to7, status_change_8to11);
if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK) {
- amd_sdw_process_wake_event(amd_manager);
+ amd_acp63_sdw_process_wake_event(amd_manager);
status_change_8to11 &= ~AMD_SDW_WAKE_STAT_MASK;
}
if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
- amd_sdw_read_and_process_ping_status(amd_manager);
+ amd_acp63_sdw_read_and_process_ping_status(amd_manager);
} else {
/* Check for the updated status on peripheral device */
amd_sdw_update_slave_status(status_change_0to7, status_change_8to11, amd_manager);
@@ -1165,17 +1173,17 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
prop = &amd_manager->bus.prop;
if (!prop->hw_disabled) {
- ret = amd_sdw_clk_init_ctrl(amd_manager);
+ ret = amd_acp63_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
- ret = amd_init_sdw_manager(amd_manager);
+ ret = amd_acp63_init_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_enable_sdw_interrupts(amd_manager);
- ret = amd_enable_sdw_manager(amd_manager);
+ amd_acp63_enable_sdw_interrupts(amd_manager);
+ ret = amd_acp63_enable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_sdw_set_frameshape(amd_manager);
+ amd_acp63_sdw_set_frameshape(amd_manager);
}
/* Enable runtime PM */
pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
@@ -1211,15 +1219,15 @@ static int amd_sdw_manager_probe(struct platform_device *pdev)
amd_manager->ping_slave_stat = U32_MAX;
amd_manager->instance = pdata->instance;
amd_manager->mmio = amd_manager->acp_mmio +
- (amd_manager->instance * SDW_MANAGER_REG_OFFSET);
+ (amd_manager->instance * ACP63_SDW_MANAGER_REG_OFFSET);
amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
amd_manager->acp_rev = pdata->acp_rev;
amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
amd_manager->dev = dev;
- amd_manager->bus.ops = &amd_sdw_ops;
- amd_manager->bus.port_ops = &amd_sdw_port_ops;
- amd_manager->bus.compute_params = &amd_sdw_compute_params;
+ amd_manager->bus.ops = &amd_acp63_sdw_ops;
+ amd_manager->bus.port_ops = &amd_acp63_sdw_port_ops;
+ amd_manager->bus.compute_params = &amd_acp63_sdw_compute_params;
amd_manager->bus.clk_stop_timeout = 200;
amd_manager->bus.link_id = amd_manager->instance;
@@ -1288,15 +1296,21 @@ static void amd_sdw_manager_remove(struct platform_device *pdev)
if (pm_runtime_enabled(&pdev->dev))
pm_runtime_disable(&pdev->dev);
- amd_disable_sdw_interrupts(amd_manager);
+ amd_acp63_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
sdw_bus_master_delete(&amd_manager->bus);
- ret = amd_disable_sdw_manager(amd_manager);
+ ret = amd_acp63_disable_sdw_manager(amd_manager);
if (ret)
dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret));
}
+/*
+ * AMD SoundWire clock-stop sequence shared across all ACP revisions.
+ * Differences between revisions in this path are minimal; a switch/case
+ * on acp_rev is sufficient to accommodate any variant-specific additions
+ * needed for future platforms.
+ */
static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
{
u32 val;
@@ -1328,7 +1342,7 @@ static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
return 0;
}
-static int amd_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
+static int amd_acp63_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
{
int ret;
u32 val;
@@ -1421,7 +1435,7 @@ static int __maybe_unused amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- return amd_sdw_host_wake_enable(amd_manager, enable);
+ return amd_acp63_sdw_host_wake_enable(amd_manager, enable);
default:
dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
return -EINVAL;
@@ -1442,7 +1456,7 @@ static int __maybe_unused amd_suspend(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- amd_disable_sdw_interrupts(amd_manager);
+ amd_acp63_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
@@ -1451,13 +1465,13 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_enable_sdw_interrupts(amd_manager);
- amd_sdw_wake_enable(amd_manager, false);
+ amd_acp63_enable_sdw_interrupts(amd_manager);
+ amd_acp63_sdw_wake_enable(amd_manager, false);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
- amd_disable_sdw_interrupts(amd_manager);
+ amd_acp63_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
- amd_sdw_wake_enable(amd_manager, false);
+ amd_acp63_sdw_wake_enable(amd_manager, false);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
if (ret)
return ret;
@@ -1468,12 +1482,12 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_disable_sdw_manager(amd_manager);
+ ret = amd_acp63_disable_sdw_manager(amd_manager);
if (ret)
return ret;
/* System suspend: tear down all pads */
- if (amd_sdw_pad_ctrl_supported(amd_manager)) {
- amd_sdw_pad_ctrl(amd_manager, false);
+ if (amd_acp63_sdw_pad_ctrl_supported(amd_manager)) {
+ amd_acp63_sdw_pad_ctrl(amd_manager, false);
amd_manager->pad_fully_torn_down = true;
}
}
@@ -1483,7 +1497,7 @@ static int __maybe_unused amd_suspend(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
break;
@@ -1507,7 +1521,7 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- amd_disable_sdw_interrupts(amd_manager);
+ amd_acp63_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
@@ -1516,19 +1530,19 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_enable_sdw_interrupts(amd_manager);
- amd_sdw_wake_enable(amd_manager, true);
+ amd_acp63_enable_sdw_interrupts(amd_manager);
+ amd_acp63_sdw_wake_enable(amd_manager, true);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
- amd_disable_sdw_interrupts(amd_manager);
+ amd_acp63_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_disable_sdw_manager(amd_manager);
+ ret = amd_acp63_disable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_sdw_wake_enable(amd_manager, true);
+ amd_acp63_sdw_wake_enable(amd_manager, true);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
if (ret)
return ret;
@@ -1545,7 +1559,7 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
* wake events if wake is armed.
* For ACP 6.3 this call is a no-op.
*/
- amd_sdw_oe_ctrl(amd_manager, false);
+ amd_acp63_sdw_oe_ctrl(amd_manager, false);
}
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
@@ -1553,7 +1567,7 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
if (amd_manager->wake_en_mask) {
@@ -1586,7 +1600,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- ret = amd_sdw_clock_stop_exit(amd_manager);
+ ret = amd_acp63_sdw_clock_stop_exit(amd_manager);
if (ret)
return ret;
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
@@ -1594,10 +1608,10 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
return ret;
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
if (amd_manager->pad_fully_torn_down) {
- amd_sdw_pad_ctrl(amd_manager, true);
+ amd_acp63_sdw_pad_ctrl(amd_manager, true);
amd_manager->pad_fully_torn_down = false;
} else {
- amd_sdw_oe_ctrl(amd_manager, true);
+ amd_acp63_sdw_oe_ctrl(amd_manager, true);
}
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
/*
@@ -1627,17 +1641,17 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
}
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
- ret = amd_sdw_clk_init_ctrl(amd_manager);
+ ret = amd_acp63_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
- ret = amd_init_sdw_manager(amd_manager);
+ ret = amd_acp63_init_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_enable_sdw_interrupts(amd_manager);
- ret = amd_enable_sdw_manager(amd_manager);
+ amd_acp63_enable_sdw_interrupts(amd_manager);
+ ret = amd_acp63_enable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_sdw_set_frameshape(amd_manager);
+ amd_acp63_sdw_set_frameshape(amd_manager);
}
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
@@ -1645,7 +1659,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
+ ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
if (ret)
return ret;
break;
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index ff49e5d08f68..03d3fc2a9072 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -8,7 +8,12 @@
#include <linux/soundwire/sdw_amd.h>
-#define SDW_MANAGER_REG_OFFSET 0xc00
+/*
+ * Register offset between successive SoundWire manager instances.
+ * This value applies to ACP6.3, ACP7.0, ACP7.1, and ACP7.2. A new
+ * revision with a different offset must define its own constant.
+ */
+#define ACP63_SDW_MANAGER_REG_OFFSET 0xc00
#define AMD_SDW_DEFAULT_ROWS 50
#define AMD_SDW_DEFAULT_COLUMNS 10
#define ACP_SW0_WAKE_EN 0x0001458
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 05/11] soundwire: amd: remove unused AMD_SDW_MAX_FREQ_NUM define
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (3 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 04/11] soundwire: amd: rename hardware backend symbols to amd_acp63_*() prefix Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 06/11] soundwire: amd: introduce struct amd_sdw_hw_ops dispatch framework Vijendar Mukunda
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
The AMD_SDW_MAX_FREQ_NUM macro has no users anywhere in the driver.
Remove it as a straightforward cleanup with no functional change.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index 03d3fc2a9072..5e280fb542f8 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -157,7 +157,6 @@
#define AMD_SDW_IRQ_MASK_0TO7 0x77777777
#define AMD_SDW_IRQ_MASK_8TO11 0x000c7777
#define AMD_SDW_IRQ_ERROR_MASK 0xff
-#define AMD_SDW_MAX_FREQ_NUM 1
#define AMD_ACP63_SDW0_MAX_TX_PORTS 3
#define AMD_ACP63_SDW0_MAX_RX_PORTS 3
#define AMD_ACP63_SDW1_MAX_TX_PORTS 1
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 06/11] soundwire: amd: introduce struct amd_sdw_hw_ops dispatch framework
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (4 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 05/11] soundwire: amd: remove unused AMD_SDW_MAX_FREQ_NUM define Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 07/11] soundwire: amd: dispatch irq/work handlers to per-revision functions Vijendar Mukunda
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Add struct amd_sdw_hw_ops, a function-pointer dispatch table for
hardware-revision-specific SoundWire manager operations, including
register-level init/enable/disable, interrupt masking, frameshape,
wake/host-wake, command/response, ping handling, clock-stop exit,
and the PM suspend/resume paths. Add inline dispatch helpers for
each callback and a const hw_ops pointer in struct amd_sdw_manager.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.h | 175 ++++++++++++++++++++++++++++++
include/linux/soundwire/sdw_amd.h | 4 +
2 files changed, 179 insertions(+)
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index 5e280fb542f8..8780759b9c1c 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -264,4 +264,179 @@ static u32 sdw_manager_reg_mask_array[AMD_ACP63_SDW_MAX_MANAGER_COUNT] = {
AMD_SDW0_EXT_INTR_MASK,
AMD_SDW1_EXT_INTR_MASK
};
+
+#define AMD_SDW_HW_OPS(amd_manager, cb) ((amd_manager)->hw_ops->cb)
+
+/**
+ * struct amd_sdw_hw_ops - hardware-revision-specific SoundWire manager ops
+ *
+ * @init_sdw_manager: enable the SoundWire manager hardware and perform bus reset
+ * @enable_sdw_manager: set the manager enable bit and wait for it to assert
+ * @disable_sdw_manager: clear the manager enable bit and wait for it to deassert
+ * @enable_sdw_interrupts: unmask ACP-level and per-manager interrupt bits
+ * @disable_sdw_interrupts: mask ACP-level and per-manager interrupt bits
+ * @set_frameshape: write the row/column frame-shape register
+ * @send_cmd_get_resp: issue an immediate command and return the response;
+ * returns 0 on success with the raw u64 response in @response,
+ * or negative on hardware timeout
+ * @wake_enable: enable or disable the in-band SoundWire wake interrupt
+ * @host_wake_enable: enable or disable the host-side wake interrupt
+ * @set_device_state: write the D0/D3 device power state for the manager instance
+ * @read_and_process_ping_status: issue a PING command and process the slave
+ * attach/detach status; called from the IRQ work path
+ * @process_wake_event: handle an in-band wake notification from a slave
+ * @clk_init_ctrl: set the clock divider and update the frame shape registers
+ * based on the current bus clock frequency
+ * @clock_stop_exit: restart the SoundWire bus clock after a CLK_STOP_MODE
+ * suspend; implementation differs across ACP revisions
+ * @suspend: system-suspend hook; stops the bus and powers down the manager
+ * @suspend_runtime: runtime-suspend hook; same sequence as @suspend
+ * @resume_runtime: runtime-resume hook; reinitialises the manager and restarts
+ * the bus after a POWER_OFF_MODE or CLK_STOP_MODE suspend
+ */
+struct amd_sdw_hw_ops {
+ int (*init_sdw_manager)(struct amd_sdw_manager *amd_manager);
+ int (*enable_sdw_manager)(struct amd_sdw_manager *amd_manager);
+ int (*disable_sdw_manager)(struct amd_sdw_manager *amd_manager);
+ void (*enable_sdw_interrupts)(struct amd_sdw_manager *amd_manager);
+ void (*disable_sdw_interrupts)(struct amd_sdw_manager *amd_manager);
+ void (*set_frameshape)(struct amd_sdw_manager *amd_manager);
+ int (*send_cmd_get_resp)(struct amd_sdw_manager *amd_manager, u32 lower_data,
+ u32 upper_data, u64 *response);
+ void (*wake_enable)(struct amd_sdw_manager *amd_manager, bool enable);
+ int (*host_wake_enable)(struct amd_sdw_manager *amd_manager, bool enable);
+ int (*set_device_state)(struct amd_sdw_manager *amd_manager, u32 target_device_state);
+ void (*read_and_process_ping_status)(struct amd_sdw_manager *amd_manager);
+ void (*process_wake_event)(struct amd_sdw_manager *amd_manager);
+ int (*clk_init_ctrl)(struct amd_sdw_manager *amd_manager);
+ int (*clock_stop_exit)(struct amd_sdw_manager *amd_manager);
+ int (*suspend)(struct amd_sdw_manager *amd_manager);
+ int (*suspend_runtime)(struct amd_sdw_manager *amd_manager);
+ int (*resume_runtime)(struct amd_sdw_manager *amd_manager);
+};
+
+static inline int acp_init_sdw_manager(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->init_sdw_manager)
+ return AMD_SDW_HW_OPS(amd_manager, init_sdw_manager)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_enable_sdw_manager(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->enable_sdw_manager)
+ return AMD_SDW_HW_OPS(amd_manager, enable_sdw_manager)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_disable_sdw_manager(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->disable_sdw_manager)
+ return AMD_SDW_HW_OPS(amd_manager, disable_sdw_manager)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_enable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->enable_sdw_interrupts)
+ AMD_SDW_HW_OPS(amd_manager, enable_sdw_interrupts)(amd_manager);
+}
+
+static inline void acp_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->disable_sdw_interrupts)
+ AMD_SDW_HW_OPS(amd_manager, disable_sdw_interrupts)(amd_manager);
+}
+
+static inline void acp_sdw_set_frameshape(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->set_frameshape)
+ AMD_SDW_HW_OPS(amd_manager, set_frameshape)(amd_manager);
+}
+
+static inline int acp_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager,
+ u32 lower_data, u32 upper_data, u64 *response)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->send_cmd_get_resp)
+ return AMD_SDW_HW_OPS(amd_manager, send_cmd_get_resp)(amd_manager,
+ lower_data, upper_data,
+ response);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_sdw_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->wake_enable)
+ AMD_SDW_HW_OPS(amd_manager, wake_enable)(amd_manager, enable);
+}
+
+static inline int acp_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, bool enable)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->host_wake_enable)
+ return AMD_SDW_HW_OPS(amd_manager, host_wake_enable)(amd_manager, enable);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_sdw_set_device_state(struct amd_sdw_manager *amd_manager,
+ u32 target_device_state)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->set_device_state)
+ return AMD_SDW_HW_OPS(amd_manager, set_device_state)(amd_manager,
+ target_device_state);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->read_and_process_ping_status)
+ AMD_SDW_HW_OPS(amd_manager, read_and_process_ping_status)(amd_manager);
+}
+
+static inline void acp_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->process_wake_event)
+ AMD_SDW_HW_OPS(amd_manager, process_wake_event)(amd_manager);
+}
+
+static inline int acp_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->clk_init_ctrl)
+ return AMD_SDW_HW_OPS(amd_manager, clk_init_ctrl)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
+{
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->clock_stop_exit)
+ return AMD_SDW_HW_OPS(amd_manager, clock_stop_exit)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_sdw_suspend(struct device *dev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
+
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->suspend)
+ return AMD_SDW_HW_OPS(amd_manager, suspend)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_sdw_suspend_runtime(struct device *dev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
+
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->suspend_runtime)
+ return AMD_SDW_HW_OPS(amd_manager, suspend_runtime)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_sdw_resume_runtime(struct device *dev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
+
+ if (amd_manager && amd_manager->hw_ops && amd_manager->hw_ops->resume_runtime)
+ return AMD_SDW_HW_OPS(amd_manager, resume_runtime)(amd_manager);
+ return -EOPNOTSUPP;
+}
+
#endif
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 6174e888b433..64e7cea8bd50 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -55,10 +55,13 @@ struct sdw_amd_dai_runtime {
enum sdw_stream_type stream_type;
};
+struct amd_sdw_hw_ops;
+
/**
* struct amd_sdw_manager - amd manager driver context
* @bus: bus handle
* @dev: linux device
+ * @hw_ops: pointer to hardware-revision-specific dispatch table
* @mmio: SoundWire registers mmio base
* @acp_mmio: acp registers mmio base
* @amd_sdw_irq_thread: SoundWire manager irq workqueue
@@ -94,6 +97,7 @@ struct sdw_amd_dai_runtime {
struct amd_sdw_manager {
struct sdw_bus bus;
struct device *dev;
+ const struct amd_sdw_hw_ops *hw_ops;
void __iomem *mmio;
void __iomem *acp_mmio;
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 07/11] soundwire: amd: dispatch irq/work handlers to per-revision functions
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (5 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 06/11] soundwire: amd: introduce struct amd_sdw_hw_ops dispatch framework Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 08/11] soundwire: amd: populate hw_ops and wire all call sites through acp_*() helpers Vijendar Mukunda
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Split the top-level interrupt and status-update work handlers into a
revision-specific implementation and a generic work_struct entry point.
amd_acp63_sdw_irq_thread() and amd_acp63_sdw_update_slave_status_work()
now hold the ACP6.3-family handler logic and take struct amd_sdw_manager *.
amd_sdw_irq_thread() and amd_sdw_update_slave_status_work() remain the
work_struct handlers registered with INIT_WORK(); each recovers the
manager via container_of() and switches on acp_rev, invoking the
corresponding amd_acp63_*() implementation for ACP6.3, ACP7.0, ACP7.1
and ACP7.2 and emitting dev_err() + return on an unexpected revision.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager.c | 44 ++++++++++++++++++++++++++++-----
1 file changed, 38 insertions(+), 6 deletions(-)
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 52801059b87a..83d5f0e79cf1 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -1067,10 +1067,8 @@ static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager)
dais, num_dais);
}
-static void amd_sdw_update_slave_status_work(struct work_struct *work)
+static void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_manager)
{
- struct amd_sdw_manager *amd_manager =
- container_of(work, struct amd_sdw_manager, amd_sdw_work);
int retry_count = 0;
if (amd_manager->status[0] == SDW_SLAVE_ATTACHED) {
@@ -1103,6 +1101,24 @@ static void amd_sdw_update_slave_status_work(struct work_struct *work)
}
}
+static void amd_sdw_update_slave_status_work(struct work_struct *work)
+{
+ struct amd_sdw_manager *amd_manager =
+ container_of(work, struct amd_sdw_manager, amd_sdw_work);
+
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_acp63_sdw_update_slave_status_work(amd_manager);
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return;
+ }
+}
+
static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
struct amd_sdw_manager *amd_manager)
{
@@ -1134,10 +1150,8 @@ static void amd_acp63_sdw_process_wake_event(struct amd_sdw_manager *amd_manager
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
}
-static void amd_sdw_irq_thread(struct work_struct *work)
+static void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager)
{
- struct amd_sdw_manager *amd_manager =
- container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread);
u32 status_change_8to11;
u32 status_change_0to7;
@@ -1166,6 +1180,24 @@ static void amd_sdw_irq_thread(struct work_struct *work)
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
}
+static void amd_sdw_irq_thread(struct work_struct *work)
+{
+ struct amd_sdw_manager *amd_manager =
+ container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread);
+
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_acp63_sdw_irq_thread(amd_manager);
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return;
+ }
+}
+
int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
{
struct sdw_master_prop *prop;
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 08/11] soundwire: amd: populate hw_ops and wire all call sites through acp_*() helpers
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (6 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 07/11] soundwire: amd: dispatch irq/work handlers to per-revision functions Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 09/11] soundwire: amd: extract shared functions into amd_manager_common.c Vijendar Mukunda
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Populate the amd_sdw_hw_ops dispatch table introduced in the previous
patch and convert every direct amd_acp63_*() call site in the driver to
use the corresponding acp_*() inline helper.
Introduce a static const struct amd_sdw_hw_ops amd_acp63_hw_ops table
that registers all amd_acp63_*() callbacks. Add amd_sdw_hw_init_ops()
which assigns the table pointer based on acp_rev, and call it from
amd_sdw_manager_probe() so hw_ops is set before any dispatch helper is
reached.
Extract the PM suspend/resume logic into amd_acp63_sdw_suspend(),
amd_acp63_sdw_suspend_runtime() and amd_acp63_sdw_resume_runtime()
hw_ops callbacks (taking struct amd_sdw_manager * instead of
struct device *). The existing amd_suspend(), amd_suspend_runtime() and
amd_resume_runtime() device-PM entry points become thin one-liners that
delegate to the corresponding acp_sdw_*() helpers.
The system-suspend CLK_STOP_MODE path in amd_acp63_sdw_suspend() gains
the interrupt-gate sequence already present in the POWER_OFF_MODE paths:
interrupts are disabled and both amd_sdw_irq_thread and amd_sdw_work are
drained before the hardware sequence begins, preventing new work from
being queued during clock stop. amd_sdw_host_wake_enable_by_rev() is
now also called in the CLK_STOP_MODE path so wake signalling is disabled
before clock stop. acp_sdw_wake_enable() is called after clock stop to
match the POWER_OFF_MODE ordering.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_init.c | 5 +-
drivers/soundwire/amd_manager.c | 361 ++++++++++++++++++--------------
2 files changed, 210 insertions(+), 156 deletions(-)
diff --git a/drivers/soundwire/amd_init.c b/drivers/soundwire/amd_init.c
index 2d3b88cdd484..d8e559439cef 100644
--- a/drivers/soundwire/amd_init.c
+++ b/drivers/soundwire/amd_init.c
@@ -16,7 +16,8 @@
#include "amd_init.h"
-static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, u32 acp_rev, struct device *dev)
+static int amd_acp63_enable_sdw_pads(void __iomem *mmio, u32 link_mask,
+ u32 acp_rev, struct device *dev)
{
u32 pad_keeper_mask, pad_pulldown_mask, pad_schmen_mask, pad_oe_mask;
@@ -111,7 +112,7 @@ static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res)
return NULL;
}
- ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->acp_rev, res->parent);
+ ret = amd_acp63_enable_sdw_pads(res->mmio_base, res->link_mask, res->acp_rev, res->parent);
if (ret)
return NULL;
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_manager.c
index 83d5f0e79cf1..820fbc6422e7 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_manager.c
@@ -422,8 +422,7 @@ amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
scp_msg.buf = &msg->addr_page1;
scp_msg.flags = SDW_MSG_FLAG_WRITE;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data,
- &response_buf[0])) {
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[0])) {
dev_err_ratelimited(amd_manager->dev,
"SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -431,8 +430,7 @@ amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
scp_msg.addr = SDW_SCP_ADDRPAGE2;
scp_msg.buf = &msg->addr_page2;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data,
- &response_buf[1])) {
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[1])) {
dev_err_ratelimited(amd_manager->dev,
"SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -505,7 +503,7 @@ static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struc
u32 upper_data = 0, lower_data = 0;
amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);
- if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
msg->dev_num);
return SDW_CMD_TIMEOUT;
@@ -568,7 +566,7 @@ static void amd_acp63_sdw_read_and_process_ping_status(struct amd_sdw_manager *a
u64 response;
mutex_lock(&amd_manager->bus.msg_lock);
- if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ if (acp_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
mutex_unlock(&amd_manager->bus.msg_lock);
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
@@ -588,7 +586,7 @@ static u32 amd_acp63_sdw_read_ping_status(struct sdw_bus *bus)
u32 slave_stat;
lockdep_assert_held(&bus->msg_lock);
- if (amd_acp63_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
+ if (acp_sdw_send_cmd_get_resp(amd_manager, 0, 0, &response)) {
dev_err_ratelimited(amd_manager->dev, "SDW%x ping status timeout\n",
amd_manager->instance);
return amd_manager->ping_slave_stat == U32_MAX ? 0 : amd_manager->ping_slave_stat;
@@ -1091,7 +1089,7 @@ static void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_m
ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7);
writel(AMD_SDW_IRQ_MASK_8TO11, amd_manager->mmio +
ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11);
- amd_acp63_sdw_read_and_process_ping_status(amd_manager);
+ acp_sdw_read_and_process_ping_status(amd_manager);
goto update_status;
} else {
dev_err_ratelimited(amd_manager->dev,
@@ -1164,12 +1162,12 @@ static void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager)
amd_manager->instance, status_change_0to7, status_change_8to11);
if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK) {
- amd_acp63_sdw_process_wake_event(amd_manager);
+ acp_sdw_process_wake_event(amd_manager);
status_change_8to11 &= ~AMD_SDW_WAKE_STAT_MASK;
}
if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
- amd_acp63_sdw_read_and_process_ping_status(amd_manager);
+ acp_sdw_read_and_process_ping_status(amd_manager);
} else {
/* Check for the updated status on peripheral device */
amd_sdw_update_slave_status(status_change_0to7, status_change_8to11, amd_manager);
@@ -1205,17 +1203,17 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
prop = &amd_manager->bus.prop;
if (!prop->hw_disabled) {
- ret = amd_acp63_sdw_clk_init_ctrl(amd_manager);
+ ret = acp_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
- ret = amd_acp63_init_sdw_manager(amd_manager);
+ ret = acp_init_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_acp63_enable_sdw_interrupts(amd_manager);
- ret = amd_acp63_enable_sdw_manager(amd_manager);
+ acp_enable_sdw_interrupts(amd_manager);
+ ret = acp_enable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_acp63_sdw_set_frameshape(amd_manager);
+ acp_sdw_set_frameshape(amd_manager);
}
/* Enable runtime PM */
pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
@@ -1226,117 +1224,6 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
return 0;
}
-static int amd_sdw_manager_probe(struct platform_device *pdev)
-{
- const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
- struct resource *res;
- struct device *dev = &pdev->dev;
- struct sdw_master_prop *prop;
- struct amd_sdw_manager *amd_manager;
- int ret;
-
- amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL);
- if (!amd_manager)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOMEM;
-
- amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res));
- if (!amd_manager->acp_mmio) {
- dev_err(dev, "mmio not found\n");
- return -ENOMEM;
- }
- amd_manager->ping_slave_stat = U32_MAX;
- amd_manager->instance = pdata->instance;
- amd_manager->mmio = amd_manager->acp_mmio +
- (amd_manager->instance * ACP63_SDW_MANAGER_REG_OFFSET);
- amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
- amd_manager->acp_rev = pdata->acp_rev;
- amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
- amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
- amd_manager->dev = dev;
- amd_manager->bus.ops = &amd_acp63_sdw_ops;
- amd_manager->bus.port_ops = &amd_acp63_sdw_port_ops;
- amd_manager->bus.compute_params = &amd_acp63_sdw_compute_params;
- amd_manager->bus.clk_stop_timeout = 200;
- amd_manager->bus.link_id = amd_manager->instance;
-
- /*
- * Due to BIOS compatibility, the two links are exposed within
- * the scope of a single controller. If this changes, the
- * controller_id will have to be updated with drv_data
- * information.
- */
- amd_manager->bus.controller_id = 0;
- dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- switch (amd_manager->instance) {
- case ACP_SDW0:
- amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
- break;
- case ACP_SDW1:
- amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
- break;
- default:
- return -EINVAL;
- }
- break;
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- amd_manager->num_dout_ports = AMD_ACP70_SDW_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP70_SDW_MAX_RX_PORTS;
- break;
- default:
- return -EINVAL;
- }
- amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports;
- amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports,
- sizeof(int), GFP_KERNEL);
- if (!amd_manager->port_offset_map)
- return -ENOMEM;
-
- prop = &amd_manager->bus.prop;
- prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
-
- ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
- if (ret) {
- dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret);
- return ret;
- }
- ret = amd_sdw_register_dais(amd_manager);
- if (ret) {
- dev_err(dev, "CPU DAI registration failed\n");
- sdw_bus_master_delete(&amd_manager->bus);
- return ret;
- }
- INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
- INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
- dev_set_drvdata(dev, amd_manager);
- return 0;
-}
-
-static void amd_sdw_manager_remove(struct platform_device *pdev)
-{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
- int ret;
-
- if (pm_runtime_enabled(&pdev->dev))
- pm_runtime_disable(&pdev->dev);
- amd_acp63_disable_sdw_interrupts(amd_manager);
- cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
- cancel_work_sync(&amd_manager->amd_sdw_work);
- sdw_bus_master_delete(&amd_manager->bus);
- ret = amd_acp63_disable_sdw_manager(amd_manager);
- if (ret)
- dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret));
-}
-
/*
* AMD SoundWire clock-stop sequence shared across all ACP revisions.
* Differences between revisions in this path are minimal; a switch/case
@@ -1467,7 +1354,7 @@ static int __maybe_unused amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- return amd_acp63_sdw_host_wake_enable(amd_manager, enable);
+ return acp_sdw_host_wake_enable(amd_manager, enable);
default:
dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
return -EINVAL;
@@ -1475,9 +1362,8 @@ static int __maybe_unused amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager
return 0;
}
-static int __maybe_unused amd_suspend(struct device *dev)
+static int amd_acp63_sdw_suspend(struct amd_sdw_manager *amd_manager)
{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
@@ -1488,7 +1374,7 @@ static int __maybe_unused amd_suspend(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- amd_acp63_disable_sdw_interrupts(amd_manager);
+ acp_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
@@ -1497,13 +1383,13 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_acp63_enable_sdw_interrupts(amd_manager);
- amd_acp63_sdw_wake_enable(amd_manager, false);
+ acp_enable_sdw_interrupts(amd_manager);
+ acp_sdw_wake_enable(amd_manager, false);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
- amd_acp63_disable_sdw_interrupts(amd_manager);
+ acp_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
- amd_acp63_sdw_wake_enable(amd_manager, false);
+ acp_sdw_wake_enable(amd_manager, false);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
if (ret)
return ret;
@@ -1514,7 +1400,7 @@ static int __maybe_unused amd_suspend(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_acp63_disable_sdw_manager(amd_manager);
+ ret = acp_disable_sdw_manager(amd_manager);
if (ret)
return ret;
/* System suspend: tear down all pads */
@@ -1529,7 +1415,7 @@ static int __maybe_unused amd_suspend(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ ret = acp_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
break;
@@ -1540,9 +1426,8 @@ static int __maybe_unused amd_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused amd_suspend_runtime(struct device *dev)
+static int amd_acp63_sdw_suspend_runtime(struct amd_sdw_manager *amd_manager)
{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
u32 val;
@@ -1553,7 +1438,7 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
return 0;
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- amd_acp63_disable_sdw_interrupts(amd_manager);
+ acp_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
@@ -1562,19 +1447,19 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- amd_acp63_enable_sdw_interrupts(amd_manager);
- amd_acp63_sdw_wake_enable(amd_manager, true);
+ acp_enable_sdw_interrupts(amd_manager);
+ acp_sdw_wake_enable(amd_manager, true);
} else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
- amd_acp63_disable_sdw_interrupts(amd_manager);
+ acp_disable_sdw_interrupts(amd_manager);
cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
cancel_work_sync(&amd_manager->amd_sdw_work);
ret = amd_sdw_clock_stop(amd_manager);
if (ret)
return ret;
- ret = amd_acp63_disable_sdw_manager(amd_manager);
+ ret = acp_disable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_acp63_sdw_wake_enable(amd_manager, true);
+ acp_sdw_wake_enable(amd_manager, true);
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, true);
if (ret)
return ret;
@@ -1599,7 +1484,7 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
+ ret = acp_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D3);
if (ret)
return ret;
if (amd_manager->wake_en_mask) {
@@ -1618,9 +1503,8 @@ static int __maybe_unused amd_suspend_runtime(struct device *dev)
return 0;
}
-static int __maybe_unused amd_resume_runtime(struct device *dev)
+static int amd_acp63_sdw_resume_runtime(struct amd_sdw_manager *amd_manager)
{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
struct sdw_bus *bus = &amd_manager->bus;
int ret;
u32 val;
@@ -1632,7 +1516,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
- ret = amd_acp63_sdw_clock_stop_exit(amd_manager);
+ ret = acp_sdw_clock_stop_exit(amd_manager);
if (ret)
return ret;
ret = amd_sdw_host_wake_enable_by_rev(amd_manager, false);
@@ -1673,17 +1557,17 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
}
}
sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
- ret = amd_acp63_sdw_clk_init_ctrl(amd_manager);
+ ret = acp_sdw_clk_init_ctrl(amd_manager);
if (ret)
return ret;
- ret = amd_acp63_init_sdw_manager(amd_manager);
+ ret = acp_init_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_acp63_enable_sdw_interrupts(amd_manager);
- ret = amd_acp63_enable_sdw_manager(amd_manager);
+ acp_enable_sdw_interrupts(amd_manager);
+ ret = acp_enable_sdw_manager(amd_manager);
if (ret)
return ret;
- amd_acp63_sdw_set_frameshape(amd_manager);
+ acp_sdw_set_frameshape(amd_manager);
}
switch (amd_manager->acp_rev) {
case ACP63_PCI_REV_ID:
@@ -1691,7 +1575,7 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
case ACP70_PCI_REV_ID:
case ACP71_PCI_REV_ID:
case ACP72_PCI_REV_ID:
- ret = amd_acp63_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
+ ret = acp_sdw_set_device_state(amd_manager, AMD_SDW_DEVICE_STATE_D0);
if (ret)
return ret;
break;
@@ -1702,6 +1586,175 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
return 0;
}
+static int __maybe_unused amd_suspend(struct device *dev)
+{
+ return acp_sdw_suspend(dev);
+}
+
+static int __maybe_unused amd_suspend_runtime(struct device *dev)
+{
+ return acp_sdw_suspend_runtime(dev);
+}
+
+static int __maybe_unused amd_resume_runtime(struct device *dev)
+{
+ return acp_sdw_resume_runtime(dev);
+}
+
+static int amd_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager);
+
+static int amd_sdw_manager_probe(struct platform_device *pdev)
+{
+ const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct sdw_master_prop *prop;
+ struct amd_sdw_manager *amd_manager;
+ int ret;
+
+ amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL);
+ if (!amd_manager)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOMEM;
+
+ amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res));
+ if (!amd_manager->acp_mmio) {
+ dev_err(dev, "mmio not found\n");
+ return -ENOMEM;
+ }
+ amd_manager->ping_slave_stat = U32_MAX;
+ amd_manager->instance = pdata->instance;
+ amd_manager->mmio = amd_manager->acp_mmio +
+ (amd_manager->instance * ACP63_SDW_MANAGER_REG_OFFSET);
+ amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
+ amd_manager->acp_rev = pdata->acp_rev;
+ amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
+ amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
+ amd_manager->dev = dev;
+ amd_manager->bus.ops = &amd_acp63_sdw_ops;
+ amd_manager->bus.port_ops = &amd_acp63_sdw_port_ops;
+ amd_manager->bus.compute_params = &amd_acp63_sdw_compute_params;
+ amd_manager->bus.clk_stop_timeout = 200;
+ amd_manager->bus.link_id = amd_manager->instance;
+ ret = amd_sdw_hw_init_ops(amd_manager);
+ if (ret) {
+ dev_err(dev, "Failed to initialize hw ops (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Due to BIOS compatibility, the two links are exposed within
+ * the scope of a single controller. If this changes, the
+ * controller_id will have to be updated with drv_data
+ * information.
+ */
+ amd_manager->bus.controller_id = 0;
+ dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
+ break;
+ case ACP_SDW1:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_manager->num_dout_ports = AMD_ACP70_SDW_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP70_SDW_MAX_RX_PORTS;
+ break;
+ default:
+ /* unreachable: amd_sdw_hw_init_ops() rejects unknown revisions earlier in probe */
+ return -EINVAL;
+ }
+ amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports;
+ amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports,
+ sizeof(int), GFP_KERNEL);
+ if (!amd_manager->port_offset_map)
+ return -ENOMEM;
+
+ prop = &amd_manager->bus.prop;
+ prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
+
+ ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
+ if (ret) {
+ dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret);
+ return ret;
+ }
+ ret = amd_sdw_register_dais(amd_manager);
+ if (ret) {
+ dev_err(dev, "CPU DAI registration failed\n");
+ sdw_bus_master_delete(&amd_manager->bus);
+ return ret;
+ }
+ INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
+ INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
+ dev_set_drvdata(dev, amd_manager);
+ return 0;
+}
+
+static void amd_sdw_manager_remove(struct platform_device *pdev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (pm_runtime_enabled(&pdev->dev))
+ pm_runtime_disable(&pdev->dev);
+ acp_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
+ cancel_work_sync(&amd_manager->amd_sdw_work);
+ sdw_bus_master_delete(&amd_manager->bus);
+ ret = acp_disable_sdw_manager(amd_manager);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret));
+}
+
+static const struct amd_sdw_hw_ops amd_acp63_hw_ops = {
+ .init_sdw_manager = amd_acp63_init_sdw_manager,
+ .enable_sdw_manager = amd_acp63_enable_sdw_manager,
+ .disable_sdw_manager = amd_acp63_disable_sdw_manager,
+ .enable_sdw_interrupts = amd_acp63_enable_sdw_interrupts,
+ .disable_sdw_interrupts = amd_acp63_disable_sdw_interrupts,
+ .set_frameshape = amd_acp63_sdw_set_frameshape,
+ .send_cmd_get_resp = amd_acp63_sdw_send_cmd_get_resp,
+ .wake_enable = amd_acp63_sdw_wake_enable,
+ .host_wake_enable = amd_acp63_sdw_host_wake_enable,
+ .set_device_state = amd_acp63_sdw_set_device_state,
+ .read_and_process_ping_status = amd_acp63_sdw_read_and_process_ping_status,
+ .process_wake_event = amd_acp63_sdw_process_wake_event,
+ .clk_init_ctrl = amd_acp63_sdw_clk_init_ctrl,
+ .clock_stop_exit = amd_acp63_sdw_clock_stop_exit,
+ .suspend = amd_acp63_sdw_suspend,
+ .suspend_runtime = amd_acp63_sdw_suspend_runtime,
+ .resume_runtime = amd_acp63_sdw_resume_runtime,
+};
+
+static int amd_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager)
+{
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_manager->hw_ops = &amd_acp63_hw_ops;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static const struct dev_pm_ops amd_pm = {
.prepare = amd_pm_prepare,
SET_SYSTEM_SLEEP_PM_OPS(amd_suspend, amd_resume_runtime)
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 09/11] soundwire: amd: extract shared functions into amd_manager_common.c
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (7 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 08/11] soundwire: amd: populate hw_ops and wire all call sites through acp_*() helpers Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 10/11] soundwire: amd: guard DAI PCM caps by acp_rev in register_dais() Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 11/11] soundwire: amd: guard clock-stop register access by acp_rev Vijendar Mukunda
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Split amd_manager.c along revision boundaries:
- Move all revision-independent amd_sdw_*() functions - message transfer
helpers, ping and slave-status processing, DAI ops and registration,
clock-stop sequence, PM prepare/suspend/resume wrappers, the
amd_sdw_hw_init_ops() dispatch wrapper, and the platform_driver
infrastructure - into a new amd_manager_common.c.
- Rename amd_manager.c to amd_acp63_manager.c since it now contains
only the amd_acp63_*() register-level functions and the
amd_acp63_hw_ops table.
- Update the Makefile to build amd_manager_common.o and
amd_acp63_manager.o in place of amd_manager.o.
- Add cross-file prototypes to amd_manager.h for symbols shared between
the two compilation units.
- Drop static from amd_acp63_sdw_ops, amd_acp63_sdw_port_ops,
amd_acp63_sdw_compute_params, amd_acp63_sdw_irq_thread,
amd_acp63_sdw_update_slave_status_work, and amd_acp63_sdw_hw_init_ops,
which amd_manager_common.c now calls.
- Move the revision-specific register-map tables
(struct sdw_manager_dp_reg,
the acp63/acp70 dp_reg arrays, and sdw_manager_reg_mask_array) from
amd_manager.h into amd_acp63_manager.c to avoid -Wunused-variable when
amd_manager_common.c includes the shared header.
No functional change.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/Makefile | 2 +-
.../{amd_manager.c => amd_acp63_manager.c} | 839 ++----------------
drivers/soundwire/amd_manager.h | 84 +-
drivers/soundwire/amd_manager_common.c | 786 ++++++++++++++++
4 files changed, 882 insertions(+), 829 deletions(-)
rename drivers/soundwire/{amd_manager.c => amd_acp63_manager.c} (59%)
create mode 100644 drivers/soundwire/amd_manager_common.c
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index e80a2c2cf3e7..1011599c20e7 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -20,7 +20,7 @@ soundwire-bus-y += irq.o
endif
#AMD driver
-soundwire-amd-y := amd_init.o amd_manager.o
+soundwire-amd-y := amd_init.o amd_manager_common.o amd_acp63_manager.o
obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o
#Cadence Objs
diff --git a/drivers/soundwire/amd_manager.c b/drivers/soundwire/amd_acp63_manager.c
similarity index 59%
rename from drivers/soundwire/amd_manager.c
rename to drivers/soundwire/amd_acp63_manager.c
index 820fbc6422e7..320cd26545f9 100644
--- a/drivers/soundwire/amd_manager.c
+++ b/drivers/soundwire/amd_acp63_manager.c
@@ -1,12 +1,11 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
- * SoundWire AMD Manager driver
+ * SoundWire AMD ACP6.3 (and compatible) Manager hardware ops
*
- * Copyright 2023-24 Advanced Micro Devices, Inc.
+ * Copyright 2023-24, 2026 Advanced Micro Devices, Inc.
*/
#include <linux/completion.h>
-#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/jiffies.h>
@@ -23,10 +22,76 @@
#include "amd_init.h"
#include "amd_manager.h"
-#define DRV_NAME "amd_sdw_manager"
-
#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)
+struct sdw_manager_dp_reg {
+ u32 frame_fmt_reg;
+ u32 sample_int_reg;
+ u32 hctrl_dp0_reg;
+ u32 offset_reg;
+ u32 lane_ctrl_ch_en_reg;
+};
+
+/*
+ * SDW0 Manager instance registers 6 CPU DAI (3 TX & 3 RX Ports)
+ * whereas SDW1 Manager Instance registers 2 CPU DAI (one TX & one RX port)
+ * Below is the CPU DAI <->Manager port number mapping
+ * i.e SDW0 Pin0 -> port number 0 -> AUDIO0 TX
+ * SDW0 Pin1 -> Port number 1 -> AUDIO1 TX
+ * SDW0 Pin2 -> Port number 2 -> AUDIO2 TX
+ * SDW0 Pin3 -> port number 3 -> AUDIO0 RX
+ * SDW0 Pin4 -> Port number 4 -> AUDIO1 RX
+ * SDW0 Pin5 -> Port number 5 -> AUDIO2 RX
+ * Whereas for SDW1 instance
+ * SDW1 Pin0 -> port number 0 -> AUDIO1 TX
+ * SDW1 Pin1 -> Port number 1 -> AUDIO1 RX
+ * Same mapping should be used for programming DMA controller registers in SoundWire DMA driver.
+ * i.e if AUDIO0 TX channel is selected then we need to use AUDIO0 TX registers for DMA programming
+ * in SoundWire DMA driver.
+ */
+
+static struct sdw_manager_dp_reg acp63_sdw0_dp_reg[AMD_ACP63_SDW0_MAX_DAI] = {
+ {ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
+ ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
+ ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO2_TX_FRAME_FORMAT, ACP_SW_AUDIO2_TX_SAMPLEINTERVAL, ACP_SW_AUDIO2_TX_HCTRL,
+ ACP_SW_AUDIO2_TX_OFFSET, ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO0_RX_FRAME_FORMAT, ACP_SW_AUDIO0_RX_SAMPLEINTERVAL, ACP_SW_AUDIO0_RX_HCTRL_DP0,
+ ACP_SW_AUDIO0_RX_OFFSET_DP0, ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
+ ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO2_RX_FRAME_FORMAT, ACP_SW_AUDIO2_RX_SAMPLEINTERVAL, ACP_SW_AUDIO2_RX_HCTRL,
+ ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
+};
+
+static struct sdw_manager_dp_reg acp63_sdw1_dp_reg[AMD_ACP63_SDW1_MAX_DAI] = {
+ {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
+ ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
+ ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
+};
+
+static struct sdw_manager_dp_reg acp70_sdw_dp_reg[AMD_ACP70_SDW_MAX_DAI] = {
+ {ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
+ ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
+ ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO2_TX_FRAME_FORMAT, ACP_SW_AUDIO2_TX_SAMPLEINTERVAL, ACP_SW_AUDIO2_TX_HCTRL,
+ ACP_SW_AUDIO2_TX_OFFSET, ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO0_RX_FRAME_FORMAT, ACP_SW_AUDIO0_RX_SAMPLEINTERVAL, ACP_SW_AUDIO0_RX_HCTRL_DP0,
+ ACP_SW_AUDIO0_RX_OFFSET_DP0, ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
+ ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0},
+ {ACP_SW_AUDIO2_RX_FRAME_FORMAT, ACP_SW_AUDIO2_RX_SAMPLEINTERVAL, ACP_SW_AUDIO2_RX_HCTRL,
+ ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
+};
+
+static u32 sdw_manager_reg_mask_array[AMD_ACP63_SDW_MAX_MANAGER_COUNT] = {
+ AMD_SDW0_EXT_INTR_MASK,
+ AMD_SDW1_EXT_INTR_MASK
+};
+
static int amd_acp63_sdw_clk_init_ctrl(struct amd_sdw_manager *amd_manager)
{
struct sdw_bus *bus = &amd_manager->bus;
@@ -139,7 +204,7 @@ static void amd_acp63_disable_sdw_interrupts(struct amd_sdw_manager *amd_manager
/*
* Clear the flag before updating hardware masks so that any IRQ
* that fires during the mask-register writes sees the flag as false
- * and does not queue new work. Once the mask registers are written,
+ * and does not queue new work. Once the mask registers are written,
* the ACP hardware will not assert any new SDW interrupt, so no
* synchronize_irq() is required after this function returns.
*/
@@ -332,32 +397,6 @@ static int amd_acp63_sdw_host_wake_enable(struct amd_sdw_manager *amd_manager, b
return 0;
}
-static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg,
- int cmd_offset)
-{
- u32 upper_data;
- u32 lower_data = 0;
- u16 addr;
- u8 upper_addr, lower_addr;
- u8 data = 0;
-
- addr = msg->addr + cmd_offset;
- upper_addr = (addr & 0xFF00) >> 8;
- lower_addr = addr & 0xFF;
-
- if (msg->flags == SDW_MSG_FLAG_WRITE)
- data = msg->buf[cmd_offset];
-
- upper_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num);
- upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, msg->flags + 2);
- upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, upper_addr);
- lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, lower_addr);
- lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data);
-
- *upper_word = upper_data;
- *lower_word = lower_data;
-}
-
static int amd_acp63_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager, u32 lower_data,
u32 upper_data, u64 *response)
{
@@ -409,158 +448,6 @@ static int amd_acp63_sdw_send_cmd_get_resp(struct amd_sdw_manager *amd_manager,
return 0;
}
-static enum sdw_command_response
-amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
-{
- struct sdw_msg scp_msg = {0};
- u64 response_buf[2] = {0};
- u32 upper_data = 0, lower_data = 0;
- int index;
-
- scp_msg.dev_num = msg->dev_num;
- scp_msg.addr = SDW_SCP_ADDRPAGE1;
- scp_msg.buf = &msg->addr_page1;
- scp_msg.flags = SDW_MSG_FLAG_WRITE;
- amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[0])) {
- dev_err_ratelimited(amd_manager->dev,
- "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
- return SDW_CMD_TIMEOUT;
- }
- scp_msg.addr = SDW_SCP_ADDRPAGE2;
- scp_msg.buf = &msg->addr_page2;
- amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
- if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[1])) {
- dev_err_ratelimited(amd_manager->dev,
- "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
- return SDW_CMD_TIMEOUT;
- }
-
- for (index = 0; index < 2; index++) {
- if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
- if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
- dev_err_ratelimited(amd_manager->dev,
- "SCP_addrpage NACKed for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_FAIL;
- }
- dev_dbg_ratelimited(amd_manager->dev, "SCP_addrpage ignored for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_IGNORED;
- }
- }
- return SDW_CMD_OK;
-}
-
-static int amd_prep_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
-{
- int ret;
-
- if (msg->page) {
- ret = amd_program_scp_addr(amd_manager, msg);
- if (ret) {
- msg->len = 0;
- return ret;
- }
- }
- switch (msg->flags) {
- case SDW_MSG_FLAG_READ:
- case SDW_MSG_FLAG_WRITE:
- break;
- default:
- dev_err(amd_manager->dev, "Invalid msg cmd: %d\n", msg->flags);
- return -EINVAL;
- }
- return 0;
-}
-
-static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *amd_manager,
- struct sdw_msg *msg, u64 response,
- int offset)
-{
- if (response & AMD_SDW_MCP_RESP_ACK) {
- if (msg->flags == SDW_MSG_FLAG_READ)
- msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
- } else {
- if (response & AMD_SDW_MCP_RESP_NACK) {
- dev_err_ratelimited(amd_manager->dev,
- "command response NACK received for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_FAIL;
- }
- dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_IGNORED;
- }
- return SDW_CMD_OK;
-}
-
-/* AMD SoundWire bus helper shared across all ACP revisions. */
-static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg,
- int cmd_offset)
-{
- u64 response;
- u32 upper_data = 0, lower_data = 0;
-
- amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);
- if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
- dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
- msg->dev_num);
- return SDW_CMD_TIMEOUT;
- }
- return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);
-}
-
-/* AMD SoundWire bus helper shared across all ACP revisions. */
-static enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
-{
- struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
- int ret, i;
-
- ret = amd_prep_msg(amd_manager, msg);
- if (ret)
- return SDW_CMD_FAIL_OTHER;
- for (i = 0; i < msg->len; i++) {
- ret = _amd_sdw_xfer_msg(amd_manager, msg, i);
- if (ret)
- return ret;
- }
- return SDW_CMD_OK;
-}
-
-/* AMD SoundWire bus helper shared across all ACP revisions. */
-static void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status)
-{
- switch (status) {
- case SDW_SLAVE_ATTACHED:
- case SDW_SLAVE_UNATTACHED:
- case SDW_SLAVE_ALERT:
- amd_manager->status[index] = status;
- break;
- default:
- amd_manager->status[index] = SDW_SLAVE_RESERVED;
- break;
- }
-}
-
-/* AMD SoundWire bus helper shared across all ACP revisions. */
-static void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager)
-{
- u64 slave_stat;
- u32 val;
- u16 dev_index;
-
- /* slave status response */
- slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
- slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
- dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat);
- for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
- val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK;
- dev_dbg(amd_manager->dev, "val:0x%x\n", val);
- amd_sdw_fill_slave_status(amd_manager, dev_index, val);
- }
-}
-
static void amd_acp63_sdw_read_and_process_ping_status(struct amd_sdw_manager *amd_manager)
{
u64 response;
@@ -599,7 +486,7 @@ static u32 amd_acp63_sdw_read_ping_status(struct sdw_bus *bus)
return slave_stat;
}
-static int amd_acp63_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
+int amd_acp63_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream)
{
struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
struct sdw_transport_data t_data = {0};
@@ -844,228 +731,19 @@ static int amd_acp63_sdw_port_enable(struct sdw_bus *bus,
return 0;
}
-static int sdw_master_read_amd_prop(struct sdw_bus *bus)
-{
- struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
- struct fwnode_handle *link;
- struct sdw_master_prop *prop;
- u32 quirk_mask = 0;
- u32 wake_en_mask = 0;
- u32 power_mode_mask = 0;
- char name[32];
-
- prop = &bus->prop;
- /* Find manager handle */
- snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", bus->link_id);
- link = device_get_named_child_node(bus->dev, name);
- if (!link) {
- dev_err(bus->dev, "Manager node %s not found\n", name);
- return -EIO;
- }
- fwnode_property_read_u32(link, "amd-sdw-enable", &quirk_mask);
- if (!(quirk_mask & AMD_SDW_QUIRK_MASK_BUS_ENABLE))
- prop->hw_disabled = true;
- prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
- SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
-
- fwnode_property_read_u32(link, "amd-sdw-wakeup-enable", &wake_en_mask);
- amd_manager->wake_en_mask = wake_en_mask;
- fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
- amd_manager->power_mode_mask = power_mode_mask;
-
- fwnode_handle_put(link);
-
- return 0;
-}
-
-/* AMD SoundWire bus helper shared across all ACP revisions. */
-static int amd_prop_read(struct sdw_bus *bus)
-{
- sdw_master_read_prop(bus);
- sdw_master_read_amd_prop(bus);
- return 0;
-}
-
-static const struct sdw_master_port_ops amd_acp63_sdw_port_ops = {
+const struct sdw_master_port_ops amd_acp63_sdw_port_ops = {
.dpn_set_port_params = amd_acp63_sdw_port_params,
.dpn_set_port_transport_params = amd_acp63_sdw_transport_params,
.dpn_port_enable_ch = amd_acp63_sdw_port_enable,
};
-static const struct sdw_master_ops amd_acp63_sdw_ops = {
+const struct sdw_master_ops amd_acp63_sdw_ops = {
.read_prop = amd_prop_read,
.xfer_msg = amd_sdw_xfer_msg,
.read_ping_status = amd_acp63_sdw_read_ping_status,
};
-static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
- struct sdw_amd_dai_runtime *dai_runtime;
- struct sdw_stream_config sconfig;
- int ch, dir;
- int ret;
-
- dai_runtime = amd_manager->dai_runtime_array[dai->id];
- if (!dai_runtime)
- return -EIO;
-
- ch = params_channels(params);
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- dir = SDW_DATA_DIR_RX;
- else
- dir = SDW_DATA_DIR_TX;
- dev_dbg(amd_manager->dev, "dir:%d dai->id:0x%x\n", dir, dai->id);
-
- sconfig.direction = dir;
- sconfig.ch_count = ch;
- sconfig.frame_rate = params_rate(params);
- sconfig.type = dai_runtime->stream_type;
-
- sconfig.bps = snd_pcm_format_width(params_format(params));
-
- /* Port configuration */
- struct sdw_port_config *pconfig __free(kfree) = kzalloc_obj(*pconfig);
- if (!pconfig)
- return -ENOMEM;
-
- pconfig->num = dai->id;
- pconfig->ch_mask = (1 << ch) - 1;
- ret = sdw_stream_add_master(&amd_manager->bus, &sconfig,
- pconfig, 1, dai_runtime->stream);
- if (ret)
- dev_err(amd_manager->dev, "add manager to stream failed:%d\n", ret);
-
- return ret;
-}
-
-static int amd_sdw_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
-{
- struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
- struct sdw_amd_dai_runtime *dai_runtime;
- int ret;
-
- dai_runtime = amd_manager->dai_runtime_array[dai->id];
- if (!dai_runtime)
- return -EIO;
-
- ret = sdw_stream_remove_master(&amd_manager->bus, dai_runtime->stream);
- if (ret < 0)
- dev_err(dai->dev, "remove manager from stream %s failed: %d\n",
- dai_runtime->stream->name, ret);
- return ret;
-}
-
-static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
-{
- struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
- struct sdw_amd_dai_runtime *dai_runtime;
-
- dai_runtime = amd_manager->dai_runtime_array[dai->id];
- if (stream) {
- /* first paranoia check */
- if (dai_runtime) {
- dev_err(dai->dev, "dai_runtime already allocated for dai %s\n", dai->name);
- return -EINVAL;
- }
-
- /* allocate and set dai_runtime info */
- dai_runtime = kzalloc_obj(*dai_runtime);
- if (!dai_runtime)
- return -ENOMEM;
-
- dai_runtime->stream_type = SDW_STREAM_PCM;
- dai_runtime->bus = &amd_manager->bus;
- dai_runtime->stream = stream;
- amd_manager->dai_runtime_array[dai->id] = dai_runtime;
- } else {
- /* second paranoia check */
- if (!dai_runtime) {
- dev_err(dai->dev, "dai_runtime not allocated for dai %s\n", dai->name);
- return -EINVAL;
- }
-
- /* for NULL stream we release allocated dai_runtime */
- kfree(dai_runtime);
- amd_manager->dai_runtime_array[dai->id] = NULL;
- }
- return 0;
-}
-
-static int amd_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
-{
- return amd_set_sdw_stream(dai, stream, direction);
-}
-
-static void *amd_get_sdw_stream(struct snd_soc_dai *dai, int direction)
-{
- struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
- struct sdw_amd_dai_runtime *dai_runtime;
-
- dai_runtime = amd_manager->dai_runtime_array[dai->id];
- if (!dai_runtime)
- return ERR_PTR(-EINVAL);
-
- return dai_runtime->stream;
-}
-
-static const struct snd_soc_dai_ops amd_sdw_dai_ops = {
- .hw_params = amd_sdw_hw_params,
- .hw_free = amd_sdw_hw_free,
- .set_stream = amd_pcm_set_sdw_stream,
- .get_stream = amd_get_sdw_stream,
-};
-
-static const struct snd_soc_component_driver amd_sdw_dai_component = {
- .name = "soundwire",
-};
-
-static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager)
-{
- struct sdw_amd_dai_runtime **dai_runtime_array;
- struct snd_soc_dai_driver *dais;
- struct snd_soc_pcm_stream *stream;
- struct device *dev;
- int i, num_dais;
-
- dev = amd_manager->dev;
- num_dais = amd_manager->num_dout_ports + amd_manager->num_din_ports;
- dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
- if (!dais)
- return -ENOMEM;
-
- dai_runtime_array = devm_kcalloc(dev, num_dais,
- sizeof(struct sdw_amd_dai_runtime *),
- GFP_KERNEL);
- if (!dai_runtime_array)
- return -ENOMEM;
- amd_manager->dai_runtime_array = dai_runtime_array;
- for (i = 0; i < num_dais; i++) {
- dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", amd_manager->instance,
- i);
- if (!dais[i].name)
- return -ENOMEM;
- if (i < amd_manager->num_dout_ports)
- stream = &dais[i].playback;
- else
- stream = &dais[i].capture;
-
- stream->channels_min = 2;
- stream->channels_max = 2;
- stream->rates = SNDRV_PCM_RATE_48000;
- stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
-
- dais[i].ops = &amd_sdw_dai_ops;
- dais[i].id = i;
- }
-
- return devm_snd_soc_register_component(dev, &amd_sdw_dai_component,
- dais, num_dais);
-}
-
-static void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_manager)
+void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_manager)
{
int retry_count = 0;
@@ -1099,48 +777,6 @@ static void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_m
}
}
-static void amd_sdw_update_slave_status_work(struct work_struct *work)
-{
- struct amd_sdw_manager *amd_manager =
- container_of(work, struct amd_sdw_manager, amd_sdw_work);
-
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- amd_acp63_sdw_update_slave_status_work(amd_manager);
- break;
- default:
- dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
- return;
- }
-}
-
-static void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
- struct amd_sdw_manager *amd_manager)
-{
- u64 slave_stat;
- u32 val;
- int dev_index;
-
- if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
- memset(amd_manager->status, 0, sizeof(amd_manager->status));
- slave_stat = status_change_0to7;
- slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
- dev_dbg(amd_manager->dev, "status_change_0to7:0x%x status_change_8to11:0x%x\n",
- status_change_0to7, status_change_8to11);
- if (slave_stat) {
- for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
- if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) {
- val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) &
- AMD_SDW_MCP_SLAVE_STATUS_MASK;
- amd_sdw_fill_slave_status(amd_manager, dev_index, val);
- }
- }
- }
-}
-
static void amd_acp63_sdw_process_wake_event(struct amd_sdw_manager *amd_manager)
{
dev_dbg(amd_manager->dev, "SoundWire Wake event reported\n");
@@ -1148,7 +784,7 @@ static void amd_acp63_sdw_process_wake_event(struct amd_sdw_manager *amd_manager
writel(0x00, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
}
-static void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager)
+void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager)
{
u32 status_change_8to11;
u32 status_change_0to7;
@@ -1178,89 +814,6 @@ static void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager)
writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_0TO7);
}
-static void amd_sdw_irq_thread(struct work_struct *work)
-{
- struct amd_sdw_manager *amd_manager =
- container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread);
-
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- amd_acp63_sdw_irq_thread(amd_manager);
- break;
- default:
- dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
- return;
- }
-}
-
-int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
-{
- struct sdw_master_prop *prop;
- int ret;
-
- prop = &amd_manager->bus.prop;
- if (!prop->hw_disabled) {
- ret = acp_sdw_clk_init_ctrl(amd_manager);
- if (ret)
- return ret;
- ret = acp_init_sdw_manager(amd_manager);
- if (ret)
- return ret;
- acp_enable_sdw_interrupts(amd_manager);
- ret = acp_enable_sdw_manager(amd_manager);
- if (ret)
- return ret;
- acp_sdw_set_frameshape(amd_manager);
- }
- /* Enable runtime PM */
- pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
- pm_runtime_use_autosuspend(amd_manager->dev);
- pm_runtime_mark_last_busy(amd_manager->dev);
- pm_runtime_set_active(amd_manager->dev);
- pm_runtime_enable(amd_manager->dev);
- return 0;
-}
-
-/*
- * AMD SoundWire clock-stop sequence shared across all ACP revisions.
- * Differences between revisions in this path are minimal; a switch/case
- * on acp_rev is sufficient to accommodate any variant-specific additions
- * needed for future platforms.
- */
-static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
-{
- u32 val;
- int ret;
-
- ret = sdw_bus_prep_clk_stop(&amd_manager->bus);
- if (ret < 0 && ret != -ENODATA) {
- dev_err(amd_manager->dev, "prepare clock stop failed %d", ret);
- return 0;
- }
- ret = sdw_bus_clk_stop(&amd_manager->bus);
- if (ret < 0 && ret != -ENODATA) {
- dev_err(amd_manager->dev, "bus clock stop failed %d", ret);
- return 0;
- }
-
- ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
- (val & AMD_SDW_CLK_STOP_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT);
- if (ret) {
- dev_err(amd_manager->dev, "SDW%x clock stop failed\n", amd_manager->instance);
- return 0;
- }
-
- amd_manager->clk_stopped = true;
- if (amd_manager->wake_en_mask)
- writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
-
- dev_dbg(amd_manager->dev, "SDW%x clock stop successful\n", amd_manager->instance);
- return 0;
-}
-
static int amd_acp63_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
{
int ret;
@@ -1290,78 +843,6 @@ static int amd_acp63_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager)
return 0;
}
-static int amd_resume_child_device(struct device *dev, void *data)
-{
- struct sdw_slave *slave = dev_to_sdw_dev(dev);
- int ret;
-
- if (!slave->probed) {
- dev_dbg(dev, "skipping device, no probed driver\n");
- return 0;
- }
- if (!slave->dev_num_sticky) {
- dev_dbg(dev, "skipping device, never detected on bus\n");
- return 0;
- }
- ret = pm_request_resume(dev);
- if (ret < 0) {
- dev_err(dev, "pm_request_resume failed: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int __maybe_unused amd_pm_prepare(struct device *dev)
-{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
- struct sdw_bus *bus = &amd_manager->bus;
- int ret;
-
- if (bus->prop.hw_disabled) {
- dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n",
- bus->link_id);
- return 0;
- }
- /*
- * When multiple peripheral devices connected over the same link, if SoundWire manager
- * device is not in runtime suspend state, observed that device alerts are missing
- * without pm_prepare on AMD platforms in clockstop mode0.
- */
- if (amd_manager->power_mode_mask) {
- ret = pm_runtime_resume(dev);
- if (ret < 0) {
- dev_err(bus->dev, "pm_runtime_resume failed: %d\n", ret);
- return 0;
- }
- }
- /* To force peripheral devices to system level suspend state, resume the devices
- * from runtime suspend state first. Without that unable to dispatch the alert
- * status to peripheral driver during system level resume as they are in runtime
- * suspend state.
- */
- ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
- if (ret < 0)
- dev_err(dev, "amd_resume_child_device failed: %d\n", ret);
- return 0;
-}
-
-static int __maybe_unused amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager *amd_manager,
- bool enable)
-{
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- break;
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- return acp_sdw_host_wake_enable(amd_manager, enable);
- default:
- dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
- return -EINVAL;
- }
- return 0;
-}
-
static int amd_acp63_sdw_suspend(struct amd_sdw_manager *amd_manager)
{
struct sdw_bus *bus = &amd_manager->bus;
@@ -1586,140 +1067,6 @@ static int amd_acp63_sdw_resume_runtime(struct amd_sdw_manager *amd_manager)
return 0;
}
-static int __maybe_unused amd_suspend(struct device *dev)
-{
- return acp_sdw_suspend(dev);
-}
-
-static int __maybe_unused amd_suspend_runtime(struct device *dev)
-{
- return acp_sdw_suspend_runtime(dev);
-}
-
-static int __maybe_unused amd_resume_runtime(struct device *dev)
-{
- return acp_sdw_resume_runtime(dev);
-}
-
-static int amd_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager);
-
-static int amd_sdw_manager_probe(struct platform_device *pdev)
-{
- const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
- struct resource *res;
- struct device *dev = &pdev->dev;
- struct sdw_master_prop *prop;
- struct amd_sdw_manager *amd_manager;
- int ret;
-
- amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL);
- if (!amd_manager)
- return -ENOMEM;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENOMEM;
-
- amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res));
- if (!amd_manager->acp_mmio) {
- dev_err(dev, "mmio not found\n");
- return -ENOMEM;
- }
- amd_manager->ping_slave_stat = U32_MAX;
- amd_manager->instance = pdata->instance;
- amd_manager->mmio = amd_manager->acp_mmio +
- (amd_manager->instance * ACP63_SDW_MANAGER_REG_OFFSET);
- amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
- amd_manager->acp_rev = pdata->acp_rev;
- amd_manager->cols_index = sdw_find_col_index(AMD_SDW_DEFAULT_COLUMNS);
- amd_manager->rows_index = sdw_find_row_index(AMD_SDW_DEFAULT_ROWS);
- amd_manager->dev = dev;
- amd_manager->bus.ops = &amd_acp63_sdw_ops;
- amd_manager->bus.port_ops = &amd_acp63_sdw_port_ops;
- amd_manager->bus.compute_params = &amd_acp63_sdw_compute_params;
- amd_manager->bus.clk_stop_timeout = 200;
- amd_manager->bus.link_id = amd_manager->instance;
- ret = amd_sdw_hw_init_ops(amd_manager);
- if (ret) {
- dev_err(dev, "Failed to initialize hw ops (%d)\n", ret);
- return ret;
- }
-
- /*
- * Due to BIOS compatibility, the two links are exposed within
- * the scope of a single controller. If this changes, the
- * controller_id will have to be updated with drv_data
- * information.
- */
- amd_manager->bus.controller_id = 0;
- dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- switch (amd_manager->instance) {
- case ACP_SDW0:
- amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
- break;
- case ACP_SDW1:
- amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
- break;
- default:
- return -EINVAL;
- }
- break;
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- amd_manager->num_dout_ports = AMD_ACP70_SDW_MAX_TX_PORTS;
- amd_manager->num_din_ports = AMD_ACP70_SDW_MAX_RX_PORTS;
- break;
- default:
- /* unreachable: amd_sdw_hw_init_ops() rejects unknown revisions earlier in probe */
- return -EINVAL;
- }
- amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports;
- amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports,
- sizeof(int), GFP_KERNEL);
- if (!amd_manager->port_offset_map)
- return -ENOMEM;
-
- prop = &amd_manager->bus.prop;
- prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
-
- ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
- if (ret) {
- dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret);
- return ret;
- }
- ret = amd_sdw_register_dais(amd_manager);
- if (ret) {
- dev_err(dev, "CPU DAI registration failed\n");
- sdw_bus_master_delete(&amd_manager->bus);
- return ret;
- }
- INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
- INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
- dev_set_drvdata(dev, amd_manager);
- return 0;
-}
-
-static void amd_sdw_manager_remove(struct platform_device *pdev)
-{
- struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
- int ret;
-
- if (pm_runtime_enabled(&pdev->dev))
- pm_runtime_disable(&pdev->dev);
- acp_disable_sdw_interrupts(amd_manager);
- cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
- cancel_work_sync(&amd_manager->amd_sdw_work);
- sdw_bus_master_delete(&amd_manager->bus);
- ret = acp_disable_sdw_manager(amd_manager);
- if (ret)
- dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret));
-}
-
static const struct amd_sdw_hw_ops amd_acp63_hw_ops = {
.init_sdw_manager = amd_acp63_init_sdw_manager,
.enable_sdw_manager = amd_acp63_enable_sdw_manager,
@@ -1740,38 +1087,8 @@ static const struct amd_sdw_hw_ops amd_acp63_hw_ops = {
.resume_runtime = amd_acp63_sdw_resume_runtime,
};
-static int amd_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager)
+int amd_acp63_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager)
{
- switch (amd_manager->acp_rev) {
- case ACP63_PCI_REV_ID:
- case ACP70_PCI_REV_ID:
- case ACP71_PCI_REV_ID:
- case ACP72_PCI_REV_ID:
- amd_manager->hw_ops = &amd_acp63_hw_ops;
- break;
- default:
- return -EINVAL;
- }
+ amd_manager->hw_ops = &amd_acp63_hw_ops;
return 0;
}
-
-static const struct dev_pm_ops amd_pm = {
- .prepare = amd_pm_prepare,
- SET_SYSTEM_SLEEP_PM_OPS(amd_suspend, amd_resume_runtime)
- SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL)
-};
-
-static struct platform_driver amd_sdw_driver = {
- .probe = &amd_sdw_manager_probe,
- .remove = &amd_sdw_manager_remove,
- .driver = {
- .name = "amd_sdw_manager",
- .pm = &amd_pm,
- }
-};
-module_platform_driver(amd_sdw_driver);
-
-MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
-MODULE_DESCRIPTION("AMD SoundWire driver");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/soundwire/amd_manager.h b/drivers/soundwire/amd_manager.h
index 8780759b9c1c..2e87c79580f4 100644
--- a/drivers/soundwire/amd_manager.h
+++ b/drivers/soundwire/amd_manager.h
@@ -197,73 +197,23 @@
#define AMD_SDW_DEVICE_STATE_D3 3
#define ACP_PME_EN 0x0001400
-struct sdw_manager_dp_reg {
- u32 frame_fmt_reg;
- u32 sample_int_reg;
- u32 hctrl_dp0_reg;
- u32 offset_reg;
- u32 lane_ctrl_ch_en_reg;
-};
-
-/*
- * SDW0 Manager instance registers 6 CPU DAI (3 TX & 3 RX Ports)
- * whereas SDW1 Manager Instance registers 2 CPU DAI (one TX & one RX port)
- * Below is the CPU DAI <->Manager port number mapping
- * i.e SDW0 Pin0 -> port number 0 -> AUDIO0 TX
- * SDW0 Pin1 -> Port number 1 -> AUDIO1 TX
- * SDW0 Pin2 -> Port number 2 -> AUDIO2 TX
- * SDW0 Pin3 -> port number 3 -> AUDIO0 RX
- * SDW0 Pin4 -> Port number 4 -> AUDIO1 RX
- * SDW0 Pin5 -> Port number 5 -> AUDIO2 RX
- * Whereas for SDW1 instance
- * SDW1 Pin0 -> port number 0 -> AUDIO1 TX
- * SDW1 Pin1 -> Port number 1 -> AUDIO1 RX
- * Same mapping should be used for programming DMA controller registers in SoundWire DMA driver.
- * i.e if AUDIO0 TX channel is selected then we need to use AUDIO0 TX registers for DMA programming
- * in SoundWire DMA driver.
- */
-
-static struct sdw_manager_dp_reg acp63_sdw0_dp_reg[AMD_ACP63_SDW0_MAX_DAI] = {
- {ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
- ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
- ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO2_TX_FRAME_FORMAT, ACP_SW_AUDIO2_TX_SAMPLEINTERVAL, ACP_SW_AUDIO2_TX_HCTRL,
- ACP_SW_AUDIO2_TX_OFFSET, ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO0_RX_FRAME_FORMAT, ACP_SW_AUDIO0_RX_SAMPLEINTERVAL, ACP_SW_AUDIO0_RX_HCTRL_DP0,
- ACP_SW_AUDIO0_RX_OFFSET_DP0, ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
- ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO2_RX_FRAME_FORMAT, ACP_SW_AUDIO2_RX_SAMPLEINTERVAL, ACP_SW_AUDIO2_RX_HCTRL,
- ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
-};
-
-static struct sdw_manager_dp_reg acp63_sdw1_dp_reg[AMD_ACP63_SDW1_MAX_DAI] = {
- {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
- ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
- ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0}
-};
-
-static struct sdw_manager_dp_reg acp70_sdw_dp_reg[AMD_ACP70_SDW_MAX_DAI] = {
- {ACP_SW_AUDIO0_TX_FRAME_FORMAT, ACP_SW_AUDIO0_TX_SAMPLEINTERVAL, ACP_SW_AUDIO0_TX_HCTRL_DP0,
- ACP_SW_AUDIO0_TX_OFFSET_DP0, ACP_SW_AUDIO0_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO1_TX_FRAME_FORMAT, ACP_SW_AUDIO1_TX_SAMPLEINTERVAL, ACP_SW_AUDIO1_TX_HCTRL,
- ACP_SW_AUDIO1_TX_OFFSET, ACP_SW_AUDIO1_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO2_TX_FRAME_FORMAT, ACP_SW_AUDIO2_TX_SAMPLEINTERVAL, ACP_SW_AUDIO2_TX_HCTRL,
- ACP_SW_AUDIO2_TX_OFFSET, ACP_SW_AUDIO2_TX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO0_RX_FRAME_FORMAT, ACP_SW_AUDIO0_RX_SAMPLEINTERVAL, ACP_SW_AUDIO0_RX_HCTRL_DP0,
- ACP_SW_AUDIO0_RX_OFFSET_DP0, ACP_SW_AUDIO0_RX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO1_RX_FRAME_FORMAT, ACP_SW_AUDIO1_RX_SAMPLEINTERVAL, ACP_SW_AUDIO1_RX_HCTRL,
- ACP_SW_AUDIO1_RX_OFFSET, ACP_SW_AUDIO1_RX_CHANNEL_ENABLE_DP0},
- {ACP_SW_AUDIO2_RX_FRAME_FORMAT, ACP_SW_AUDIO2_RX_SAMPLEINTERVAL, ACP_SW_AUDIO2_RX_HCTRL,
- ACP_SW_AUDIO2_RX_OFFSET, ACP_SW_AUDIO2_RX_CHANNEL_ENABLE_DP0},
-};
-
-static u32 sdw_manager_reg_mask_array[AMD_ACP63_SDW_MAX_MANAGER_COUNT] = {
- AMD_SDW0_EXT_INTR_MASK,
- AMD_SDW1_EXT_INTR_MASK
-};
+/* Cross-file prototypes: amd_manager_common.c functions used by amd_acp63_manager.c */
+enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg);
+void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status);
+void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager);
+void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
+ struct amd_sdw_manager *amd_manager);
+int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager);
+int amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager *amd_manager, bool enable);
+int amd_prop_read(struct sdw_bus *bus);
+
+/* Cross-file prototypes: amd_acp63_manager.c symbols used by amd_manager_common.c */
+int amd_acp63_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager);
+int amd_acp63_sdw_compute_params(struct sdw_bus *bus, struct sdw_stream_runtime *stream);
+extern const struct sdw_master_port_ops amd_acp63_sdw_port_ops;
+extern const struct sdw_master_ops amd_acp63_sdw_ops;
+void amd_acp63_sdw_irq_thread(struct amd_sdw_manager *amd_manager);
+void amd_acp63_sdw_update_slave_status_work(struct amd_sdw_manager *amd_manager);
#define AMD_SDW_HW_OPS(amd_manager, cb) ((amd_manager)->hw_ops->cb)
diff --git a/drivers/soundwire/amd_manager_common.c b/drivers/soundwire/amd_manager_common.c
new file mode 100644
index 000000000000..06aef9c03f18
--- /dev/null
+++ b/drivers/soundwire/amd_manager_common.c
@@ -0,0 +1,786 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/*
+ * SoundWire AMD Manager driver
+ *
+ * Copyright 2026 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/completion.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/pm_runtime.h>
+#include <linux/wait.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "bus.h"
+#include "amd_init.h"
+#include "amd_manager.h"
+
+#define DRV_NAME "amd_sdw_manager"
+
+#define to_amd_sdw(b) container_of(b, struct amd_sdw_manager, bus)
+
+static void amd_sdw_ctl_word_prep(u32 *lower_word, u32 *upper_word, struct sdw_msg *msg,
+ int cmd_offset)
+{
+ u32 upper_data;
+ u32 lower_data = 0;
+ u16 addr;
+ u8 upper_addr, lower_addr;
+ u8 data = 0;
+
+ addr = msg->addr + cmd_offset;
+ upper_addr = (addr & 0xFF00) >> 8;
+ lower_addr = addr & 0xFF;
+
+ if (msg->flags == SDW_MSG_FLAG_WRITE)
+ data = msg->buf[cmd_offset];
+
+ upper_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num);
+ upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, msg->flags + 2);
+ upper_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, upper_addr);
+ lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, lower_addr);
+ lower_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data);
+
+ *upper_word = upper_data;
+ *lower_word = lower_data;
+}
+
+static enum sdw_command_response
+amd_program_scp_addr(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
+{
+ struct sdw_msg scp_msg = {0};
+ u64 response_buf[2] = {0};
+ u32 upper_data = 0, lower_data = 0;
+ int index;
+
+ scp_msg.dev_num = msg->dev_num;
+ scp_msg.addr = SDW_SCP_ADDRPAGE1;
+ scp_msg.buf = &msg->addr_page1;
+ scp_msg.flags = SDW_MSG_FLAG_WRITE;
+ amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[0])) {
+ dev_err_ratelimited(amd_manager->dev,
+ "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
+ scp_msg.addr = SDW_SCP_ADDRPAGE2;
+ scp_msg.buf = &msg->addr_page2;
+ amd_sdw_ctl_word_prep(&lower_data, &upper_data, &scp_msg, 0);
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response_buf[1])) {
+ dev_err_ratelimited(amd_manager->dev,
+ "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
+
+ for (index = 0; index < 2; index++) {
+ if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
+ if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
+ dev_err_ratelimited(amd_manager->dev,
+ "SCP_addrpage NACKed for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_FAIL;
+ }
+ dev_dbg_ratelimited(amd_manager->dev, "SCP_addrpage ignored for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_IGNORED;
+ }
+ }
+ return SDW_CMD_OK;
+}
+
+static int amd_prep_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg)
+{
+ int ret;
+
+ if (msg->page) {
+ ret = amd_program_scp_addr(amd_manager, msg);
+ if (ret) {
+ msg->len = 0;
+ return ret;
+ }
+ }
+ switch (msg->flags) {
+ case SDW_MSG_FLAG_READ:
+ case SDW_MSG_FLAG_WRITE:
+ break;
+ default:
+ dev_err(amd_manager->dev, "Invalid msg cmd: %d\n", msg->flags);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static enum sdw_command_response amd_sdw_fill_msg_resp(struct amd_sdw_manager *amd_manager,
+ struct sdw_msg *msg, u64 response,
+ int offset)
+{
+ if (response & AMD_SDW_MCP_RESP_ACK) {
+ if (msg->flags == SDW_MSG_FLAG_READ)
+ msg->buf[offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
+ } else {
+ if (response & AMD_SDW_MCP_RESP_NACK) {
+ dev_err_ratelimited(amd_manager->dev,
+ "command response NACK received for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_FAIL;
+ }
+ dev_dbg_ratelimited(amd_manager->dev, "command is ignored for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_IGNORED;
+ }
+ return SDW_CMD_OK;
+}
+
+/* AMD SoundWire bus helper shared across all ACP revisions. */
+static unsigned int _amd_sdw_xfer_msg(struct amd_sdw_manager *amd_manager, struct sdw_msg *msg,
+ int cmd_offset)
+{
+ u64 response;
+ u32 upper_data = 0, lower_data = 0;
+
+ amd_sdw_ctl_word_prep(&lower_data, &upper_data, msg, cmd_offset);
+ if (acp_sdw_send_cmd_get_resp(amd_manager, lower_data, upper_data, &response)) {
+ dev_err_ratelimited(amd_manager->dev, "command timeout for Slave %d\n",
+ msg->dev_num);
+ return SDW_CMD_TIMEOUT;
+ }
+ return amd_sdw_fill_msg_resp(amd_manager, msg, response, cmd_offset);
+}
+
+/* AMD SoundWire bus helper shared across all ACP revisions. */
+enum sdw_command_response amd_sdw_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
+{
+ struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
+ int ret, i;
+
+ ret = amd_prep_msg(amd_manager, msg);
+ if (ret)
+ return SDW_CMD_FAIL_OTHER;
+ for (i = 0; i < msg->len; i++) {
+ ret = _amd_sdw_xfer_msg(amd_manager, msg, i);
+ if (ret)
+ return ret;
+ }
+ return SDW_CMD_OK;
+}
+
+/* AMD SoundWire bus helper shared across all ACP revisions. */
+void amd_sdw_fill_slave_status(struct amd_sdw_manager *amd_manager, u16 index, u32 status)
+{
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ case SDW_SLAVE_UNATTACHED:
+ case SDW_SLAVE_ALERT:
+ amd_manager->status[index] = status;
+ break;
+ default:
+ amd_manager->status[index] = SDW_SLAVE_RESERVED;
+ break;
+ }
+}
+
+/* AMD SoundWire bus helper shared across all ACP revisions. */
+void amd_sdw_process_ping_status(u64 response, struct amd_sdw_manager *amd_manager)
+{
+ u64 slave_stat;
+ u32 val;
+ u16 dev_index;
+
+ /* slave status response */
+ slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
+ slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
+ dev_dbg(amd_manager->dev, "slave_stat:0x%llx\n", slave_stat);
+ for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
+ val = (slave_stat >> (dev_index * 2)) & AMD_SDW_MCP_SLAVE_STATUS_MASK;
+ dev_dbg(amd_manager->dev, "val:0x%x\n", val);
+ amd_sdw_fill_slave_status(amd_manager, dev_index, val);
+ }
+}
+
+static int sdw_master_read_amd_prop(struct sdw_bus *bus)
+{
+ struct amd_sdw_manager *amd_manager = to_amd_sdw(bus);
+ struct fwnode_handle *link;
+ struct sdw_master_prop *prop;
+ u32 quirk_mask = 0;
+ u32 wake_en_mask = 0;
+ u32 power_mode_mask = 0;
+ char name[32];
+
+ prop = &bus->prop;
+ /* Find manager handle */
+ snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", bus->link_id);
+ link = device_get_named_child_node(bus->dev, name);
+ if (!link) {
+ dev_err(bus->dev, "Manager node %s not found\n", name);
+ return -EIO;
+ }
+ fwnode_property_read_u32(link, "amd-sdw-enable", &quirk_mask);
+ if (!(quirk_mask & AMD_SDW_QUIRK_MASK_BUS_ENABLE))
+ prop->hw_disabled = true;
+ prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
+ SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
+
+ fwnode_property_read_u32(link, "amd-sdw-wakeup-enable", &wake_en_mask);
+ amd_manager->wake_en_mask = wake_en_mask;
+ fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
+ amd_manager->power_mode_mask = power_mode_mask;
+
+ fwnode_handle_put(link);
+
+ return 0;
+}
+
+/* AMD SoundWire bus helper shared across all ACP revisions. */
+int amd_prop_read(struct sdw_bus *bus)
+{
+ sdw_master_read_prop(bus);
+ sdw_master_read_amd_prop(bus);
+ return 0;
+}
+
+static int amd_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
+ struct sdw_amd_dai_runtime *dai_runtime;
+ struct sdw_stream_config sconfig;
+ int ch, dir;
+ int ret;
+
+ dai_runtime = amd_manager->dai_runtime_array[dai->id];
+ if (!dai_runtime)
+ return -EIO;
+
+ ch = params_channels(params);
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ dir = SDW_DATA_DIR_RX;
+ else
+ dir = SDW_DATA_DIR_TX;
+ dev_dbg(amd_manager->dev, "dir:%d dai->id:0x%x\n", dir, dai->id);
+
+ sconfig.direction = dir;
+ sconfig.ch_count = ch;
+ sconfig.frame_rate = params_rate(params);
+ sconfig.type = dai_runtime->stream_type;
+
+ sconfig.bps = snd_pcm_format_width(params_format(params));
+
+ /* Port configuration */
+ struct sdw_port_config *pconfig __free(kfree) = kzalloc_obj(*pconfig);
+ if (!pconfig)
+ return -ENOMEM;
+
+ pconfig->num = dai->id;
+ pconfig->ch_mask = (1 << ch) - 1;
+ ret = sdw_stream_add_master(&amd_manager->bus, &sconfig,
+ pconfig, 1, dai_runtime->stream);
+ if (ret)
+ dev_err(amd_manager->dev, "add manager to stream failed:%d\n", ret);
+
+ return ret;
+}
+
+static int amd_sdw_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
+ struct sdw_amd_dai_runtime *dai_runtime;
+ int ret;
+
+ dai_runtime = amd_manager->dai_runtime_array[dai->id];
+ if (!dai_runtime)
+ return -EIO;
+
+ ret = sdw_stream_remove_master(&amd_manager->bus, dai_runtime->stream);
+ if (ret < 0)
+ dev_err(dai->dev, "remove manager from stream %s failed: %d\n",
+ dai_runtime->stream->name, ret);
+ return ret;
+}
+
+static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
+ struct sdw_amd_dai_runtime *dai_runtime;
+
+ dai_runtime = amd_manager->dai_runtime_array[dai->id];
+ if (stream) {
+ /* first paranoia check */
+ if (dai_runtime) {
+ dev_err(dai->dev, "dai_runtime already allocated for dai %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ /* allocate and set dai_runtime info */
+ dai_runtime = kzalloc_obj(*dai_runtime);
+ if (!dai_runtime)
+ return -ENOMEM;
+
+ dai_runtime->stream_type = SDW_STREAM_PCM;
+ dai_runtime->bus = &amd_manager->bus;
+ dai_runtime->stream = stream;
+ amd_manager->dai_runtime_array[dai->id] = dai_runtime;
+ } else {
+ /* second paranoia check */
+ if (!dai_runtime) {
+ dev_err(dai->dev, "dai_runtime not allocated for dai %s\n", dai->name);
+ return -EINVAL;
+ }
+
+ /* for NULL stream we release allocated dai_runtime */
+ kfree(dai_runtime);
+ amd_manager->dai_runtime_array[dai->id] = NULL;
+ }
+ return 0;
+}
+
+static int amd_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ return amd_set_sdw_stream(dai, stream, direction);
+}
+
+static void *amd_get_sdw_stream(struct snd_soc_dai *dai, int direction)
+{
+ struct amd_sdw_manager *amd_manager = snd_soc_dai_get_drvdata(dai);
+ struct sdw_amd_dai_runtime *dai_runtime;
+
+ dai_runtime = amd_manager->dai_runtime_array[dai->id];
+ if (!dai_runtime)
+ return ERR_PTR(-EINVAL);
+
+ return dai_runtime->stream;
+}
+
+static const struct snd_soc_dai_ops amd_sdw_dai_ops = {
+ .hw_params = amd_sdw_hw_params,
+ .hw_free = amd_sdw_hw_free,
+ .set_stream = amd_pcm_set_sdw_stream,
+ .get_stream = amd_get_sdw_stream,
+};
+
+static const struct snd_soc_component_driver amd_sdw_dai_component = {
+ .name = "soundwire",
+};
+
+static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager)
+{
+ struct sdw_amd_dai_runtime **dai_runtime_array;
+ struct snd_soc_dai_driver *dais;
+ struct snd_soc_pcm_stream *stream;
+ struct device *dev;
+ int i, num_dais;
+
+ dev = amd_manager->dev;
+ num_dais = amd_manager->num_dout_ports + amd_manager->num_din_ports;
+ dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
+ if (!dais)
+ return -ENOMEM;
+
+ dai_runtime_array = devm_kcalloc(dev, num_dais,
+ sizeof(struct sdw_amd_dai_runtime *),
+ GFP_KERNEL);
+ if (!dai_runtime_array)
+ return -ENOMEM;
+ amd_manager->dai_runtime_array = dai_runtime_array;
+ for (i = 0; i < num_dais; i++) {
+ dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", amd_manager->instance,
+ i);
+ if (!dais[i].name)
+ return -ENOMEM;
+ if (i < amd_manager->num_dout_ports)
+ stream = &dais[i].playback;
+ else
+ stream = &dais[i].capture;
+
+ stream->channels_min = 2;
+ stream->channels_max = 2;
+ stream->rates = SNDRV_PCM_RATE_48000;
+ stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
+
+ dais[i].ops = &amd_sdw_dai_ops;
+ dais[i].id = i;
+ }
+
+ return devm_snd_soc_register_component(dev, &amd_sdw_dai_component,
+ dais, num_dais);
+}
+
+static void amd_sdw_update_slave_status_work(struct work_struct *work)
+{
+ struct amd_sdw_manager *amd_manager =
+ container_of(work, struct amd_sdw_manager, amd_sdw_work);
+
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_acp63_sdw_update_slave_status_work(amd_manager);
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return;
+ }
+}
+
+void amd_sdw_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
+ struct amd_sdw_manager *amd_manager)
+{
+ u64 slave_stat;
+ u32 val;
+ int dev_index;
+
+ if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
+ memset(amd_manager->status, 0, sizeof(amd_manager->status));
+ slave_stat = status_change_0to7;
+ slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
+ dev_dbg(amd_manager->dev, "status_change_0to7:0x%x status_change_8to11:0x%x\n",
+ status_change_0to7, status_change_8to11);
+ if (slave_stat) {
+ for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
+ if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) {
+ val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) &
+ AMD_SDW_MCP_SLAVE_STATUS_MASK;
+ amd_sdw_fill_slave_status(amd_manager, dev_index, val);
+ }
+ }
+ }
+}
+
+static void amd_sdw_irq_thread(struct work_struct *work)
+{
+ struct amd_sdw_manager *amd_manager =
+ container_of(work, struct amd_sdw_manager, amd_sdw_irq_thread);
+
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_acp63_sdw_irq_thread(amd_manager);
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return;
+ }
+}
+
+int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
+{
+ struct sdw_master_prop *prop;
+ int ret;
+
+ prop = &amd_manager->bus.prop;
+ if (!prop->hw_disabled) {
+ ret = acp_sdw_clk_init_ctrl(amd_manager);
+ if (ret)
+ return ret;
+ ret = acp_init_sdw_manager(amd_manager);
+ if (ret)
+ return ret;
+ acp_enable_sdw_interrupts(amd_manager);
+ ret = acp_enable_sdw_manager(amd_manager);
+ if (ret)
+ return ret;
+ acp_sdw_set_frameshape(amd_manager);
+ }
+ /* Enable runtime PM */
+ pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(amd_manager->dev);
+ pm_runtime_mark_last_busy(amd_manager->dev);
+ pm_runtime_set_active(amd_manager->dev);
+ pm_runtime_enable(amd_manager->dev);
+ return 0;
+}
+
+/*
+ * AMD SoundWire clock-stop sequence shared across all ACP revisions.
+ * Differences between revisions in this path are minimal; a switch/case
+ * on acp_rev is sufficient to accommodate any variant-specific additions
+ * needed for future platforms.
+ */
+int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
+{
+ u32 val;
+ int ret;
+
+ ret = sdw_bus_prep_clk_stop(&amd_manager->bus);
+ if (ret < 0 && ret != -ENODATA) {
+ dev_err(amd_manager->dev, "prepare clock stop failed %d", ret);
+ return 0;
+ }
+ ret = sdw_bus_clk_stop(&amd_manager->bus);
+ if (ret < 0 && ret != -ENODATA) {
+ dev_err(amd_manager->dev, "bus clock stop failed %d", ret);
+ return 0;
+ }
+
+ ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
+ (val & AMD_SDW_CLK_STOP_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT);
+ if (ret) {
+ dev_err(amd_manager->dev, "SDW%x clock stop failed\n", amd_manager->instance);
+ return 0;
+ }
+
+ amd_manager->clk_stopped = true;
+ if (amd_manager->wake_en_mask)
+ writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
+
+ dev_dbg(amd_manager->dev, "SDW%x clock stop successful\n", amd_manager->instance);
+ return 0;
+}
+
+static int amd_resume_child_device(struct device *dev, void *data)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!slave->probed) {
+ dev_dbg(dev, "skipping device, no probed driver\n");
+ return 0;
+ }
+ if (!slave->dev_num_sticky) {
+ dev_dbg(dev, "skipping device, never detected on bus\n");
+ return 0;
+ }
+ ret = pm_request_resume(dev);
+ if (ret < 0) {
+ dev_err(dev, "pm_request_resume failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int __maybe_unused amd_pm_prepare(struct device *dev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev);
+ struct sdw_bus *bus = &amd_manager->bus;
+ int ret;
+
+ if (bus->prop.hw_disabled) {
+ dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n",
+ bus->link_id);
+ return 0;
+ }
+ /*
+ * When multiple peripheral devices connected over the same link, if SoundWire manager
+ * device is not in runtime suspend state, observed that device alerts are missing
+ * without pm_prepare on AMD platforms in clockstop mode0.
+ */
+ if (amd_manager->power_mode_mask) {
+ ret = pm_runtime_resume(dev);
+ if (ret < 0) {
+ dev_err(bus->dev, "pm_runtime_resume failed: %d\n", ret);
+ return 0;
+ }
+ }
+ /* To force peripheral devices to system level suspend state, resume the devices
+ * from runtime suspend state first. Without that unable to dispatch the alert
+ * status to peripheral driver during system level resume as they are in runtime
+ * suspend state.
+ */
+ ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
+ if (ret < 0)
+ dev_err(dev, "amd_resume_child_device failed: %d\n", ret);
+ return 0;
+}
+
+int amd_sdw_host_wake_enable_by_rev(struct amd_sdw_manager *amd_manager, bool enable)
+{
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ return acp_sdw_host_wake_enable(amd_manager, enable);
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __maybe_unused amd_suspend(struct device *dev)
+{
+ return acp_sdw_suspend(dev);
+}
+
+static int __maybe_unused amd_suspend_runtime(struct device *dev)
+{
+ return acp_sdw_suspend_runtime(dev);
+}
+
+static int __maybe_unused amd_resume_runtime(struct device *dev)
+{
+ return acp_sdw_resume_runtime(dev);
+}
+
+static int amd_sdw_hw_init_ops(struct amd_sdw_manager *amd_manager)
+{
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ return amd_acp63_sdw_hw_init_ops(amd_manager);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int amd_sdw_manager_probe(struct platform_device *pdev)
+{
+ const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct sdw_master_prop *prop;
+ struct amd_sdw_manager *amd_manager;
+ int ret;
+
+ amd_manager = devm_kzalloc(dev, sizeof(struct amd_sdw_manager), GFP_KERNEL);
+ if (!amd_manager)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOMEM;
+
+ amd_manager->acp_mmio = devm_ioremap(dev, res->start, resource_size(res));
+ if (!amd_manager->acp_mmio) {
+ dev_err(dev, "mmio not found\n");
+ return -ENOMEM;
+ }
+ amd_manager->ping_slave_stat = U32_MAX;
+ amd_manager->instance = pdata->instance;
+ amd_manager->acp_sdw_lock = pdata->acp_sdw_lock;
+ amd_manager->acp_rev = pdata->acp_rev;
+ amd_manager->dev = dev;
+ amd_manager->bus.clk_stop_timeout = 200;
+ amd_manager->bus.link_id = amd_manager->instance;
+ ret = amd_sdw_hw_init_ops(amd_manager);
+ if (ret) {
+ dev_err(dev, "Failed to initialize hw ops (%d)\n", ret);
+ return ret;
+ }
+
+ /*
+ * Due to BIOS compatibility, the two links are exposed within
+ * the scope of a single controller. If this changes, the
+ * controller_id will have to be updated with drv_data
+ * information.
+ */
+ amd_manager->bus.controller_id = 0;
+ dev_dbg(dev, "acp_rev:0x%x\n", amd_manager->acp_rev);
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ switch (amd_manager->instance) {
+ case ACP_SDW0:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW0_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW0_MAX_RX_PORTS;
+ break;
+ case ACP_SDW1:
+ amd_manager->num_dout_ports = AMD_ACP63_SDW1_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP63_SDW1_MAX_RX_PORTS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_manager->num_dout_ports = AMD_ACP70_SDW_MAX_TX_PORTS;
+ amd_manager->num_din_ports = AMD_ACP70_SDW_MAX_RX_PORTS;
+ break;
+ default:
+ dev_err(dev, "unsupported ACP revision: 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
+ }
+ amd_manager->max_ports = amd_manager->num_dout_ports + amd_manager->num_din_ports;
+
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ amd_manager->mmio = amd_manager->acp_mmio +
+ (amd_manager->instance * ACP63_SDW_MANAGER_REG_OFFSET);
+ amd_manager->bus.ops = &amd_acp63_sdw_ops;
+ amd_manager->bus.port_ops = &amd_acp63_sdw_port_ops;
+ amd_manager->bus.compute_params = &amd_acp63_sdw_compute_params;
+ amd_manager->port_offset_map = devm_kcalloc(dev, amd_manager->max_ports,
+ sizeof(int), GFP_KERNEL);
+ if (!amd_manager->port_offset_map)
+ return -ENOMEM;
+ break;
+ default:
+ dev_err(dev, "unsupported ACP revision: 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
+ }
+
+ prop = &amd_manager->bus.prop;
+ prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
+
+ ret = sdw_bus_master_add(&amd_manager->bus, dev, dev->fwnode);
+ if (ret) {
+ dev_err(dev, "Failed to register SoundWire manager(%d)\n", ret);
+ return ret;
+ }
+ ret = amd_sdw_register_dais(amd_manager);
+ if (ret) {
+ dev_err(dev, "CPU DAI registration failed\n");
+ sdw_bus_master_delete(&amd_manager->bus);
+ return ret;
+ }
+ INIT_WORK(&amd_manager->amd_sdw_irq_thread, amd_sdw_irq_thread);
+ INIT_WORK(&amd_manager->amd_sdw_work, amd_sdw_update_slave_status_work);
+ dev_set_drvdata(dev, amd_manager);
+ return 0;
+}
+
+static void amd_sdw_manager_remove(struct platform_device *pdev)
+{
+ struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (pm_runtime_enabled(&pdev->dev))
+ pm_runtime_disable(&pdev->dev);
+ acp_disable_sdw_interrupts(amd_manager);
+ cancel_work_sync(&amd_manager->amd_sdw_irq_thread);
+ cancel_work_sync(&amd_manager->amd_sdw_work);
+ sdw_bus_master_delete(&amd_manager->bus);
+ ret = acp_disable_sdw_manager(amd_manager);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to disable device (%pe)\n", ERR_PTR(ret));
+}
+
+static const struct dev_pm_ops amd_pm = {
+ .prepare = amd_pm_prepare,
+ SET_SYSTEM_SLEEP_PM_OPS(amd_suspend, amd_resume_runtime)
+ SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL)
+};
+
+static struct platform_driver amd_sdw_driver = {
+ .probe = &amd_sdw_manager_probe,
+ .remove = &amd_sdw_manager_remove,
+ .driver = {
+ .name = "amd_sdw_manager",
+ .pm = &amd_pm,
+ }
+};
+module_platform_driver(amd_sdw_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD SoundWire driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 10/11] soundwire: amd: guard DAI PCM caps by acp_rev in register_dais()
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (8 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 09/11] soundwire: amd: extract shared functions into amd_manager_common.c Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 11/11] soundwire: amd: guard clock-stop register access by acp_rev Vijendar Mukunda
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Replace the bare stream->rates and stream->formats assignments in
amd_sdw_register_dais() with an explicit switch/case on acp_rev
covering ACP63, ACP70, ACP71, and ACP72. An unrecognised revision
now returns -EINVAL at DAI registration time rather than silently
inheriting capabilities that may not apply to future hardware.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager_common.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/soundwire/amd_manager_common.c b/drivers/soundwire/amd_manager_common.c
index 06aef9c03f18..4a414fc1b02d 100644
--- a/drivers/soundwire/amd_manager_common.c
+++ b/drivers/soundwire/amd_manager_common.c
@@ -403,8 +403,19 @@ static int amd_sdw_register_dais(struct amd_sdw_manager *amd_manager)
stream->channels_min = 2;
stream->channels_max = 2;
- stream->rates = SNDRV_PCM_RATE_48000;
- stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ stream->rates = SNDRV_PCM_RATE_48000;
+ stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n",
+ amd_manager->acp_rev);
+ return -EINVAL;
+ }
dais[i].ops = &amd_sdw_dai_ops;
dais[i].id = i;
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v3 11/11] soundwire: amd: guard clock-stop register access by acp_rev
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
` (9 preceding siblings ...)
2026-11-07 16:12 ` [PATCH v3 10/11] soundwire: amd: guard DAI PCM caps by acp_rev in register_dais() Vijendar Mukunda
@ 2026-11-07 16:12 ` Vijendar Mukunda
10 siblings, 0 replies; 12+ messages in thread
From: Vijendar Mukunda @ 2026-11-07 16:12 UTC (permalink / raw)
To: vkoul
Cc: yung-chuan.liao, pierre-louis.bossart, Basavaraj.Hiregoudar,
Sunil-kumar.Dommati, venkataprasad.potturu, Syed.SabaKareem,
Mario.Limonciello, Richard.Gong, Poojitha.N, linux-sound,
linux-kernel, Vijendar Mukunda
Wrap the readl_poll_timeout and wake-enable writel calls in
amd_sdw_clock_stop() in explicit switch/case blocks on acp_rev
covering ACP63, ACP70, ACP71, and ACP72. An unrecognised revision
now returns -EINVAL rather than silently accessing register offsets
that may not apply to future hardware.
Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
drivers/soundwire/amd_manager_common.c | 35 ++++++++++++++++++++++----
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/soundwire/amd_manager_common.c b/drivers/soundwire/amd_manager_common.c
index 4a414fc1b02d..7e3c9176e793 100644
--- a/drivers/soundwire/amd_manager_common.c
+++ b/drivers/soundwire/amd_manager_common.c
@@ -521,7 +521,7 @@ int amd_sdw_manager_start(struct amd_sdw_manager *amd_manager)
*/
int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
{
- u32 val;
+ u32 clk_resume_ctrl, val;
int ret;
ret = sdw_bus_prep_clk_stop(&amd_manager->bus);
@@ -535,16 +535,41 @@ int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager)
return 0;
}
- ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val,
- (val & AMD_SDW_CLK_STOP_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT);
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ clk_resume_ctrl = ACP_SW_CLK_RESUME_CTRL;
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n", amd_manager->acp_rev);
+ return -EINVAL;
+ }
+
+ ret = readl_poll_timeout(amd_manager->mmio + clk_resume_ctrl, val,
+ (val & AMD_SDW_CLK_STOP_DONE),
+ ACP_DELAY_US, AMD_SDW_TIMEOUT);
if (ret) {
dev_err(amd_manager->dev, "SDW%x clock stop failed\n", amd_manager->instance);
return 0;
}
amd_manager->clk_stopped = true;
- if (amd_manager->wake_en_mask)
- writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
+ if (amd_manager->wake_en_mask) {
+ switch (amd_manager->acp_rev) {
+ case ACP63_PCI_REV_ID:
+ case ACP70_PCI_REV_ID:
+ case ACP71_PCI_REV_ID:
+ case ACP72_PCI_REV_ID:
+ writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance));
+ break;
+ default:
+ dev_err(amd_manager->dev, "unexpected acp_rev 0x%x\n",
+ amd_manager->acp_rev);
+ return -EINVAL;
+ }
+ }
dev_dbg(amd_manager->dev, "SDW%x clock stop successful\n", amd_manager->instance);
return 0;
--
2.53.0
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-11-07 16:15 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-07 16:12 [PATCH v3 00/11] soundwire: amd: introduce hw_ops dispatch and split driver into revision-specific files Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 01/11] soundwire: amd: rename AMD_SDW_MAX_MANAGER_COUNT macro Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 02/11] soundwire: amd: allocate pdevinfo and sdw_pdata by ACP revision in probe Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 03/11] soundwire: amd: allocate sdw_amd_ctx pdev array dynamically Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 04/11] soundwire: amd: rename hardware backend symbols to amd_acp63_*() prefix Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 05/11] soundwire: amd: remove unused AMD_SDW_MAX_FREQ_NUM define Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 06/11] soundwire: amd: introduce struct amd_sdw_hw_ops dispatch framework Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 07/11] soundwire: amd: dispatch irq/work handlers to per-revision functions Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 08/11] soundwire: amd: populate hw_ops and wire all call sites through acp_*() helpers Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 09/11] soundwire: amd: extract shared functions into amd_manager_common.c Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 10/11] soundwire: amd: guard DAI PCM caps by acp_rev in register_dais() Vijendar Mukunda
2026-11-07 16:12 ` [PATCH v3 11/11] soundwire: amd: guard clock-stop register access by acp_rev Vijendar Mukunda
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®