All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/19] Add soundwire support for Pink Sardine platform
@ 2023-01-11  9:02 Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                   ` (19 more replies)
  0 siblings, 20 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti

Pink Sardine(ps) platform is based on ACP6.3 Architecture.
ACP6.3 IP has two soundwire controller instance support.
This patchset add support for Soundwire controller on Pink Sardine
platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Vijendar Mukunda (19):
  ASoC: amd: ps: create platform devices based on acp config
  soundwire: amd: Add support for AMD Master driver
  soundwire: amd: register sdw controller dai ops
  soundwire: amd: enable build for AMD soundwire master driver
  soundwire: amd: add soundwire interrupt handling
  ASoC: amd: ps: add support for soundwire interrupts in acp pci driver
  ASoC: amd: ps: add soundwire dma driver for pink sardine platform
  ASoC: amd: ps: add soundwire dma driver dma ops
  ASoC: amd: ps: add support for Soundwire DMA interrupts
  ASoC: amd: ps: enable Soundwire DMA driver build
  ASoC: amd: update comments in Kconfig file
  ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops.
  ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver
  soundwire: amd: add runtime pm ops for AMD master driver
  soundwire: amd: add startup and shutdown dai ops
  soundwire: amd: handle wake enable interrupt
  soundwire: amd: add pm_prepare callback and pm ops support
  ASoC: amd: ps: implement system level pm ops for soundwire dma driver
  ASoC: amd: ps: increase runtime suspend delay

 drivers/soundwire/Kconfig         |    9 +
 drivers/soundwire/Makefile        |    4 +
 drivers/soundwire/amd_master.c    | 1734 +++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |  284 +++++
 include/linux/soundwire/sdw_amd.h |   65 ++
 sound/soc/amd/Kconfig             |    3 +-
 sound/soc/amd/ps/Makefile         |    2 +
 sound/soc/amd/ps/acp63.h          |   98 +-
 sound/soc/amd/ps/pci-ps.c         |  383 ++++++-
 sound/soc/amd/ps/ps-sdw-dma.c     |  728 ++++++++++++
 10 files changed, 3287 insertions(+), 23 deletions(-)
 create mode 100644 drivers/soundwire/amd_master.c
 create mode 100644 drivers/soundwire/amd_master.h
 create mode 100644 include/linux/soundwire/sdw_amd.h
 create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 170+ messages in thread

* [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, Liam Girdwood,
	Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

Create platform devices for sdw controllers and PDM controller
based on ACP pin config selection and ACPI fw handle for
pink sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 include/linux/soundwire/sdw_amd.h |  18 +++
 sound/soc/amd/ps/acp63.h          |  24 ++-
 sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
 3 files changed, 277 insertions(+), 13 deletions(-)
 create mode 100644 include/linux/soundwire/sdw_amd.h

diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
new file mode 100644
index 000000000000..f0123815af46
--- /dev/null
+++ b/include/linux/soundwire/sdw_amd.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __SDW_AMD_H
+#define __SDW_AMD_H
+
+#include <linux/soundwire/sdw.h>
+
+#define AMD_SDW_CLK_STOP_MODE		1
+#define AMD_SDW_POWER_OFF_MODE		2
+
+struct acp_sdw_pdata {
+	u16 instance;
+	struct mutex *sdw_lock;
+};
+#endif
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index b7535c7d093f..ed979e6d0c1d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -10,7 +10,7 @@
 #define ACP_DEVICE_ID 0x15E2
 #define ACP63_REG_START		0x1240000
 #define ACP63_REG_END		0x1250200
-#define ACP63_DEVS		3
+#define ACP63_DEVS		5
 
 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
 #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
@@ -55,8 +55,14 @@
 
 #define ACP63_DMIC_ADDR		2
 #define ACP63_PDM_MODE_DEVS		3
-#define ACP63_PDM_DEV_MASK		1
 #define ACP_DMIC_DEV	2
+#define ACP63_SDW0_MODE_DEVS		2
+#define ACP63_SDW0_SDW1_MODE_DEVS	3
+#define ACP63_SDW0_PDM_MODE_DEVS	4
+#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
+#define ACP63_DMIC_ADDR			2
+#define ACP63_SDW_ADDR			5
+#define AMD_SDW_MAX_CONTROLLERS		2
 
 enum acp_config {
 	ACP_CONFIG_0 = 0,
@@ -77,6 +83,12 @@ enum acp_config {
 	ACP_CONFIG_15,
 };
 
+enum acp_pdev_mask {
+	ACP63_PDM_DEV_MASK = 1,
+	ACP63_SDW_DEV_MASK,
+	ACP63_SDW_PDM_DEV_MASK,
+};
+
 struct pdm_stream_instance {
 	u16 num_pages;
 	u16 channels;
@@ -107,7 +119,15 @@ struct acp63_dev_data {
 	struct resource *res;
 	struct platform_device *pdev[ACP63_DEVS];
 	struct mutex acp_lock; /* protect shared registers */
+	struct fwnode_handle *sdw_fw_node;
 	u16 pdev_mask;
 	u16 pdev_count;
 	u16 pdm_dev_index;
+	u8 sdw_master_count;
+	u16 sdw0_dev_index;
+	u16 sdw1_dev_index;
+	u16 sdw_dma_dev_index;
+	bool is_dmic_dev;
+	bool is_sdw_dev;
+	bool acp_sdw_power_off;
 };
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index e86f23d97584..85154cf0b2a2 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -14,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <sound/pcm_params.h>
 #include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_amd.h>
 
 #include "acp63.h"
 
@@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 	return IRQ_NONE;
 }
 
-static void get_acp63_device_config(u32 config, struct pci_dev *pci,
-				    struct acp63_dev_data *acp_data)
+static int sdw_amd_scan_controller(struct device *dev)
+{
+	struct acp63_dev_data *acp_data;
+	struct fwnode_handle *link;
+	char name[32];
+	u8 count = 0;
+	u32 acp_sdw_power_mode = 0;
+	int index;
+	int ret;
+
+	acp_data = dev_get_drvdata(dev);
+	acp_data->acp_sdw_power_off = true;
+	/* Found controller, find links supported */
+	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
+					    "mipi-sdw-master-count", &count, 1);
+
+	if (ret) {
+		dev_err(dev,
+			"Failed to read mipi-sdw-master-count: %d\n", ret);
+		return -EINVAL;
+	}
+
+	/* Check count is within bounds */
+	if (count > AMD_SDW_MAX_CONTROLLERS) {
+		dev_err(dev, "Controller count %d exceeds max %d\n",
+			count, AMD_SDW_MAX_CONTROLLERS);
+		return -EINVAL;
+	}
+
+	if (!count) {
+		dev_warn(dev, "No SoundWire controllers detected\n");
+		return -EINVAL;
+	}
+	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
+	acp_data->sdw_master_count  = count;
+	for (index = 0; index < count; index++) {
+		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
+		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
+		if (!link) {
+			dev_err(dev, "Master node %s not found\n", name);
+			return -EIO;
+		}
+
+		fwnode_property_read_u32(link, "amd-sdw-power-mode",
+					 &acp_sdw_power_mode);
+		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
+			acp_data->acp_sdw_power_off = false;
+	}
+	return 0;
+}
+
+static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
 {
 	struct acpi_device *dmic_dev;
+	struct acpi_device *sdw_dev;
+	struct device *dev;
 	const union acpi_object *obj;
 	bool is_dmic_dev = false;
+	bool is_sdw_dev = false;
+	int ret;
+
+	dev = &pci->dev;
 
 	dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
 	if (dmic_dev) {
@@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
 			is_dmic_dev = true;
 	}
 
+	sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
+	if (sdw_dev) {
+		is_sdw_dev = true;
+		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
+		ret = sdw_amd_scan_controller(dev);
+		if (ret)
+			return ret;
+	}
+
+	dev_dbg(&pci->dev, "Audio Mode %d\n", config);
 	switch (config) {
-	case ACP_CONFIG_0:
-	case ACP_CONFIG_1:
+	case ACP_CONFIG_4:
+	case ACP_CONFIG_5:
+	case ACP_CONFIG_10:
+	case ACP_CONFIG_11:
+		if (is_dmic_dev) {
+			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+		}
+		break;
 	case ACP_CONFIG_2:
 	case ACP_CONFIG_3:
-	case ACP_CONFIG_9:
-	case ACP_CONFIG_15:
-		dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+		if (is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
 		break;
-	default:
-		if (is_dmic_dev) {
+	case ACP_CONFIG_6:
+	case ACP_CONFIG_7:
+	case ACP_CONFIG_12:
+	case ACP_CONFIG_8:
+	case ACP_CONFIG_13:
+	case ACP_CONFIG_14:
+		if (is_dmic_dev && is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else if (is_dmic_dev) {
 			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
 			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+		} else if (is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
 		}
 		break;
+	default:
+		break;
 	}
+	return 0;
 }
 
 static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
@@ -188,6 +307,7 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
 
 static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
 {
+	struct acp_sdw_pdata *sdw_pdata;
 	struct platform_device_info pdevinfo[ACP63_DEVS];
 	struct device *parent;
 	int index;
@@ -220,8 +340,110 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
 		acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
 					     0, NULL, 0, NULL, 0);
 		break;
+	case ACP63_SDW_DEV_MASK:
+		if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata->instance = 0;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->sdw0_dev_index = 0;
+			adata->sdw_dma_dev_index = 1;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     sdw_pdata, sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+		} else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata[0].instance = 0;
+			sdw_pdata[1].instance = 1;
+			sdw_pdata[0].sdw_lock = &adata->acp_lock;
+			sdw_pdata[1].sdw_lock = &adata->acp_lock;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->sdw0_dev_index = 0;
+			adata->sdw1_dev_index = 1;
+			adata->sdw_dma_dev_index = 2;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 1, adata->res, 1,
+						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+		}
+		break;
+	case ACP63_SDW_PDM_DEV_MASK:
+		if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata->instance = 0;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->pdm_dev_index  = 0;
+			adata->sdw0_dev_index = 1;
+			adata->sdw_dma_dev_index = 2;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     sdw_pdata, sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec",
+						     0, NULL, 0, NULL, 0);
+		} else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+			sdw_pdata[0].instance = 0;
+			sdw_pdata[1].instance = 1;
+			sdw_pdata[0].sdw_lock = &adata->acp_lock;
+			sdw_pdata[1].sdw_lock = &adata->acp_lock;
+			adata->pdm_dev_index = 0;
+			adata->sdw0_dev_index = 1;
+			adata->sdw1_dev_index = 2;
+			adata->sdw_dma_dev_index = 3;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 1, adata->res, 1,
+						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec",
+						     0, NULL, 0, NULL, 0);
+		}
+		break;
 	default:
-		dev_dbg(&pci->dev, "No PDM devices found\n");
+		dev_dbg(&pci->dev, "No PDM or Soundwire controller devices found\n");
 		return 0;
 	}
 
@@ -299,7 +521,11 @@ static int snd_acp63_probe(struct pci_dev *pci,
 		goto de_init;
 	}
 	val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
-	get_acp63_device_config(val, pci, adata);
+	ret = get_acp63_device_config(val, pci, adata);
+	if (ret) {
+		dev_err(&pci->dev, "get acp device config failed:%d\n", ret);
+		goto de_init;
+	}
 	ret = create_acp63_platform_devs(pci, adata, addr);
 	if (ret < 0) {
 		dev_err(&pci->dev, "ACP platform devices creation failed\n");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Pierre-Louis Bossart,
	Liam Girdwood, Nathan Chancellor, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao,
	Syed Saba Kareem

Create platform devices for sdw controllers and PDM controller
based on ACP pin config selection and ACPI fw handle for
pink sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 include/linux/soundwire/sdw_amd.h |  18 +++
 sound/soc/amd/ps/acp63.h          |  24 ++-
 sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
 3 files changed, 277 insertions(+), 13 deletions(-)
 create mode 100644 include/linux/soundwire/sdw_amd.h

diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
new file mode 100644
index 000000000000..f0123815af46
--- /dev/null
+++ b/include/linux/soundwire/sdw_amd.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __SDW_AMD_H
+#define __SDW_AMD_H
+
+#include <linux/soundwire/sdw.h>
+
+#define AMD_SDW_CLK_STOP_MODE		1
+#define AMD_SDW_POWER_OFF_MODE		2
+
+struct acp_sdw_pdata {
+	u16 instance;
+	struct mutex *sdw_lock;
+};
+#endif
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index b7535c7d093f..ed979e6d0c1d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -10,7 +10,7 @@
 #define ACP_DEVICE_ID 0x15E2
 #define ACP63_REG_START		0x1240000
 #define ACP63_REG_END		0x1250200
-#define ACP63_DEVS		3
+#define ACP63_DEVS		5
 
 #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
 #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
@@ -55,8 +55,14 @@
 
 #define ACP63_DMIC_ADDR		2
 #define ACP63_PDM_MODE_DEVS		3
-#define ACP63_PDM_DEV_MASK		1
 #define ACP_DMIC_DEV	2
+#define ACP63_SDW0_MODE_DEVS		2
+#define ACP63_SDW0_SDW1_MODE_DEVS	3
+#define ACP63_SDW0_PDM_MODE_DEVS	4
+#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
+#define ACP63_DMIC_ADDR			2
+#define ACP63_SDW_ADDR			5
+#define AMD_SDW_MAX_CONTROLLERS		2
 
 enum acp_config {
 	ACP_CONFIG_0 = 0,
@@ -77,6 +83,12 @@ enum acp_config {
 	ACP_CONFIG_15,
 };
 
+enum acp_pdev_mask {
+	ACP63_PDM_DEV_MASK = 1,
+	ACP63_SDW_DEV_MASK,
+	ACP63_SDW_PDM_DEV_MASK,
+};
+
 struct pdm_stream_instance {
 	u16 num_pages;
 	u16 channels;
@@ -107,7 +119,15 @@ struct acp63_dev_data {
 	struct resource *res;
 	struct platform_device *pdev[ACP63_DEVS];
 	struct mutex acp_lock; /* protect shared registers */
+	struct fwnode_handle *sdw_fw_node;
 	u16 pdev_mask;
 	u16 pdev_count;
 	u16 pdm_dev_index;
+	u8 sdw_master_count;
+	u16 sdw0_dev_index;
+	u16 sdw1_dev_index;
+	u16 sdw_dma_dev_index;
+	bool is_dmic_dev;
+	bool is_sdw_dev;
+	bool acp_sdw_power_off;
 };
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index e86f23d97584..85154cf0b2a2 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -14,6 +14,7 @@
 #include <linux/interrupt.h>
 #include <sound/pcm_params.h>
 #include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_amd.h>
 
 #include "acp63.h"
 
@@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 	return IRQ_NONE;
 }
 
-static void get_acp63_device_config(u32 config, struct pci_dev *pci,
-				    struct acp63_dev_data *acp_data)
+static int sdw_amd_scan_controller(struct device *dev)
+{
+	struct acp63_dev_data *acp_data;
+	struct fwnode_handle *link;
+	char name[32];
+	u8 count = 0;
+	u32 acp_sdw_power_mode = 0;
+	int index;
+	int ret;
+
+	acp_data = dev_get_drvdata(dev);
+	acp_data->acp_sdw_power_off = true;
+	/* Found controller, find links supported */
+	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
+					    "mipi-sdw-master-count", &count, 1);
+
+	if (ret) {
+		dev_err(dev,
+			"Failed to read mipi-sdw-master-count: %d\n", ret);
+		return -EINVAL;
+	}
+
+	/* Check count is within bounds */
+	if (count > AMD_SDW_MAX_CONTROLLERS) {
+		dev_err(dev, "Controller count %d exceeds max %d\n",
+			count, AMD_SDW_MAX_CONTROLLERS);
+		return -EINVAL;
+	}
+
+	if (!count) {
+		dev_warn(dev, "No SoundWire controllers detected\n");
+		return -EINVAL;
+	}
+	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
+	acp_data->sdw_master_count  = count;
+	for (index = 0; index < count; index++) {
+		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
+		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
+		if (!link) {
+			dev_err(dev, "Master node %s not found\n", name);
+			return -EIO;
+		}
+
+		fwnode_property_read_u32(link, "amd-sdw-power-mode",
+					 &acp_sdw_power_mode);
+		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
+			acp_data->acp_sdw_power_off = false;
+	}
+	return 0;
+}
+
+static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
 {
 	struct acpi_device *dmic_dev;
+	struct acpi_device *sdw_dev;
+	struct device *dev;
 	const union acpi_object *obj;
 	bool is_dmic_dev = false;
+	bool is_sdw_dev = false;
+	int ret;
+
+	dev = &pci->dev;
 
 	dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
 	if (dmic_dev) {
@@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
 			is_dmic_dev = true;
 	}
 
+	sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
+	if (sdw_dev) {
+		is_sdw_dev = true;
+		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
+		ret = sdw_amd_scan_controller(dev);
+		if (ret)
+			return ret;
+	}
+
+	dev_dbg(&pci->dev, "Audio Mode %d\n", config);
 	switch (config) {
-	case ACP_CONFIG_0:
-	case ACP_CONFIG_1:
+	case ACP_CONFIG_4:
+	case ACP_CONFIG_5:
+	case ACP_CONFIG_10:
+	case ACP_CONFIG_11:
+		if (is_dmic_dev) {
+			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+		}
+		break;
 	case ACP_CONFIG_2:
 	case ACP_CONFIG_3:
-	case ACP_CONFIG_9:
-	case ACP_CONFIG_15:
-		dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+		if (is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
 		break;
-	default:
-		if (is_dmic_dev) {
+	case ACP_CONFIG_6:
+	case ACP_CONFIG_7:
+	case ACP_CONFIG_12:
+	case ACP_CONFIG_8:
+	case ACP_CONFIG_13:
+	case ACP_CONFIG_14:
+		if (is_dmic_dev && is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else if (is_dmic_dev) {
 			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
 			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+		} else if (is_sdw_dev) {
+			switch (acp_data->sdw_master_count) {
+			case 1:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
+				break;
+			case 2:
+				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
+				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
+				break;
+			default:
+				return -EINVAL;
+			}
 		}
 		break;
+	default:
+		break;
 	}
+	return 0;
 }
 
 static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
@@ -188,6 +307,7 @@ static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
 
 static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
 {
+	struct acp_sdw_pdata *sdw_pdata;
 	struct platform_device_info pdevinfo[ACP63_DEVS];
 	struct device *parent;
 	int index;
@@ -220,8 +340,110 @@ static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data
 		acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
 					     0, NULL, 0, NULL, 0);
 		break;
+	case ACP63_SDW_DEV_MASK:
+		if (adata->pdev_count == ACP63_SDW0_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata->instance = 0;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->sdw0_dev_index = 0;
+			adata->sdw_dma_dev_index = 1;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     sdw_pdata, sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+		} else if (adata->pdev_count == ACP63_SDW0_SDW1_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata[0].instance = 0;
+			sdw_pdata[1].instance = 1;
+			sdw_pdata[0].sdw_lock = &adata->acp_lock;
+			sdw_pdata[1].sdw_lock = &adata->acp_lock;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->sdw0_dev_index = 0;
+			adata->sdw1_dev_index = 1;
+			adata->sdw_dma_dev_index = 2;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 1, adata->res, 1,
+						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+		}
+		break;
+	case ACP63_SDW_PDM_DEV_MASK:
+		if (adata->pdev_count == ACP63_SDW0_PDM_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata),
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+
+			sdw_pdata->instance = 0;
+			sdw_pdata->sdw_lock = &adata->acp_lock;
+			adata->pdm_dev_index  = 0;
+			adata->sdw0_dev_index = 1;
+			adata->sdw_dma_dev_index = 2;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     sdw_pdata, sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "dmic-codec",
+						     0, NULL, 0, NULL, 0);
+		} else if (adata->pdev_count == ACP63_SDW0_SDW1_PDM_MODE_DEVS) {
+			sdw_pdata = devm_kzalloc(&pci->dev, sizeof(struct acp_sdw_pdata) * 2,
+						 GFP_KERNEL);
+			if (!sdw_pdata) {
+				ret = -ENOMEM;
+				goto de_init;
+			}
+			sdw_pdata[0].instance = 0;
+			sdw_pdata[1].instance = 1;
+			sdw_pdata[0].sdw_lock = &adata->acp_lock;
+			sdw_pdata[1].sdw_lock = &adata->acp_lock;
+			adata->pdm_dev_index = 0;
+			adata->sdw0_dev_index = 1;
+			adata->sdw1_dev_index = 2;
+			adata->sdw_dma_dev_index = 3;
+			acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[1], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 0, adata->res, 1,
+						     &sdw_pdata[0], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[2], parent, adata->sdw_fw_node,
+						     "amd_sdw_controller", 1, adata->res, 1,
+						     &sdw_pdata[1], sizeof(struct acp_sdw_pdata));
+			acp63_fill_platform_dev_info(&pdevinfo[3], parent, NULL, "amd_ps_sdw_dma",
+						     0, adata->res, 1, &adata->acp_lock,
+						     sizeof(adata->acp_lock));
+			acp63_fill_platform_dev_info(&pdevinfo[4], parent, NULL, "dmic-codec",
+						     0, NULL, 0, NULL, 0);
+		}
+		break;
 	default:
-		dev_dbg(&pci->dev, "No PDM devices found\n");
+		dev_dbg(&pci->dev, "No PDM or Soundwire controller devices found\n");
 		return 0;
 	}
 
@@ -299,7 +521,11 @@ static int snd_acp63_probe(struct pci_dev *pci,
 		goto de_init;
 	}
 	val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
-	get_acp63_device_config(val, pci, adata);
+	ret = get_acp63_device_config(val, pci, adata);
+	if (ret) {
+		dev_err(&pci->dev, "get acp device config failed:%d\n", ret);
+		goto de_init;
+	}
 	ret = create_acp63_platform_devs(pci, adata, addr);
 	if (ret < 0) {
 		dev_err(&pci->dev, "ACP platform devices creation failed\n");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

AMD ACP IP block has two soundwire controller devices.
Add support for
- Master driver probe & remove sequence
- Helper functions to enable/disable interrupts, Initialize sdw controller,
  enable sdw pads
- Master driver sdw_master_ops & port_ops callbacks

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |  279 ++++++++
 include/linux/soundwire/sdw_amd.h |   21 +
 3 files changed, 1375 insertions(+)
 create mode 100644 drivers/soundwire/amd_master.c
 create mode 100644 drivers/soundwire/amd_master.h

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
new file mode 100644
index 000000000000..7e1f618254ac
--- /dev/null
+++ b/drivers/soundwire/amd_master.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SoundWire AMD Master driver
+ *
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/completion.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/soundwire/sdw_amd.h>
+#include <linux/wait.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "bus.h"
+#include "amd_master.h"
+
+#define DRV_NAME "amd_sdw_controller"
+
+#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
+
+static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 sw_pad_enable_mask;
+	u32 sw_pad_pulldown_mask;
+	u32 sw_pad_pulldown_val;
+	u32 val = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
+		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+		break;
+	case ACP_SDW1:
+		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
+		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
+	val |= sw_pad_enable_mask;
+	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
+	mutex_unlock(ctrl->sdw_lock);
+	usleep_range(1000, 1500);
+
+	mutex_lock(ctrl->sdw_lock);
+	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
+	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
+	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
+	mutex_unlock(ctrl->sdw_lock);
+	return 0;
+}
+
+static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
+	u32 val = 0;
+	u32 timeout = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+
+	/* Sdw Controller reset */
+	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
+	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
+		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+		if (timeout > AMD_DELAY_LOOP_ITERATION)
+			break;
+		usleep_range(1, 5);
+		timeout++;
+	}
+	timeout = 0;
+	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
+	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+	while (val) {
+		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+		if (timeout > AMD_DELAY_LOOP_ITERATION)
+			break;
+		usleep_range(1, 5);
+		timeout++;
+	}
+	if (timeout == AMD_DELAY_LOOP_ITERATION) {
+		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+	retry_count = 0;
+	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (!val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 acp_sw_en_reg;
+	u32 acp_sw_en_stat_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 clk_resume_ctrl_reg;
+	u32 acp_sw_en_reg;
+	u32 acp_sw_en_stat_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
+
+	/*
+	 * After invoking controller disable sequence, check whether
+	 * controller has executed clock stop sequence. In this case,
+	 * controller should ignore checking enable status register.
+	 */
+	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+	if (val)
+		return 0;
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (!val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 val;
+	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
+	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;
+		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
+		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
+		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = SW_ERROR_INTR_MASK;
+		break;
+	case ACP_SDW1:
+		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
+		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
+		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
+		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
+	val |= acp_sdw_intr_mask;
+	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
+	mutex_unlock(ctrl->sdw_lock);
+	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
+	if (val)
+		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
+	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
+	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
+	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
+	return 0;
+}
+
+static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 val;
+	u32 acp_ext_intr_cntl;
+	u32 acp_sdw_intr_mask;
+	u32 sw_stat_mask_0to7;
+	u32 sw_stat_mask_8to11;
+	u32 sw_err_intr_mask;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_ext_intr_cntl = ACP_EXTERNAL_INTR_CNTL;
+		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
+		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = SW_ERROR_INTR_MASK;
+		break;
+	case ACP_SDW1:
+		acp_ext_intr_cntl = ACP_EXTERNAL_INTR_CNTL1;
+		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
+		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_cntl);
+	val &= ~acp_sdw_intr_mask;
+	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_cntl);
+	mutex_unlock(ctrl->sdw_lock);
+
+	acp_reg_writel(0x00, ctrl->mmio + sw_stat_mask_0to7);
+	acp_reg_writel(0x00, ctrl->mmio + sw_stat_mask_8to11);
+	acp_reg_writel(0x00, ctrl->mmio + sw_err_intr_mask);
+	return 0;
+}
+
+static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
+{
+	u32 sdw_rows, sdw_cols, frame_size;
+	u32 acp_sw_frame_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_frame_reg = ACP_SW_FRAMESIZE;
+		break;
+	case ACP_SDW1:
+		acp_sw_frame_reg = ACP_P1_SW_FRAMESIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sdw_cols = sdw_find_col_index(cols);
+	sdw_rows = sdw_find_row_index(rows);
+	frame_size = (sdw_rows << 3) | sdw_cols;
+	acp_reg_writel(frame_size, ctrl->mmio + acp_sw_frame_reg);
+	return 0;
+}
+
+static void amd_sdwc_ctl_word_prep(u32 *low_word, u32 *high_word, u32 cmd_type,
+				   struct sdw_msg *msg, int cmd_offset)
+{
+	u32 low_data = 0, high_data = 0;
+	u16 addr;
+	u8 addr_high, addr_low;
+	u8 data = 0;
+
+	addr = msg->addr + cmd_offset;
+	addr_high = (addr & 0xFF00) >> 8;
+	addr_low = addr & 0xFF;
+
+	if (cmd_type == AMD_SDW_CMD_WRITE)
+		data = msg->buf[cmd_offset];
+
+	high_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num);
+	high_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, cmd_type);
+	high_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, addr_high);
+	low_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, addr_low);
+	low_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data);
+
+	*high_word = high_data;
+	*low_word = low_data;
+}
+
+static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
+{
+	u64 resp = 0;
+	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
+	u32 imm_resp_uword_reg, imm_resp_lword_reg;
+	u32 resp_lower, resp_high;
+	u32 sts = 0;
+	u32 timeout = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		imm_cmd_stat_reg = SW_IMM_CMD_STS;
+		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
+		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
+		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
+		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
+		break;
+	case ACP_SDW1:
+		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
+		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
+		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
+		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
+		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+	while (sts & AMD_SDW_IMM_CMD_BUSY) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
+				ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+
+	timeout = 0;
+	if (sts & AMD_SDW_IMM_RES_VALID) {
+		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
+		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
+	}
+	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
+	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
+
+	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
+	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
+	timeout = 0;
+	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
+	while ((sts & AMD_SDW_IMM_RES_VALID)) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+	resp = resp_high;
+	resp = (resp << 32) | resp_lower;
+	return resp;
+}
+
+static enum sdw_command_response
+amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
+{
+	struct sdw_msg scp_msg = {0};
+	u64 response_buf[2] = {0};
+	u32 uword = 0, lword = 0;
+	int nack = 0, no_ack = 0;
+	int index, timeout = 0;
+
+	scp_msg.dev_num = msg->dev_num;
+	scp_msg.addr = SDW_SCP_ADDRPAGE1;
+	scp_msg.buf = &msg->addr_page1;
+	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
+	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+	scp_msg.addr = SDW_SCP_ADDRPAGE2;
+	scp_msg.buf = &msg->addr_page2;
+	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
+	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+
+	/* check response the writes */
+	for (index = 0; index < 2; index++) {
+		if (response_buf[index] == -ETIMEDOUT) {
+			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
+			timeout = 1;
+		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
+			no_ack = 1;
+			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
+				nack = 1;
+				dev_err(ctrl->dev, "Program SCP NACK received\n");
+			}
+		}
+	}
+
+	if (timeout) {
+		dev_err_ratelimited(ctrl->dev,
+				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+		return SDW_CMD_TIMEOUT;
+	}
+
+	if (nack) {
+		dev_err_ratelimited(ctrl->dev,
+				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
+		return SDW_CMD_FAIL;
+	}
+
+	if (no_ack) {
+		dev_dbg_ratelimited(ctrl->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_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
+{
+	int ret;
+
+	if (msg->page) {
+		ret = amd_program_scp_addr(ctrl, msg);
+		if (ret) {
+			msg->len = 0;
+			return ret;
+		}
+	}
+	switch (msg->flags) {
+	case SDW_MSG_FLAG_READ:
+		*cmd = AMD_SDW_CMD_READ;
+		break;
+	case SDW_MSG_FLAG_WRITE:
+		*cmd = AMD_SDW_CMD_WRITE;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
+				       int cmd, int cmd_offset)
+{
+	u64 response = 0;
+	u32 uword = 0, lword = 0;
+	int nack = 0, no_ack = 0;
+	int timeout = 0;
+
+	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
+	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+
+	if (response & AMD_SDW_MCP_RESP_ACK) {
+		if (cmd == AMD_SDW_CMD_READ)
+			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
+	} else {
+		no_ack = 1;
+		if (response == -ETIMEDOUT) {
+			timeout = 1;
+		} else if (response & AMD_SDW_MCP_RESP_NACK) {
+			nack = 1;
+			dev_err(ctrl->dev, "Program SCP NACK received\n");
+		}
+	}
+
+	if (timeout) {
+		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
+		return SDW_CMD_TIMEOUT;
+	}
+	if (nack) {
+		dev_err_ratelimited(ctrl->dev,
+				    "command response NACK received for Slave %d\n", msg->dev_num);
+		return SDW_CMD_FAIL;
+	}
+
+	if (no_ack) {
+		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
+		return SDW_CMD_IGNORED;
+	}
+	return SDW_CMD_OK;
+}
+
+static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	int ret, i;
+	int cmd = 0;
+
+	ret = amd_prep_msg(ctrl, msg, &cmd);
+	if (ret)
+		return SDW_CMD_FAIL_OTHER;
+	for (i = 0; i < msg->len; i++) {
+		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
+		if (ret)
+			return ret;
+	}
+	return SDW_CMD_OK;
+}
+
+static enum sdw_command_response
+amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	struct sdw_msg msg;
+
+	/* Create dummy message with valid device number */
+	memset(&msg, 0, sizeof(msg));
+	msg.dev_num = dev_num;
+	return amd_program_scp_addr(ctrl, &msg);
+}
+
+static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u64 response;
+	u32 slave_stat = 0;
+
+	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
+	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
+	return slave_stat;
+}
+
+static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
+					 struct sdw_transport_data *t_data)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+	struct sdw_port_runtime *p_rt;
+	int port_bo, sample_int;
+	unsigned int rate, bps, ch = 0;
+	unsigned int slave_total_ch;
+	struct sdw_bus_params *b_params = &m_rt->bus->params;
+
+	port_bo = t_data->block_offset;
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		rate = m_rt->stream->params.rate;
+		bps = m_rt->stream->params.bps;
+		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
+		slave_total_ch = 0;
+
+		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
+
+			sdw_fill_xport_params(&p_rt->transport_params,
+					      p_rt->num, false,
+					      SDW_BLK_GRP_CNT_1,
+					      sample_int, port_bo, port_bo >> 8,
+					      t_data->hstart,
+					      t_data->hstop,
+					      SDW_BLK_PKG_PER_PORT, 0x0);
+
+			sdw_fill_port_params(&p_rt->port_params,
+					     p_rt->num, bps,
+					     SDW_PORT_FLOW_MODE_ISOCH,
+					     b_params->s_data_mode);
+
+			port_bo += bps * ch;
+			slave_total_ch += ch;
+		}
+
+		if (m_rt->direction == SDW_DATA_DIR_TX &&
+		    m_rt->ch_count == slave_total_ch) {
+			port_bo = t_data->block_offset;
+		}
+	}
+}
+
+static int amd_sdwc_compute_params(struct sdw_bus *bus)
+{
+	struct sdw_transport_data t_data = {0};
+	struct sdw_master_runtime *m_rt;
+	struct sdw_port_runtime *p_rt;
+	struct sdw_bus_params *b_params = &bus->params;
+	int port_bo, hstart, hstop, sample_int;
+	unsigned int rate, bps;
+
+	port_bo  = 0;
+	hstart = 1;
+	hstop = bus->params.col - 1;
+	t_data.hstop = hstop;
+	t_data.hstart = hstart;
+
+	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
+		rate = m_rt->stream->params.rate;
+		bps = m_rt->stream->params.bps;
+		sample_int = (bus->params.curr_dr_freq / rate);
+		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+			port_bo = (p_rt->num * 64) + 1;
+			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
+				p_rt->num, hstart, hstop, port_bo);
+			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
+					      false, SDW_BLK_GRP_CNT_1, sample_int,
+					      port_bo, port_bo >> 8, hstart, hstop,
+					      SDW_BLK_PKG_PER_PORT, 0x0);
+
+			sdw_fill_port_params(&p_rt->port_params,
+					     p_rt->num, bps,
+					     SDW_PORT_FLOW_MODE_ISOCH,
+					     b_params->m_data_mode);
+			t_data.hstart = hstart;
+			t_data.hstop = hstop;
+			t_data.block_offset = port_bo;
+			t_data.sub_block_offset = 0;
+		}
+		amd_sdwc_compute_slave_ports(m_rt, &t_data);
+	}
+	return 0;
+}
+
+static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
+				unsigned int bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
+
+	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		channel_type = p_params->num;
+		break;
+	case ACP_SDW1:
+		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_HS_TX:
+		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_BT_TX:
+		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW1_BT_TX:
+		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_AUDIO_RX:
+		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_HS_RX:
+		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_BT_RX:
+		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW1_BT_RX:
+		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
+		break;
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
+	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
+	return 0;
+}
+
+static int amd_sdwc_transport_params(struct sdw_bus *bus,
+				     struct sdw_transport_params *params,
+				     enum sdw_reg_bank bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 ssp_counter_reg;
+	u32 dpn_frame_fmt;
+	u32 dpn_sampleinterval;
+	u32 dpn_hctrl;
+	u32 dpn_offsetctrl;
+	u32 dpn_lanectrl;
+	u32 channel_type;
+	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
+	u32 offset_reg, lane_ctrl_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		ssp_counter_reg = ACP_SW_SSP_COUNTER;
+		channel_type = params->port_num;
+		break;
+	case ACP_SDW1:
+		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
+		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
+	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
+		__func__, params->port_num, channel_type);
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+	{
+		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
+		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
+		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_HS_TX:
+	{
+		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
+		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
+		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_BT_TX:
+	{
+		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
+		offset_reg = ACP_SW_BT_TX_OFFSET;
+		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW1_BT_TX:
+	{
+		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
+		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
+		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_AUDIO_RX:
+	{
+		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
+		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
+		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_HS_RX:
+	{
+		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
+		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
+		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_BT_RX:
+	{
+		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
+		offset_reg = ACP_SW_BT_RX_OFFSET;
+		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW1_BT_RX:
+	{
+		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
+		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
+		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
+	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
+	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
+	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
+	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
+
+	dpn_sampleinterval = params->sample_interval - 1;
+	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
+
+	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
+	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
+	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
+
+	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
+	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
+	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
+
+	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
+	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
+	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
+	return 0;
+}
+
+static int amd_sdwc_port_enable(struct sdw_bus *bus,
+				struct sdw_enable_ch *enable_ch,
+				unsigned int bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 dpn_ch_enable;
+	u32 ch_enable_reg, channel_type;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		channel_type = enable_ch->port_num;
+		break;
+	case ACP_SDW1:
+		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_HS_TX:
+		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_BT_TX:
+		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW1_BT_TX:
+		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_AUDIO_RX:
+		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_HS_RX:
+		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_BT_RX:
+		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW1_BT_RX:
+		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+
+	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
+	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
+	if (enable_ch->enable)
+		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
+	else
+		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
+	return 0;
+}
+
+static int sdw_master_read_amd_prop(struct sdw_bus *bus)
+{
+	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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-wake-enable", &wake_en_mask);
+	ctrl->wake_en_mask = wake_en_mask;
+	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
+	ctrl->power_mode_mask = power_mode_mask;
+	return 0;
+}
+
+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_sdwc_port_ops = {
+	.dpn_set_port_params = amd_sdwc_port_params,
+	.dpn_set_port_transport_params = amd_sdwc_transport_params,
+	.dpn_port_enable_ch = amd_sdwc_port_enable,
+};
+
+static const struct sdw_master_ops amd_sdwc_ops = {
+	.read_prop = amd_prop_read,
+	.xfer_msg = amd_sdwc_xfer_msg,
+	.reset_page_addr = amd_reset_page_addr,
+	.read_ping_status = amd_sdwc_read_ping_status,
+};
+
+static void amd_sdwc_probe_work(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
+	struct sdw_master_prop *prop;
+	int ret;
+
+	prop = &ctrl->bus.prop;
+	if (!prop->hw_disabled) {
+		ret = amd_enable_sdw_pads(ctrl);
+		if (ret)
+			return;
+		ret = amd_init_sdw_controller(ctrl);
+		if (ret)
+			return;
+		amd_enable_sdw_interrupts(ctrl);
+		ret = amd_enable_sdw_controller(ctrl);
+		if (ret)
+			return;
+		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
+		if (!ret)
+			ctrl->startup_done = true;
+	}
+}
+
+static int amd_sdwc_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 sdw_bus_params *params;
+	struct amd_sdwc_ctrl *ctrl;
+	int ret;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "platform_data not retrieved\n");
+		return -ENODEV;
+	}
+	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+		return -ENOMEM;
+	}
+	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (IS_ERR(ctrl->mmio)) {
+		dev_err(&pdev->dev, "mmio not found\n");
+		return PTR_ERR(ctrl->mmio);
+	}
+	ctrl->instance = pdata->instance;
+	ctrl->sdw_lock  = pdata->sdw_lock;
+	ctrl->rows_index = sdw_find_row_index(50);
+	ctrl->cols_index = sdw_find_col_index(10);
+
+	ctrl->dev = dev;
+	dev_set_drvdata(&pdev->dev, ctrl);
+
+	ctrl->bus.ops = &amd_sdwc_ops;
+	ctrl->bus.port_ops = &amd_sdwc_port_ops;
+	ctrl->bus.compute_params = &amd_sdwc_compute_params;
+	ctrl->bus.clk_stop_timeout = 1;
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
+		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
+		break;
+	case ACP_SDW1:
+		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
+		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	params = &ctrl->bus.params;
+	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
+	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
+	params->col = 10;
+	params->row = 50;
+
+	prop = &ctrl->bus.prop;
+	prop->clk_freq = &amd_sdwc_freq_tbl[0];
+	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
+	ctrl->bus.link_id = ctrl->instance;
+	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
+	if (ret) {
+		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
+			ret);
+		return ret;
+	}
+	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
+	schedule_work(&ctrl->probe_work);
+	return 0;
+}
+
+static int amd_sdwc_remove(struct platform_device *pdev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	amd_disable_sdw_interrupts(ctrl);
+	sdw_bus_master_delete(&ctrl->bus);
+	ret = amd_disable_sdw_controller(ctrl);
+	return ret;
+}
+
+static struct platform_driver amd_sdwc_driver = {
+	.probe	= &amd_sdwc_probe,
+	.remove = &amd_sdwc_remove,
+	.driver = {
+		.name	= "amd_sdw_controller",
+	}
+};
+module_platform_driver(amd_sdwc_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD soundwire driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
new file mode 100644
index 000000000000..42f32ca0c7a8
--- /dev/null
+++ b/drivers/soundwire/amd_master.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __AMD_MASTER_H
+#define __AMD_MASTER_H
+
+#define ACP_PAD_PULLDOWN_CTRL                         0x0001448
+#define ACP_SW_PAD_KEEPER_EN                          0x0001454
+#define ACP_SW_WAKE_EN                                0x0001458
+#define ACP_SW1_WAKE_EN                               0x0001460
+#define ACP_SW_I2S_ERROR_REASON                       0x00018B4
+
+#define ACP_EXTERNAL_INTR_ENB                         0x0001A00
+#define ACP_EXTERNAL_INTR_CNTL                        0x0001A04
+#define ACP_EXTERNAL_INTR_CNTL1                       0x0001A08
+#define ACP_EXTERNAL_INTR_STAT                        0x0001A0C
+#define ACP_EXTERNAL_INTR_STAT1                       0x0001A10
+#define ACP_ERROR_STATUS                              0x0001A4C
+#define ACP_P1_SW_I2S_ERROR_REASON                    0x0001A50
+
+#define ACP_SW_EN                                     0x0003000
+#define ACP_SW_EN_STATUS                              0x0003004
+#define ACP_SW_FRAMESIZE                              0x0003008
+#define ACP_SW_SSP_COUNTER                            0x000300C
+#define ACP_SW_AUDIO_TX_EN                            0x0003010
+#define ACP_SW_AUDIO_TX_EN_STATUS                     0x0003014
+#define ACP_SW_AUDIO_TX_FRAME_FORMAT                  0x0003018
+#define ACP_SW_AUDIO_TX_SAMPLEINTERVAL                0x000301C
+#define ACP_SW_AUDIO_TX_HCTRL_DP0                     0x0003020
+#define ACP_SW_AUDIO_TX_HCTRL_DP1                     0x0003024
+#define ACP_SW_AUDIO_TX_HCTRL_DP2                     0x0003028
+#define ACP_SW_AUDIO_TX_HCTRL_DP3                     0x000302C
+#define ACP_SW_AUDIO_TX_OFFSET_DP0                    0x0003030
+#define ACP_SW_AUDIO_TX_OFFSET_DP1                    0x0003034
+#define ACP_SW_AUDIO_TX_OFFSET_DP2                    0x0003038
+#define ACP_SW_AUDIO_TX_OFFSET_DP3                    0x000303C
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0            0x0003040
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP1            0x0003044
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP2            0x0003048
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP3            0x000304C
+#define ACP_SW_BT_TX_EN                               0x0003050
+#define ACP_SW_BT_TX_EN_STATUS                        0x0003054
+#define ACP_SW_BT_TX_FRAME_FORMAT                     0x0003058
+#define ACP_SW_BT_TX_SAMPLEINTERVAL                   0x000305C
+#define ACP_SW_BT_TX_HCTRL                            0x0003060
+#define ACP_SW_BT_TX_OFFSET                           0x0003064
+#define ACP_SW_BT_TX_CHANNEL_ENABLE_DP0               0x0003068
+#define ACP_SW_HEADSET_TX_EN                          0x000306C
+#define ACP_SW_HEADSET_TX_EN_STATUS                   0x0003070
+#define ACP_SW_HEADSET_TX_FRAME_FORMAT                0x0003074
+#define ACP_SW_HEADSET_TX_SAMPLEINTERVAL              0x0003078
+#define ACP_SW_HEADSET_TX_HCTRL                       0x000307C
+#define ACP_SW_HEADSET_TX_OFFSET                      0x0003080
+#define ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0          0x0003084
+#define ACP_SW_AUDIO_RX_EN                            0x0003088
+#define ACP_SW_AUDIO_RX_EN_STATUS                     0x000308C
+#define ACP_SW_AUDIO_RX_FRAME_FORMAT                  0x0003090
+#define ACP_SW_AUDIO_RX_SAMPLEINTERVAL                0x0003094
+#define ACP_SW_AUDIO_RX_HCTRL_DP0                     0x0003098
+#define ACP_SW_AUDIO_RX_HCTRL_DP1                     0x000309C
+#define ACP_SW_AUDIO_RX_HCTRL_DP2                     0x0003100
+#define ACP_SW_AUDIO_RX_HCTRL_DP3                     0x0003104
+#define ACP_SW_AUDIO_RX_OFFSET_DP0                    0x0003108
+#define ACP_SW_AUDIO_RX_OFFSET_DP1                    0x000310C
+#define ACP_SW_AUDIO_RX_OFFSET_DP2                    0x0003110
+#define ACP_SW_AUDIO_RX_OFFSET_DP3                    0x0003114
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0            0x0003118
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP1            0x000311C
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP2            0x0003120
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP3            0x0003124
+#define ACP_SW_BT_RX_EN                               0x0003128
+#define ACP_SW_BT_RX_EN_STATUS                        0x000312C
+#define ACP_SW_BT_RX_FRAME_FORMAT                     0x0003130
+#define ACP_SW_BT_RX_SAMPLEINTERVAL                   0x0003134
+#define ACP_SW_BT_RX_HCTRL                            0x0003138
+#define ACP_SW_BT_RX_OFFSET                           0x000313C
+#define ACP_SW_BT_RX_CHANNEL_ENABLE_DP0               0x0003140
+#define ACP_SW_HEADSET_RX_EN                          0x0003144
+#define ACP_SW_HEADSET_RX_EN_STATUS                   0x0003148
+#define ACP_SW_HEADSET_RX_FRAME_FORMAT                0x000314C
+#define ACP_SW_HEADSET_RX_SAMPLEINTERVAL              0x0003150
+#define ACP_SW_HEADSET_RX_HCTRL                       0x0003154
+#define ACP_SW_HEADSET_RX_OFFSET                      0x0003158
+#define ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0          0x000315C
+#define ACP_SW_BPT_PORT_EN                            0x0003160
+#define ACP_SW_BPT_PORT_EN_STATUS                     0x0003164
+#define ACP_SW_BPT_PORT_FRAME_FORMAT                  0x0003168
+#define ACP_SW_BPT_PORT_SAMPLEINTERVAL                0x000316C
+#define ACP_SW_BPT_PORT_HCTRL                         0x0003170
+#define ACP_SW_BPT_PORT_OFFSET                        0x0003174
+#define ACP_SW_BPT_PORT_CHANNEL_ENABLE                0x0003178
+#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR               0x000317C
+#define ACP_SW_CLK_RESUME_CTRL                        0x0003180
+#define ACP_SW_CLK_RESUME_DELAY_CNTR                  0x0003184
+#define ACP_SW_BUS_RESET_CTRL                         0x0003188
+#define ACP_SW_PRBS_ERR_STATUS                        0x000318C
+#define SW_IMM_CMD_UPPER_WORD                         0x0003230
+#define SW_IMM_CMD_LOWER_QWORD                        0x0003234
+#define SW_IMM_RESP_UPPER_WORD                        0x0003238
+#define SW_IMM_RESP_LOWER_QWORD                       0x000323C
+#define SW_IMM_CMD_STS                                0x0003240
+#define SW_BRA_BASE_ADDRESS                           0x0003244
+#define SW_BRA_TRANSFER_SIZE                          0x0003248
+#define SW_BRA_DMA_BUSY                               0x000324C
+#define SW_BRA_RESP                                   0x0003250
+#define SW_BRA_RESP_FRAME_ADDR                        0x0003254
+#define SW_BRA_CURRENT_TRANSFER_SIZE                  0x0003258
+#define SW_STATE_CHANGE_STATUS_0TO7                   0x000325C
+#define SW_STATE_CHANGE_STATUS_8TO11                  0x0003260
+#define SW_STATE_CHANGE_STATUS_MASK_0TO7              0x0003264
+#define SW_STATE_CHANGE_STATUS_MASK_8TO11             0x0003268
+#define SW_CLK_FREQUENCY_CTRL                         0x000326C
+#define SW_ERROR_INTR_MASK                            0x0003270
+#define SW_PHY_TEST_MODE_DATA_OFF                     0x0003274
+
+#define ACP_P1_SW_EN                                  0x0003C00
+#define ACP_P1_SW_EN_STATUS                           0x0003C04
+#define ACP_P1_SW_FRAMESIZE                           0x0003C08
+#define ACP_P1_SW_SSP_COUNTER                         0x0003C0C
+#define ACP_P1_SW_BT_TX_EN                            0x0003C50
+#define ACP_P1_SW_BT_TX_EN_STATUS                     0x0003C54
+#define ACP_P1_SW_BT_TX_FRAME_FORMAT                  0x0003C58
+#define ACP_P1_SW_BT_TX_SAMPLEINTERVAL                0x0003C5C
+#define ACP_P1_SW_BT_TX_HCTRL                         0x0003C60
+#define ACP_P1_SW_BT_TX_OFFSET                        0x0003C64
+#define ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0            0x0003C68
+#define ACP_P1_SW_BT_RX_EN                            0x0003D28
+#define ACP_P1_SW_BT_RX_EN_STATUS                     0x0003D2C
+#define ACP_P1_SW_BT_RX_FRAME_FORMAT                  0x0003D30
+#define ACP_P1_SW_BT_RX_SAMPLEINTERVAL                0x0003D34
+#define ACP_P1_SW_BT_RX_HCTRL                         0x0003D38
+#define ACP_P1_SW_BT_RX_OFFSET                        0x0003D3C
+#define ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0            0x0003D40
+#define ACP_P1_SW_BPT_PORT_EN                         0x0003D60
+#define ACP_P1_SW_BPT_PORT_EN_STATUS                  0x0003D64
+#define ACP_P1_SW_BPT_PORT_FRAME_FORMAT               0x0003D68
+#define ACP_P1_SW_BPT_PORT_SAMPLEINTERVAL             0x0003D6C
+#define ACP_P1_SW_BPT_PORT_HCTRL                      0x0003D70
+#define ACP_P1_SW_BPT_PORT_OFFSET                     0x0003D74
+#define ACP_P1_SW_BPT_PORT_CHANNEL_ENABLE             0x0003D78
+#define ACP_P1_SW_BPT_PORT_FIRST_BYTE_ADDR            0x0003D7C
+#define ACP_P1_SW_CLK_RESUME_CTRL                     0x0003D80
+#define ACP_P1_SW_CLK_RESUME_DELAY_CNTR               0x0003D84
+#define ACP_P1_SW_BUS_RESET_CTRL                      0x0003D88
+#define ACP_P1_SW_PRBS_ERR_STATUS                     0x0003D8C
+
+#define P1_SW_IMM_CMD_UPPER_WORD                      0x0003E30
+#define P1_SW_IMM_CMD_LOWER_QWORD                     0x0003E34
+#define P1_SW_IMM_RESP_UPPER_WORD                     0x0003E38
+#define P1_SW_IMM_RESP_LOWER_QWORD                    0x0003E3C
+#define P1_SW_IMM_CMD_STS                             0x0003E40
+#define P1_SW_BRA_BASE_ADDRESS                        0x0003E44
+#define P1_SW_BRA_TRANSFER_SIZE                       0x0003E48
+#define P1_SW_BRA_DMA_BUSY                            0x0003E4C
+#define P1_SW_BRA_RESP                                0x0003E50
+#define P1_SW_BRA_RESP_FRAME_ADDR                     0x0003E54
+#define P1_SW_BRA_CURRENT_TRANSFER_SIZE               0x0003E58
+#define P1_SW_STATE_CHANGE_STATUS_0TO7                0x0003E5C
+#define P1_SW_STATE_CHANGE_STATUS_8TO11               0x0003E60
+#define P1_SW_STATE_CHANGE_STATUS_MASK_0TO7           0x0003E64
+#define P1_SW_STATE_CHANGE_STATUS_MASK_8TO11          0x0003E68
+#define P1_SW_CLK_FREQUENCY_CTRL                      0x0003E6C
+#define P1_SW_ERROR_INTR_MASK                         0x0003E70
+#define P1_SW_PHY_TEST_MODE_DATA_OFF                  0x0003E74
+
+#define ACP_PHY_BASE_ADDRESS		0x1240000
+#define AMD_DELAY_LOOP_ITERATION	1000
+#define AMD_SDW_DEFAULT_CLK_FREQ	12000000
+#define AMD_SDW_RETRY_COUNT		1000
+
+#define AMD_SDW_MCP_RESP_ACK		BIT(0)
+#define AMD_SDW_MCP_RESP_NACK		BIT(1)
+#define AMD_SDW_MCP_RESP_RDATA		GENMASK(14, 7)
+
+#define AMD_SDW_MCP_CMD_SSP_TAG			BIT(31)
+#define AMD_SDW_MCP_CMD_COMMAND			GENMASK(14, 12)
+#define AMD_SDW_MCP_CMD_DEV_ADDR		GENMASK(11, 8)
+#define AMD_SDW_MCP_CMD_REG_ADDR_HIGH		GENMASK(7, 0)
+#define AMD_SDW_MCP_CMD_REG_ADDR_LOW		GENMASK(31, 24)
+#define AMD_SDW_MCP_CMD_REG_DATA		GENMASK(14, 7)
+#define AMD_SDW_MCP_SLAVE_STAT_0_3		GENMASK(14, 7)
+#define AMD_SDW_MCP_SLAVE_STAT_4_11		GENMASK(39, 24)
+#define AMD_SDW_MCP_SLAVE_STATUS_MASK		GENMASK(1, 0)
+#define AMD_SDW_MCP_SLAVE_STATUS_BITS		GENMASK(3, 2)
+#define AMD_SDW_MCP_SLAVE_STATUS_8TO_11		GENMASK(15, 0)
+#define AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(x)  BIT(((x) * 4))
+#define AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(x)	(((x) * 4) + 1)
+
+#define AMD_SDW_MASTER_SUSPEND_DELAY_MS		3000
+#define AMD_SDW_CLK_STOP_MAX_RETRY_COUNT	100
+#define AMD_SDW_QUIRK_MASK_BUS_ENABLE		BIT(0)
+
+#define AMD_SDW_IMM_RES_VALID		1
+#define AMD_SDW_IMM_CMD_BUSY		2
+#define AMD_SDW_ENABLE			1
+#define AMD_SDW_DISABLE			0
+#define AMD_SDW_BUS_RESET_CLEAR_REQ	0
+#define AMD_SDW_BUS_RESET_REQ		1
+#define AMD_SDW_BUS_RESET_DONE		2
+#define AMD_SDW_BUS_BASE_FREQ		24000000
+
+#define AMD_SDW0_EXT_INTR_MASK		0x200000
+#define AMD_SDW1_EXT_INTR_MASK		4
+#define AMD_SDW_IRQ_MASK_0TO7		0x77777777
+#define AMD_SDW_IRQ_MASK_8TO11		0x000D7777
+#define AMD_SDW_IRQ_ERROR_MASK		0xFF
+#define AMD_SDW_MAX_FREQ_NUM		1
+#define AMD_SDW0_MAX_TX_PORTS		3
+#define AMD_SDW0_MAX_RX_PORTS		3
+#define AMD_SDW1_MAX_TX_PORTS		1
+#define AMD_SDW1_MAX_RX_PORTS		1
+
+#define AMD_SDW_SLAVE_0_ATTACHED	5
+#define AMD_SDW_SSP_COUNTER_VAL	3
+
+#define AMD_DPN_FRAME_FMT_PFM			GENMASK(1, 0)
+#define AMD_DPN_FRAME_FMT_PDM			GENMASK(3, 2)
+#define AMD_DPN_FRAME_FMT_BLK_PKG_MODE		BIT(4)
+#define AMD_DPN_FRAME_FMT_BLK_GRP_CTRL		GENMASK(6, 5)
+#define AMD_DPN_FRAME_FMT_WORD_LEN		GENMASK(12, 7)
+#define AMD_DPN_FRAME_FMT_PCM_OR_PDM		BIT(13)
+#define AMD_DPN_HCTRL_HSTOP			GENMASK(3, 0)
+#define AMD_DPN_HCTRL_HSTART			GENMASK(7, 4)
+#define AMD_DPN_OFFSET_CTRL_1			GENMASK(7, 0)
+#define AMD_DPN_OFFSET_CTRL_2			GENMASK(15, 8)
+#define AMD_DPN_CH_EN_LCTRL			GENMASK(2, 0)
+#define AMD_DPN_CH_EN_CHMASK			GENMASK(10, 3)
+#define AMD_SDW_STAT_MAX_RETRY_COUNT		100
+#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK	0x7F9F
+#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK	0x7FFA
+#define AMD_SDW0_PAD_PULLDOWN_CTRL_DISABLE_MASK	0x60
+#define AMD_SDW1_PAD_PULLDOWN_CTRL_DISABLE_MASK	5
+#define AMD_SDW0_PAD_KEEPER_EN_MASK	1
+#define AMD_SDW1_PAD_KEEPER_EN_MASK	0x10
+#define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
+#define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
+
+enum amd_sdw_channel {
+	/* SDW0 */
+	ACP_SDW0_AUDIO_TX = 0,
+	ACP_SDW0_BT_TX,
+	ACP_SDW0_HS_TX,
+	ACP_SDW0_AUDIO_RX,
+	ACP_SDW0_BT_RX,
+	ACP_SDW0_HS_RX,
+	/* SDW1 */
+	ACP_SDW1_BT_TX,
+	ACP_SDW1_BT_RX,
+};
+
+enum amd_sdw_cmd_type {
+	AMD_SDW_CMD_PING = 0,
+	AMD_SDW_CMD_READ = 2,
+	AMD_SDW_CMD_WRITE = 3,
+};
+
+static u32 amd_sdwc_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = {
+	AMD_SDW_DEFAULT_CLK_FREQ,
+};
+
+struct sdw_transport_data {
+	int hstart;
+	int hstop;
+	int block_offset;
+	int sub_block_offset;
+};
+
+static inline u32 acp_reg_readl(void __iomem *base_addr)
+{
+	return readl(base_addr);
+}
+
+static inline void acp_reg_writel(u32 val, void __iomem *base_addr)
+{
+	writel(val, base_addr);
+}
+#endif
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index f0123815af46..5ec39f8c2f2e 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -10,9 +10,30 @@
 
 #define AMD_SDW_CLK_STOP_MODE		1
 #define AMD_SDW_POWER_OFF_MODE		2
+#define ACP_SDW0	0
+#define ACP_SDW1	1
+#define ACP_SDW0_MAX_DAI	6
 
 struct acp_sdw_pdata {
 	u16 instance;
 	struct mutex *sdw_lock;
 };
+
+struct amd_sdwc_ctrl {
+	struct sdw_bus bus;
+	struct device *dev;
+	void __iomem *mmio;
+	struct work_struct probe_work;
+	struct mutex *sdw_lock;
+	int num_din_ports;
+	int num_dout_ports;
+	int cols_index;
+	int rows_index;
+	u32 instance;
+	u32 quirks;
+	u32 wake_en_mask;
+	int num_ports;
+	bool startup_done;
+	u32 power_mode_mask;
+};
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

AMD ACP IP block has two soundwire controller devices.
Add support for
- Master driver probe & remove sequence
- Helper functions to enable/disable interrupts, Initialize sdw controller,
  enable sdw pads
- Master driver sdw_master_ops & port_ops callbacks

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |  279 ++++++++
 include/linux/soundwire/sdw_amd.h |   21 +
 3 files changed, 1375 insertions(+)
 create mode 100644 drivers/soundwire/amd_master.c
 create mode 100644 drivers/soundwire/amd_master.h

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
new file mode 100644
index 000000000000..7e1f618254ac
--- /dev/null
+++ b/drivers/soundwire/amd_master.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SoundWire AMD Master driver
+ *
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/completion.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/soundwire/sdw_amd.h>
+#include <linux/wait.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "bus.h"
+#include "amd_master.h"
+
+#define DRV_NAME "amd_sdw_controller"
+
+#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
+
+static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 sw_pad_enable_mask;
+	u32 sw_pad_pulldown_mask;
+	u32 sw_pad_pulldown_val;
+	u32 val = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
+		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+		break;
+	case ACP_SDW1:
+		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
+		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
+	val |= sw_pad_enable_mask;
+	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
+	mutex_unlock(ctrl->sdw_lock);
+	usleep_range(1000, 1500);
+
+	mutex_lock(ctrl->sdw_lock);
+	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
+	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
+	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
+	mutex_unlock(ctrl->sdw_lock);
+	return 0;
+}
+
+static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
+	u32 val = 0;
+	u32 timeout = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+
+	/* Sdw Controller reset */
+	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
+	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
+		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+		if (timeout > AMD_DELAY_LOOP_ITERATION)
+			break;
+		usleep_range(1, 5);
+		timeout++;
+	}
+	timeout = 0;
+	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
+	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+	while (val) {
+		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
+		if (timeout > AMD_DELAY_LOOP_ITERATION)
+			break;
+		usleep_range(1, 5);
+		timeout++;
+	}
+	if (timeout == AMD_DELAY_LOOP_ITERATION) {
+		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+	retry_count = 0;
+	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (!val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 acp_sw_en_reg;
+	u32 acp_sw_en_stat_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 clk_resume_ctrl_reg;
+	u32 acp_sw_en_reg;
+	u32 acp_sw_en_stat_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_en_reg = ACP_SW_EN;
+		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		acp_sw_en_reg = ACP_P1_SW_EN;
+		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
+
+	/*
+	 * After invoking controller disable sequence, check whether
+	 * controller has executed clock stop sequence. In this case,
+	 * controller should ignore checking enable status register.
+	 */
+	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+	if (val)
+		return 0;
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
+		if (!val)
+			break;
+		usleep_range(10, 50);
+	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
+
+	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
+		return -ETIMEDOUT;
+	return 0;
+}
+
+static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 val;
+	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
+	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;
+		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
+		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
+		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = SW_ERROR_INTR_MASK;
+		break;
+	case ACP_SDW1:
+		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
+		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
+		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
+		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
+	val |= acp_sdw_intr_mask;
+	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
+	mutex_unlock(ctrl->sdw_lock);
+	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
+	if (val)
+		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
+	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
+	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
+	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
+	return 0;
+}
+
+static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 val;
+	u32 acp_ext_intr_cntl;
+	u32 acp_sdw_intr_mask;
+	u32 sw_stat_mask_0to7;
+	u32 sw_stat_mask_8to11;
+	u32 sw_err_intr_mask;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_ext_intr_cntl = ACP_EXTERNAL_INTR_CNTL;
+		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
+		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = SW_ERROR_INTR_MASK;
+		break;
+	case ACP_SDW1:
+		acp_ext_intr_cntl = ACP_EXTERNAL_INTR_CNTL1;
+		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
+		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(ctrl->sdw_lock);
+	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_cntl);
+	val &= ~acp_sdw_intr_mask;
+	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_cntl);
+	mutex_unlock(ctrl->sdw_lock);
+
+	acp_reg_writel(0x00, ctrl->mmio + sw_stat_mask_0to7);
+	acp_reg_writel(0x00, ctrl->mmio + sw_stat_mask_8to11);
+	acp_reg_writel(0x00, ctrl->mmio + sw_err_intr_mask);
+	return 0;
+}
+
+static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
+{
+	u32 sdw_rows, sdw_cols, frame_size;
+	u32 acp_sw_frame_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		acp_sw_frame_reg = ACP_SW_FRAMESIZE;
+		break;
+	case ACP_SDW1:
+		acp_sw_frame_reg = ACP_P1_SW_FRAMESIZE;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sdw_cols = sdw_find_col_index(cols);
+	sdw_rows = sdw_find_row_index(rows);
+	frame_size = (sdw_rows << 3) | sdw_cols;
+	acp_reg_writel(frame_size, ctrl->mmio + acp_sw_frame_reg);
+	return 0;
+}
+
+static void amd_sdwc_ctl_word_prep(u32 *low_word, u32 *high_word, u32 cmd_type,
+				   struct sdw_msg *msg, int cmd_offset)
+{
+	u32 low_data = 0, high_data = 0;
+	u16 addr;
+	u8 addr_high, addr_low;
+	u8 data = 0;
+
+	addr = msg->addr + cmd_offset;
+	addr_high = (addr & 0xFF00) >> 8;
+	addr_low = addr & 0xFF;
+
+	if (cmd_type == AMD_SDW_CMD_WRITE)
+		data = msg->buf[cmd_offset];
+
+	high_data = FIELD_PREP(AMD_SDW_MCP_CMD_DEV_ADDR, msg->dev_num);
+	high_data |= FIELD_PREP(AMD_SDW_MCP_CMD_COMMAND, cmd_type);
+	high_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_HIGH, addr_high);
+	low_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_ADDR_LOW, addr_low);
+	low_data |= FIELD_PREP(AMD_SDW_MCP_CMD_REG_DATA, data);
+
+	*high_word = high_data;
+	*low_word = low_data;
+}
+
+static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
+{
+	u64 resp = 0;
+	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
+	u32 imm_resp_uword_reg, imm_resp_lword_reg;
+	u32 resp_lower, resp_high;
+	u32 sts = 0;
+	u32 timeout = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		imm_cmd_stat_reg = SW_IMM_CMD_STS;
+		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
+		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
+		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
+		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
+		break;
+	case ACP_SDW1:
+		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
+		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
+		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
+		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
+		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+	while (sts & AMD_SDW_IMM_CMD_BUSY) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
+				ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+
+	timeout = 0;
+	if (sts & AMD_SDW_IMM_RES_VALID) {
+		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
+		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
+	}
+	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
+	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
+
+	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
+	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
+	timeout = 0;
+	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
+	while ((sts & AMD_SDW_IMM_RES_VALID)) {
+		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
+		if (timeout > AMD_SDW_RETRY_COUNT) {
+			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
+			return -ETIMEDOUT;
+		}
+		timeout++;
+	}
+	resp = resp_high;
+	resp = (resp << 32) | resp_lower;
+	return resp;
+}
+
+static enum sdw_command_response
+amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
+{
+	struct sdw_msg scp_msg = {0};
+	u64 response_buf[2] = {0};
+	u32 uword = 0, lword = 0;
+	int nack = 0, no_ack = 0;
+	int index, timeout = 0;
+
+	scp_msg.dev_num = msg->dev_num;
+	scp_msg.addr = SDW_SCP_ADDRPAGE1;
+	scp_msg.buf = &msg->addr_page1;
+	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
+	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+	scp_msg.addr = SDW_SCP_ADDRPAGE2;
+	scp_msg.buf = &msg->addr_page2;
+	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
+	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+
+	/* check response the writes */
+	for (index = 0; index < 2; index++) {
+		if (response_buf[index] == -ETIMEDOUT) {
+			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
+			timeout = 1;
+		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
+			no_ack = 1;
+			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
+				nack = 1;
+				dev_err(ctrl->dev, "Program SCP NACK received\n");
+			}
+		}
+	}
+
+	if (timeout) {
+		dev_err_ratelimited(ctrl->dev,
+				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
+		return SDW_CMD_TIMEOUT;
+	}
+
+	if (nack) {
+		dev_err_ratelimited(ctrl->dev,
+				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
+		return SDW_CMD_FAIL;
+	}
+
+	if (no_ack) {
+		dev_dbg_ratelimited(ctrl->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_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
+{
+	int ret;
+
+	if (msg->page) {
+		ret = amd_program_scp_addr(ctrl, msg);
+		if (ret) {
+			msg->len = 0;
+			return ret;
+		}
+	}
+	switch (msg->flags) {
+	case SDW_MSG_FLAG_READ:
+		*cmd = AMD_SDW_CMD_READ;
+		break;
+	case SDW_MSG_FLAG_WRITE:
+		*cmd = AMD_SDW_CMD_WRITE;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
+				       int cmd, int cmd_offset)
+{
+	u64 response = 0;
+	u32 uword = 0, lword = 0;
+	int nack = 0, no_ack = 0;
+	int timeout = 0;
+
+	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
+	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
+
+	if (response & AMD_SDW_MCP_RESP_ACK) {
+		if (cmd == AMD_SDW_CMD_READ)
+			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
+	} else {
+		no_ack = 1;
+		if (response == -ETIMEDOUT) {
+			timeout = 1;
+		} else if (response & AMD_SDW_MCP_RESP_NACK) {
+			nack = 1;
+			dev_err(ctrl->dev, "Program SCP NACK received\n");
+		}
+	}
+
+	if (timeout) {
+		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
+		return SDW_CMD_TIMEOUT;
+	}
+	if (nack) {
+		dev_err_ratelimited(ctrl->dev,
+				    "command response NACK received for Slave %d\n", msg->dev_num);
+		return SDW_CMD_FAIL;
+	}
+
+	if (no_ack) {
+		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
+		return SDW_CMD_IGNORED;
+	}
+	return SDW_CMD_OK;
+}
+
+static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	int ret, i;
+	int cmd = 0;
+
+	ret = amd_prep_msg(ctrl, msg, &cmd);
+	if (ret)
+		return SDW_CMD_FAIL_OTHER;
+	for (i = 0; i < msg->len; i++) {
+		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
+		if (ret)
+			return ret;
+	}
+	return SDW_CMD_OK;
+}
+
+static enum sdw_command_response
+amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	struct sdw_msg msg;
+
+	/* Create dummy message with valid device number */
+	memset(&msg, 0, sizeof(msg));
+	msg.dev_num = dev_num;
+	return amd_program_scp_addr(ctrl, &msg);
+}
+
+static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u64 response;
+	u32 slave_stat = 0;
+
+	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
+	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
+	return slave_stat;
+}
+
+static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
+					 struct sdw_transport_data *t_data)
+{
+	struct sdw_slave_runtime *s_rt = NULL;
+	struct sdw_port_runtime *p_rt;
+	int port_bo, sample_int;
+	unsigned int rate, bps, ch = 0;
+	unsigned int slave_total_ch;
+	struct sdw_bus_params *b_params = &m_rt->bus->params;
+
+	port_bo = t_data->block_offset;
+	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
+		rate = m_rt->stream->params.rate;
+		bps = m_rt->stream->params.bps;
+		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
+		slave_total_ch = 0;
+
+		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
+			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
+
+			sdw_fill_xport_params(&p_rt->transport_params,
+					      p_rt->num, false,
+					      SDW_BLK_GRP_CNT_1,
+					      sample_int, port_bo, port_bo >> 8,
+					      t_data->hstart,
+					      t_data->hstop,
+					      SDW_BLK_PKG_PER_PORT, 0x0);
+
+			sdw_fill_port_params(&p_rt->port_params,
+					     p_rt->num, bps,
+					     SDW_PORT_FLOW_MODE_ISOCH,
+					     b_params->s_data_mode);
+
+			port_bo += bps * ch;
+			slave_total_ch += ch;
+		}
+
+		if (m_rt->direction == SDW_DATA_DIR_TX &&
+		    m_rt->ch_count == slave_total_ch) {
+			port_bo = t_data->block_offset;
+		}
+	}
+}
+
+static int amd_sdwc_compute_params(struct sdw_bus *bus)
+{
+	struct sdw_transport_data t_data = {0};
+	struct sdw_master_runtime *m_rt;
+	struct sdw_port_runtime *p_rt;
+	struct sdw_bus_params *b_params = &bus->params;
+	int port_bo, hstart, hstop, sample_int;
+	unsigned int rate, bps;
+
+	port_bo  = 0;
+	hstart = 1;
+	hstop = bus->params.col - 1;
+	t_data.hstop = hstop;
+	t_data.hstart = hstart;
+
+	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
+		rate = m_rt->stream->params.rate;
+		bps = m_rt->stream->params.bps;
+		sample_int = (bus->params.curr_dr_freq / rate);
+		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
+			port_bo = (p_rt->num * 64) + 1;
+			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
+				p_rt->num, hstart, hstop, port_bo);
+			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
+					      false, SDW_BLK_GRP_CNT_1, sample_int,
+					      port_bo, port_bo >> 8, hstart, hstop,
+					      SDW_BLK_PKG_PER_PORT, 0x0);
+
+			sdw_fill_port_params(&p_rt->port_params,
+					     p_rt->num, bps,
+					     SDW_PORT_FLOW_MODE_ISOCH,
+					     b_params->m_data_mode);
+			t_data.hstart = hstart;
+			t_data.hstop = hstop;
+			t_data.block_offset = port_bo;
+			t_data.sub_block_offset = 0;
+		}
+		amd_sdwc_compute_slave_ports(m_rt, &t_data);
+	}
+	return 0;
+}
+
+static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
+				unsigned int bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
+
+	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		channel_type = p_params->num;
+		break;
+	case ACP_SDW1:
+		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_HS_TX:
+		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_BT_TX:
+		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW1_BT_TX:
+		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_AUDIO_RX:
+		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_HS_RX:
+		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW0_BT_RX:
+		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
+		break;
+	case ACP_SDW1_BT_RX:
+		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
+		break;
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
+	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
+	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
+	return 0;
+}
+
+static int amd_sdwc_transport_params(struct sdw_bus *bus,
+				     struct sdw_transport_params *params,
+				     enum sdw_reg_bank bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 ssp_counter_reg;
+	u32 dpn_frame_fmt;
+	u32 dpn_sampleinterval;
+	u32 dpn_hctrl;
+	u32 dpn_offsetctrl;
+	u32 dpn_lanectrl;
+	u32 channel_type;
+	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
+	u32 offset_reg, lane_ctrl_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		ssp_counter_reg = ACP_SW_SSP_COUNTER;
+		channel_type = params->port_num;
+		break;
+	case ACP_SDW1:
+		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
+		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
+	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
+		__func__, params->port_num, channel_type);
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+	{
+		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
+		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
+		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_HS_TX:
+	{
+		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
+		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
+		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_BT_TX:
+	{
+		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
+		offset_reg = ACP_SW_BT_TX_OFFSET;
+		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW1_BT_TX:
+	{
+		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
+		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
+		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
+		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_AUDIO_RX:
+	{
+		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
+		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
+		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_HS_RX:
+	{
+		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
+		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
+		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW0_BT_RX:
+	{
+		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
+		offset_reg = ACP_SW_BT_RX_OFFSET;
+		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	case ACP_SDW1_BT_RX:
+	{
+		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
+		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
+		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
+		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
+		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	}
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
+	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
+	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
+	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
+	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
+
+	dpn_sampleinterval = params->sample_interval - 1;
+	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
+
+	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
+	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
+	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
+
+	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
+	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
+	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
+
+	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
+	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
+	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
+	return 0;
+}
+
+static int amd_sdwc_port_enable(struct sdw_bus *bus,
+				struct sdw_enable_ch *enable_ch,
+				unsigned int bank)
+{
+	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
+	u32 dpn_ch_enable;
+	u32 ch_enable_reg, channel_type;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		channel_type = enable_ch->port_num;
+		break;
+	case ACP_SDW1:
+		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (channel_type) {
+	case ACP_SDW0_AUDIO_TX:
+		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_HS_TX:
+		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_BT_TX:
+		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW1_BT_TX:
+		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_AUDIO_RX:
+		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_HS_RX:
+		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW0_BT_RX:
+		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	case ACP_SDW1_BT_RX:
+		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
+		break;
+	default:
+		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
+		return -EINVAL;
+	}
+
+	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
+	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
+	if (enable_ch->enable)
+		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
+	else
+		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
+	return 0;
+}
+
+static int sdw_master_read_amd_prop(struct sdw_bus *bus)
+{
+	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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-wake-enable", &wake_en_mask);
+	ctrl->wake_en_mask = wake_en_mask;
+	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
+	ctrl->power_mode_mask = power_mode_mask;
+	return 0;
+}
+
+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_sdwc_port_ops = {
+	.dpn_set_port_params = amd_sdwc_port_params,
+	.dpn_set_port_transport_params = amd_sdwc_transport_params,
+	.dpn_port_enable_ch = amd_sdwc_port_enable,
+};
+
+static const struct sdw_master_ops amd_sdwc_ops = {
+	.read_prop = amd_prop_read,
+	.xfer_msg = amd_sdwc_xfer_msg,
+	.reset_page_addr = amd_reset_page_addr,
+	.read_ping_status = amd_sdwc_read_ping_status,
+};
+
+static void amd_sdwc_probe_work(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
+	struct sdw_master_prop *prop;
+	int ret;
+
+	prop = &ctrl->bus.prop;
+	if (!prop->hw_disabled) {
+		ret = amd_enable_sdw_pads(ctrl);
+		if (ret)
+			return;
+		ret = amd_init_sdw_controller(ctrl);
+		if (ret)
+			return;
+		amd_enable_sdw_interrupts(ctrl);
+		ret = amd_enable_sdw_controller(ctrl);
+		if (ret)
+			return;
+		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
+		if (!ret)
+			ctrl->startup_done = true;
+	}
+}
+
+static int amd_sdwc_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 sdw_bus_params *params;
+	struct amd_sdwc_ctrl *ctrl;
+	int ret;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "platform_data not retrieved\n");
+		return -ENODEV;
+	}
+	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+		return -ENOMEM;
+	}
+	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (IS_ERR(ctrl->mmio)) {
+		dev_err(&pdev->dev, "mmio not found\n");
+		return PTR_ERR(ctrl->mmio);
+	}
+	ctrl->instance = pdata->instance;
+	ctrl->sdw_lock  = pdata->sdw_lock;
+	ctrl->rows_index = sdw_find_row_index(50);
+	ctrl->cols_index = sdw_find_col_index(10);
+
+	ctrl->dev = dev;
+	dev_set_drvdata(&pdev->dev, ctrl);
+
+	ctrl->bus.ops = &amd_sdwc_ops;
+	ctrl->bus.port_ops = &amd_sdwc_port_ops;
+	ctrl->bus.compute_params = &amd_sdwc_compute_params;
+	ctrl->bus.clk_stop_timeout = 1;
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
+		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
+		break;
+	case ACP_SDW1:
+		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
+		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	params = &ctrl->bus.params;
+	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
+	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
+	params->col = 10;
+	params->row = 50;
+
+	prop = &ctrl->bus.prop;
+	prop->clk_freq = &amd_sdwc_freq_tbl[0];
+	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
+	ctrl->bus.link_id = ctrl->instance;
+	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
+	if (ret) {
+		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
+			ret);
+		return ret;
+	}
+	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
+	schedule_work(&ctrl->probe_work);
+	return 0;
+}
+
+static int amd_sdwc_remove(struct platform_device *pdev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
+	int ret;
+
+	amd_disable_sdw_interrupts(ctrl);
+	sdw_bus_master_delete(&ctrl->bus);
+	ret = amd_disable_sdw_controller(ctrl);
+	return ret;
+}
+
+static struct platform_driver amd_sdwc_driver = {
+	.probe	= &amd_sdwc_probe,
+	.remove = &amd_sdwc_remove,
+	.driver = {
+		.name	= "amd_sdw_controller",
+	}
+};
+module_platform_driver(amd_sdwc_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD soundwire driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
new file mode 100644
index 000000000000..42f32ca0c7a8
--- /dev/null
+++ b/drivers/soundwire/amd_master.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ */
+
+#ifndef __AMD_MASTER_H
+#define __AMD_MASTER_H
+
+#define ACP_PAD_PULLDOWN_CTRL                         0x0001448
+#define ACP_SW_PAD_KEEPER_EN                          0x0001454
+#define ACP_SW_WAKE_EN                                0x0001458
+#define ACP_SW1_WAKE_EN                               0x0001460
+#define ACP_SW_I2S_ERROR_REASON                       0x00018B4
+
+#define ACP_EXTERNAL_INTR_ENB                         0x0001A00
+#define ACP_EXTERNAL_INTR_CNTL                        0x0001A04
+#define ACP_EXTERNAL_INTR_CNTL1                       0x0001A08
+#define ACP_EXTERNAL_INTR_STAT                        0x0001A0C
+#define ACP_EXTERNAL_INTR_STAT1                       0x0001A10
+#define ACP_ERROR_STATUS                              0x0001A4C
+#define ACP_P1_SW_I2S_ERROR_REASON                    0x0001A50
+
+#define ACP_SW_EN                                     0x0003000
+#define ACP_SW_EN_STATUS                              0x0003004
+#define ACP_SW_FRAMESIZE                              0x0003008
+#define ACP_SW_SSP_COUNTER                            0x000300C
+#define ACP_SW_AUDIO_TX_EN                            0x0003010
+#define ACP_SW_AUDIO_TX_EN_STATUS                     0x0003014
+#define ACP_SW_AUDIO_TX_FRAME_FORMAT                  0x0003018
+#define ACP_SW_AUDIO_TX_SAMPLEINTERVAL                0x000301C
+#define ACP_SW_AUDIO_TX_HCTRL_DP0                     0x0003020
+#define ACP_SW_AUDIO_TX_HCTRL_DP1                     0x0003024
+#define ACP_SW_AUDIO_TX_HCTRL_DP2                     0x0003028
+#define ACP_SW_AUDIO_TX_HCTRL_DP3                     0x000302C
+#define ACP_SW_AUDIO_TX_OFFSET_DP0                    0x0003030
+#define ACP_SW_AUDIO_TX_OFFSET_DP1                    0x0003034
+#define ACP_SW_AUDIO_TX_OFFSET_DP2                    0x0003038
+#define ACP_SW_AUDIO_TX_OFFSET_DP3                    0x000303C
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0            0x0003040
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP1            0x0003044
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP2            0x0003048
+#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP3            0x000304C
+#define ACP_SW_BT_TX_EN                               0x0003050
+#define ACP_SW_BT_TX_EN_STATUS                        0x0003054
+#define ACP_SW_BT_TX_FRAME_FORMAT                     0x0003058
+#define ACP_SW_BT_TX_SAMPLEINTERVAL                   0x000305C
+#define ACP_SW_BT_TX_HCTRL                            0x0003060
+#define ACP_SW_BT_TX_OFFSET                           0x0003064
+#define ACP_SW_BT_TX_CHANNEL_ENABLE_DP0               0x0003068
+#define ACP_SW_HEADSET_TX_EN                          0x000306C
+#define ACP_SW_HEADSET_TX_EN_STATUS                   0x0003070
+#define ACP_SW_HEADSET_TX_FRAME_FORMAT                0x0003074
+#define ACP_SW_HEADSET_TX_SAMPLEINTERVAL              0x0003078
+#define ACP_SW_HEADSET_TX_HCTRL                       0x000307C
+#define ACP_SW_HEADSET_TX_OFFSET                      0x0003080
+#define ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0          0x0003084
+#define ACP_SW_AUDIO_RX_EN                            0x0003088
+#define ACP_SW_AUDIO_RX_EN_STATUS                     0x000308C
+#define ACP_SW_AUDIO_RX_FRAME_FORMAT                  0x0003090
+#define ACP_SW_AUDIO_RX_SAMPLEINTERVAL                0x0003094
+#define ACP_SW_AUDIO_RX_HCTRL_DP0                     0x0003098
+#define ACP_SW_AUDIO_RX_HCTRL_DP1                     0x000309C
+#define ACP_SW_AUDIO_RX_HCTRL_DP2                     0x0003100
+#define ACP_SW_AUDIO_RX_HCTRL_DP3                     0x0003104
+#define ACP_SW_AUDIO_RX_OFFSET_DP0                    0x0003108
+#define ACP_SW_AUDIO_RX_OFFSET_DP1                    0x000310C
+#define ACP_SW_AUDIO_RX_OFFSET_DP2                    0x0003110
+#define ACP_SW_AUDIO_RX_OFFSET_DP3                    0x0003114
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0            0x0003118
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP1            0x000311C
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP2            0x0003120
+#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP3            0x0003124
+#define ACP_SW_BT_RX_EN                               0x0003128
+#define ACP_SW_BT_RX_EN_STATUS                        0x000312C
+#define ACP_SW_BT_RX_FRAME_FORMAT                     0x0003130
+#define ACP_SW_BT_RX_SAMPLEINTERVAL                   0x0003134
+#define ACP_SW_BT_RX_HCTRL                            0x0003138
+#define ACP_SW_BT_RX_OFFSET                           0x000313C
+#define ACP_SW_BT_RX_CHANNEL_ENABLE_DP0               0x0003140
+#define ACP_SW_HEADSET_RX_EN                          0x0003144
+#define ACP_SW_HEADSET_RX_EN_STATUS                   0x0003148
+#define ACP_SW_HEADSET_RX_FRAME_FORMAT                0x000314C
+#define ACP_SW_HEADSET_RX_SAMPLEINTERVAL              0x0003150
+#define ACP_SW_HEADSET_RX_HCTRL                       0x0003154
+#define ACP_SW_HEADSET_RX_OFFSET                      0x0003158
+#define ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0          0x000315C
+#define ACP_SW_BPT_PORT_EN                            0x0003160
+#define ACP_SW_BPT_PORT_EN_STATUS                     0x0003164
+#define ACP_SW_BPT_PORT_FRAME_FORMAT                  0x0003168
+#define ACP_SW_BPT_PORT_SAMPLEINTERVAL                0x000316C
+#define ACP_SW_BPT_PORT_HCTRL                         0x0003170
+#define ACP_SW_BPT_PORT_OFFSET                        0x0003174
+#define ACP_SW_BPT_PORT_CHANNEL_ENABLE                0x0003178
+#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR               0x000317C
+#define ACP_SW_CLK_RESUME_CTRL                        0x0003180
+#define ACP_SW_CLK_RESUME_DELAY_CNTR                  0x0003184
+#define ACP_SW_BUS_RESET_CTRL                         0x0003188
+#define ACP_SW_PRBS_ERR_STATUS                        0x000318C
+#define SW_IMM_CMD_UPPER_WORD                         0x0003230
+#define SW_IMM_CMD_LOWER_QWORD                        0x0003234
+#define SW_IMM_RESP_UPPER_WORD                        0x0003238
+#define SW_IMM_RESP_LOWER_QWORD                       0x000323C
+#define SW_IMM_CMD_STS                                0x0003240
+#define SW_BRA_BASE_ADDRESS                           0x0003244
+#define SW_BRA_TRANSFER_SIZE                          0x0003248
+#define SW_BRA_DMA_BUSY                               0x000324C
+#define SW_BRA_RESP                                   0x0003250
+#define SW_BRA_RESP_FRAME_ADDR                        0x0003254
+#define SW_BRA_CURRENT_TRANSFER_SIZE                  0x0003258
+#define SW_STATE_CHANGE_STATUS_0TO7                   0x000325C
+#define SW_STATE_CHANGE_STATUS_8TO11                  0x0003260
+#define SW_STATE_CHANGE_STATUS_MASK_0TO7              0x0003264
+#define SW_STATE_CHANGE_STATUS_MASK_8TO11             0x0003268
+#define SW_CLK_FREQUENCY_CTRL                         0x000326C
+#define SW_ERROR_INTR_MASK                            0x0003270
+#define SW_PHY_TEST_MODE_DATA_OFF                     0x0003274
+
+#define ACP_P1_SW_EN                                  0x0003C00
+#define ACP_P1_SW_EN_STATUS                           0x0003C04
+#define ACP_P1_SW_FRAMESIZE                           0x0003C08
+#define ACP_P1_SW_SSP_COUNTER                         0x0003C0C
+#define ACP_P1_SW_BT_TX_EN                            0x0003C50
+#define ACP_P1_SW_BT_TX_EN_STATUS                     0x0003C54
+#define ACP_P1_SW_BT_TX_FRAME_FORMAT                  0x0003C58
+#define ACP_P1_SW_BT_TX_SAMPLEINTERVAL                0x0003C5C
+#define ACP_P1_SW_BT_TX_HCTRL                         0x0003C60
+#define ACP_P1_SW_BT_TX_OFFSET                        0x0003C64
+#define ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0            0x0003C68
+#define ACP_P1_SW_BT_RX_EN                            0x0003D28
+#define ACP_P1_SW_BT_RX_EN_STATUS                     0x0003D2C
+#define ACP_P1_SW_BT_RX_FRAME_FORMAT                  0x0003D30
+#define ACP_P1_SW_BT_RX_SAMPLEINTERVAL                0x0003D34
+#define ACP_P1_SW_BT_RX_HCTRL                         0x0003D38
+#define ACP_P1_SW_BT_RX_OFFSET                        0x0003D3C
+#define ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0            0x0003D40
+#define ACP_P1_SW_BPT_PORT_EN                         0x0003D60
+#define ACP_P1_SW_BPT_PORT_EN_STATUS                  0x0003D64
+#define ACP_P1_SW_BPT_PORT_FRAME_FORMAT               0x0003D68
+#define ACP_P1_SW_BPT_PORT_SAMPLEINTERVAL             0x0003D6C
+#define ACP_P1_SW_BPT_PORT_HCTRL                      0x0003D70
+#define ACP_P1_SW_BPT_PORT_OFFSET                     0x0003D74
+#define ACP_P1_SW_BPT_PORT_CHANNEL_ENABLE             0x0003D78
+#define ACP_P1_SW_BPT_PORT_FIRST_BYTE_ADDR            0x0003D7C
+#define ACP_P1_SW_CLK_RESUME_CTRL                     0x0003D80
+#define ACP_P1_SW_CLK_RESUME_DELAY_CNTR               0x0003D84
+#define ACP_P1_SW_BUS_RESET_CTRL                      0x0003D88
+#define ACP_P1_SW_PRBS_ERR_STATUS                     0x0003D8C
+
+#define P1_SW_IMM_CMD_UPPER_WORD                      0x0003E30
+#define P1_SW_IMM_CMD_LOWER_QWORD                     0x0003E34
+#define P1_SW_IMM_RESP_UPPER_WORD                     0x0003E38
+#define P1_SW_IMM_RESP_LOWER_QWORD                    0x0003E3C
+#define P1_SW_IMM_CMD_STS                             0x0003E40
+#define P1_SW_BRA_BASE_ADDRESS                        0x0003E44
+#define P1_SW_BRA_TRANSFER_SIZE                       0x0003E48
+#define P1_SW_BRA_DMA_BUSY                            0x0003E4C
+#define P1_SW_BRA_RESP                                0x0003E50
+#define P1_SW_BRA_RESP_FRAME_ADDR                     0x0003E54
+#define P1_SW_BRA_CURRENT_TRANSFER_SIZE               0x0003E58
+#define P1_SW_STATE_CHANGE_STATUS_0TO7                0x0003E5C
+#define P1_SW_STATE_CHANGE_STATUS_8TO11               0x0003E60
+#define P1_SW_STATE_CHANGE_STATUS_MASK_0TO7           0x0003E64
+#define P1_SW_STATE_CHANGE_STATUS_MASK_8TO11          0x0003E68
+#define P1_SW_CLK_FREQUENCY_CTRL                      0x0003E6C
+#define P1_SW_ERROR_INTR_MASK                         0x0003E70
+#define P1_SW_PHY_TEST_MODE_DATA_OFF                  0x0003E74
+
+#define ACP_PHY_BASE_ADDRESS		0x1240000
+#define AMD_DELAY_LOOP_ITERATION	1000
+#define AMD_SDW_DEFAULT_CLK_FREQ	12000000
+#define AMD_SDW_RETRY_COUNT		1000
+
+#define AMD_SDW_MCP_RESP_ACK		BIT(0)
+#define AMD_SDW_MCP_RESP_NACK		BIT(1)
+#define AMD_SDW_MCP_RESP_RDATA		GENMASK(14, 7)
+
+#define AMD_SDW_MCP_CMD_SSP_TAG			BIT(31)
+#define AMD_SDW_MCP_CMD_COMMAND			GENMASK(14, 12)
+#define AMD_SDW_MCP_CMD_DEV_ADDR		GENMASK(11, 8)
+#define AMD_SDW_MCP_CMD_REG_ADDR_HIGH		GENMASK(7, 0)
+#define AMD_SDW_MCP_CMD_REG_ADDR_LOW		GENMASK(31, 24)
+#define AMD_SDW_MCP_CMD_REG_DATA		GENMASK(14, 7)
+#define AMD_SDW_MCP_SLAVE_STAT_0_3		GENMASK(14, 7)
+#define AMD_SDW_MCP_SLAVE_STAT_4_11		GENMASK(39, 24)
+#define AMD_SDW_MCP_SLAVE_STATUS_MASK		GENMASK(1, 0)
+#define AMD_SDW_MCP_SLAVE_STATUS_BITS		GENMASK(3, 2)
+#define AMD_SDW_MCP_SLAVE_STATUS_8TO_11		GENMASK(15, 0)
+#define AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(x)  BIT(((x) * 4))
+#define AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(x)	(((x) * 4) + 1)
+
+#define AMD_SDW_MASTER_SUSPEND_DELAY_MS		3000
+#define AMD_SDW_CLK_STOP_MAX_RETRY_COUNT	100
+#define AMD_SDW_QUIRK_MASK_BUS_ENABLE		BIT(0)
+
+#define AMD_SDW_IMM_RES_VALID		1
+#define AMD_SDW_IMM_CMD_BUSY		2
+#define AMD_SDW_ENABLE			1
+#define AMD_SDW_DISABLE			0
+#define AMD_SDW_BUS_RESET_CLEAR_REQ	0
+#define AMD_SDW_BUS_RESET_REQ		1
+#define AMD_SDW_BUS_RESET_DONE		2
+#define AMD_SDW_BUS_BASE_FREQ		24000000
+
+#define AMD_SDW0_EXT_INTR_MASK		0x200000
+#define AMD_SDW1_EXT_INTR_MASK		4
+#define AMD_SDW_IRQ_MASK_0TO7		0x77777777
+#define AMD_SDW_IRQ_MASK_8TO11		0x000D7777
+#define AMD_SDW_IRQ_ERROR_MASK		0xFF
+#define AMD_SDW_MAX_FREQ_NUM		1
+#define AMD_SDW0_MAX_TX_PORTS		3
+#define AMD_SDW0_MAX_RX_PORTS		3
+#define AMD_SDW1_MAX_TX_PORTS		1
+#define AMD_SDW1_MAX_RX_PORTS		1
+
+#define AMD_SDW_SLAVE_0_ATTACHED	5
+#define AMD_SDW_SSP_COUNTER_VAL	3
+
+#define AMD_DPN_FRAME_FMT_PFM			GENMASK(1, 0)
+#define AMD_DPN_FRAME_FMT_PDM			GENMASK(3, 2)
+#define AMD_DPN_FRAME_FMT_BLK_PKG_MODE		BIT(4)
+#define AMD_DPN_FRAME_FMT_BLK_GRP_CTRL		GENMASK(6, 5)
+#define AMD_DPN_FRAME_FMT_WORD_LEN		GENMASK(12, 7)
+#define AMD_DPN_FRAME_FMT_PCM_OR_PDM		BIT(13)
+#define AMD_DPN_HCTRL_HSTOP			GENMASK(3, 0)
+#define AMD_DPN_HCTRL_HSTART			GENMASK(7, 4)
+#define AMD_DPN_OFFSET_CTRL_1			GENMASK(7, 0)
+#define AMD_DPN_OFFSET_CTRL_2			GENMASK(15, 8)
+#define AMD_DPN_CH_EN_LCTRL			GENMASK(2, 0)
+#define AMD_DPN_CH_EN_CHMASK			GENMASK(10, 3)
+#define AMD_SDW_STAT_MAX_RETRY_COUNT		100
+#define AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK	0x7F9F
+#define AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK	0x7FFA
+#define AMD_SDW0_PAD_PULLDOWN_CTRL_DISABLE_MASK	0x60
+#define AMD_SDW1_PAD_PULLDOWN_CTRL_DISABLE_MASK	5
+#define AMD_SDW0_PAD_KEEPER_EN_MASK	1
+#define AMD_SDW1_PAD_KEEPER_EN_MASK	0x10
+#define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
+#define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
+
+enum amd_sdw_channel {
+	/* SDW0 */
+	ACP_SDW0_AUDIO_TX = 0,
+	ACP_SDW0_BT_TX,
+	ACP_SDW0_HS_TX,
+	ACP_SDW0_AUDIO_RX,
+	ACP_SDW0_BT_RX,
+	ACP_SDW0_HS_RX,
+	/* SDW1 */
+	ACP_SDW1_BT_TX,
+	ACP_SDW1_BT_RX,
+};
+
+enum amd_sdw_cmd_type {
+	AMD_SDW_CMD_PING = 0,
+	AMD_SDW_CMD_READ = 2,
+	AMD_SDW_CMD_WRITE = 3,
+};
+
+static u32 amd_sdwc_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = {
+	AMD_SDW_DEFAULT_CLK_FREQ,
+};
+
+struct sdw_transport_data {
+	int hstart;
+	int hstop;
+	int block_offset;
+	int sub_block_offset;
+};
+
+static inline u32 acp_reg_readl(void __iomem *base_addr)
+{
+	return readl(base_addr);
+}
+
+static inline void acp_reg_writel(u32 val, void __iomem *base_addr)
+{
+	writel(val, base_addr);
+}
+#endif
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index f0123815af46..5ec39f8c2f2e 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -10,9 +10,30 @@
 
 #define AMD_SDW_CLK_STOP_MODE		1
 #define AMD_SDW_POWER_OFF_MODE		2
+#define ACP_SDW0	0
+#define ACP_SDW1	1
+#define ACP_SDW0_MAX_DAI	6
 
 struct acp_sdw_pdata {
 	u16 instance;
 	struct mutex *sdw_lock;
 };
+
+struct amd_sdwc_ctrl {
+	struct sdw_bus bus;
+	struct device *dev;
+	void __iomem *mmio;
+	struct work_struct probe_work;
+	struct mutex *sdw_lock;
+	int num_din_ports;
+	int num_dout_ports;
+	int cols_index;
+	int rows_index;
+	u32 instance;
+	u32 quirks;
+	u32 wake_en_mask;
+	int num_ports;
+	bool startup_done;
+	u32 power_mode_mask;
+};
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 03/19] soundwire: amd: register sdw controller dai ops
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Register dai ops for two controller instances.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c    | 186 ++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw_amd.h |  21 ++++
 2 files changed, 207 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 7e1f618254ac..93bffe6ff9e2 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
 	.read_ping_status = amd_sdwc_read_ping_status,
 };
 
+static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params,
+			      struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+	struct sdw_stream_config sconfig;
+	struct sdw_port_config *pconfig;
+	int ch, dir;
+	int ret;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
+	dma->hw_params = params;
+
+	sconfig.direction = dir;
+	sconfig.ch_count = ch;
+	sconfig.frame_rate = params_rate(params);
+	sconfig.type = dma->stream_type;
+
+	sconfig.bps = snd_pcm_format_width(params_format(params));
+
+	/* Port configuration */
+	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
+	if (!pconfig) {
+		ret =  -ENOMEM;
+		goto error;
+	}
+
+	pconfig->num = dai->id;
+	pconfig->ch_mask = (1 << ch) - 1;
+	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
+				    pconfig, 1, dma->stream);
+	if (ret)
+		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
+
+	kfree(pconfig);
+error:
+	return ret;
+}
+
+static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+	int ret;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		return -EIO;
+
+	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
+	if (ret < 0) {
+		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
+			dma->stream->name, ret);
+		return ret;
+	}
+	dma->hw_params = NULL;
+	return 0;
+}
+
+static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+
+	if (stream) {
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			dma = dai->playback_dma_data;
+		else
+			dma = dai->capture_dma_data;
+
+		if (dma) {
+			dev_err(dai->dev,
+				"dma_data already allocated for dai %s\n",
+				dai->name);
+			return -EINVAL;
+		}
+
+		/* allocate and set dma info */
+		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+		if (!dma)
+			return -ENOMEM;
+		dma->stream_type = SDW_STREAM_PCM;
+		dma->bus = &ctrl->bus;
+		dma->link_id = ctrl->instance;
+		dma->stream = stream;
+
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			dai->playback_dma_data = dma;
+		else
+			dai->capture_dma_data = dma;
+	} else {
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			kfree(dai->playback_dma_data);
+			dai->playback_dma_data = NULL;
+		} else {
+			kfree(dai->capture_dma_data);
+			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
+
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dma = dai->playback_dma_data;
+	else
+		dma = dai->capture_dma_data;
+
+	if (!dma)
+		return ERR_PTR(-EINVAL);
+
+	return dma->stream;
+}
+
+static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
+	.hw_params = amd_sdwc_hw_params,
+	.hw_free = amd_sdwc_hw_free,
+	.set_stream = amd_pcm_set_sdw_stream,
+	.get_stream = amd_get_sdw_stream,
+};
+
+static const struct snd_soc_component_driver amd_sdwc_dai_component = {
+	.name = "soundwire",
+};
+
+static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
+{
+	struct snd_soc_dai_driver *dais;
+	struct snd_soc_pcm_stream *stream;
+	struct device *dev;
+	int i, num_dais;
+
+	dev = ctrl->dev;
+	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
+	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
+	if (!dais)
+		return -ENOMEM;
+	for (i = 0; i < num_dais; i++) {
+		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
+		if (!dais[i].name) {
+			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");
+			return -ENOMEM;
+		}
+
+		if (i < ctrl->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_sdwc_dai_ops;
+		dais[i].id = i;
+	}
+
+	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
+					       dais, num_dais);
+}
+
 static void amd_sdwc_probe_work(struct work_struct *work)
 {
 	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
@@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 			ret);
 		return ret;
 	}
+	ret = amd_sdwc_register_dais(ctrl);
+	if (ret) {
+		dev_err(dev, "CPU DAI registration failed\n");
+		sdw_bus_master_delete(&ctrl->bus);
+		return ret;
+	}
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
 	return 0;
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 5ec39f8c2f2e..7a99d782969f 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -13,6 +13,7 @@
 #define ACP_SDW0	0
 #define ACP_SDW1	1
 #define ACP_SDW0_MAX_DAI	6
+#define AMD_SDW_MAX_DAIS	8
 
 struct acp_sdw_pdata {
 	u16 instance;
@@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
 	void __iomem *mmio;
 	struct work_struct probe_work;
 	struct mutex *sdw_lock;
+	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
 	int num_din_ports;
 	int num_dout_ports;
 	int cols_index;
@@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
 	bool startup_done;
 	u32 power_mode_mask;
 };
+
+/**
+ * struct sdw_amd_dma_data: AMD DMA data
+ *
+ * @name: SoundWire stream name
+ * @stream: stream runtime
+ * @bus: Bus handle
+ * @stream_type: Stream type
+ * @link_id: Master link id
+ * @hw_params: hw_params to be applied in .prepare step
+ */
+struct sdw_amd_dma_data {
+	char *name;
+	struct sdw_stream_runtime *stream;
+	struct sdw_bus *bus;
+	enum sdw_stream_type stream_type;
+	int link_id;
+	struct snd_pcm_hw_params *hw_params;
+};
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 03/19] soundwire: amd: register sdw controller dai ops
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Register dai ops for two controller instances.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c    | 186 ++++++++++++++++++++++++++++++
 include/linux/soundwire/sdw_amd.h |  21 ++++
 2 files changed, 207 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 7e1f618254ac..93bffe6ff9e2 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
 	.read_ping_status = amd_sdwc_read_ping_status,
 };
 
+static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params,
+			      struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+	struct sdw_stream_config sconfig;
+	struct sdw_port_config *pconfig;
+	int ch, dir;
+	int ret;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
+	dma->hw_params = params;
+
+	sconfig.direction = dir;
+	sconfig.ch_count = ch;
+	sconfig.frame_rate = params_rate(params);
+	sconfig.type = dma->stream_type;
+
+	sconfig.bps = snd_pcm_format_width(params_format(params));
+
+	/* Port configuration */
+	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
+	if (!pconfig) {
+		ret =  -ENOMEM;
+		goto error;
+	}
+
+	pconfig->num = dai->id;
+	pconfig->ch_mask = (1 << ch) - 1;
+	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
+				    pconfig, 1, dma->stream);
+	if (ret)
+		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
+
+	kfree(pconfig);
+error:
+	return ret;
+}
+
+static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+	int ret;
+
+	dma = snd_soc_dai_get_dma_data(dai, substream);
+	if (!dma)
+		return -EIO;
+
+	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
+	if (ret < 0) {
+		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
+			dma->stream->name, ret);
+		return ret;
+	}
+	dma->hw_params = NULL;
+	return 0;
+}
+
+static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	struct sdw_amd_dma_data *dma;
+
+	if (stream) {
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			dma = dai->playback_dma_data;
+		else
+			dma = dai->capture_dma_data;
+
+		if (dma) {
+			dev_err(dai->dev,
+				"dma_data already allocated for dai %s\n",
+				dai->name);
+			return -EINVAL;
+		}
+
+		/* allocate and set dma info */
+		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+		if (!dma)
+			return -ENOMEM;
+		dma->stream_type = SDW_STREAM_PCM;
+		dma->bus = &ctrl->bus;
+		dma->link_id = ctrl->instance;
+		dma->stream = stream;
+
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+			dai->playback_dma_data = dma;
+		else
+			dai->capture_dma_data = dma;
+	} else {
+		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+			kfree(dai->playback_dma_data);
+			dai->playback_dma_data = NULL;
+		} else {
+			kfree(dai->capture_dma_data);
+			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
+
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dma = dai->playback_dma_data;
+	else
+		dma = dai->capture_dma_data;
+
+	if (!dma)
+		return ERR_PTR(-EINVAL);
+
+	return dma->stream;
+}
+
+static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
+	.hw_params = amd_sdwc_hw_params,
+	.hw_free = amd_sdwc_hw_free,
+	.set_stream = amd_pcm_set_sdw_stream,
+	.get_stream = amd_get_sdw_stream,
+};
+
+static const struct snd_soc_component_driver amd_sdwc_dai_component = {
+	.name = "soundwire",
+};
+
+static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
+{
+	struct snd_soc_dai_driver *dais;
+	struct snd_soc_pcm_stream *stream;
+	struct device *dev;
+	int i, num_dais;
+
+	dev = ctrl->dev;
+	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
+	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
+	if (!dais)
+		return -ENOMEM;
+	for (i = 0; i < num_dais; i++) {
+		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
+		if (!dais[i].name) {
+			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");
+			return -ENOMEM;
+		}
+
+		if (i < ctrl->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_sdwc_dai_ops;
+		dais[i].id = i;
+	}
+
+	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
+					       dais, num_dais);
+}
+
 static void amd_sdwc_probe_work(struct work_struct *work)
 {
 	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
@@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 			ret);
 		return ret;
 	}
+	ret = amd_sdwc_register_dais(ctrl);
+	if (ret) {
+		dev_err(dev, "CPU DAI registration failed\n");
+		sdw_bus_master_delete(&ctrl->bus);
+		return ret;
+	}
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
 	return 0;
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 5ec39f8c2f2e..7a99d782969f 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -13,6 +13,7 @@
 #define ACP_SDW0	0
 #define ACP_SDW1	1
 #define ACP_SDW0_MAX_DAI	6
+#define AMD_SDW_MAX_DAIS	8
 
 struct acp_sdw_pdata {
 	u16 instance;
@@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
 	void __iomem *mmio;
 	struct work_struct probe_work;
 	struct mutex *sdw_lock;
+	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
 	int num_din_ports;
 	int num_dout_ports;
 	int cols_index;
@@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
 	bool startup_done;
 	u32 power_mode_mask;
 };
+
+/**
+ * struct sdw_amd_dma_data: AMD DMA data
+ *
+ * @name: SoundWire stream name
+ * @stream: stream runtime
+ * @bus: Bus handle
+ * @stream_type: Stream type
+ * @link_id: Master link id
+ * @hw_params: hw_params to be applied in .prepare step
+ */
+struct sdw_amd_dma_data {
+	char *name;
+	struct sdw_stream_runtime *stream;
+	struct sdw_bus *bus;
+	enum sdw_stream_type stream_type;
+	int link_id;
+	struct snd_pcm_hw_params *hw_params;
+};
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Enable build for AMD soundwire master driver for AMD platforms.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/Kconfig  | 9 +++++++++
 drivers/soundwire/Makefile | 4 ++++
 2 files changed, 13 insertions(+)

diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
index 2b7795233282..a597bb6c42fd 100644
--- a/drivers/soundwire/Kconfig
+++ b/drivers/soundwire/Kconfig
@@ -46,4 +46,13 @@ config SOUNDWIRE_QCOM
 config SOUNDWIRE_GENERIC_ALLOCATION
 	tristate
 
+config SOUNDWIRE_AMD
+	tristate "AMD Soundwire Master driver"
+	depends on ACPI && SND_SOC
+	help
+	  Soundwire AMD Master driver.
+	  If you have an AMD platform which has a Soundwire Master then
+	  enable this config option to get the Soundwire support for that
+	  device.
+
 endif
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index ca97414ada70..86ecae19eacd 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -26,3 +26,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
 #Qualcomm driver
 soundwire-qcom-y :=	qcom.o
 obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o
+
+#AMD driver
+soundwire-amd-y :=	amd_master.o
+obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Enable build for AMD soundwire master driver for AMD platforms.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/Kconfig  | 9 +++++++++
 drivers/soundwire/Makefile | 4 ++++
 2 files changed, 13 insertions(+)

diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
index 2b7795233282..a597bb6c42fd 100644
--- a/drivers/soundwire/Kconfig
+++ b/drivers/soundwire/Kconfig
@@ -46,4 +46,13 @@ config SOUNDWIRE_QCOM
 config SOUNDWIRE_GENERIC_ALLOCATION
 	tristate
 
+config SOUNDWIRE_AMD
+	tristate "AMD Soundwire Master driver"
+	depends on ACPI && SND_SOC
+	help
+	  Soundwire AMD Master driver.
+	  If you have an AMD platform which has a Soundwire Master then
+	  enable this config option to get the Soundwire support for that
+	  device.
+
 endif
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
index ca97414ada70..86ecae19eacd 100644
--- a/drivers/soundwire/Makefile
+++ b/drivers/soundwire/Makefile
@@ -26,3 +26,7 @@ obj-$(CONFIG_SOUNDWIRE_INTEL) += soundwire-intel.o
 #Qualcomm driver
 soundwire-qcom-y :=	qcom.o
 obj-$(CONFIG_SOUNDWIRE_QCOM) += soundwire-qcom.o
+
+#AMD driver
+soundwire-amd-y :=	amd_master.o
+obj-$(CONFIG_SOUNDWIRE_AMD) += soundwire-amd.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Add support for handling soundwire controller interrupts.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 156 ++++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |   1 +
 include/linux/soundwire/sdw_amd.h |   3 +
 3 files changed, 160 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 93bffe6ff9e2..c7063b8bdd7b 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -557,6 +557,47 @@ amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
 	return amd_program_scp_addr(ctrl, &msg);
 }
 
+static void amd_sdwc_process_ping_status(u64 response, struct amd_sdwc_ctrl *ctrl)
+{
+	u64 slave_stat = 0;
+	u32 val = 0;
+	u16 dev_index;
+
+	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%llx\n", __func__, 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(ctrl->dev, "%s val:0x%x\n", __func__, val);
+		switch (val) {
+		case SDW_SLAVE_ATTACHED:
+			ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
+			break;
+		case SDW_SLAVE_UNATTACHED:
+			ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
+			break;
+		case SDW_SLAVE_ALERT:
+			ctrl->status[dev_index] = SDW_SLAVE_ALERT;
+			break;
+		default:
+			ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
+			break;
+		}
+	}
+}
+
+static void amd_sdwc_read_and_process_ping_status(struct amd_sdwc_ctrl *ctrl)
+{
+	u64 response = 0;
+
+	mutex_lock(&ctrl->bus.msg_lock);
+	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
+	mutex_unlock(&ctrl->bus.msg_lock);
+	amd_sdwc_process_ping_status(response, ctrl);
+}
+
 static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
 {
 	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
@@ -1132,6 +1173,119 @@ static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
 					       dais, num_dais);
 }
 
+static void amd_sdwc_update_slave_status_work(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl =
+		container_of(work, struct amd_sdwc_ctrl, amd_sdw_work);
+	u32 sw_status_change_mask_0to7_reg;
+	u32 sw_status_change_mask_8to11_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_status_change_mask_8to11_reg = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		break;
+	case ACP_SDW1:
+		sw_status_change_mask_0to7_reg = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_status_change_mask_8to11_reg = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
+		return;
+	}
+
+	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
+		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_0to7_reg);
+		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_8to11_reg);
+	}
+
+update_status:
+	sdw_handle_slave_status(&ctrl->bus, ctrl->status);
+	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
+		acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_status_change_mask_0to7_reg);
+		acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11,
+			       ctrl->mmio + sw_status_change_mask_8to11_reg);
+		amd_sdwc_read_and_process_ping_status(ctrl);
+		goto update_status;
+	}
+}
+
+static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
+					 struct amd_sdwc_ctrl *ctrl)
+{
+	u64 slave_stat = 0;
+	u32 val = 0;
+	int dev_index;
+
+	if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
+		memset(ctrl->status, 0, sizeof(ctrl->status));
+	slave_stat = status_change_0to7;
+	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
+	dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
+		__func__, 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;
+				switch (val) {
+				case SDW_SLAVE_ATTACHED:
+					ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
+					break;
+				case SDW_SLAVE_UNATTACHED:
+					ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
+					break;
+				case SDW_SLAVE_ALERT:
+					ctrl->status[dev_index] = SDW_SLAVE_ALERT;
+					break;
+				default:
+					ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
+					break;
+				}
+			}
+		}
+	}
+}
+
+static void amd_sdwc_irq_thread(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl =
+			container_of(work, struct amd_sdwc_ctrl, amd_sdw_irq_thread);
+	u32 sw_status_change_0to7_reg;
+	u32 sw_status_change_8to11_reg;
+	u32 status_change_8to11;
+	u32 status_change_0to7;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_status_change_0to7_reg = SW_STATE_CHANGE_STATUS_0TO7;
+		sw_status_change_8to11_reg = SW_STATE_CHANGE_STATUS_8TO11;
+		break;
+	case ACP_SDW1:
+		sw_status_change_0to7_reg = P1_SW_STATE_CHANGE_STATUS_0TO7;
+		sw_status_change_8to11_reg = P1_SW_STATE_CHANGE_STATUS_8TO11;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
+		return;
+	}
+
+	status_change_8to11 = acp_reg_readl(ctrl->mmio + sw_status_change_8to11_reg);
+	status_change_0to7 = acp_reg_readl(ctrl->mmio + sw_status_change_0to7_reg);
+	dev_dbg(ctrl->dev, "%s [SDW%d] SDW INT: 0to7=0x%x, 8to11=0x%x\n",
+		__func__, ctrl->instance, status_change_0to7, status_change_8to11);
+	if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
+		amd_sdwc_read_and_process_ping_status(ctrl);
+	} else {
+		/* Check for the updated status on Slave device */
+		amd_sdwc_update_slave_status(status_change_0to7, status_change_8to11, ctrl);
+	}
+	if (status_change_8to11 || status_change_0to7)
+		schedule_work(&ctrl->amd_sdw_work);
+	acp_reg_writel(0x00, ctrl->mmio + sw_status_change_8to11_reg);
+	acp_reg_writel(0x00, ctrl->mmio + sw_status_change_0to7_reg);
+}
+
 static void amd_sdwc_probe_work(struct work_struct *work)
 {
 	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
@@ -1229,6 +1383,8 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 		sdw_bus_master_delete(&ctrl->bus);
 		return ret;
 	}
+	INIT_WORK(&ctrl->amd_sdw_irq_thread, amd_sdwc_irq_thread);
+	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
 	return 0;
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index 42f32ca0c7a8..b43a5d6496cb 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -236,6 +236,7 @@
 #define AMD_SDW1_PAD_KEEPER_EN_MASK	0x10
 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
 #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
+#define AMD_SDW_PREQ_INTR_STAT  BIT(19)
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 7a99d782969f..2db03b2f0c3b 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -24,9 +24,12 @@ struct amd_sdwc_ctrl {
 	struct sdw_bus bus;
 	struct device *dev;
 	void __iomem *mmio;
+	struct work_struct amd_sdw_irq_thread;
+	struct work_struct amd_sdw_work;
 	struct work_struct probe_work;
 	struct mutex *sdw_lock;
 	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
+	enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
 	int num_din_ports;
 	int num_dout_ports;
 	int cols_index;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Add support for handling soundwire controller interrupts.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 156 ++++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |   1 +
 include/linux/soundwire/sdw_amd.h |   3 +
 3 files changed, 160 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 93bffe6ff9e2..c7063b8bdd7b 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -557,6 +557,47 @@ amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
 	return amd_program_scp_addr(ctrl, &msg);
 }
 
+static void amd_sdwc_process_ping_status(u64 response, struct amd_sdwc_ctrl *ctrl)
+{
+	u64 slave_stat = 0;
+	u32 val = 0;
+	u16 dev_index;
+
+	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%llx\n", __func__, 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(ctrl->dev, "%s val:0x%x\n", __func__, val);
+		switch (val) {
+		case SDW_SLAVE_ATTACHED:
+			ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
+			break;
+		case SDW_SLAVE_UNATTACHED:
+			ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
+			break;
+		case SDW_SLAVE_ALERT:
+			ctrl->status[dev_index] = SDW_SLAVE_ALERT;
+			break;
+		default:
+			ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
+			break;
+		}
+	}
+}
+
+static void amd_sdwc_read_and_process_ping_status(struct amd_sdwc_ctrl *ctrl)
+{
+	u64 response = 0;
+
+	mutex_lock(&ctrl->bus.msg_lock);
+	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
+	mutex_unlock(&ctrl->bus.msg_lock);
+	amd_sdwc_process_ping_status(response, ctrl);
+}
+
 static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
 {
 	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
@@ -1132,6 +1173,119 @@ static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
 					       dais, num_dais);
 }
 
+static void amd_sdwc_update_slave_status_work(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl =
+		container_of(work, struct amd_sdwc_ctrl, amd_sdw_work);
+	u32 sw_status_change_mask_0to7_reg;
+	u32 sw_status_change_mask_8to11_reg;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_status_change_mask_8to11_reg = SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		break;
+	case ACP_SDW1:
+		sw_status_change_mask_0to7_reg = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
+		sw_status_change_mask_8to11_reg = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
+		return;
+	}
+
+	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
+		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_0to7_reg);
+		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_8to11_reg);
+	}
+
+update_status:
+	sdw_handle_slave_status(&ctrl->bus, ctrl->status);
+	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
+		acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_status_change_mask_0to7_reg);
+		acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11,
+			       ctrl->mmio + sw_status_change_mask_8to11_reg);
+		amd_sdwc_read_and_process_ping_status(ctrl);
+		goto update_status;
+	}
+}
+
+static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
+					 struct amd_sdwc_ctrl *ctrl)
+{
+	u64 slave_stat = 0;
+	u32 val = 0;
+	int dev_index;
+
+	if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
+		memset(ctrl->status, 0, sizeof(ctrl->status));
+	slave_stat = status_change_0to7;
+	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
+	dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
+		__func__, 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;
+				switch (val) {
+				case SDW_SLAVE_ATTACHED:
+					ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
+					break;
+				case SDW_SLAVE_UNATTACHED:
+					ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
+					break;
+				case SDW_SLAVE_ALERT:
+					ctrl->status[dev_index] = SDW_SLAVE_ALERT;
+					break;
+				default:
+					ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
+					break;
+				}
+			}
+		}
+	}
+}
+
+static void amd_sdwc_irq_thread(struct work_struct *work)
+{
+	struct amd_sdwc_ctrl *ctrl =
+			container_of(work, struct amd_sdwc_ctrl, amd_sdw_irq_thread);
+	u32 sw_status_change_0to7_reg;
+	u32 sw_status_change_8to11_reg;
+	u32 status_change_8to11;
+	u32 status_change_0to7;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		sw_status_change_0to7_reg = SW_STATE_CHANGE_STATUS_0TO7;
+		sw_status_change_8to11_reg = SW_STATE_CHANGE_STATUS_8TO11;
+		break;
+	case ACP_SDW1:
+		sw_status_change_0to7_reg = P1_SW_STATE_CHANGE_STATUS_0TO7;
+		sw_status_change_8to11_reg = P1_SW_STATE_CHANGE_STATUS_8TO11;
+		break;
+	default:
+		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
+		return;
+	}
+
+	status_change_8to11 = acp_reg_readl(ctrl->mmio + sw_status_change_8to11_reg);
+	status_change_0to7 = acp_reg_readl(ctrl->mmio + sw_status_change_0to7_reg);
+	dev_dbg(ctrl->dev, "%s [SDW%d] SDW INT: 0to7=0x%x, 8to11=0x%x\n",
+		__func__, ctrl->instance, status_change_0to7, status_change_8to11);
+	if (status_change_8to11 & AMD_SDW_PREQ_INTR_STAT) {
+		amd_sdwc_read_and_process_ping_status(ctrl);
+	} else {
+		/* Check for the updated status on Slave device */
+		amd_sdwc_update_slave_status(status_change_0to7, status_change_8to11, ctrl);
+	}
+	if (status_change_8to11 || status_change_0to7)
+		schedule_work(&ctrl->amd_sdw_work);
+	acp_reg_writel(0x00, ctrl->mmio + sw_status_change_8to11_reg);
+	acp_reg_writel(0x00, ctrl->mmio + sw_status_change_0to7_reg);
+}
+
 static void amd_sdwc_probe_work(struct work_struct *work)
 {
 	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
@@ -1229,6 +1383,8 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 		sdw_bus_master_delete(&ctrl->bus);
 		return ret;
 	}
+	INIT_WORK(&ctrl->amd_sdw_irq_thread, amd_sdwc_irq_thread);
+	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
 	return 0;
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index 42f32ca0c7a8..b43a5d6496cb 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -236,6 +236,7 @@
 #define AMD_SDW1_PAD_KEEPER_EN_MASK	0x10
 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
 #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
+#define AMD_SDW_PREQ_INTR_STAT  BIT(19)
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 7a99d782969f..2db03b2f0c3b 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -24,9 +24,12 @@ struct amd_sdwc_ctrl {
 	struct sdw_bus bus;
 	struct device *dev;
 	void __iomem *mmio;
+	struct work_struct amd_sdw_irq_thread;
+	struct work_struct amd_sdw_work;
 	struct work_struct probe_work;
 	struct mutex *sdw_lock;
 	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
+	enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
 	int num_din_ports;
 	int num_dout_ports;
 	int cols_index;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 06/19] ASoC: amd: ps: add support for soundwire interrupts in acp pci driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, Pierre-Louis Bossart, open list

Handle soundwire controller related interrupts in ACP PCI driver irq
handler and schedule controller work queue for further processing.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h  |  4 ++++
 sound/soc/amd/ps/pci-ps.c | 40 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index ed979e6d0c1d..0bd9dc363461 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -64,6 +64,10 @@
 #define ACP63_SDW_ADDR			5
 #define AMD_SDW_MAX_CONTROLLERS		2
 
+#define ACP_SDW0_IRQ_MASK	21
+#define ACP_SDW1_IRQ_MASK	2
+#define ACP_ERROR_IRQ_MASK      29
+
 enum acp_config {
 	ACP_CONFIG_0 = 0,
 	ACP_CONFIG_1,
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 85154cf0b2a2..0fbe5e27f3fb 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -67,6 +67,7 @@ static int acp63_reset(void __iomem *acp_base)
 static void acp63_enable_interrupts(void __iomem *acp_base)
 {
 	acp63_writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+	acp63_writel(BIT(ACP_ERROR_IRQ_MASK), acp_base + ACP_EXTERNAL_INTR_CNTL);
 }
 
 static void acp63_disable_interrupts(void __iomem *acp_base)
@@ -116,23 +117,52 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 {
 	struct acp63_dev_data *adata;
 	struct pdm_dev_data *ps_pdm_data;
-	u32 val;
+	struct amd_sdwc_ctrl *ctrl;
+	u32 ext_intr_stat, ext_intr_stat1;
+	u16 irq_flag = 0;
 	u16 pdev_index;
 
 	adata = dev_id;
 	if (!adata)
 		return IRQ_NONE;
+	ext_intr_stat = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+	if (ext_intr_stat & BIT(ACP_SDW0_IRQ_MASK)) {
+		pdev_index = adata->sdw0_dev_index;
+		ctrl = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+		acp63_writel(BIT(ACP_SDW0_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+		schedule_work(&ctrl->amd_sdw_irq_thread);
+		irq_flag = 1;
+	}
 
-	val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
-	if (val & BIT(PDM_DMA_STAT)) {
+	ext_intr_stat1 = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+	if (ext_intr_stat1 & BIT(ACP_SDW1_IRQ_MASK)) {
+		pdev_index = adata->sdw1_dev_index;
+		ctrl = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+		acp63_writel(BIT(ACP_SDW1_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		schedule_work(&ctrl->amd_sdw_irq_thread);
+		irq_flag = 1;
+	}
+
+	if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) {
+		acp63_writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+		acp63_writel(0, adata->acp63_base + ACP_SW_I2S_ERROR_REASON);
+		acp63_writel(0, adata->acp63_base + ACP_P1_SW_I2S_ERROR_REASON);
+		acp63_writel(0, adata->acp63_base + ACP_ERROR_STATUS);
+		irq_flag = 1;
+	}
+
+	if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
 		pdev_index = adata->pdm_dev_index;
 		ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
 		acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
 		if (ps_pdm_data->capture_stream)
 			snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
-		return IRQ_HANDLED;
+		irq_flag = 1;
 	}
-	return IRQ_NONE;
+	if (irq_flag)
+		return IRQ_HANDLED;
+	else
+		return IRQ_NONE;
 }
 
 static int sdw_amd_scan_controller(struct device *dev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 06/19] ASoC: amd: ps: add support for soundwire interrupts in acp pci driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Pierre-Louis Bossart, Mastan.Katragadda, Sunil-kumar.Dommati,
	open list, Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, Vijendar Mukunda,
	arungopal.kondaveeti, Syed Saba Kareem

Handle soundwire controller related interrupts in ACP PCI driver irq
handler and schedule controller work queue for further processing.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h  |  4 ++++
 sound/soc/amd/ps/pci-ps.c | 40 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index ed979e6d0c1d..0bd9dc363461 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -64,6 +64,10 @@
 #define ACP63_SDW_ADDR			5
 #define AMD_SDW_MAX_CONTROLLERS		2
 
+#define ACP_SDW0_IRQ_MASK	21
+#define ACP_SDW1_IRQ_MASK	2
+#define ACP_ERROR_IRQ_MASK      29
+
 enum acp_config {
 	ACP_CONFIG_0 = 0,
 	ACP_CONFIG_1,
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 85154cf0b2a2..0fbe5e27f3fb 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -67,6 +67,7 @@ static int acp63_reset(void __iomem *acp_base)
 static void acp63_enable_interrupts(void __iomem *acp_base)
 {
 	acp63_writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+	acp63_writel(BIT(ACP_ERROR_IRQ_MASK), acp_base + ACP_EXTERNAL_INTR_CNTL);
 }
 
 static void acp63_disable_interrupts(void __iomem *acp_base)
@@ -116,23 +117,52 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 {
 	struct acp63_dev_data *adata;
 	struct pdm_dev_data *ps_pdm_data;
-	u32 val;
+	struct amd_sdwc_ctrl *ctrl;
+	u32 ext_intr_stat, ext_intr_stat1;
+	u16 irq_flag = 0;
 	u16 pdev_index;
 
 	adata = dev_id;
 	if (!adata)
 		return IRQ_NONE;
+	ext_intr_stat = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+	if (ext_intr_stat & BIT(ACP_SDW0_IRQ_MASK)) {
+		pdev_index = adata->sdw0_dev_index;
+		ctrl = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+		acp63_writel(BIT(ACP_SDW0_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+		schedule_work(&ctrl->amd_sdw_irq_thread);
+		irq_flag = 1;
+	}
 
-	val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
-	if (val & BIT(PDM_DMA_STAT)) {
+	ext_intr_stat1 = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+	if (ext_intr_stat1 & BIT(ACP_SDW1_IRQ_MASK)) {
+		pdev_index = adata->sdw1_dev_index;
+		ctrl = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+		acp63_writel(BIT(ACP_SDW1_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		schedule_work(&ctrl->amd_sdw_irq_thread);
+		irq_flag = 1;
+	}
+
+	if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) {
+		acp63_writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+		acp63_writel(0, adata->acp63_base + ACP_SW_I2S_ERROR_REASON);
+		acp63_writel(0, adata->acp63_base + ACP_P1_SW_I2S_ERROR_REASON);
+		acp63_writel(0, adata->acp63_base + ACP_ERROR_STATUS);
+		irq_flag = 1;
+	}
+
+	if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
 		pdev_index = adata->pdm_dev_index;
 		ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
 		acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
 		if (ps_pdm_data->capture_stream)
 			snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
-		return IRQ_HANDLED;
+		irq_flag = 1;
 	}
-	return IRQ_NONE;
+	if (irq_flag)
+		return IRQ_HANDLED;
+	else
+		return IRQ_NONE;
 }
 
 static int sdw_amd_scan_controller(struct device *dev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

Soundwire DMA platform driver binds to the platform device created by
ACP PCI device. Soundwire DMA driver registers ALSA DMA component
with ASoC framework.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h      |  5 +++
 sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 0bd9dc363461..b462320fdf2a 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -135,3 +135,8 @@ struct acp63_dev_data {
 	bool is_sdw_dev;
 	bool acp_sdw_power_off;
 };
+
+struct sdw_dma_dev_data {
+	void __iomem *acp_base;
+	struct mutex *acp_lock;
+};
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
new file mode 100644
index 000000000000..388a4b7df715
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
+ *
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "acp63.h"
+
+#define DRV_NAME "amd_ps_sdw_dma"
+
+static const struct snd_soc_component_driver acp63_sdw_component = {
+	.name		= DRV_NAME,
+};
+
+static int acp63_sdw_platform_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct sdw_dma_dev_data *sdw_data;
+	int status;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "platform_data not retrieved\n");
+		return -ENODEV;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+		return -ENODEV;
+	}
+
+	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
+	if (!sdw_data)
+		return -ENOMEM;
+
+	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!sdw_data->acp_base)
+		return -ENOMEM;
+
+	sdw_data->acp_lock = pdev->dev.platform_data;
+	dev_set_drvdata(&pdev->dev, sdw_data);
+	status = devm_snd_soc_register_component(&pdev->dev,
+						 &acp63_sdw_component,
+						 NULL, 0);
+	if (status) {
+		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct platform_driver acp63_sdw_dma_driver = {
+	.probe = acp63_sdw_platform_probe,
+	.driver = {
+		.name = "amd_ps_sdw_dma",
+	},
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti,
	Syed Saba Kareem

Soundwire DMA platform driver binds to the platform device created by
ACP PCI device. Soundwire DMA driver registers ALSA DMA component
with ASoC framework.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h      |  5 +++
 sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 0bd9dc363461..b462320fdf2a 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -135,3 +135,8 @@ struct acp63_dev_data {
 	bool is_sdw_dev;
 	bool acp_sdw_power_off;
 };
+
+struct sdw_dma_dev_data {
+	void __iomem *acp_base;
+	struct mutex *acp_lock;
+};
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
new file mode 100644
index 000000000000..388a4b7df715
--- /dev/null
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
+ *
+ * Copyright 2023 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "acp63.h"
+
+#define DRV_NAME "amd_ps_sdw_dma"
+
+static const struct snd_soc_component_driver acp63_sdw_component = {
+	.name		= DRV_NAME,
+};
+
+static int acp63_sdw_platform_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct sdw_dma_dev_data *sdw_data;
+	int status;
+
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "platform_data not retrieved\n");
+		return -ENODEV;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+		return -ENODEV;
+	}
+
+	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
+	if (!sdw_data)
+		return -ENOMEM;
+
+	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!sdw_data->acp_base)
+		return -ENOMEM;
+
+	sdw_data->acp_lock = pdev->dev.platform_data;
+	dev_set_drvdata(&pdev->dev, sdw_data);
+	status = devm_snd_soc_register_component(&pdev->dev,
+						 &acp63_sdw_component,
+						 NULL, 0);
+	if (status) {
+		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
+
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static struct platform_driver acp63_sdw_dma_driver = {
+	.probe = acp63_sdw_platform_probe,
+	.driver = {
+		.name = "amd_ps_sdw_dma",
+	},
+};
+
+module_platform_driver(acp63_sdw_dma_driver);
+
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
+MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

Add Soundwire DMA driver dma ops for Pink Sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h      |  61 ++++
 sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
 2 files changed, 592 insertions(+)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index b462320fdf2a..8963cfb6120d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -67,6 +67,38 @@
 #define ACP_SDW0_IRQ_MASK	21
 #define ACP_SDW1_IRQ_MASK	2
 #define ACP_ERROR_IRQ_MASK      29
+#define ACP_AUDIO_TX_THRESHOLD	28
+#define ACP_BT_TX_THRESHOLD	26
+#define ACP_HS_TX_THRESHOLD	24
+#define ACP_AUDIO_RX_THRESHOLD	27
+#define ACP_BT_RX_THRESHOLD	25
+#define ACP_HS_RX_THRESHOLD	23
+#define ACP_P1_BT_TX_THRESHOLD	6
+#define ACP_P1_BT_RX_THRESHOLD	5
+#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
+#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
+#define ACP63_SDW_MAX_STREAMS		8
+
+#define ACP_DELAY_US		5
+#define SDW_MEM_WINDOW_START	0x4800000
+#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
+#define SDW_PTE_OFFSET		0x400
+#define SDW_FIFO_SIZE		0x100
+#define SDW_DMA_SIZE		0x40
+#define ACP_SDW_FIFO_OFFSET	0x100
+#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
+
+#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
+#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
+#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
+#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
+#define SDW_CAPTURE_MIN_NUM_PERIODS     2
+#define SDW_CAPTURE_MAX_NUM_PERIODS     8
+#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
+#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
+
+#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
+#define SDW_MIN_BUFFER SDW_MAX_BUFFER
 
 enum acp_config {
 	ACP_CONFIG_0 = 0,
@@ -93,6 +125,17 @@ enum acp_pdev_mask {
 	ACP63_SDW_PDM_DEV_MASK,
 };
 
+enum channel_type {
+	ACP_SDW_AUDIO_TX = 0,
+	ACP_SDW_BT_TX,
+	ACP_SDW_HS_TX,
+	ACP_SDW_AUDIO_RX,
+	ACP_SDW_BT_RX,
+	ACP_SDW_HS_RX,
+	ACP_SDW1_BT_TX,
+	ACP_SDW1_BT_RX,
+};
+
 struct pdm_stream_instance {
 	u16 num_pages;
 	u16 channels;
@@ -139,4 +182,22 @@ struct acp63_dev_data {
 struct sdw_dma_dev_data {
 	void __iomem *acp_base;
 	struct mutex *acp_lock;
+	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
+};
+
+struct sdw_stream_instance {
+	u16 num_pages;
+	u16 channels;
+	u32 stream_id;
+	dma_addr_t dma_addr;
+	u64 bytescount;
+	void __iomem *acp_base;
+};
+
+union acp_sdw_dma_count {
+	struct {
+	u32 low;
+	u32 high;
+	} bcount;
+	u64 bytescount;
 };
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index 388a4b7df715..e94f76053c66 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -12,12 +12,543 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
+#include <linux/soundwire/sdw_amd.h>
 #include "acp63.h"
 
 #define DRV_NAME "amd_ps_sdw_dma"
 
+static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
+		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
+	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
+		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
+	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
+{
+	u16 page_idx;
+	u32 low, high, val;
+	dma_addr_t addr;
+
+	addr = sdw_ins->dma_addr;
+	val = SDW_PTE_OFFSET + (stream_id * 256);
+
+	/* Group Enable */
+	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
+		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
+		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
+		/* Load the low address of page int ACP SRAM through SRBM */
+		low = lower_32_bits(addr);
+		high = upper_32_bits(addr);
+
+		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
+		high |= BIT(31);
+		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+		val += 8;
+		addr += PAGE_SIZE;
+	}
+
+	/*cache Invalidation added for Testing */
+	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size)
+{
+	u32 reg_dma_size;
+	u32 reg_fifo_addr;
+	u32 reg_fifo_size;
+	u32 reg_ring_buf_size;
+	u32 reg_ring_buf_addr;
+	u32 sdw_fifo_addr;
+	u32 sdw_ring_buf_addr;
+	u32 sdw_ring_buf_size;
+
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		reg_dma_size = ACP_AUDIO_TX_DMA_SIZE;
+		reg_fifo_addr =	ACP_AUDIO_TX_FIFOADDR;
+		reg_fifo_size = ACP_AUDIO_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_AUDIO_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_AUDIO_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_BT_TX:
+		reg_dma_size = ACP_BT_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_BT_TX_FIFOADDR;
+		reg_fifo_size = ACP_BT_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_BT_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_BT_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_HS_TX:
+		reg_dma_size = ACP_HS_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+		reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_HS_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_HS_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW1_BT_TX:
+		reg_dma_size = ACP_P1_BT_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_P1_BT_TX_FIFOADDR;
+		reg_fifo_size = ACP_P1_BT_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_P1_BT_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_P1_BT_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		reg_dma_size = ACP_AUDIO_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_AUDIO_RX_FIFOADDR;
+		reg_fifo_size = ACP_AUDIO_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_AUDIO_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_AUDIO_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW_BT_RX:
+		reg_dma_size = ACP_BT_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_BT_RX_FIFOADDR;
+		reg_fifo_size = ACP_BT_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_BT_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_BT_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW_HS_RX:
+		reg_dma_size = ACP_HS_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+		reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_HS_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_HS_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW1_BT_RX:
+		reg_dma_size = ACP_P1_BT_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_P1_BT_RX_FIFOADDR;
+		reg_fifo_size = ACP_P1_BT_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_P1_BT_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_P1_BT_RX_RINGBUFADDR;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sdw_fifo_addr = ACP_SDW_FIFO_OFFSET * stream_id;
+	sdw_ring_buf_addr = SDW_MEM_WINDOW_START + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET);
+	sdw_ring_buf_size = size;
+	acp63_writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size);
+	acp63_writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr);
+	acp63_writel(sdw_fifo_addr, acp_base + reg_fifo_addr);
+	acp63_writel(SDW_DMA_SIZE, acp_base + reg_dma_size);
+	acp63_writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size);
+	return 0;
+}
+
+static int acp63_sdw_dma_open(struct snd_soc_component *component,
+			      struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct sdw_dma_dev_data *sdw_dev_data;
+	struct sdw_stream_instance *sdw_stream_data;
+	struct snd_soc_dai *cpu_dai;
+	struct amd_sdwc_ctrl *ctrl;
+	struct snd_soc_pcm_runtime *prtd = substream->private_data;
+	int ret;
+
+	runtime = substream->runtime;
+	sdw_dev_data = dev_get_drvdata(component->dev);
+	cpu_dai = asoc_rtd_to_cpu(prtd, 0);
+	ctrl = snd_soc_dai_get_drvdata(cpu_dai);
+	sdw_stream_data = kzalloc(sizeof(*sdw_stream_data), GFP_KERNEL);
+	if (!sdw_stream_data)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = acp63_sdw_hardware_playback;
+	else
+		runtime->hw = acp63_sdw_hardware_capture;
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(component->dev, "set integer constraint failed\n");
+		kfree(sdw_stream_data);
+		return ret;
+	}
+
+	if (ctrl->instance == ACP_SDW1)
+		sdw_stream_data->stream_id = cpu_dai->id + ACP_SDW0_MAX_DAI;
+	else
+		sdw_stream_data->stream_id = cpu_dai->id;
+	sdw_stream_data->acp_base = sdw_dev_data->acp_base;
+	runtime->private_data = sdw_stream_data;
+	return ret;
+}
+
+static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
+				   struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params)
+{
+	struct sdw_stream_instance *sdw_stream_data;
+	struct sdw_dma_dev_data *sdw_data;
+	u32 period_bytes;
+	u32 water_mark_size_reg;
+	u32 irq_mask, ext_intr_ctrl;
+	u64 size;
+	u32 stream_id;
+	u32 acp_ext_intr_cntl_reg;
+	int ret;
+
+	stream_id = 0;
+	sdw_data = dev_get_drvdata(component->dev);
+	sdw_stream_data = substream->runtime->private_data;
+	if (!sdw_stream_data)
+		return -EINVAL;
+	stream_id = sdw_stream_data->stream_id;
+	sdw_data->sdw_stream[stream_id] = substream;
+	size = params_buffer_bytes(params);
+	period_bytes = params_period_bytes(params);
+	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
+	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+	acp63_config_dma(sdw_stream_data, stream_id);
+	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
+	if (ret) {
+		dev_err(component->dev, "Invalid channel type\n");
+		return -EINVAL;
+	}
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_BT_TX:
+		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_HS_TX:
+		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW1_BT_TX:
+		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_BT_RX:
+		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_HS_RX:
+		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW1_BT_RX:
+		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+		break;
+	default:
+		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
+		return -EINVAL;
+	}
+
+	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
+	ext_intr_ctrl |= irq_mask;
+	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
+	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
+	return 0;
+}
+
+static u64 acp63_sdw_get_byte_count(struct sdw_stream_instance *rtd)
+{
+	union acp_sdw_dma_count byte_count;
+	u32 pos_low_reg, pos_high_reg;
+
+	byte_count.bytescount = 0;
+	switch (rtd->stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		pos_low_reg = ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_BT_TX:
+		pos_low_reg = ACP_BT_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_BT_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_HS_TX:
+		pos_low_reg = ACP_HS_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_HS_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW1_BT_TX:
+		pos_low_reg = ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		pos_low_reg = ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_BT_RX:
+		pos_low_reg = ACP_BT_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_BT_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_HS_RX:
+		pos_low_reg = ACP_HS_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_HS_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW1_BT_RX:
+		pos_low_reg = ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	default:
+		pr_err("%s Invalid stream id:%d\n", __func__, rtd->stream_id);
+		return -EINVAL;
+	}
+
+	if (pos_low_reg) {
+		byte_count.bcount.high = acp63_readl(rtd->acp_base + pos_high_reg);
+		byte_count.bcount.low = acp63_readl(rtd->acp_base + pos_low_reg);
+	}
+	return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
+					       struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	u32 pos, buffersize;
+	u64 bytescount;
+
+	sdw_ins = stream->runtime->private_data;
+	buffersize = frames_to_bytes(stream->runtime,
+				     stream->runtime->buffer_size);
+	bytescount = acp63_sdw_get_byte_count(sdw_ins);
+	if (bytescount > sdw_ins->bytescount)
+		bytescount -= sdw_ins->bytescount;
+	pos = do_div(bytescount, buffersize);
+	return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp63_sdw_dma_new(struct snd_soc_component *component,
+			     struct snd_soc_pcm_runtime *rtd)
+{
+	struct device *parent = component->dev->parent;
+
+	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+				       parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER);
+	return 0;
+}
+
+static int acp63_sdw_dma_close(struct snd_soc_component *component,
+			       struct snd_pcm_substream *substream)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+	struct snd_soc_pcm_runtime *prtd;
+	struct sdw_stream_instance *sdw_ins;
+
+	prtd = asoc_substream_to_rtd(substream);
+	component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+	sdw_dma_data = dev_get_drvdata(component->dev);
+	sdw_ins = substream->runtime->private_data;
+	if (!sdw_ins)
+		return -EINVAL;
+	sdw_dma_data->sdw_stream[sdw_ins->stream_id] = NULL;
+	kfree(sdw_ins);
+	return 0;
+}
+
+static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_soc_pcm_runtime *prtd;
+	u32 stream_id;
+	u32 sdw_dma_reg;
+	u32 sdw_dma_en_stat_reg;
+	u32 sdw_dma_stat;
+	u32 val;
+	int timeout = 0;
+
+	sdw_ins = stream->runtime->private_data;
+	prtd = stream->private_data;
+	stream_id = sdw_ins->stream_id;
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_TX:
+		sdw_dma_reg = ACP_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_TX:
+		sdw_dma_reg = ACP_SW_HEADSET_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_TX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_TX:
+		sdw_dma_reg = ACP_P1_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		sdw_dma_reg = ACP_SW_AUDIO_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_RX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_RX:
+		sdw_dma_reg = ACP_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_RX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_RX:
+		sdw_dma_reg = ACP_SW_HEADSET_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_RX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_RX:
+		sdw_dma_reg = ACP_P1_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_RX_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp63_writel(0x01, sdw_ins->acp_base + sdw_dma_reg);
+	while (++timeout < ACP_COUNTER) {
+		sdw_dma_stat =  acp63_readl(sdw_ins->acp_base + sdw_dma_en_stat_reg);
+		if (sdw_dma_stat & BIT(0)) {
+			val = acp63_readl(sdw_ins->acp_base + sdw_dma_reg);
+			dev_dbg(prtd->dev, "%s stream_id:0x%x dma_reg[0x%x]:0x%x\n",
+				__func__, stream_id, sdw_dma_reg, val);
+			return 0;
+		}
+		udelay(ACP_DELAY_US);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp63_sdw_dma_stop(struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_soc_pcm_runtime *prtd;
+	u32 stream_id;
+	u32 sdw_dma_reg;
+	u32 sdw_dma_en_stat_reg;
+	u32 sdw_dma_stat;
+	u32 val;
+	int timeout = 0;
+
+	prtd = stream->private_data;
+	sdw_ins = stream->runtime->private_data;
+	stream_id = sdw_ins->stream_id;
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_TX:
+		sdw_dma_reg = ACP_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_TX:
+		sdw_dma_reg = ACP_SW_HEADSET_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_TX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_TX:
+		sdw_dma_reg = ACP_P1_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		sdw_dma_reg = ACP_SW_AUDIO_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_RX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_RX:
+		sdw_dma_reg = ACP_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_RX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_RX:
+		sdw_dma_reg = ACP_SW_HEADSET_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_RX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_RX:
+		sdw_dma_reg = ACP_P1_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_RX_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp63_writel(0, sdw_ins->acp_base + sdw_dma_reg);
+	while (++timeout < ACP_COUNTER) {
+		sdw_dma_stat =  acp63_readl(sdw_ins->acp_base + sdw_dma_en_stat_reg);
+		if (sdw_dma_stat == 0) {
+			val = acp63_readl(sdw_ins->acp_base + sdw_dma_reg);
+			dev_dbg(prtd->dev, "%s stream_id:0x%x dma_reg[0x%x]:0x%x\n",
+				__func__, stream_id, sdw_dma_reg, val);
+			return 0;
+		}
+		udelay(ACP_DELAY_US);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
+				 struct snd_pcm_substream *substream,
+				 int cmd)
+{
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		ret = acp63_sdw_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		ret = acp63_sdw_dma_stop(substream);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret)
+		dev_err(comp->dev, "trigger %d failed: %d", cmd, ret);
+	return ret;
+}
+
 static const struct snd_soc_component_driver acp63_sdw_component = {
 	.name		= DRV_NAME,
+	.open		= acp63_sdw_dma_open,
+	.close		= acp63_sdw_dma_close,
+	.hw_params	= acp63_sdw_dma_hw_params,
+	.trigger	= acp63_sdw_dma_trigger,
+	.pointer	= acp63_sdw_dma_pointer,
+	.pcm_construct	= acp63_sdw_dma_new,
 };
 
 static int acp63_sdw_platform_probe(struct platform_device *pdev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti,
	Syed Saba Kareem

Add Soundwire DMA driver dma ops for Pink Sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h      |  61 ++++
 sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
 2 files changed, 592 insertions(+)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index b462320fdf2a..8963cfb6120d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -67,6 +67,38 @@
 #define ACP_SDW0_IRQ_MASK	21
 #define ACP_SDW1_IRQ_MASK	2
 #define ACP_ERROR_IRQ_MASK      29
+#define ACP_AUDIO_TX_THRESHOLD	28
+#define ACP_BT_TX_THRESHOLD	26
+#define ACP_HS_TX_THRESHOLD	24
+#define ACP_AUDIO_RX_THRESHOLD	27
+#define ACP_BT_RX_THRESHOLD	25
+#define ACP_HS_RX_THRESHOLD	23
+#define ACP_P1_BT_TX_THRESHOLD	6
+#define ACP_P1_BT_RX_THRESHOLD	5
+#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
+#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
+#define ACP63_SDW_MAX_STREAMS		8
+
+#define ACP_DELAY_US		5
+#define SDW_MEM_WINDOW_START	0x4800000
+#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
+#define SDW_PTE_OFFSET		0x400
+#define SDW_FIFO_SIZE		0x100
+#define SDW_DMA_SIZE		0x40
+#define ACP_SDW_FIFO_OFFSET	0x100
+#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
+
+#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
+#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
+#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
+#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
+#define SDW_CAPTURE_MIN_NUM_PERIODS     2
+#define SDW_CAPTURE_MAX_NUM_PERIODS     8
+#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
+#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
+
+#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
+#define SDW_MIN_BUFFER SDW_MAX_BUFFER
 
 enum acp_config {
 	ACP_CONFIG_0 = 0,
@@ -93,6 +125,17 @@ enum acp_pdev_mask {
 	ACP63_SDW_PDM_DEV_MASK,
 };
 
+enum channel_type {
+	ACP_SDW_AUDIO_TX = 0,
+	ACP_SDW_BT_TX,
+	ACP_SDW_HS_TX,
+	ACP_SDW_AUDIO_RX,
+	ACP_SDW_BT_RX,
+	ACP_SDW_HS_RX,
+	ACP_SDW1_BT_TX,
+	ACP_SDW1_BT_RX,
+};
+
 struct pdm_stream_instance {
 	u16 num_pages;
 	u16 channels;
@@ -139,4 +182,22 @@ struct acp63_dev_data {
 struct sdw_dma_dev_data {
 	void __iomem *acp_base;
 	struct mutex *acp_lock;
+	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
+};
+
+struct sdw_stream_instance {
+	u16 num_pages;
+	u16 channels;
+	u32 stream_id;
+	dma_addr_t dma_addr;
+	u64 bytescount;
+	void __iomem *acp_base;
+};
+
+union acp_sdw_dma_count {
+	struct {
+	u32 low;
+	u32 high;
+	} bcount;
+	u64 bytescount;
 };
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index 388a4b7df715..e94f76053c66 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -12,12 +12,543 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
+#include <linux/soundwire/sdw_amd.h>
 #include "acp63.h"
 
 #define DRV_NAME "amd_ps_sdw_dma"
 
+static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
+		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
+	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
+};
+
+static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
+		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.channels_min = 2,
+	.channels_max = 2,
+	.rates = SNDRV_PCM_RATE_48000,
+	.rate_min = 48000,
+	.rate_max = 48000,
+	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
+	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
+};
+
+static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
+{
+	u16 page_idx;
+	u32 low, high, val;
+	dma_addr_t addr;
+
+	addr = sdw_ins->dma_addr;
+	val = SDW_PTE_OFFSET + (stream_id * 256);
+
+	/* Group Enable */
+	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
+		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
+	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
+		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
+	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
+		/* Load the low address of page int ACP SRAM through SRBM */
+		low = lower_32_bits(addr);
+		high = upper_32_bits(addr);
+
+		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
+		high |= BIT(31);
+		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+		val += 8;
+		addr += PAGE_SIZE;
+	}
+
+	/*cache Invalidation added for Testing */
+	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
+}
+
+static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size)
+{
+	u32 reg_dma_size;
+	u32 reg_fifo_addr;
+	u32 reg_fifo_size;
+	u32 reg_ring_buf_size;
+	u32 reg_ring_buf_addr;
+	u32 sdw_fifo_addr;
+	u32 sdw_ring_buf_addr;
+	u32 sdw_ring_buf_size;
+
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		reg_dma_size = ACP_AUDIO_TX_DMA_SIZE;
+		reg_fifo_addr =	ACP_AUDIO_TX_FIFOADDR;
+		reg_fifo_size = ACP_AUDIO_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_AUDIO_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_AUDIO_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_BT_TX:
+		reg_dma_size = ACP_BT_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_BT_TX_FIFOADDR;
+		reg_fifo_size = ACP_BT_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_BT_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_BT_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_HS_TX:
+		reg_dma_size = ACP_HS_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+		reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_HS_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_HS_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW1_BT_TX:
+		reg_dma_size = ACP_P1_BT_TX_DMA_SIZE;
+		reg_fifo_addr = ACP_P1_BT_TX_FIFOADDR;
+		reg_fifo_size = ACP_P1_BT_TX_FIFOSIZE;
+		reg_ring_buf_size = ACP_P1_BT_TX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_P1_BT_TX_RINGBUFADDR;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		reg_dma_size = ACP_AUDIO_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_AUDIO_RX_FIFOADDR;
+		reg_fifo_size = ACP_AUDIO_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_AUDIO_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_AUDIO_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW_BT_RX:
+		reg_dma_size = ACP_BT_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_BT_RX_FIFOADDR;
+		reg_fifo_size = ACP_BT_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_BT_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_BT_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW_HS_RX:
+		reg_dma_size = ACP_HS_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+		reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_HS_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_HS_RX_RINGBUFADDR;
+		break;
+	case ACP_SDW1_BT_RX:
+		reg_dma_size = ACP_P1_BT_RX_DMA_SIZE;
+		reg_fifo_addr = ACP_P1_BT_RX_FIFOADDR;
+		reg_fifo_size = ACP_P1_BT_RX_FIFOSIZE;
+		reg_ring_buf_size = ACP_P1_BT_RX_RINGBUFSIZE;
+		reg_ring_buf_addr = ACP_P1_BT_RX_RINGBUFADDR;
+		break;
+	default:
+		return -EINVAL;
+	}
+	sdw_fifo_addr = ACP_SDW_FIFO_OFFSET * stream_id;
+	sdw_ring_buf_addr = SDW_MEM_WINDOW_START + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET);
+	sdw_ring_buf_size = size;
+	acp63_writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size);
+	acp63_writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr);
+	acp63_writel(sdw_fifo_addr, acp_base + reg_fifo_addr);
+	acp63_writel(SDW_DMA_SIZE, acp_base + reg_dma_size);
+	acp63_writel(SDW_FIFO_SIZE, acp_base + reg_fifo_size);
+	return 0;
+}
+
+static int acp63_sdw_dma_open(struct snd_soc_component *component,
+			      struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct sdw_dma_dev_data *sdw_dev_data;
+	struct sdw_stream_instance *sdw_stream_data;
+	struct snd_soc_dai *cpu_dai;
+	struct amd_sdwc_ctrl *ctrl;
+	struct snd_soc_pcm_runtime *prtd = substream->private_data;
+	int ret;
+
+	runtime = substream->runtime;
+	sdw_dev_data = dev_get_drvdata(component->dev);
+	cpu_dai = asoc_rtd_to_cpu(prtd, 0);
+	ctrl = snd_soc_dai_get_drvdata(cpu_dai);
+	sdw_stream_data = kzalloc(sizeof(*sdw_stream_data), GFP_KERNEL);
+	if (!sdw_stream_data)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = acp63_sdw_hardware_playback;
+	else
+		runtime->hw = acp63_sdw_hardware_capture;
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		dev_err(component->dev, "set integer constraint failed\n");
+		kfree(sdw_stream_data);
+		return ret;
+	}
+
+	if (ctrl->instance == ACP_SDW1)
+		sdw_stream_data->stream_id = cpu_dai->id + ACP_SDW0_MAX_DAI;
+	else
+		sdw_stream_data->stream_id = cpu_dai->id;
+	sdw_stream_data->acp_base = sdw_dev_data->acp_base;
+	runtime->private_data = sdw_stream_data;
+	return ret;
+}
+
+static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
+				   struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params)
+{
+	struct sdw_stream_instance *sdw_stream_data;
+	struct sdw_dma_dev_data *sdw_data;
+	u32 period_bytes;
+	u32 water_mark_size_reg;
+	u32 irq_mask, ext_intr_ctrl;
+	u64 size;
+	u32 stream_id;
+	u32 acp_ext_intr_cntl_reg;
+	int ret;
+
+	stream_id = 0;
+	sdw_data = dev_get_drvdata(component->dev);
+	sdw_stream_data = substream->runtime->private_data;
+	if (!sdw_stream_data)
+		return -EINVAL;
+	stream_id = sdw_stream_data->stream_id;
+	sdw_data->sdw_stream[stream_id] = substream;
+	size = params_buffer_bytes(params);
+	period_bytes = params_period_bytes(params);
+	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
+	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
+	acp63_config_dma(sdw_stream_data, stream_id);
+	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
+	if (ret) {
+		dev_err(component->dev, "Invalid channel type\n");
+		return -EINVAL;
+	}
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_BT_TX:
+		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_HS_TX:
+		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW1_BT_TX:
+		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_BT_RX:
+		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW_HS_RX:
+		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+		break;
+	case ACP_SDW1_BT_RX:
+		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
+		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
+		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+		break;
+	default:
+		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
+		return -EINVAL;
+	}
+
+	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
+	ext_intr_ctrl |= irq_mask;
+	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
+	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
+	return 0;
+}
+
+static u64 acp63_sdw_get_byte_count(struct sdw_stream_instance *rtd)
+{
+	union acp_sdw_dma_count byte_count;
+	u32 pos_low_reg, pos_high_reg;
+
+	byte_count.bytescount = 0;
+	switch (rtd->stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		pos_low_reg = ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_BT_TX:
+		pos_low_reg = ACP_BT_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_BT_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_HS_TX:
+		pos_low_reg = ACP_HS_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_HS_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW1_BT_TX:
+		pos_low_reg = ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		pos_low_reg = ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_BT_RX:
+		pos_low_reg = ACP_BT_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_BT_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW_HS_RX:
+		pos_low_reg = ACP_HS_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_HS_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	case ACP_SDW1_BT_RX:
+		pos_low_reg = ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW;
+		pos_high_reg = ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH;
+		break;
+	default:
+		pr_err("%s Invalid stream id:%d\n", __func__, rtd->stream_id);
+		return -EINVAL;
+	}
+
+	if (pos_low_reg) {
+		byte_count.bcount.high = acp63_readl(rtd->acp_base + pos_high_reg);
+		byte_count.bcount.low = acp63_readl(rtd->acp_base + pos_low_reg);
+	}
+	return byte_count.bytescount;
+}
+
+static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
+					       struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	u32 pos, buffersize;
+	u64 bytescount;
+
+	sdw_ins = stream->runtime->private_data;
+	buffersize = frames_to_bytes(stream->runtime,
+				     stream->runtime->buffer_size);
+	bytescount = acp63_sdw_get_byte_count(sdw_ins);
+	if (bytescount > sdw_ins->bytescount)
+		bytescount -= sdw_ins->bytescount;
+	pos = do_div(bytescount, buffersize);
+	return bytes_to_frames(stream->runtime, pos);
+}
+
+static int acp63_sdw_dma_new(struct snd_soc_component *component,
+			     struct snd_soc_pcm_runtime *rtd)
+{
+	struct device *parent = component->dev->parent;
+
+	snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+				       parent, SDW_MIN_BUFFER, SDW_MAX_BUFFER);
+	return 0;
+}
+
+static int acp63_sdw_dma_close(struct snd_soc_component *component,
+			       struct snd_pcm_substream *substream)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+	struct snd_soc_pcm_runtime *prtd;
+	struct sdw_stream_instance *sdw_ins;
+
+	prtd = asoc_substream_to_rtd(substream);
+	component = snd_soc_rtdcom_lookup(prtd, DRV_NAME);
+	sdw_dma_data = dev_get_drvdata(component->dev);
+	sdw_ins = substream->runtime->private_data;
+	if (!sdw_ins)
+		return -EINVAL;
+	sdw_dma_data->sdw_stream[sdw_ins->stream_id] = NULL;
+	kfree(sdw_ins);
+	return 0;
+}
+
+static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_soc_pcm_runtime *prtd;
+	u32 stream_id;
+	u32 sdw_dma_reg;
+	u32 sdw_dma_en_stat_reg;
+	u32 sdw_dma_stat;
+	u32 val;
+	int timeout = 0;
+
+	sdw_ins = stream->runtime->private_data;
+	prtd = stream->private_data;
+	stream_id = sdw_ins->stream_id;
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_TX:
+		sdw_dma_reg = ACP_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_TX:
+		sdw_dma_reg = ACP_SW_HEADSET_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_TX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_TX:
+		sdw_dma_reg = ACP_P1_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		sdw_dma_reg = ACP_SW_AUDIO_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_RX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_RX:
+		sdw_dma_reg = ACP_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_RX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_RX:
+		sdw_dma_reg = ACP_SW_HEADSET_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_RX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_RX:
+		sdw_dma_reg = ACP_P1_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_RX_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp63_writel(0x01, sdw_ins->acp_base + sdw_dma_reg);
+	while (++timeout < ACP_COUNTER) {
+		sdw_dma_stat =  acp63_readl(sdw_ins->acp_base + sdw_dma_en_stat_reg);
+		if (sdw_dma_stat & BIT(0)) {
+			val = acp63_readl(sdw_ins->acp_base + sdw_dma_reg);
+			dev_dbg(prtd->dev, "%s stream_id:0x%x dma_reg[0x%x]:0x%x\n",
+				__func__, stream_id, sdw_dma_reg, val);
+			return 0;
+		}
+		udelay(ACP_DELAY_US);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp63_sdw_dma_stop(struct snd_pcm_substream *stream)
+{
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_soc_pcm_runtime *prtd;
+	u32 stream_id;
+	u32 sdw_dma_reg;
+	u32 sdw_dma_en_stat_reg;
+	u32 sdw_dma_stat;
+	u32 val;
+	int timeout = 0;
+
+	prtd = stream->private_data;
+	sdw_ins = stream->runtime->private_data;
+	stream_id = sdw_ins->stream_id;
+	switch (stream_id) {
+	case ACP_SDW_AUDIO_TX:
+		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_TX:
+		sdw_dma_reg = ACP_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_TX:
+		sdw_dma_reg = ACP_SW_HEADSET_TX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_TX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_TX:
+		sdw_dma_reg = ACP_P1_SW_BT_TX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_TX_EN_STATUS;
+		break;
+	case ACP_SDW_AUDIO_RX:
+		sdw_dma_reg = ACP_SW_AUDIO_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_AUDIO_RX_EN_STATUS;
+		break;
+	case ACP_SDW_BT_RX:
+		sdw_dma_reg = ACP_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_BT_RX_EN_STATUS;
+		break;
+	case ACP_SDW_HS_RX:
+		sdw_dma_reg = ACP_SW_HEADSET_RX_EN;
+		sdw_dma_en_stat_reg = ACP_SW_HEADSET_RX_EN_STATUS;
+		break;
+	case ACP_SDW1_BT_RX:
+		sdw_dma_reg = ACP_P1_SW_BT_RX_EN;
+		sdw_dma_en_stat_reg = ACP_P1_SW_BT_RX_EN_STATUS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	acp63_writel(0, sdw_ins->acp_base + sdw_dma_reg);
+	while (++timeout < ACP_COUNTER) {
+		sdw_dma_stat =  acp63_readl(sdw_ins->acp_base + sdw_dma_en_stat_reg);
+		if (sdw_dma_stat == 0) {
+			val = acp63_readl(sdw_ins->acp_base + sdw_dma_reg);
+			dev_dbg(prtd->dev, "%s stream_id:0x%x dma_reg[0x%x]:0x%x\n",
+				__func__, stream_id, sdw_dma_reg, val);
+			return 0;
+		}
+		udelay(ACP_DELAY_US);
+	}
+	return -ETIMEDOUT;
+}
+
+static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
+				 struct snd_pcm_substream *substream,
+				 int cmd)
+{
+	int ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		ret = acp63_sdw_dma_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+		ret = acp63_sdw_dma_stop(substream);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret)
+		dev_err(comp->dev, "trigger %d failed: %d", cmd, ret);
+	return ret;
+}
+
 static const struct snd_soc_component_driver acp63_sdw_component = {
 	.name		= DRV_NAME,
+	.open		= acp63_sdw_dma_open,
+	.close		= acp63_sdw_dma_close,
+	.hw_params	= acp63_sdw_dma_hw_params,
+	.trigger	= acp63_sdw_dma_trigger,
+	.pointer	= acp63_sdw_dma_pointer,
+	.pcm_construct	= acp63_sdw_dma_new,
 };
 
 static int acp63_sdw_platform_probe(struct platform_device *pdev)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Pierre-Louis Bossart, Mastan.Katragadda, Sunil-kumar.Dommati,
	open list, Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, Vijendar Mukunda,
	arungopal.kondaveeti, Syed Saba Kareem

Initialize workqueue for DMA interrupts handling.
Whenever audio data equal to the Soundwire FIFO watermark level are
produced/consumed, interrupt is generated.
Acknowledge the interrupt and schedule the workqueue.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h  |  2 +
 sound/soc/amd/ps/pci-ps.c | 81 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 8963cfb6120d..833d0b5aa73d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -167,9 +167,11 @@ struct acp63_dev_data {
 	struct platform_device *pdev[ACP63_DEVS];
 	struct mutex acp_lock; /* protect shared registers */
 	struct fwnode_handle *sdw_fw_node;
+	struct work_struct acp_sdw_dma_work;
 	u16 pdev_mask;
 	u16 pdev_count;
 	u16 pdm_dev_index;
+	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];
 	u8 sdw_master_count;
 	u16 sdw0_dev_index;
 	u16 sdw1_dev_index;
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 0fbe5e27f3fb..5b82ee8e3ad8 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
 	return 0;
 }
 
+static void acp63_sdw_dma_workthread(struct work_struct *work)
+{
+	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
+						    acp_sdw_dma_work);
+	struct sdw_dma_dev_data *sdw_dma_data;
+	u32 stream_index;
+	u16 pdev_index;
+
+	pdev_index = adata->sdw_dma_dev_index;
+	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+
+	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
+		if (adata->dma_intr_stat[stream_index]) {
+			if (sdw_dma_data->sdw_stream[stream_index])
+				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);
+			adata->dma_intr_stat[stream_index] = 0;
+		}
+	}
+}
+
 static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 {
 	struct acp63_dev_data *adata;
 	struct pdm_dev_data *ps_pdm_data;
 	struct amd_sdwc_ctrl *ctrl;
 	u32 ext_intr_stat, ext_intr_stat1;
+	u32 stream_id = 0;
 	u16 irq_flag = 0;
+	u16 sdw_dma_irq_flag = 0;
 	u16 pdev_index;
+	u16 index;
 
 	adata = dev_id;
 	if (!adata)
@@ -159,7 +182,58 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 			snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
 		irq_flag = 1;
 	}
-	if (irq_flag)
+	if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
+		for (index = ACP_HS_RX_THRESHOLD; index <= ACP_AUDIO_TX_THRESHOLD; index++) {
+			if (ext_intr_stat & BIT(index)) {
+				acp63_writel(BIT(index),
+					     adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+				switch (index) {
+				case ACP_AUDIO_TX_THRESHOLD:
+					stream_id = ACP_SDW_AUDIO_TX;
+					break;
+				case ACP_BT_TX_THRESHOLD:
+					stream_id = ACP_SDW_BT_TX;
+					break;
+				case ACP_HS_TX_THRESHOLD:
+					stream_id = ACP_SDW_HS_TX;
+					break;
+				case ACP_AUDIO_RX_THRESHOLD:
+					stream_id = ACP_SDW_AUDIO_RX;
+					break;
+				case ACP_BT_RX_THRESHOLD:
+					stream_id = ACP_SDW_BT_RX;
+					break;
+				case ACP_HS_RX_THRESHOLD:
+					stream_id = ACP_SDW_HS_RX;
+					break;
+				}
+
+				adata->dma_intr_stat[stream_id] = 1;
+				sdw_dma_irq_flag = 1;
+			}
+		}
+	}
+
+	/* SDW1 BT RX */
+	if (ext_intr_stat1 & BIT(ACP_P1_BT_RX_THRESHOLD)) {
+		acp63_writel(BIT(ACP_P1_BT_RX_THRESHOLD),
+			     adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		adata->dma_intr_stat[ACP_SDW1_BT_RX] = 1;
+		sdw_dma_irq_flag = 1;
+	}
+
+	/* SDW1 BT TX*/
+	if (ext_intr_stat1 & BIT(ACP_P1_BT_TX_THRESHOLD)) {
+		acp63_writel(BIT(ACP_P1_BT_TX_THRESHOLD),
+			     adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		adata->dma_intr_stat[ACP_SDW1_BT_TX] = 1;
+		sdw_dma_irq_flag = 1;
+	}
+
+	if (sdw_dma_irq_flag)
+		schedule_work(&adata->acp_sdw_dma_work);
+
+	if (irq_flag || sdw_dma_irq_flag)
 		return IRQ_HANDLED;
 	else
 		return IRQ_NONE;
@@ -240,6 +314,7 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63
 	if (sdw_dev) {
 		is_sdw_dev = true;
 		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
+		INIT_WORK(&acp_data->acp_sdw_dma_work, acp63_sdw_dma_workthread);
 		ret = sdw_amd_scan_controller(dev);
 		if (ret)
 			return ret;
@@ -612,6 +687,10 @@ static void snd_acp63_remove(struct pci_dev *pci)
 	int ret, index;
 
 	adata = pci_get_drvdata(pci);
+
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK)
+		cancel_work_sync(&adata->acp_sdw_dma_work);
+
 	for (index = 0; index < adata->pdev_count; index++)
 		platform_device_unregister(adata->pdev[index]);
 	ret = acp63_deinit(adata->acp63_base, &pci->dev);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, Pierre-Louis Bossart, open list

Initialize workqueue for DMA interrupts handling.
Whenever audio data equal to the Soundwire FIFO watermark level are
produced/consumed, interrupt is generated.
Acknowledge the interrupt and schedule the workqueue.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h  |  2 +
 sound/soc/amd/ps/pci-ps.c | 81 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 82 insertions(+), 1 deletion(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 8963cfb6120d..833d0b5aa73d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -167,9 +167,11 @@ struct acp63_dev_data {
 	struct platform_device *pdev[ACP63_DEVS];
 	struct mutex acp_lock; /* protect shared registers */
 	struct fwnode_handle *sdw_fw_node;
+	struct work_struct acp_sdw_dma_work;
 	u16 pdev_mask;
 	u16 pdev_count;
 	u16 pdm_dev_index;
+	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];
 	u8 sdw_master_count;
 	u16 sdw0_dev_index;
 	u16 sdw1_dev_index;
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 0fbe5e27f3fb..5b82ee8e3ad8 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
 	return 0;
 }
 
+static void acp63_sdw_dma_workthread(struct work_struct *work)
+{
+	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
+						    acp_sdw_dma_work);
+	struct sdw_dma_dev_data *sdw_dma_data;
+	u32 stream_index;
+	u16 pdev_index;
+
+	pdev_index = adata->sdw_dma_dev_index;
+	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
+
+	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
+		if (adata->dma_intr_stat[stream_index]) {
+			if (sdw_dma_data->sdw_stream[stream_index])
+				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);
+			adata->dma_intr_stat[stream_index] = 0;
+		}
+	}
+}
+
 static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 {
 	struct acp63_dev_data *adata;
 	struct pdm_dev_data *ps_pdm_data;
 	struct amd_sdwc_ctrl *ctrl;
 	u32 ext_intr_stat, ext_intr_stat1;
+	u32 stream_id = 0;
 	u16 irq_flag = 0;
+	u16 sdw_dma_irq_flag = 0;
 	u16 pdev_index;
+	u16 index;
 
 	adata = dev_id;
 	if (!adata)
@@ -159,7 +182,58 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
 			snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
 		irq_flag = 1;
 	}
-	if (irq_flag)
+	if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
+		for (index = ACP_HS_RX_THRESHOLD; index <= ACP_AUDIO_TX_THRESHOLD; index++) {
+			if (ext_intr_stat & BIT(index)) {
+				acp63_writel(BIT(index),
+					     adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+				switch (index) {
+				case ACP_AUDIO_TX_THRESHOLD:
+					stream_id = ACP_SDW_AUDIO_TX;
+					break;
+				case ACP_BT_TX_THRESHOLD:
+					stream_id = ACP_SDW_BT_TX;
+					break;
+				case ACP_HS_TX_THRESHOLD:
+					stream_id = ACP_SDW_HS_TX;
+					break;
+				case ACP_AUDIO_RX_THRESHOLD:
+					stream_id = ACP_SDW_AUDIO_RX;
+					break;
+				case ACP_BT_RX_THRESHOLD:
+					stream_id = ACP_SDW_BT_RX;
+					break;
+				case ACP_HS_RX_THRESHOLD:
+					stream_id = ACP_SDW_HS_RX;
+					break;
+				}
+
+				adata->dma_intr_stat[stream_id] = 1;
+				sdw_dma_irq_flag = 1;
+			}
+		}
+	}
+
+	/* SDW1 BT RX */
+	if (ext_intr_stat1 & BIT(ACP_P1_BT_RX_THRESHOLD)) {
+		acp63_writel(BIT(ACP_P1_BT_RX_THRESHOLD),
+			     adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		adata->dma_intr_stat[ACP_SDW1_BT_RX] = 1;
+		sdw_dma_irq_flag = 1;
+	}
+
+	/* SDW1 BT TX*/
+	if (ext_intr_stat1 & BIT(ACP_P1_BT_TX_THRESHOLD)) {
+		acp63_writel(BIT(ACP_P1_BT_TX_THRESHOLD),
+			     adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+		adata->dma_intr_stat[ACP_SDW1_BT_TX] = 1;
+		sdw_dma_irq_flag = 1;
+	}
+
+	if (sdw_dma_irq_flag)
+		schedule_work(&adata->acp_sdw_dma_work);
+
+	if (irq_flag || sdw_dma_irq_flag)
 		return IRQ_HANDLED;
 	else
 		return IRQ_NONE;
@@ -240,6 +314,7 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63
 	if (sdw_dev) {
 		is_sdw_dev = true;
 		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
+		INIT_WORK(&acp_data->acp_sdw_dma_work, acp63_sdw_dma_workthread);
 		ret = sdw_amd_scan_controller(dev);
 		if (ret)
 			return ret;
@@ -612,6 +687,10 @@ static void snd_acp63_remove(struct pci_dev *pci)
 	int ret, index;
 
 	adata = pci_get_drvdata(pci);
+
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK)
+		cancel_work_sync(&adata->acp_sdw_dma_work);
+
 	for (index = 0; index < adata->pdev_count; index++)
 		platform_device_unregister(adata->pdev[index]);
 	ret = acp63_deinit(adata->acp63_base, &pci->dev);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 10/19] ASoC: amd: ps: enable Soundwire DMA driver build
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

Enable Soundwire DMA driver build for PS platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
index 383973a12f6a..a3da8670e3e2 100644
--- a/sound/soc/amd/ps/Makefile
+++ b/sound/soc/amd/ps/Makefile
@@ -3,7 +3,9 @@
 snd-pci-ps-objs := pci-ps.o
 snd-ps-pdm-dma-objs := ps-pdm-dma.o
 snd-soc-ps-mach-objs := ps-mach.o
+snd-ps-sdw-dma-objs	:= ps-sdw-dma.o
 
 obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
 obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o
 obj-$(CONFIG_SND_SOC_AMD_PS_MACH)   += snd-soc-ps-mach.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 10/19] ASoC: amd: ps: enable Soundwire DMA driver build
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti,
	Syed Saba Kareem

Enable Soundwire DMA driver build for PS platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
index 383973a12f6a..a3da8670e3e2 100644
--- a/sound/soc/amd/ps/Makefile
+++ b/sound/soc/amd/ps/Makefile
@@ -3,7 +3,9 @@
 snd-pci-ps-objs := pci-ps.o
 snd-ps-pdm-dma-objs := ps-pdm-dma.o
 snd-soc-ps-mach-objs := ps-mach.o
+snd-ps-sdw-dma-objs	:= ps-sdw-dma.o
 
 obj-$(CONFIG_SND_SOC_AMD_PS) += snd-pci-ps.o
 obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-pdm-dma.o
+obj-$(CONFIG_SND_SOC_AMD_PS) += snd-ps-sdw-dma.o
 obj-$(CONFIG_SND_SOC_AMD_PS_MACH)   += snd-soc-ps-mach.o
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 11/19] ASoC: amd: update comments in Kconfig file
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, syed saba kareem,
	Ajit Kumar Pandey, Randy Dunlap, open list

Update comments in Kconfig file for PS platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/Kconfig | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index c88ebd84bdd5..50bcec55a275 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -134,7 +134,8 @@ config SND_SOC_AMD_PS
         help
           This option enables Audio Coprocessor i.e ACP v6.3 support on
           AMD Pink sardine platform. By enabling this flag build will be
-          triggered for ACP PCI driver, ACP PDM DMA driver.
+          triggered for ACP PCI driver, ACP PDM DMA driver, ACP SDW DMA
+          driver.
           Say m if you have such a device.
           If unsure select "N".
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 11/19] ASoC: amd: update comments in Kconfig file
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Ajit Kumar Pandey,
	open list, Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Randy Dunlap, Mario.Limonciello, Vijendar Mukunda,
	arungopal.kondaveeti, syed saba kareem

Update comments in Kconfig file for PS platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/Kconfig | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index c88ebd84bdd5..50bcec55a275 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -134,7 +134,8 @@ config SND_SOC_AMD_PS
         help
           This option enables Audio Coprocessor i.e ACP v6.3 support on
           AMD Pink sardine platform. By enabling this flag build will be
-          triggered for ACP PCI driver, ACP PDM DMA driver.
+          triggered for ACP PCI driver, ACP PDM DMA driver, ACP SDW DMA
+          driver.
           Say m if you have such a device.
           If unsure select "N".
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 12/19] ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops.
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Pierre-Louis Bossart, Mastan.Katragadda, Sunil-kumar.Dommati,
	open list, Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, Vijendar Mukunda,
	arungopal.kondaveeti, Syed Saba Kareem

AMD Soundwire controller supports different power modes.
In case of Soundwire Power off Mode, ACP pci parent driver
should invoke acp de-init and init sequence during suspend/resume
callbacks.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/pci-ps.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 5b82ee8e3ad8..7e152241c155 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -655,10 +655,15 @@ static int snd_acp63_probe(struct pci_dev *pci,
 static int __maybe_unused snd_acp63_suspend(struct device *dev)
 {
 	struct acp63_dev_data *adata;
-	int ret;
+	int ret = 0;
 
 	adata = dev_get_drvdata(dev);
-	ret = acp63_deinit(adata->acp63_base, dev);
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK) {
+		if (adata->acp_sdw_power_off)
+			ret = acp63_deinit(adata->acp63_base, dev);
+	} else {
+		ret = acp63_deinit(adata->acp63_base, dev);
+	}
 	if (ret)
 		dev_err(dev, "ACP de-init failed\n");
 	return ret;
@@ -667,10 +672,15 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev)
 static int __maybe_unused snd_acp63_resume(struct device *dev)
 {
 	struct acp63_dev_data *adata;
-	int ret;
+	int ret = 0;
 
 	adata = dev_get_drvdata(dev);
-	ret = acp63_init(adata->acp63_base, dev);
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK) {
+		if (adata->acp_sdw_power_off)
+			ret = acp63_init(adata->acp63_base, dev);
+	} else {
+		ret = acp63_init(adata->acp63_base, dev);
+	}
 	if (ret)
 		dev_err(dev, "ACP init failed\n");
 	return ret;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 12/19] ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops.
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, Pierre-Louis Bossart, open list

AMD Soundwire controller supports different power modes.
In case of Soundwire Power off Mode, ACP pci parent driver
should invoke acp de-init and init sequence during suspend/resume
callbacks.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/pci-ps.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 5b82ee8e3ad8..7e152241c155 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -655,10 +655,15 @@ static int snd_acp63_probe(struct pci_dev *pci,
 static int __maybe_unused snd_acp63_suspend(struct device *dev)
 {
 	struct acp63_dev_data *adata;
-	int ret;
+	int ret = 0;
 
 	adata = dev_get_drvdata(dev);
-	ret = acp63_deinit(adata->acp63_base, dev);
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK) {
+		if (adata->acp_sdw_power_off)
+			ret = acp63_deinit(adata->acp63_base, dev);
+	} else {
+		ret = acp63_deinit(adata->acp63_base, dev);
+	}
 	if (ret)
 		dev_err(dev, "ACP de-init failed\n");
 	return ret;
@@ -667,10 +672,15 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev)
 static int __maybe_unused snd_acp63_resume(struct device *dev)
 {
 	struct acp63_dev_data *adata;
-	int ret;
+	int ret = 0;
 
 	adata = dev_get_drvdata(dev);
-	ret = acp63_init(adata->acp63_base, dev);
+	if (adata->pdev_mask & ACP63_SDW_DEV_MASK) {
+		if (adata->acp_sdw_power_off)
+			ret = acp63_init(adata->acp63_base, dev);
+	} else {
+		ret = acp63_init(adata->acp63_base, dev);
+	}
 	if (ret)
 		dev_err(dev, "ACP init failed\n");
 	return ret;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 13/19] ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, open list

Add support for runtime pm ops for soundwire dma driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/ps-sdw-dma.c | 66 +++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index e94f76053c66..960c0bc5e848 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -12,6 +12,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
 #include <linux/soundwire/sdw_amd.h>
 #include "acp63.h"
 
@@ -56,6 +57,30 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
 	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
 };
 
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable)
+{
+	u32 ext_intr_cntl, ext_intr_cntl1, irq_mask, irq_mask1;
+
+	irq_mask = ACP_SDW_DMA_IRQ_MASK;
+	irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK;
+
+	if (enable) {
+		ext_intr_cntl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl |= irq_mask;
+		acp63_writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl1 = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+		ext_intr_cntl1 |= irq_mask1;
+		acp63_writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+	} else {
+		ext_intr_cntl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl &= ~irq_mask;
+		acp63_writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl1 = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+		ext_intr_cntl1 &= ~irq_mask1;
+		acp63_writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+	}
+}
+
 static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
 {
 	u16 page_idx;
@@ -585,13 +610,54 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev)
 
 		return -ENODEV;
 	}
+	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_allow(&pdev->dev);
+	return 0;
+}
+
+static int acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int __maybe_unused acp63_sdw_pcm_runtime_suspend(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	mutex_lock(sdw_dma_data->acp_lock);
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, false);
+	mutex_unlock(sdw_dma_data->acp_lock);
+
+	return 0;
+}
+
+static int __maybe_unused acp63_sdw_pcm_runtime_resume(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	mutex_lock(sdw_dma_data->acp_lock);
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, true);
+	mutex_unlock(sdw_dma_data->acp_lock);
+
 	return 0;
 }
 
+static const struct dev_pm_ops acp63_pm_ops = {
+	SET_RUNTIME_PM_OPS(acp63_sdw_pcm_runtime_suspend,
+			   acp63_sdw_pcm_runtime_resume, NULL)
+};
+
 static struct platform_driver acp63_sdw_dma_driver = {
 	.probe = acp63_sdw_platform_probe,
+	.remove = acp63_sdw_platform_remove,
 	.driver = {
 		.name = "amd_ps_sdw_dma",
+		.pm = &acp63_pm_ops,
 	},
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 13/19] ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti

Add support for runtime pm ops for soundwire dma driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/ps-sdw-dma.c | 66 +++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index e94f76053c66..960c0bc5e848 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -12,6 +12,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
+#include <linux/pm_runtime.h>
 #include <linux/soundwire/sdw_amd.h>
 #include "acp63.h"
 
@@ -56,6 +57,30 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
 	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
 };
 
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable)
+{
+	u32 ext_intr_cntl, ext_intr_cntl1, irq_mask, irq_mask1;
+
+	irq_mask = ACP_SDW_DMA_IRQ_MASK;
+	irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK;
+
+	if (enable) {
+		ext_intr_cntl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl |= irq_mask;
+		acp63_writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl1 = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+		ext_intr_cntl1 |= irq_mask1;
+		acp63_writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+	} else {
+		ext_intr_cntl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl &= ~irq_mask;
+		acp63_writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+		ext_intr_cntl1 = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+		ext_intr_cntl1 &= ~irq_mask1;
+		acp63_writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+	}
+}
+
 static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
 {
 	u16 page_idx;
@@ -585,13 +610,54 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev)
 
 		return -ENODEV;
 	}
+	pm_runtime_set_autosuspend_delay(&pdev->dev, ACP_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_allow(&pdev->dev);
+	return 0;
+}
+
+static int acp63_sdw_platform_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static int __maybe_unused acp63_sdw_pcm_runtime_suspend(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	mutex_lock(sdw_dma_data->acp_lock);
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, false);
+	mutex_unlock(sdw_dma_data->acp_lock);
+
+	return 0;
+}
+
+static int __maybe_unused acp63_sdw_pcm_runtime_resume(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	mutex_lock(sdw_dma_data->acp_lock);
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, true);
+	mutex_unlock(sdw_dma_data->acp_lock);
+
 	return 0;
 }
 
+static const struct dev_pm_ops acp63_pm_ops = {
+	SET_RUNTIME_PM_OPS(acp63_sdw_pcm_runtime_suspend,
+			   acp63_sdw_pcm_runtime_resume, NULL)
+};
+
 static struct platform_driver acp63_sdw_dma_driver = {
 	.probe = acp63_sdw_platform_probe,
+	.remove = acp63_sdw_platform_remove,
 	.driver = {
 		.name = "amd_ps_sdw_dma",
+		.pm = &acp63_pm_ops,
 	},
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Add support for runtime pm ops for AMD master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |   3 +
 include/linux/soundwire/sdw_amd.h |   1 +
 3 files changed, 209 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index c7063b8bdd7b..d2d7f07de202 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -15,6 +15,7 @@
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_registers.h>
 #include <linux/soundwire/sdw_amd.h>
+#include <linux/pm_runtime.h>
 #include <linux/wait.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
@@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
 	return 0;
 }
 
+static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	int ret;
+
+	ret = amd_disable_sdw_interrupts(ctrl);
+	if (ret)
+		return ret;
+	ret = amd_disable_sdw_controller(ctrl);
+	return ret;
+}
+
 static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
 {
 	u32 sdw_rows, sdw_cols, frame_size;
@@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
+	/* Enable runtime PM */
+	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
 	return 0;
 }
 
@@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
 	amd_disable_sdw_interrupts(ctrl);
 	sdw_bus_master_delete(&ctrl->bus);
 	ret = amd_disable_sdw_controller(ctrl);
+	pm_runtime_disable(&pdev->dev);
 	return ret;
 }
 
+static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 clk_resume_ctrl_reg;
+	u32 wake_en_reg;
+	u32 val;
+	u32 retry_count = 0;
+	int ret;
+
+	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
+	if (ret < 0 && ret != -ENODATA) {
+		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
+		return ret;
+	}
+	ret = sdw_bus_clk_stop(&ctrl->bus);
+	if (ret < 0 && ret != -ENODATA) {
+		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
+		return ret;
+	}
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		wake_en_reg = ACP_SW_WAKE_EN;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		wake_en_reg = ACP_SW1_WAKE_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		if (val & AMD_SDW_CLK_STOP_DONE) {
+			ctrl->clk_stopped = true;
+			break;
+		}
+	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+
+	if (!ctrl->clk_stopped) {
+		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+
+	if (ctrl->wake_en_mask)
+		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
+
+	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
+	return 0;
+}
+
+static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
+{
+	int ret;
+	u32 clk_resume_ctrl_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ctrl->clk_stopped) {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		val |= AMD_SDW_CLK_RESUME_REQ;
+		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
+		do {
+			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+			if (val & AMD_SDW_CLK_RESUME_DONE)
+				break;
+			usleep_range(10, 100);
+		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+		if (val & AMD_SDW_CLK_RESUME_DONE) {
+			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
+			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
+			if (ret < 0)
+				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
+			ctrl->clk_stopped = false;
+		}
+	}
+	if (ctrl->clk_stopped) {
+		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+
+	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
+
+	return 0;
+}
+
+static int __maybe_unused amd_suspend_runtime(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_deinit_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int __maybe_unused amd_resume_runtime(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+	u32 clk_resume_ctrl_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop_exit(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		if (val) {
+			val |= AMD_SDW_CLK_RESUME_REQ;
+			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
+			do {
+				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+				if (val & AMD_SDW_CLK_RESUME_DONE)
+					break;
+				usleep_range(10, 100);
+			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+			if (val & AMD_SDW_CLK_RESUME_DONE) {
+				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
+				ctrl->clk_stopped = false;
+			}
+		}
+		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
+		amd_init_sdw_controller(ctrl);
+		amd_enable_sdw_interrupts(ctrl);
+		ret = amd_enable_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct dev_pm_ops amd_pm = {
+	SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL)
+};
+
 static struct platform_driver amd_sdwc_driver = {
 	.probe	= &amd_sdwc_probe,
 	.remove = &amd_sdwc_remove,
 	.driver = {
 		.name	= "amd_sdw_controller",
+		.pm = &amd_pm,
 	}
 };
 module_platform_driver(amd_sdwc_driver);
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index b43a5d6496cb..cc254255512b 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -237,6 +237,9 @@
 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
 #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
 #define AMD_SDW_PREQ_INTR_STAT  BIT(19)
+#define AMD_SDW_CLK_STOP_DONE		1
+#define AMD_SDW_CLK_RESUME_REQ		2
+#define AMD_SDW_CLK_RESUME_DONE		3
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 2db03b2f0c3b..f362f195b887 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -38,6 +38,7 @@ struct amd_sdwc_ctrl {
 	u32 quirks;
 	u32 wake_en_mask;
 	int num_ports;
+	bool clk_stopped;
 	bool startup_done;
 	u32 power_mode_mask;
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Add support for runtime pm ops for AMD master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
 drivers/soundwire/amd_master.h    |   3 +
 include/linux/soundwire/sdw_amd.h |   1 +
 3 files changed, 209 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index c7063b8bdd7b..d2d7f07de202 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -15,6 +15,7 @@
 #include <linux/soundwire/sdw.h>
 #include <linux/soundwire/sdw_registers.h>
 #include <linux/soundwire/sdw_amd.h>
+#include <linux/pm_runtime.h>
 #include <linux/wait.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
@@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
 	return 0;
 }
 
+static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
+{
+	int ret;
+
+	ret = amd_disable_sdw_interrupts(ctrl);
+	if (ret)
+		return ret;
+	ret = amd_disable_sdw_controller(ctrl);
+	return ret;
+}
+
 static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
 {
 	u32 sdw_rows, sdw_cols, frame_size;
@@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
 	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
 	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
 	schedule_work(&ctrl->probe_work);
+	/* Enable runtime PM */
+	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
 	return 0;
 }
 
@@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
 	amd_disable_sdw_interrupts(ctrl);
 	sdw_bus_master_delete(&ctrl->bus);
 	ret = amd_disable_sdw_controller(ctrl);
+	pm_runtime_disable(&pdev->dev);
 	return ret;
 }
 
+static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
+{
+	u32 clk_resume_ctrl_reg;
+	u32 wake_en_reg;
+	u32 val;
+	u32 retry_count = 0;
+	int ret;
+
+	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
+	if (ret < 0 && ret != -ENODATA) {
+		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
+		return ret;
+	}
+	ret = sdw_bus_clk_stop(&ctrl->bus);
+	if (ret < 0 && ret != -ENODATA) {
+		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
+		return ret;
+	}
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		wake_en_reg = ACP_SW_WAKE_EN;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		wake_en_reg = ACP_SW1_WAKE_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	do {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		if (val & AMD_SDW_CLK_STOP_DONE) {
+			ctrl->clk_stopped = true;
+			break;
+		}
+	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+
+	if (!ctrl->clk_stopped) {
+		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+
+	if (ctrl->wake_en_mask)
+		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
+
+	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
+	return 0;
+}
+
+static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
+{
+	int ret;
+	u32 clk_resume_ctrl_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (ctrl->clk_stopped) {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		val |= AMD_SDW_CLK_RESUME_REQ;
+		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
+		do {
+			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+			if (val & AMD_SDW_CLK_RESUME_DONE)
+				break;
+			usleep_range(10, 100);
+		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+		if (val & AMD_SDW_CLK_RESUME_DONE) {
+			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
+			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
+			if (ret < 0)
+				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
+			ctrl->clk_stopped = false;
+		}
+	}
+	if (ctrl->clk_stopped) {
+		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
+		return -ETIMEDOUT;
+	}
+
+	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
+
+	return 0;
+}
+
+static int __maybe_unused amd_suspend_runtime(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_deinit_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static int __maybe_unused amd_resume_runtime(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+	u32 clk_resume_ctrl_reg;
+	u32 val = 0;
+	u32 retry_count = 0;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+
+	switch (ctrl->instance) {
+	case ACP_SDW0:
+		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
+		break;
+	case ACP_SDW1:
+		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop_exit(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+		if (val) {
+			val |= AMD_SDW_CLK_RESUME_REQ;
+			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
+			do {
+				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
+				if (val & AMD_SDW_CLK_RESUME_DONE)
+					break;
+				usleep_range(10, 100);
+			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
+			if (val & AMD_SDW_CLK_RESUME_DONE) {
+				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
+				ctrl->clk_stopped = false;
+			}
+		}
+		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
+		amd_init_sdw_controller(ctrl);
+		amd_enable_sdw_interrupts(ctrl);
+		ret = amd_enable_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct dev_pm_ops amd_pm = {
+	SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL)
+};
+
 static struct platform_driver amd_sdwc_driver = {
 	.probe	= &amd_sdwc_probe,
 	.remove = &amd_sdwc_remove,
 	.driver = {
 		.name	= "amd_sdw_controller",
+		.pm = &amd_pm,
 	}
 };
 module_platform_driver(amd_sdwc_driver);
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index b43a5d6496cb..cc254255512b 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -237,6 +237,9 @@
 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK        0x1E
 #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK	0xF
 #define AMD_SDW_PREQ_INTR_STAT  BIT(19)
+#define AMD_SDW_CLK_STOP_DONE		1
+#define AMD_SDW_CLK_RESUME_REQ		2
+#define AMD_SDW_CLK_RESUME_DONE		3
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index 2db03b2f0c3b..f362f195b887 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -38,6 +38,7 @@ struct amd_sdwc_ctrl {
 	u32 quirks;
 	u32 wake_en_mask;
 	int num_ports;
+	bool clk_stopped;
 	bool startup_done;
 	u32 power_mode_mask;
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Add start up and shutdown dai ops for AMD Master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index d2d7f07de202..290c59ab7760 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1117,6 +1117,15 @@ static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int directi
 	return 0;
 }
 
+static void amd_sdwc_shutdown(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+
+	pm_runtime_mark_last_busy(ctrl->dev);
+	pm_runtime_put_autosuspend(ctrl->dev);
+}
+
 static int amd_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
 {
 	return amd_set_sdw_stream(dai, stream, direction);
@@ -1137,9 +1146,27 @@ static void *amd_get_sdw_stream(struct snd_soc_dai *dai, int direction)
 	return dma->stream;
 }
 
+static int amd_sdwc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	ret = pm_runtime_get_sync(ctrl->dev);
+	if (ret < 0 && ret != -EACCES) {
+		dev_err_ratelimited(ctrl->dev,
+				    "pm_runtime_get_sync failed in %s, ret %d\n",
+				     __func__, ret);
+		pm_runtime_put_noidle(ctrl->dev);
+		return ret;
+	}
+	return 0;
+}
+
 static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
 	.hw_params = amd_sdwc_hw_params,
 	.hw_free = amd_sdwc_hw_free,
+	.startup = amd_sdwc_startup,
+	.shutdown = amd_sdwc_shutdown,
 	.set_stream = amd_pcm_set_sdw_stream,
 	.get_stream = amd_get_sdw_stream,
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Add start up and shutdown dai ops for AMD Master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 drivers/soundwire/amd_master.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index d2d7f07de202..290c59ab7760 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1117,6 +1117,15 @@ static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int directi
 	return 0;
 }
 
+static void amd_sdwc_shutdown(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+
+	pm_runtime_mark_last_busy(ctrl->dev);
+	pm_runtime_put_autosuspend(ctrl->dev);
+}
+
 static int amd_pcm_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
 {
 	return amd_set_sdw_stream(dai, stream, direction);
@@ -1137,9 +1146,27 @@ static void *amd_get_sdw_stream(struct snd_soc_dai *dai, int direction)
 	return dma->stream;
 }
 
+static int amd_sdwc_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
+	int ret;
+
+	ret = pm_runtime_get_sync(ctrl->dev);
+	if (ret < 0 && ret != -EACCES) {
+		dev_err_ratelimited(ctrl->dev,
+				    "pm_runtime_get_sync failed in %s, ret %d\n",
+				     __func__, ret);
+		pm_runtime_put_noidle(ctrl->dev);
+		return ret;
+	}
+	return 0;
+}
+
 static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
 	.hw_params = amd_sdwc_hw_params,
 	.hw_free = amd_sdwc_hw_free,
+	.startup = amd_sdwc_startup,
+	.shutdown = amd_sdwc_shutdown,
 	.set_stream = amd_pcm_set_sdw_stream,
 	.get_stream = amd_get_sdw_stream,
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 16/19] soundwire: amd: handle wake enable interrupt
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Add wake enable interrupt support for both the soundwire controller
instances.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 9 +++++++++
 drivers/soundwire/amd_master.h    | 1 +
 include/linux/soundwire/sdw_amd.h | 1 +
 3 files changed, 11 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 290c59ab7760..2fd77a673c22 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
 	u32 sw_status_change_mask_0to7_reg;
 	u32 sw_status_change_mask_8to11_reg;
 
+	if (ctrl->wake_event) {
+		pm_runtime_resume(ctrl->dev);
+		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
+		ctrl->wake_event = false;
+		return;
+	}
+
 	switch (ctrl->instance) {
 	case ACP_SDW0:
 		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
@@ -1258,6 +1265,8 @@ static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_chan
 
 	if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
 		memset(ctrl->status, 0, sizeof(ctrl->status));
+	if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK)
+		ctrl->wake_event = true;
 	slave_stat = status_change_0to7;
 	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
 	dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index cc254255512b..e32884e3a45b 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -240,6 +240,7 @@
 #define AMD_SDW_CLK_STOP_DONE		1
 #define AMD_SDW_CLK_RESUME_REQ		2
 #define AMD_SDW_CLK_RESUME_DONE		3
+#define AMD_SDW_WAKE_STAT_MASK		BIT(16)
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index f362f195b887..e3e539b868be 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -40,6 +40,7 @@ struct amd_sdwc_ctrl {
 	int num_ports;
 	bool clk_stopped;
 	bool startup_done;
+	bool wake_event;
 	u32 power_mode_mask;
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 16/19] soundwire: amd: handle wake enable interrupt
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Add wake enable interrupt support for both the soundwire controller
instances.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c    | 9 +++++++++
 drivers/soundwire/amd_master.h    | 1 +
 include/linux/soundwire/sdw_amd.h | 1 +
 3 files changed, 11 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 290c59ab7760..2fd77a673c22 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
 	u32 sw_status_change_mask_0to7_reg;
 	u32 sw_status_change_mask_8to11_reg;
 
+	if (ctrl->wake_event) {
+		pm_runtime_resume(ctrl->dev);
+		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
+		ctrl->wake_event = false;
+		return;
+	}
+
 	switch (ctrl->instance) {
 	case ACP_SDW0:
 		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
@@ -1258,6 +1265,8 @@ static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_chan
 
 	if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
 		memset(ctrl->status, 0, sizeof(ctrl->status));
+	if (status_change_8to11 & AMD_SDW_WAKE_STAT_MASK)
+		ctrl->wake_event = true;
 	slave_stat = status_change_0to7;
 	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
 	dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
diff --git a/drivers/soundwire/amd_master.h b/drivers/soundwire/amd_master.h
index cc254255512b..e32884e3a45b 100644
--- a/drivers/soundwire/amd_master.h
+++ b/drivers/soundwire/amd_master.h
@@ -240,6 +240,7 @@
 #define AMD_SDW_CLK_STOP_DONE		1
 #define AMD_SDW_CLK_RESUME_REQ		2
 #define AMD_SDW_CLK_RESUME_DONE		3
+#define AMD_SDW_WAKE_STAT_MASK		BIT(16)
 
 enum amd_sdw_channel {
 	/* SDW0 */
diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
index f362f195b887..e3e539b868be 100644
--- a/include/linux/soundwire/sdw_amd.h
+++ b/include/linux/soundwire/sdw_amd.h
@@ -40,6 +40,7 @@ struct amd_sdwc_ctrl {
 	int num_ports;
 	bool clk_stopped;
 	bool startup_done;
+	bool wake_event;
 	u32 power_mode_mask;
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	Vijendar Mukunda, arungopal.kondaveeti, Sanyog Kale, Bard Liao

Add pm_prepare callback and System level pm ops support for
AMD master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 2fd77a673c22..f4478cc17aac 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
 	return 0;
 }
 
+static int amd_resume_child_device(struct device *dev, void *data)
+{
+	int ret;
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+
+	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;
+	}
+
+	if (!pm_runtime_suspended(dev))
+		return 0;
+	ret = pm_request_resume(dev);
+	if (ret < 0)
+		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int __maybe_unused amd_pm_prepare(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
+	if (ret < 0)
+		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
+	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = pm_request_resume(dev);
+		if (ret < 0) {
+			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static int __maybe_unused amd_suspend(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_deinit_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
 static int __maybe_unused amd_suspend_runtime(struct device *dev)
 {
 	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
@@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
 }
 
 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)
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Bard Liao, Pierre-Louis Bossart, Sanyog Kale, open list

Add pm_prepare callback and System level pm ops support for
AMD master driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
---
 drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
index 2fd77a673c22..f4478cc17aac 100644
--- a/drivers/soundwire/amd_master.c
+++ b/drivers/soundwire/amd_master.c
@@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
 	return 0;
 }
 
+static int amd_resume_child_device(struct device *dev, void *data)
+{
+	int ret;
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+
+	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;
+	}
+
+	if (!pm_runtime_suspended(dev))
+		return 0;
+	ret = pm_request_resume(dev);
+	if (ret < 0)
+		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int __maybe_unused amd_pm_prepare(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
+	if (ret < 0)
+		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
+	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = pm_request_resume(dev);
+		if (ret < 0) {
+			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static int __maybe_unused amd_suspend(struct device *dev)
+{
+	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
+	struct sdw_bus *bus = &ctrl->bus;
+	int ret;
+
+	if (bus->prop.hw_disabled || !ctrl->startup_done) {
+		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
+			bus->link_id);
+		return 0;
+	}
+
+	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
+		ret = amd_sdwc_clock_stop(ctrl);
+		if (ret)
+			return ret;
+		ret = amd_deinit_sdw_controller(ctrl);
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
 static int __maybe_unused amd_suspend_runtime(struct device *dev)
 {
 	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
@@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
 }
 
 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)
 };
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 18/19] ASoC: amd: ps: implement system level pm ops for soundwire dma driver
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti

Add support for system level pm ops for soundwire dma driver for
pink sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/ps-sdw-dma.c | 59 +++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index 960c0bc5e848..ba2eea23d41e 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -623,6 +623,64 @@ static int acp63_sdw_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_pcm_runtime *runtime;
+	u32 period_bytes, buf_size, water_mark_size_reg;
+	int ret;
+	int index;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	for (index = 0; index < ACP63_SDW_MAX_STREAMS; index++) {
+		if (sdw_dma_data->sdw_stream[index] &&
+		    sdw_dma_data->sdw_stream[index]->runtime) {
+			switch (index) {
+			case ACP_SDW_AUDIO_TX:
+				water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_BT_TX:
+				water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_HS_TX:
+				water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW1_BT_TX:
+				water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_AUDIO_RX:
+				water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_BT_RX:
+				water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_HS_RX:
+				water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW1_BT_RX:
+				water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
+				break;
+			default:
+				dev_err(dev, "%s: Invalid channel type\n", __func__);
+				return -EINVAL;
+			}
+			runtime = sdw_dma_data->sdw_stream[index]->runtime;
+			sdw_ins = runtime->private_data;
+			period_bytes = frames_to_bytes(runtime, runtime->period_size);
+			buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+			acp63_config_dma(sdw_ins, index);
+			ret = acp63_configure_sdw_ringbuffer(sdw_dma_data->acp_base, index,
+							     buf_size);
+			if (ret)
+				return ret;
+			acp63_writel(period_bytes, sdw_dma_data->acp_base + water_mark_size_reg);
+		}
+	}
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, true);
+	return 0;
+}
+
 static int __maybe_unused acp63_sdw_pcm_runtime_suspend(struct device *dev)
 {
 	struct sdw_dma_dev_data *sdw_dma_data;
@@ -650,6 +708,7 @@ static int __maybe_unused acp63_sdw_pcm_runtime_resume(struct device *dev)
 static const struct dev_pm_ops acp63_pm_ops = {
 	SET_RUNTIME_PM_OPS(acp63_sdw_pcm_runtime_suspend,
 			   acp63_sdw_pcm_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(acp63_sdw_pcm_runtime_suspend, acp63_sdw_pcm_resume)
 };
 
 static struct platform_driver acp63_sdw_dma_driver = {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 18/19] ASoC: amd: ps: implement system level pm ops for soundwire dma driver
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, open list

Add support for system level pm ops for soundwire dma driver for
pink sardine platform.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/ps-sdw-dma.c | 59 +++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index 960c0bc5e848..ba2eea23d41e 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -623,6 +623,64 @@ static int acp63_sdw_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev)
+{
+	struct sdw_dma_dev_data *sdw_dma_data;
+	struct sdw_stream_instance *sdw_ins;
+	struct snd_pcm_runtime *runtime;
+	u32 period_bytes, buf_size, water_mark_size_reg;
+	int ret;
+	int index;
+
+	sdw_dma_data = dev_get_drvdata(dev);
+	for (index = 0; index < ACP63_SDW_MAX_STREAMS; index++) {
+		if (sdw_dma_data->sdw_stream[index] &&
+		    sdw_dma_data->sdw_stream[index]->runtime) {
+			switch (index) {
+			case ACP_SDW_AUDIO_TX:
+				water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_BT_TX:
+				water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_HS_TX:
+				water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW1_BT_TX:
+				water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_AUDIO_RX:
+				water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_BT_RX:
+				water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW_HS_RX:
+				water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
+				break;
+			case ACP_SDW1_BT_RX:
+				water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
+				break;
+			default:
+				dev_err(dev, "%s: Invalid channel type\n", __func__);
+				return -EINVAL;
+			}
+			runtime = sdw_dma_data->sdw_stream[index]->runtime;
+			sdw_ins = runtime->private_data;
+			period_bytes = frames_to_bytes(runtime, runtime->period_size);
+			buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+			acp63_config_dma(sdw_ins, index);
+			ret = acp63_configure_sdw_ringbuffer(sdw_dma_data->acp_base, index,
+							     buf_size);
+			if (ret)
+				return ret;
+			acp63_writel(period_bytes, sdw_dma_data->acp_base + water_mark_size_reg);
+		}
+	}
+	acp63_enable_disable_sdw_dma_interrupts(sdw_dma_data->acp_base, true);
+	return 0;
+}
+
 static int __maybe_unused acp63_sdw_pcm_runtime_suspend(struct device *dev)
 {
 	struct sdw_dma_dev_data *sdw_dma_data;
@@ -650,6 +708,7 @@ static int __maybe_unused acp63_sdw_pcm_runtime_resume(struct device *dev)
 static const struct dev_pm_ops acp63_pm_ops = {
 	SET_RUNTIME_PM_OPS(acp63_sdw_pcm_runtime_suspend,
 			   acp63_sdw_pcm_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(acp63_sdw_pcm_runtime_suspend, acp63_sdw_pcm_resume)
 };
 
 static struct platform_driver acp63_sdw_dma_driver = {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
@ 2023-01-11  9:02   ` Vijendar Mukunda
  2023-01-11  9:02   ` Vijendar Mukunda
                     ` (18 subsequent siblings)
  19 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, Vijendar Mukunda, arungopal.kondaveeti,
	Syed Saba Kareem

To avoid ACP entering into D3 state during slave enumeration and
initialization on two soundwire controller instances for multiple codecs,
increase the runtime suspend delay to 3 seconds.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 833d0b5aa73d..6c8849f2bcec 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -51,7 +51,7 @@
 #define MIN_BUFFER MAX_BUFFER
 
 /* time in ms for runtime suspend delay */
-#define ACP_SUSPEND_DELAY_MS	2000
+#define ACP_SUSPEND_DELAY_MS	3000
 
 #define ACP63_DMIC_ADDR		2
 #define ACP63_PDM_MODE_DEVS		3
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-11  9:02   ` Vijendar Mukunda
  0 siblings, 0 replies; 170+ messages in thread
From: Vijendar Mukunda @ 2023-01-11  9:02 UTC (permalink / raw)
  To: broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Vijendar Mukunda,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

To avoid ACP entering into D3 state during slave enumeration and
initialization on two soundwire controller instances for multiple codecs,
increase the runtime suspend delay to 3 seconds.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
---
 sound/soc/amd/ps/acp63.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 833d0b5aa73d..6c8849f2bcec 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -51,7 +51,7 @@
 #define MIN_BUFFER MAX_BUFFER
 
 /* time in ms for runtime suspend delay */
-#define ACP_SUSPEND_DELAY_MS	2000
+#define ACP_SUSPEND_DELAY_MS	3000
 
 #define ACP63_DMIC_ADDR		2
 #define ACP63_PDM_MODE_DEVS		3
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 13:04     ` Mark Brown
  -1 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-11 13:04 UTC (permalink / raw)
  To: Vijendar Mukunda
  Cc: vkoul, alsa-devel, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
	Mario.Limonciello, Mastan.Katragadda, arungopal.kondaveeti,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

On Wed, Jan 11, 2023 at 02:32:11PM +0530, Vijendar Mukunda wrote:

> +static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
> +{
> +	struct sdw_stream_instance *sdw_ins;
> +	struct snd_soc_pcm_runtime *prtd;
> +	u32 stream_id;
> +	u32 sdw_dma_reg;
> +	u32 sdw_dma_en_stat_reg;
> +	u32 sdw_dma_stat;
> +	u32 val;
> +	int timeout = 0;
> +
> +	sdw_ins = stream->runtime->private_data;
> +	prtd = stream->private_data;
> +	stream_id = sdw_ins->stream_id;
> +	switch (stream_id) {
> +	case ACP_SDW_AUDIO_TX:
> +		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
> +		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
> +		break;

Not super urgent but if you're respinning then it looks like the
register selection here is the same in _dma_stop() so they could be
shared.  Indeed it generally feels like it might be nicer to have a
table of structs listing the registers needed per stream so all these
switch statements can be more like

	sdw_dma_reg = stream_registers[sdw_ins->stream_id];

That'd make each function smaller and I'd expect it'd be a bit easier to
add new streams for new hardware that way.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-11 13:04     ` Mark Brown
  0 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-11 13:04 UTC (permalink / raw)
  To: Vijendar Mukunda
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, alsa-devel, Liam Girdwood,
	vkoul, Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

[-- Attachment #1: Type: text/plain, Size: 1097 bytes --]

On Wed, Jan 11, 2023 at 02:32:11PM +0530, Vijendar Mukunda wrote:

> +static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
> +{
> +	struct sdw_stream_instance *sdw_ins;
> +	struct snd_soc_pcm_runtime *prtd;
> +	u32 stream_id;
> +	u32 sdw_dma_reg;
> +	u32 sdw_dma_en_stat_reg;
> +	u32 sdw_dma_stat;
> +	u32 val;
> +	int timeout = 0;
> +
> +	sdw_ins = stream->runtime->private_data;
> +	prtd = stream->private_data;
> +	stream_id = sdw_ins->stream_id;
> +	switch (stream_id) {
> +	case ACP_SDW_AUDIO_TX:
> +		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
> +		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
> +		break;

Not super urgent but if you're respinning then it looks like the
register selection here is the same in _dma_stop() so they could be
shared.  Indeed it generally feels like it might be nicer to have a
table of structs listing the registers needed per stream so all these
switch statements can be more like

	sdw_dma_reg = stream_registers[sdw_ins->stream_id];

That'd make each function smaller and I'd expect it'd be a bit easier to
add new streams for new hardware that way.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 13:27     ` Amadeusz Sławiński
  -1 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 13:27 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood,
	Basavaraj.Hiregoudar, Takashi Iwai, Pierre-Louis Bossart,
	open list, Nathan Chancellor, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao, Syed Saba Kareem

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> Create platform devices for sdw controllers and PDM controller
> based on ACP pin config selection and ACPI fw handle for
> pink sardine platform.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>   include/linux/soundwire/sdw_amd.h |  18 +++
>   sound/soc/amd/ps/acp63.h          |  24 ++-
>   sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
>   3 files changed, 277 insertions(+), 13 deletions(-)
>   create mode 100644 include/linux/soundwire/sdw_amd.h
> 

...

> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index e86f23d97584..85154cf0b2a2 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -14,6 +14,7 @@
>   #include <linux/interrupt.h>
>   #include <sound/pcm_params.h>
>   #include <linux/pm_runtime.h>
> +#include <linux/soundwire/sdw_amd.h>
>   
>   #include "acp63.h"
>   
> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>   	return IRQ_NONE;
>   }
>   
> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
> -				    struct acp63_dev_data *acp_data)
> +static int sdw_amd_scan_controller(struct device *dev)
> +{
> +	struct acp63_dev_data *acp_data;
> +	struct fwnode_handle *link;
> +	char name[32];
> +	u8 count = 0;
> +	u32 acp_sdw_power_mode = 0;
> +	int index;
> +	int ret;
> +
> +	acp_data = dev_get_drvdata(dev);
> +	acp_data->acp_sdw_power_off = true;
> +	/* Found controller, find links supported */
> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
> +					    "mipi-sdw-master-count", &count, 1);
> +
> +	if (ret) {
> +		dev_err(dev,
> +			"Failed to read mipi-sdw-master-count: %d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	/* Check count is within bounds */
> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
> +		dev_err(dev, "Controller count %d exceeds max %d\n",
> +			count, AMD_SDW_MAX_CONTROLLERS);
> +		return -EINVAL;
> +	}
> +
> +	if (!count) {
> +		dev_warn(dev, "No SoundWire controllers detected\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
> +	acp_data->sdw_master_count  = count;

Double space before '='.

> +	for (index = 0; index < count; index++) {
> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
> +		if (!link) {
> +			dev_err(dev, "Master node %s not found\n", name);
> +			return -EIO;
> +		}
> +
> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
> +					 &acp_sdw_power_mode);
> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
> +			acp_data->acp_sdw_power_off = false;
> +	}
> +	return 0;
> +}
> +
> +static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
>   {
>   	struct acpi_device *dmic_dev;
> +	struct acpi_device *sdw_dev;
> +	struct device *dev;
>   	const union acpi_object *obj;
>   	bool is_dmic_dev = false;
> +	bool is_sdw_dev = false;
> +	int ret;
> +
> +	dev = &pci->dev;
>   
>   	dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);

If you set dev above, you might as well use it throughout the function 
context? Like above in ACPI_COMPANION?

>   	if (dmic_dev) {
> @@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>   			is_dmic_dev = true;
>   	}
>   
> +	sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
> +	if (sdw_dev) {
> +		is_sdw_dev = true;
> +		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
> +		ret = sdw_amd_scan_controller(dev);

Or just use &pci->dev here, so there is no need for separate variable?




^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-11 13:27     ` Amadeusz Sławiński
  0 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 13:27 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Pierre-Louis Bossart,
	Liam Girdwood, Nathan Chancellor, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao, Syed Saba Kareem

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> Create platform devices for sdw controllers and PDM controller
> based on ACP pin config selection and ACPI fw handle for
> pink sardine platform.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>   include/linux/soundwire/sdw_amd.h |  18 +++
>   sound/soc/amd/ps/acp63.h          |  24 ++-
>   sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
>   3 files changed, 277 insertions(+), 13 deletions(-)
>   create mode 100644 include/linux/soundwire/sdw_amd.h
> 

...

> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index e86f23d97584..85154cf0b2a2 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -14,6 +14,7 @@
>   #include <linux/interrupt.h>
>   #include <sound/pcm_params.h>
>   #include <linux/pm_runtime.h>
> +#include <linux/soundwire/sdw_amd.h>
>   
>   #include "acp63.h"
>   
> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>   	return IRQ_NONE;
>   }
>   
> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
> -				    struct acp63_dev_data *acp_data)
> +static int sdw_amd_scan_controller(struct device *dev)
> +{
> +	struct acp63_dev_data *acp_data;
> +	struct fwnode_handle *link;
> +	char name[32];
> +	u8 count = 0;
> +	u32 acp_sdw_power_mode = 0;
> +	int index;
> +	int ret;
> +
> +	acp_data = dev_get_drvdata(dev);
> +	acp_data->acp_sdw_power_off = true;
> +	/* Found controller, find links supported */
> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
> +					    "mipi-sdw-master-count", &count, 1);
> +
> +	if (ret) {
> +		dev_err(dev,
> +			"Failed to read mipi-sdw-master-count: %d\n", ret);
> +		return -EINVAL;
> +	}
> +
> +	/* Check count is within bounds */
> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
> +		dev_err(dev, "Controller count %d exceeds max %d\n",
> +			count, AMD_SDW_MAX_CONTROLLERS);
> +		return -EINVAL;
> +	}
> +
> +	if (!count) {
> +		dev_warn(dev, "No SoundWire controllers detected\n");
> +		return -EINVAL;
> +	}
> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
> +	acp_data->sdw_master_count  = count;

Double space before '='.

> +	for (index = 0; index < count; index++) {
> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
> +		if (!link) {
> +			dev_err(dev, "Master node %s not found\n", name);
> +			return -EIO;
> +		}
> +
> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
> +					 &acp_sdw_power_mode);
> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
> +			acp_data->acp_sdw_power_off = false;
> +	}
> +	return 0;
> +}
> +
> +static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
>   {
>   	struct acpi_device *dmic_dev;
> +	struct acpi_device *sdw_dev;
> +	struct device *dev;
>   	const union acpi_object *obj;
>   	bool is_dmic_dev = false;
> +	bool is_sdw_dev = false;
> +	int ret;
> +
> +	dev = &pci->dev;
>   
>   	dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);

If you set dev above, you might as well use it throughout the function 
context? Like above in ACPI_COMPANION?

>   	if (dmic_dev) {
> @@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>   			is_dmic_dev = true;
>   	}
>   
> +	sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
> +	if (sdw_dev) {
> +		is_sdw_dev = true;
> +		acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
> +		ret = sdw_amd_scan_controller(dev);

Or just use &pci->dev here, so there is no need for separate variable?




^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 13:32     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 13:32 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list




> +#define AMD_SDW_CLK_STOP_MODE		1

there are multiple modes for clock stop in SoundWire, and multiple ways
for the link manager to deal with clock stop, you want a comment to
describe what this define refers to.

> +#define AMD_SDW_POWER_OFF_MODE		2
> +
> +struct acp_sdw_pdata {
> +	u16 instance;
> +	struct mutex *sdw_lock;

need a comment on what this lock protects.

> +};
> +#endif
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index b7535c7d093f..ed979e6d0c1d 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -10,7 +10,7 @@
>  #define ACP_DEVICE_ID 0x15E2
>  #define ACP63_REG_START		0x1240000
>  #define ACP63_REG_END		0x1250200
> -#define ACP63_DEVS		3
> +#define ACP63_DEVS		5
>  
>  #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
>  #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
> @@ -55,8 +55,14 @@
>  
>  #define ACP63_DMIC_ADDR		2
>  #define ACP63_PDM_MODE_DEVS		3
> -#define ACP63_PDM_DEV_MASK		1
>  #define ACP_DMIC_DEV	2
> +#define ACP63_SDW0_MODE_DEVS		2
> +#define ACP63_SDW0_SDW1_MODE_DEVS	3
> +#define ACP63_SDW0_PDM_MODE_DEVS	4
> +#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
> +#define ACP63_DMIC_ADDR			2
> +#define ACP63_SDW_ADDR			5
> +#define AMD_SDW_MAX_CONTROLLERS		2
>  
>  enum acp_config {
>  	ACP_CONFIG_0 = 0,
> @@ -77,6 +83,12 @@ enum acp_config {
>  	ACP_CONFIG_15,
>  };
>  
> +enum acp_pdev_mask {
> +	ACP63_PDM_DEV_MASK = 1,
> +	ACP63_SDW_DEV_MASK,
> +	ACP63_SDW_PDM_DEV_MASK,
> +};
> +
>  struct pdm_stream_instance {
>  	u16 num_pages;
>  	u16 channels;
> @@ -107,7 +119,15 @@ struct acp63_dev_data {
>  	struct resource *res;
>  	struct platform_device *pdev[ACP63_DEVS];
>  	struct mutex acp_lock; /* protect shared registers */
> +	struct fwnode_handle *sdw_fw_node;
>  	u16 pdev_mask;
>  	u16 pdev_count;
>  	u16 pdm_dev_index;
> +	u8 sdw_master_count;

for new contributions, it's recommended to use manager and peripheral.

> +	u16 sdw0_dev_index;
> +	u16 sdw1_dev_index;

probably need a comment on what the 0 and 1 refer to, it's not clear if
there's any sort of dependency/link with the 'sdw_master_count' above.

If this is related to the two controllers mentioned in the cover letter,
then an explanation of the sdw_master_count would be needed as well
(single variable for two controllers?)

> +	u16 sdw_dma_dev_index;
> +	bool is_dmic_dev;
> +	bool is_sdw_dev;
> +	bool acp_sdw_power_off;
>  };
> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index e86f23d97584..85154cf0b2a2 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -14,6 +14,7 @@
>  #include <linux/interrupt.h>
>  #include <sound/pcm_params.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/soundwire/sdw_amd.h>
>  
>  #include "acp63.h"
>  
> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>  	return IRQ_NONE;
>  }
>  
> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
> -				    struct acp63_dev_data *acp_data)
> +static int sdw_amd_scan_controller(struct device *dev)
> +{
> +	struct acp63_dev_data *acp_data;
> +	struct fwnode_handle *link;
> +	char name[32];
> +	u8 count = 0;
> +	u32 acp_sdw_power_mode = 0;
> +	int index;
> +	int ret;
> +
> +	acp_data = dev_get_drvdata(dev);
> +	acp_data->acp_sdw_power_off = true;
> +	/* Found controller, find links supported */
> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
> +					    "mipi-sdw-master-count", &count, 1);
> +
> +	if (ret) {
> +		dev_err(dev,
> +			"Failed to read mipi-sdw-master-count: %d\n", ret);

one line?

> +		return -EINVAL;
> +	}
> +
> +	/* Check count is within bounds */
> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
> +		dev_err(dev, "Controller count %d exceeds max %d\n",
> +			count, AMD_SDW_MAX_CONTROLLERS);

No. controllers and masters are different concepts, see the DisCo
specification for SoundWire. A Controller can have multiple Masters.

> +		return -EINVAL;
> +	}
> +
> +	if (!count) {
> +		dev_warn(dev, "No SoundWire controllers detected\n");
> +		return -EINVAL;
> +	}

is this really a warning, looks like a dev_dbg or info to me.

> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);

the term device is incorrect here, the DisCo spec does not expose ACPI
devices for each master.

"ACPI reports %d Managers"

> +	acp_data->sdw_master_count  = count;
> +	for (index = 0; index < count; index++) {
> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
> +		if (!link) {
> +			dev_err(dev, "Master node %s not found\n", name);
> +			return -EIO;
> +		}
> +
> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
> +					 &acp_sdw_power_mode);
> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
> +			acp_data->acp_sdw_power_off = false;

does power-off mean 'clock-stop'?

> +	}
> +	return 0;
> +}
> +

> +		if (is_dmic_dev && is_sdw_dev) {
> +			switch (acp_data->sdw_master_count) {
> +			case 1:
> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
> +				break;
> +			case 2:
> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
> +				break;

so the cover letter is indeed wrong and confuses two controllers for two
managers.

> +			default:
> +				return -EINVAL;
> +			}
> +		} else if (is_dmic_dev) {
>  			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
>  			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
> +		} else if (is_sdw_dev) {
> +			switch (acp_data->sdw_master_count) {
> +			case 1:
> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
> +				break;
> +			case 2:
> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
>  		}
>  		break;
> +	default:
> +		break;
>  	}
> +	return 0;
>  }


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-11 13:32     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 13:32 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem




> +#define AMD_SDW_CLK_STOP_MODE		1

there are multiple modes for clock stop in SoundWire, and multiple ways
for the link manager to deal with clock stop, you want a comment to
describe what this define refers to.

> +#define AMD_SDW_POWER_OFF_MODE		2
> +
> +struct acp_sdw_pdata {
> +	u16 instance;
> +	struct mutex *sdw_lock;

need a comment on what this lock protects.

> +};
> +#endif
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index b7535c7d093f..ed979e6d0c1d 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -10,7 +10,7 @@
>  #define ACP_DEVICE_ID 0x15E2
>  #define ACP63_REG_START		0x1240000
>  #define ACP63_REG_END		0x1250200
> -#define ACP63_DEVS		3
> +#define ACP63_DEVS		5
>  
>  #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
>  #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
> @@ -55,8 +55,14 @@
>  
>  #define ACP63_DMIC_ADDR		2
>  #define ACP63_PDM_MODE_DEVS		3
> -#define ACP63_PDM_DEV_MASK		1
>  #define ACP_DMIC_DEV	2
> +#define ACP63_SDW0_MODE_DEVS		2
> +#define ACP63_SDW0_SDW1_MODE_DEVS	3
> +#define ACP63_SDW0_PDM_MODE_DEVS	4
> +#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
> +#define ACP63_DMIC_ADDR			2
> +#define ACP63_SDW_ADDR			5
> +#define AMD_SDW_MAX_CONTROLLERS		2
>  
>  enum acp_config {
>  	ACP_CONFIG_0 = 0,
> @@ -77,6 +83,12 @@ enum acp_config {
>  	ACP_CONFIG_15,
>  };
>  
> +enum acp_pdev_mask {
> +	ACP63_PDM_DEV_MASK = 1,
> +	ACP63_SDW_DEV_MASK,
> +	ACP63_SDW_PDM_DEV_MASK,
> +};
> +
>  struct pdm_stream_instance {
>  	u16 num_pages;
>  	u16 channels;
> @@ -107,7 +119,15 @@ struct acp63_dev_data {
>  	struct resource *res;
>  	struct platform_device *pdev[ACP63_DEVS];
>  	struct mutex acp_lock; /* protect shared registers */
> +	struct fwnode_handle *sdw_fw_node;
>  	u16 pdev_mask;
>  	u16 pdev_count;
>  	u16 pdm_dev_index;
> +	u8 sdw_master_count;

for new contributions, it's recommended to use manager and peripheral.

> +	u16 sdw0_dev_index;
> +	u16 sdw1_dev_index;

probably need a comment on what the 0 and 1 refer to, it's not clear if
there's any sort of dependency/link with the 'sdw_master_count' above.

If this is related to the two controllers mentioned in the cover letter,
then an explanation of the sdw_master_count would be needed as well
(single variable for two controllers?)

> +	u16 sdw_dma_dev_index;
> +	bool is_dmic_dev;
> +	bool is_sdw_dev;
> +	bool acp_sdw_power_off;
>  };
> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index e86f23d97584..85154cf0b2a2 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -14,6 +14,7 @@
>  #include <linux/interrupt.h>
>  #include <sound/pcm_params.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/soundwire/sdw_amd.h>
>  
>  #include "acp63.h"
>  
> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>  	return IRQ_NONE;
>  }
>  
> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
> -				    struct acp63_dev_data *acp_data)
> +static int sdw_amd_scan_controller(struct device *dev)
> +{
> +	struct acp63_dev_data *acp_data;
> +	struct fwnode_handle *link;
> +	char name[32];
> +	u8 count = 0;
> +	u32 acp_sdw_power_mode = 0;
> +	int index;
> +	int ret;
> +
> +	acp_data = dev_get_drvdata(dev);
> +	acp_data->acp_sdw_power_off = true;
> +	/* Found controller, find links supported */
> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
> +					    "mipi-sdw-master-count", &count, 1);
> +
> +	if (ret) {
> +		dev_err(dev,
> +			"Failed to read mipi-sdw-master-count: %d\n", ret);

one line?

> +		return -EINVAL;
> +	}
> +
> +	/* Check count is within bounds */
> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
> +		dev_err(dev, "Controller count %d exceeds max %d\n",
> +			count, AMD_SDW_MAX_CONTROLLERS);

No. controllers and masters are different concepts, see the DisCo
specification for SoundWire. A Controller can have multiple Masters.

> +		return -EINVAL;
> +	}
> +
> +	if (!count) {
> +		dev_warn(dev, "No SoundWire controllers detected\n");
> +		return -EINVAL;
> +	}

is this really a warning, looks like a dev_dbg or info to me.

> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);

the term device is incorrect here, the DisCo spec does not expose ACPI
devices for each master.

"ACPI reports %d Managers"

> +	acp_data->sdw_master_count  = count;
> +	for (index = 0; index < count; index++) {
> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
> +		if (!link) {
> +			dev_err(dev, "Master node %s not found\n", name);
> +			return -EIO;
> +		}
> +
> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
> +					 &acp_sdw_power_mode);
> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
> +			acp_data->acp_sdw_power_off = false;

does power-off mean 'clock-stop'?

> +	}
> +	return 0;
> +}
> +

> +		if (is_dmic_dev && is_sdw_dev) {
> +			switch (acp_data->sdw_master_count) {
> +			case 1:
> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
> +				break;
> +			case 2:
> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
> +				break;

so the cover letter is indeed wrong and confuses two controllers for two
managers.

> +			default:
> +				return -EINVAL;
> +			}
> +		} else if (is_dmic_dev) {
>  			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
>  			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
> +		} else if (is_sdw_dev) {
> +			switch (acp_data->sdw_master_count) {
> +			case 1:
> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
> +				break;
> +			case 2:
> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
> +				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
>  		}
>  		break;
> +	default:
> +		break;
>  	}
> +	return 0;
>  }


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 00/19] Add soundwire support for Pink Sardine platform
  2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
                   ` (18 preceding siblings ...)
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 13:36 ` Pierre-Louis Bossart
  2023-01-12  9:08   ` Mukunda,Vijendar
  19 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 13:36 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: arungopal.kondaveeti, Mastan.Katragadda, Sunil-kumar.Dommati,
	Basavaraj.Hiregoudar, Mario.Limonciello



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Pink Sardine(ps) platform is based on ACP6.3 Architecture.
> ACP6.3 IP has two soundwire controller instance support.

After reviewing patch1, it looks like there's a confusion between
controller and manager? the use of the 'sdw-master-count' property is
not for controller count, it's defined within the scope of an ACPI
controller device (usually a companion device to the PCI device).

> This patchset add support for Soundwire controller on Pink Sardine
> platform.

That's great, thanks for sharing.

> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Vijendar Mukunda (19):
>   ASoC: amd: ps: create platform devices based on acp config
>   soundwire: amd: Add support for AMD Master driver
>   soundwire: amd: register sdw controller dai ops
>   soundwire: amd: enable build for AMD soundwire master driver
>   soundwire: amd: add soundwire interrupt handling
>   ASoC: amd: ps: add support for soundwire interrupts in acp pci driver
>   ASoC: amd: ps: add soundwire dma driver for pink sardine platform
>   ASoC: amd: ps: add soundwire dma driver dma ops
>   ASoC: amd: ps: add support for Soundwire DMA interrupts
>   ASoC: amd: ps: enable Soundwire DMA driver build
>   ASoC: amd: update comments in Kconfig file
>   ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops.
>   ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver
>   soundwire: amd: add runtime pm ops for AMD master driver
>   soundwire: amd: add startup and shutdown dai ops
>   soundwire: amd: handle wake enable interrupt
>   soundwire: amd: add pm_prepare callback and pm ops support
>   ASoC: amd: ps: implement system level pm ops for soundwire dma driver
>   ASoC: amd: ps: increase runtime suspend delay
> 
>  drivers/soundwire/Kconfig         |    9 +
>  drivers/soundwire/Makefile        |    4 +
>  drivers/soundwire/amd_master.c    | 1734 +++++++++++++++++++++++++++++
>  drivers/soundwire/amd_master.h    |  284 +++++
>  include/linux/soundwire/sdw_amd.h |   65 ++
>  sound/soc/amd/Kconfig             |    3 +-
>  sound/soc/amd/ps/Makefile         |    2 +
>  sound/soc/amd/ps/acp63.h          |   98 +-
>  sound/soc/amd/ps/pci-ps.c         |  383 ++++++-
>  sound/soc/amd/ps/ps-sdw-dma.c     |  728 ++++++++++++
>  10 files changed, 3287 insertions(+), 23 deletions(-)
>  create mode 100644 drivers/soundwire/amd_master.c
>  create mode 100644 drivers/soundwire/amd_master.h
>  create mode 100644 include/linux/soundwire/sdw_amd.h
>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
> 

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 13:59     ` Amadeusz Sławiński
  -1 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 13:59 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> AMD ACP IP block has two soundwire controller devices.
> Add support for
> - Master driver probe & remove sequence
> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>    enable sdw pads
> - Master driver sdw_master_ops & port_ops callbacks
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---

...

> +
> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
> +{
> +	struct sdw_transport_data t_data = {0};
> +	struct sdw_master_runtime *m_rt;
> +	struct sdw_port_runtime *p_rt;
> +	struct sdw_bus_params *b_params = &bus->params;
> +	int port_bo, hstart, hstop, sample_int;
> +	unsigned int rate, bps;
> +
> +	port_bo  = 0;

Double space before '='.

> +	hstart = 1;
> +	hstop = bus->params.col - 1;
> +	t_data.hstop = hstop;
> +	t_data.hstart = hstart;
> +
> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (bus->params.curr_dr_freq / rate);
> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
> +			port_bo = (p_rt->num * 64) + 1;
> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
> +				p_rt->num, hstart, hstop, port_bo);
> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
> +					      port_bo, port_bo >> 8, hstart, hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->m_data_mode);
> +			t_data.hstart = hstart;
> +			t_data.hstop = hstop;
> +			t_data.block_offset = port_bo;
> +			t_data.sub_block_offset = 0;
> +		}
> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
> +	}
> +	return 0;
> +}
> +

...

> +
> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
> +				struct sdw_enable_ch *enable_ch,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 dpn_ch_enable;
> +	u32 ch_enable_reg, channel_type;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = enable_ch->port_num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_TX:
> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +
> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);

Double space after '='.

> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
> +	if (enable_ch->enable)
> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
> +	else
> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
> +	return 0;
> +}
> +

...

> +
> +static void amd_sdwc_probe_work(struct work_struct *work)
> +{
> +	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);

Double space before '='.

> +	struct sdw_master_prop *prop;
> +	int ret;
> +
> +	prop = &ctrl->bus.prop;
> +	if (!prop->hw_disabled) {
> +		ret = amd_enable_sdw_pads(ctrl);
> +		if (ret)
> +			return;
> +		ret = amd_init_sdw_controller(ctrl);
> +		if (ret)
> +			return;
> +		amd_enable_sdw_interrupts(ctrl);
> +		ret = amd_enable_sdw_controller(ctrl);
> +		if (ret)
> +			return;
> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
> +		if (!ret)
> +			ctrl->startup_done = true;
> +	}
> +}
> +
> +static int amd_sdwc_probe(struct platform_device *pdev)
> +{
> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	struct device *dev = &pdev->dev;

Same as in previous patch, you assign dev here, but keep using 
&pdev->dev below?

> +	struct sdw_master_prop *prop;
> +	struct sdw_bus_params *params;
> +	struct amd_sdwc_ctrl *ctrl;
> +	int ret;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
> +	if (!ctrl)
> +		return -ENOMEM;
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENOMEM;
> +	}
> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (IS_ERR(ctrl->mmio)) {
> +		dev_err(&pdev->dev, "mmio not found\n");
> +		return PTR_ERR(ctrl->mmio);
> +	}
> +	ctrl->instance = pdata->instance;
> +	ctrl->sdw_lock  = pdata->sdw_lock;

Double space before '='.

> +	ctrl->rows_index = sdw_find_row_index(50);
> +	ctrl->cols_index = sdw_find_col_index(10);
> +
> +	ctrl->dev = dev;
> +	dev_set_drvdata(&pdev->dev, ctrl);
> +
> +	ctrl->bus.ops = &amd_sdwc_ops;
> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
> +	ctrl->bus.clk_stop_timeout = 1;
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;

Double space after '='.

> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
> +		break;
> +	case ACP_SDW1:
> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	params = &ctrl->bus.params;
> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->col = 10;
> +	params->row = 50;
> +
> +	prop = &ctrl->bus.prop;
> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
> +	ctrl->bus.link_id = ctrl->instance;
> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
> +	if (ret) {
> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
> +			ret);
> +		return ret;
> +	}
> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
> +	schedule_work(&ctrl->probe_work);
> +	return 0;
> +}
> +
> +static int amd_sdwc_remove(struct platform_device *pdev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +

You may need to cancel work if someone tries to unload driver before 
probe work completes. Something like

cancel_work_sync(&ctrl->probe_work);

should probably work here.

> +	amd_disable_sdw_interrupts(ctrl);
> +	sdw_bus_master_delete(&ctrl->bus);
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-11 13:59     ` Amadeusz Sławiński
  0 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 13:59 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Pierre-Louis Bossart, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> AMD ACP IP block has two soundwire controller devices.
> Add support for
> - Master driver probe & remove sequence
> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>    enable sdw pads
> - Master driver sdw_master_ops & port_ops callbacks
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---

...

> +
> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
> +{
> +	struct sdw_transport_data t_data = {0};
> +	struct sdw_master_runtime *m_rt;
> +	struct sdw_port_runtime *p_rt;
> +	struct sdw_bus_params *b_params = &bus->params;
> +	int port_bo, hstart, hstop, sample_int;
> +	unsigned int rate, bps;
> +
> +	port_bo  = 0;

Double space before '='.

> +	hstart = 1;
> +	hstop = bus->params.col - 1;
> +	t_data.hstop = hstop;
> +	t_data.hstart = hstart;
> +
> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (bus->params.curr_dr_freq / rate);
> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
> +			port_bo = (p_rt->num * 64) + 1;
> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
> +				p_rt->num, hstart, hstop, port_bo);
> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
> +					      port_bo, port_bo >> 8, hstart, hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->m_data_mode);
> +			t_data.hstart = hstart;
> +			t_data.hstop = hstop;
> +			t_data.block_offset = port_bo;
> +			t_data.sub_block_offset = 0;
> +		}
> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
> +	}
> +	return 0;
> +}
> +

...

> +
> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
> +				struct sdw_enable_ch *enable_ch,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 dpn_ch_enable;
> +	u32 ch_enable_reg, channel_type;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = enable_ch->port_num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_TX:
> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +
> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);

Double space after '='.

> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
> +	if (enable_ch->enable)
> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
> +	else
> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
> +	return 0;
> +}
> +

...

> +
> +static void amd_sdwc_probe_work(struct work_struct *work)
> +{
> +	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);

Double space before '='.

> +	struct sdw_master_prop *prop;
> +	int ret;
> +
> +	prop = &ctrl->bus.prop;
> +	if (!prop->hw_disabled) {
> +		ret = amd_enable_sdw_pads(ctrl);
> +		if (ret)
> +			return;
> +		ret = amd_init_sdw_controller(ctrl);
> +		if (ret)
> +			return;
> +		amd_enable_sdw_interrupts(ctrl);
> +		ret = amd_enable_sdw_controller(ctrl);
> +		if (ret)
> +			return;
> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
> +		if (!ret)
> +			ctrl->startup_done = true;
> +	}
> +}
> +
> +static int amd_sdwc_probe(struct platform_device *pdev)
> +{
> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	struct device *dev = &pdev->dev;

Same as in previous patch, you assign dev here, but keep using 
&pdev->dev below?

> +	struct sdw_master_prop *prop;
> +	struct sdw_bus_params *params;
> +	struct amd_sdwc_ctrl *ctrl;
> +	int ret;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
> +	if (!ctrl)
> +		return -ENOMEM;
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENOMEM;
> +	}
> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (IS_ERR(ctrl->mmio)) {
> +		dev_err(&pdev->dev, "mmio not found\n");
> +		return PTR_ERR(ctrl->mmio);
> +	}
> +	ctrl->instance = pdata->instance;
> +	ctrl->sdw_lock  = pdata->sdw_lock;

Double space before '='.

> +	ctrl->rows_index = sdw_find_row_index(50);
> +	ctrl->cols_index = sdw_find_col_index(10);
> +
> +	ctrl->dev = dev;
> +	dev_set_drvdata(&pdev->dev, ctrl);
> +
> +	ctrl->bus.ops = &amd_sdwc_ops;
> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
> +	ctrl->bus.clk_stop_timeout = 1;
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;

Double space after '='.

> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
> +		break;
> +	case ACP_SDW1:
> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	params = &ctrl->bus.params;
> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->col = 10;
> +	params->row = 50;
> +
> +	prop = &ctrl->bus.prop;
> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
> +	ctrl->bus.link_id = ctrl->instance;
> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
> +	if (ret) {
> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
> +			ret);
> +		return ret;
> +	}
> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
> +	schedule_work(&ctrl->probe_work);
> +	return 0;
> +}
> +
> +static int amd_sdwc_remove(struct platform_device *pdev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +

You may need to cancel work if someone tries to unload driver before 
probe work completes. Something like

cancel_work_sync(&ctrl->probe_work);

should probably work here.

> +	amd_disable_sdw_interrupts(ctrl);
> +	sdw_bus_master_delete(&ctrl->bus);
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-11 13:04     ` Mark Brown
@ 2023-01-11 14:08       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:08 UTC (permalink / raw)
  To: Mark Brown
  Cc: vkoul, alsa-devel, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
	Mario.Limonciello, Mastan.Katragadda, arungopal.kondaveeti,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	open list

On 11/01/23 18:34, Mark Brown wrote:
> On Wed, Jan 11, 2023 at 02:32:11PM +0530, Vijendar Mukunda wrote:
>
>> +static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
>> +{
>> +	struct sdw_stream_instance *sdw_ins;
>> +	struct snd_soc_pcm_runtime *prtd;
>> +	u32 stream_id;
>> +	u32 sdw_dma_reg;
>> +	u32 sdw_dma_en_stat_reg;
>> +	u32 sdw_dma_stat;
>> +	u32 val;
>> +	int timeout = 0;
>> +
>> +	sdw_ins = stream->runtime->private_data;
>> +	prtd = stream->private_data;
>> +	stream_id = sdw_ins->stream_id;
>> +	switch (stream_id) {
>> +	case ACP_SDW_AUDIO_TX:
>> +		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
>> +		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
>> +		break;
> Not super urgent but if you're respinning then it looks like the
> register selection here is the same in _dma_stop() so they could be
> shared.  Indeed it generally feels like it might be nicer to have a
> table of structs listing the registers needed per stream so all these
> switch statements can be more like
>
> 	sdw_dma_reg = stream_registers[sdw_ins->stream_id];
>
> That'd make each function smaller and I'd expect it'd be a bit easier to
> add new streams for new hardware that way.
will fix it in next version.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-11 14:08       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:08 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, alsa-devel, Liam Girdwood,
	vkoul, Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 18:34, Mark Brown wrote:
> On Wed, Jan 11, 2023 at 02:32:11PM +0530, Vijendar Mukunda wrote:
>
>> +static int acp63_sdw_dma_start(struct snd_pcm_substream *stream)
>> +{
>> +	struct sdw_stream_instance *sdw_ins;
>> +	struct snd_soc_pcm_runtime *prtd;
>> +	u32 stream_id;
>> +	u32 sdw_dma_reg;
>> +	u32 sdw_dma_en_stat_reg;
>> +	u32 sdw_dma_stat;
>> +	u32 val;
>> +	int timeout = 0;
>> +
>> +	sdw_ins = stream->runtime->private_data;
>> +	prtd = stream->private_data;
>> +	stream_id = sdw_ins->stream_id;
>> +	switch (stream_id) {
>> +	case ACP_SDW_AUDIO_TX:
>> +		sdw_dma_reg = ACP_SW_AUDIO_TX_EN;
>> +		sdw_dma_en_stat_reg = ACP_SW_AUDIO_TX_EN_STATUS;
>> +		break;
> Not super urgent but if you're respinning then it looks like the
> register selection here is the same in _dma_stop() so they could be
> shared.  Indeed it generally feels like it might be nicer to have a
> table of structs listing the registers needed per stream so all these
> switch statements can be more like
>
> 	sdw_dma_reg = stream_registers[sdw_ins->stream_id];
>
> That'd make each function smaller and I'd expect it'd be a bit easier to
> add new streams for new hardware that way.
will fix it in next version.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-11 13:27     ` Amadeusz Sławiński
@ 2023-01-11 14:13       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:13 UTC (permalink / raw)
  To: Amadeusz Sławiński, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Pierre-Louis Bossart,
	Liam Girdwood, Nathan Chancellor, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao, Syed Saba Kareem

On 11/01/23 18:57, Amadeusz Sławiński wrote:
> On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
>> Create platform devices for sdw controllers and PDM controller
>> based on ACP pin config selection and ACPI fw handle for
>> pink sardine platform.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>   include/linux/soundwire/sdw_amd.h |  18 +++
>>   sound/soc/amd/ps/acp63.h          |  24 ++-
>>   sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
>>   3 files changed, 277 insertions(+), 13 deletions(-)
>>   create mode 100644 include/linux/soundwire/sdw_amd.h
>>
>
> ...
>
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index e86f23d97584..85154cf0b2a2 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -14,6 +14,7 @@
>>   #include <linux/interrupt.h>
>>   #include <sound/pcm_params.h>
>>   #include <linux/pm_runtime.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>     #include "acp63.h"
>>   @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>>       return IRQ_NONE;
>>   }
>>   -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>> -                    struct acp63_dev_data *acp_data)
>> +static int sdw_amd_scan_controller(struct device *dev)
>> +{
>> +    struct acp63_dev_data *acp_data;
>> +    struct fwnode_handle *link;
>> +    char name[32];
>> +    u8 count = 0;
>> +    u32 acp_sdw_power_mode = 0;
>> +    int index;
>> +    int ret;
>> +
>> +    acp_data = dev_get_drvdata(dev);
>> +    acp_data->acp_sdw_power_off = true;
>> +    /* Found controller, find links supported */
>> +    ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
>> +                        "mipi-sdw-master-count", &count, 1);
>> +
>> +    if (ret) {
>> +        dev_err(dev,
>> +            "Failed to read mipi-sdw-master-count: %d\n", ret);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Check count is within bounds */
>> +    if (count > AMD_SDW_MAX_CONTROLLERS) {
>> +        dev_err(dev, "Controller count %d exceeds max %d\n",
>> +            count, AMD_SDW_MAX_CONTROLLERS);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (!count) {
>> +        dev_warn(dev, "No SoundWire controllers detected\n");
>> +        return -EINVAL;
>> +    }
>> +    dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
>> +    acp_data->sdw_master_count  = count;
>
> Double space before '='.
> will fix it.
>> +    for (index = 0; index < count; index++) {
>> +        snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
>> +        link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
>> +        if (!link) {
>> +            dev_err(dev, "Master node %s not found\n", name);
>> +            return -EIO;
>> +        }
>> +
>> +        fwnode_property_read_u32(link, "amd-sdw-power-mode",
>> +                     &acp_sdw_power_mode);
>> +        if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
>> +            acp_data->acp_sdw_power_off = false;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
>>   {
>>       struct acpi_device *dmic_dev;
>> +    struct acpi_device *sdw_dev;
>> +    struct device *dev;
>>       const union acpi_object *obj;
>>       bool is_dmic_dev = false;
>> +    bool is_sdw_dev = false;
>> +    int ret;
>> +
>> +    dev = &pci->dev;
>>         dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
>
> If you set dev above, you might as well use it throughout the function context? Like above in ACPI_COMPANION? 

> will use pci->dev throughtout the function context.
>>       if (dmic_dev) {
>> @@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>>               is_dmic_dev = true;
>>       }
>>   +    sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
>> +    if (sdw_dev) {
>> +        is_sdw_dev = true;
>> +        acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
>> +        ret = sdw_amd_scan_controller(dev);
>
> Or just use &pci->dev here, so there is no need for separate variable?

> will remove the "dev" local variable.
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-11 14:13       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:13 UTC (permalink / raw)
  To: Amadeusz Sławiński, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood,
	Basavaraj.Hiregoudar, Takashi Iwai, Pierre-Louis Bossart,
	open list, Nathan Chancellor, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao, Syed Saba Kareem

On 11/01/23 18:57, Amadeusz Sławiński wrote:
> On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
>> Create platform devices for sdw controllers and PDM controller
>> based on ACP pin config selection and ACPI fw handle for
>> pink sardine platform.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>   include/linux/soundwire/sdw_amd.h |  18 +++
>>   sound/soc/amd/ps/acp63.h          |  24 ++-
>>   sound/soc/amd/ps/pci-ps.c         | 248 ++++++++++++++++++++++++++++--
>>   3 files changed, 277 insertions(+), 13 deletions(-)
>>   create mode 100644 include/linux/soundwire/sdw_amd.h
>>
>
> ...
>
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index e86f23d97584..85154cf0b2a2 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -14,6 +14,7 @@
>>   #include <linux/interrupt.h>
>>   #include <sound/pcm_params.h>
>>   #include <linux/pm_runtime.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>     #include "acp63.h"
>>   @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>>       return IRQ_NONE;
>>   }
>>   -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>> -                    struct acp63_dev_data *acp_data)
>> +static int sdw_amd_scan_controller(struct device *dev)
>> +{
>> +    struct acp63_dev_data *acp_data;
>> +    struct fwnode_handle *link;
>> +    char name[32];
>> +    u8 count = 0;
>> +    u32 acp_sdw_power_mode = 0;
>> +    int index;
>> +    int ret;
>> +
>> +    acp_data = dev_get_drvdata(dev);
>> +    acp_data->acp_sdw_power_off = true;
>> +    /* Found controller, find links supported */
>> +    ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
>> +                        "mipi-sdw-master-count", &count, 1);
>> +
>> +    if (ret) {
>> +        dev_err(dev,
>> +            "Failed to read mipi-sdw-master-count: %d\n", ret);
>> +        return -EINVAL;
>> +    }
>> +
>> +    /* Check count is within bounds */
>> +    if (count > AMD_SDW_MAX_CONTROLLERS) {
>> +        dev_err(dev, "Controller count %d exceeds max %d\n",
>> +            count, AMD_SDW_MAX_CONTROLLERS);
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (!count) {
>> +        dev_warn(dev, "No SoundWire controllers detected\n");
>> +        return -EINVAL;
>> +    }
>> +    dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
>> +    acp_data->sdw_master_count  = count;
>
> Double space before '='.
> will fix it.
>> +    for (index = 0; index < count; index++) {
>> +        snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
>> +        link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
>> +        if (!link) {
>> +            dev_err(dev, "Master node %s not found\n", name);
>> +            return -EIO;
>> +        }
>> +
>> +        fwnode_property_read_u32(link, "amd-sdw-power-mode",
>> +                     &acp_sdw_power_mode);
>> +        if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
>> +            acp_data->acp_sdw_power_off = false;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63_dev_data *acp_data)
>>   {
>>       struct acpi_device *dmic_dev;
>> +    struct acpi_device *sdw_dev;
>> +    struct device *dev;
>>       const union acpi_object *obj;
>>       bool is_dmic_dev = false;
>> +    bool is_sdw_dev = false;
>> +    int ret;
>> +
>> +    dev = &pci->dev;
>>         dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
>
> If you set dev above, you might as well use it throughout the function context? Like above in ACPI_COMPANION? 

> will use pci->dev throughtout the function context.
>>       if (dmic_dev) {
>> @@ -149,22 +206,84 @@ static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>>               is_dmic_dev = true;
>>       }
>>   +    sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0);
>> +    if (sdw_dev) {
>> +        is_sdw_dev = true;
>> +        acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
>> +        ret = sdw_amd_scan_controller(dev);
>
> Or just use &pci->dev here, so there is no need for separate variable?

> will remove the "dev" local variable.
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-11 13:59     ` Amadeusz Sławiński
@ 2023-01-11 14:16       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:16 UTC (permalink / raw)
  To: Amadeusz Sławiński, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 11/01/23 19:29, Amadeusz Sławiński wrote:
> On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
>> AMD ACP IP block has two soundwire controller devices.
>> Add support for
>> - Master driver probe & remove sequence
>> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>>    enable sdw pads
>> - Master driver sdw_master_ops & port_ops callbacks
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>
> ...
>
>> +
>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>> +{
>> +    struct sdw_transport_data t_data = {0};
>> +    struct sdw_master_runtime *m_rt;
>> +    struct sdw_port_runtime *p_rt;
>> +    struct sdw_bus_params *b_params = &bus->params;
>> +    int port_bo, hstart, hstop, sample_int;
>> +    unsigned int rate, bps;
>> +
>> +    port_bo  = 0;
>
> Double space before '='.
will fix it.
>
>> +    hstart = 1;
>> +    hstop = bus->params.col - 1;
>> +    t_data.hstop = hstop;
>> +    t_data.hstart = hstart;
>> +
>> +    list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>> +        rate = m_rt->stream->params.rate;
>> +        bps = m_rt->stream->params.bps;
>> +        sample_int = (bus->params.curr_dr_freq / rate);
>> +        list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>> +            port_bo = (p_rt->num * 64) + 1;
>> +            dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>> +                p_rt->num, hstart, hstop, port_bo);
>> +            sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>> +                          false, SDW_BLK_GRP_CNT_1, sample_int,
>> +                          port_bo, port_bo >> 8, hstart, hstop,
>> +                          SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +            sdw_fill_port_params(&p_rt->port_params,
>> +                         p_rt->num, bps,
>> +                         SDW_PORT_FLOW_MODE_ISOCH,
>> +                         b_params->m_data_mode);
>> +            t_data.hstart = hstart;
>> +            t_data.hstop = hstop;
>> +            t_data.block_offset = port_bo;
>> +            t_data.sub_block_offset = 0;
>> +        }
>> +        amd_sdwc_compute_slave_ports(m_rt, &t_data);
>> +    }
>> +    return 0;
>> +}
>> +
>
> ...
>
>> +
>> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
>> +                struct sdw_enable_ch *enable_ch,
>> +                unsigned int bank)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +    u32 dpn_ch_enable;
>> +    u32 ch_enable_reg, channel_type;
>> +
>> +    switch (ctrl->instance) {
>> +    case ACP_SDW0:
>> +        channel_type = enable_ch->port_num;
>> +        break;
>> +    case ACP_SDW1:
>> +        channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +
>> +    switch (channel_type) {
>> +    case ACP_SDW0_AUDIO_TX:
>> +        ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_HS_TX:
>> +        ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_BT_TX:
>> +        ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW1_BT_TX:
>> +        ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_AUDIO_RX:
>> +        ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_HS_RX:
>> +        ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_BT_RX:
>> +        ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW1_BT_RX:
>> +        ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    default:
>> +        dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +        return -EINVAL;
>> +    }
>> +
>> +    dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
>
> Double space after '='.
>
will fix it.
>> +    u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
>> +    if (enable_ch->enable)
>> +        acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
>> +    else
>> +        acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
>> +    return 0;
>> +}
>> +
>
> ...
>
>> +
>> +static void amd_sdwc_probe_work(struct work_struct *work)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
>
> Double space before '='.
Will fix it.
>
>> +    struct sdw_master_prop *prop;
>> +    int ret;
>> +
>> +    prop = &ctrl->bus.prop;
>> +    if (!prop->hw_disabled) {
>> +        ret = amd_enable_sdw_pads(ctrl);
>> +        if (ret)
>> +            return;
>> +        ret = amd_init_sdw_controller(ctrl);
>> +        if (ret)
>> +            return;
>> +        amd_enable_sdw_interrupts(ctrl);
>> +        ret = amd_enable_sdw_controller(ctrl);
>> +        if (ret)
>> +            return;
>> +        ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
>> +        if (!ret)
>> +            ctrl->startup_done = true;
>> +    }
>> +}
>> +
>> +static int amd_sdwc_probe(struct platform_device *pdev)
>> +{
>> +    const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
>> +    struct resource *res;
>> +    struct device *dev = &pdev->dev;
>
> Same as in previous patch, you assign dev here, but keep using &pdev->dev below?
> will remove dev.
>> +    struct sdw_master_prop *prop;
>> +    struct sdw_bus_params *params;
>> +    struct amd_sdwc_ctrl *ctrl;
>> +    int ret;
>> +
>> +    if (!pdev->dev.platform_data) {
>> +        dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +        return -ENODEV;
>> +    }
>> +    ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
>> +    if (!ctrl)
>> +        return -ENOMEM;
>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    if (!res) {
>> +        dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +        return -ENOMEM;
>> +    }
>> +    ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +    if (IS_ERR(ctrl->mmio)) {
>> +        dev_err(&pdev->dev, "mmio not found\n");
>> +        return PTR_ERR(ctrl->mmio);
>> +    }
>> +    ctrl->instance = pdata->instance;
>> +    ctrl->sdw_lock  = pdata->sdw_lock;
>
> Double space before '='.
> will fix it.
>> +    ctrl->rows_index = sdw_find_row_index(50);
>> +    ctrl->cols_index = sdw_find_col_index(10);
>> +
>> +    ctrl->dev = dev;
>> +    dev_set_drvdata(&pdev->dev, ctrl);
>> +
>> +    ctrl->bus.ops = &amd_sdwc_ops;
>> +    ctrl->bus.port_ops = &amd_sdwc_port_ops;
>> +    ctrl->bus.compute_params = &amd_sdwc_compute_params;
>> +    ctrl->bus.clk_stop_timeout = 1;
>> +    switch (ctrl->instance) {
>> +    case ACP_SDW0:
>> +        ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
>
> Double space after '='.
>
will fix it.
>> +        ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
>> +        break;
>> +    case ACP_SDW1:
>> +        ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
>> +        ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +    params = &ctrl->bus.params;
>> +    params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +    params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +    params->col = 10;
>> +    params->row = 50;
>> +
>> +    prop = &ctrl->bus.prop;
>> +    prop->clk_freq = &amd_sdwc_freq_tbl[0];
>> +    prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
>> +    ctrl->bus.link_id = ctrl->instance;
>> +    ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
>> +    if (ret) {
>> +        dev_err(dev, "Failed to register Soundwire controller (%d)\n",
>> +            ret);
>> +        return ret;
>> +    }
>> +    INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>> +    schedule_work(&ctrl->probe_work);
>> +    return 0;
>> +}
>> +
>> +static int amd_sdwc_remove(struct platform_device *pdev)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
>> +    int ret;
>> +
>
> You may need to cancel work if someone tries to unload driver before probe work completes. Something like
>
> cancel_work_sync(&ctrl->probe_work);
>
> should probably work here.
will fix it and post the v2 patch.
>
>> +    amd_disable_sdw_interrupts(ctrl);
>> +    sdw_bus_master_delete(&ctrl->bus);
>> +    ret = amd_disable_sdw_controller(ctrl);
>> +    return ret;
>> +}
>> +
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-11 14:16       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-11 14:16 UTC (permalink / raw)
  To: Amadeusz Sławiński, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Pierre-Louis Bossart, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 11/01/23 19:29, Amadeusz Sławiński wrote:
> On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
>> AMD ACP IP block has two soundwire controller devices.
>> Add support for
>> - Master driver probe & remove sequence
>> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>>    enable sdw pads
>> - Master driver sdw_master_ops & port_ops callbacks
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>
> ...
>
>> +
>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>> +{
>> +    struct sdw_transport_data t_data = {0};
>> +    struct sdw_master_runtime *m_rt;
>> +    struct sdw_port_runtime *p_rt;
>> +    struct sdw_bus_params *b_params = &bus->params;
>> +    int port_bo, hstart, hstop, sample_int;
>> +    unsigned int rate, bps;
>> +
>> +    port_bo  = 0;
>
> Double space before '='.
will fix it.
>
>> +    hstart = 1;
>> +    hstop = bus->params.col - 1;
>> +    t_data.hstop = hstop;
>> +    t_data.hstart = hstart;
>> +
>> +    list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>> +        rate = m_rt->stream->params.rate;
>> +        bps = m_rt->stream->params.bps;
>> +        sample_int = (bus->params.curr_dr_freq / rate);
>> +        list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>> +            port_bo = (p_rt->num * 64) + 1;
>> +            dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>> +                p_rt->num, hstart, hstop, port_bo);
>> +            sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>> +                          false, SDW_BLK_GRP_CNT_1, sample_int,
>> +                          port_bo, port_bo >> 8, hstart, hstop,
>> +                          SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +            sdw_fill_port_params(&p_rt->port_params,
>> +                         p_rt->num, bps,
>> +                         SDW_PORT_FLOW_MODE_ISOCH,
>> +                         b_params->m_data_mode);
>> +            t_data.hstart = hstart;
>> +            t_data.hstop = hstop;
>> +            t_data.block_offset = port_bo;
>> +            t_data.sub_block_offset = 0;
>> +        }
>> +        amd_sdwc_compute_slave_ports(m_rt, &t_data);
>> +    }
>> +    return 0;
>> +}
>> +
>
> ...
>
>> +
>> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
>> +                struct sdw_enable_ch *enable_ch,
>> +                unsigned int bank)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +    u32 dpn_ch_enable;
>> +    u32 ch_enable_reg, channel_type;
>> +
>> +    switch (ctrl->instance) {
>> +    case ACP_SDW0:
>> +        channel_type = enable_ch->port_num;
>> +        break;
>> +    case ACP_SDW1:
>> +        channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +
>> +    switch (channel_type) {
>> +    case ACP_SDW0_AUDIO_TX:
>> +        ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_HS_TX:
>> +        ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_BT_TX:
>> +        ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW1_BT_TX:
>> +        ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_AUDIO_RX:
>> +        ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_HS_RX:
>> +        ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW0_BT_RX:
>> +        ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    case ACP_SDW1_BT_RX:
>> +        ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +        break;
>> +    default:
>> +        dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +        return -EINVAL;
>> +    }
>> +
>> +    dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
>
> Double space after '='.
>
will fix it.
>> +    u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
>> +    if (enable_ch->enable)
>> +        acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
>> +    else
>> +        acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
>> +    return 0;
>> +}
>> +
>
> ...
>
>> +
>> +static void amd_sdwc_probe_work(struct work_struct *work)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
>
> Double space before '='.
Will fix it.
>
>> +    struct sdw_master_prop *prop;
>> +    int ret;
>> +
>> +    prop = &ctrl->bus.prop;
>> +    if (!prop->hw_disabled) {
>> +        ret = amd_enable_sdw_pads(ctrl);
>> +        if (ret)
>> +            return;
>> +        ret = amd_init_sdw_controller(ctrl);
>> +        if (ret)
>> +            return;
>> +        amd_enable_sdw_interrupts(ctrl);
>> +        ret = amd_enable_sdw_controller(ctrl);
>> +        if (ret)
>> +            return;
>> +        ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
>> +        if (!ret)
>> +            ctrl->startup_done = true;
>> +    }
>> +}
>> +
>> +static int amd_sdwc_probe(struct platform_device *pdev)
>> +{
>> +    const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
>> +    struct resource *res;
>> +    struct device *dev = &pdev->dev;
>
> Same as in previous patch, you assign dev here, but keep using &pdev->dev below?
> will remove dev.
>> +    struct sdw_master_prop *prop;
>> +    struct sdw_bus_params *params;
>> +    struct amd_sdwc_ctrl *ctrl;
>> +    int ret;
>> +
>> +    if (!pdev->dev.platform_data) {
>> +        dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +        return -ENODEV;
>> +    }
>> +    ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
>> +    if (!ctrl)
>> +        return -ENOMEM;
>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +    if (!res) {
>> +        dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +        return -ENOMEM;
>> +    }
>> +    ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +    if (IS_ERR(ctrl->mmio)) {
>> +        dev_err(&pdev->dev, "mmio not found\n");
>> +        return PTR_ERR(ctrl->mmio);
>> +    }
>> +    ctrl->instance = pdata->instance;
>> +    ctrl->sdw_lock  = pdata->sdw_lock;
>
> Double space before '='.
> will fix it.
>> +    ctrl->rows_index = sdw_find_row_index(50);
>> +    ctrl->cols_index = sdw_find_col_index(10);
>> +
>> +    ctrl->dev = dev;
>> +    dev_set_drvdata(&pdev->dev, ctrl);
>> +
>> +    ctrl->bus.ops = &amd_sdwc_ops;
>> +    ctrl->bus.port_ops = &amd_sdwc_port_ops;
>> +    ctrl->bus.compute_params = &amd_sdwc_compute_params;
>> +    ctrl->bus.clk_stop_timeout = 1;
>> +    switch (ctrl->instance) {
>> +    case ACP_SDW0:
>> +        ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
>
> Double space after '='.
>
will fix it.
>> +        ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
>> +        break;
>> +    case ACP_SDW1:
>> +        ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
>> +        ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +    params = &ctrl->bus.params;
>> +    params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +    params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +    params->col = 10;
>> +    params->row = 50;
>> +
>> +    prop = &ctrl->bus.prop;
>> +    prop->clk_freq = &amd_sdwc_freq_tbl[0];
>> +    prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
>> +    ctrl->bus.link_id = ctrl->instance;
>> +    ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
>> +    if (ret) {
>> +        dev_err(dev, "Failed to register Soundwire controller (%d)\n",
>> +            ret);
>> +        return ret;
>> +    }
>> +    INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>> +    schedule_work(&ctrl->probe_work);
>> +    return 0;
>> +}
>> +
>> +static int amd_sdwc_remove(struct platform_device *pdev)
>> +{
>> +    struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
>> +    int ret;
>> +
>
> You may need to cancel work if someone tries to unload driver before probe work completes. Something like
>
> cancel_work_sync(&ctrl->probe_work);
>
> should probably work here.
will fix it and post the v2 patch.
>
>> +    amd_disable_sdw_interrupts(ctrl);
>> +    sdw_bus_master_delete(&ctrl->bus);
>> +    ret = amd_disable_sdw_controller(ctrl);
>> +    return ret;
>> +}
>> +
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 14:37     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 14:37 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> AMD ACP IP block has two soundwire controller devices.

s/controller/manager?

> Add support for
> - Master driver probe & remove sequence
> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>   enable sdw pads
> - Master driver sdw_master_ops & port_ops callbacks
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
>  drivers/soundwire/amd_master.h    |  279 ++++++++
>  include/linux/soundwire/sdw_amd.h |   21 +
>  3 files changed, 1375 insertions(+)
>  create mode 100644 drivers/soundwire/amd_master.c
>  create mode 100644 drivers/soundwire/amd_master.h
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> new file mode 100644
> index 000000000000..7e1f618254ac
> --- /dev/null
> +++ b/drivers/soundwire/amd_master.c
> @@ -0,0 +1,1075 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * SoundWire AMD Master driver
> + *
> + * Copyright 2023 Advanced Micro Devices, Inc.
> + */
> +
> +#include <linux/completion.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/soundwire/sdw_amd.h>
> +#include <linux/wait.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include "bus.h"
> +#include "amd_master.h"
> +
> +#define DRV_NAME "amd_sdw_controller"
> +
> +#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
> +
> +static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 sw_pad_enable_mask;
> +	u32 sw_pad_pulldown_mask;
> +	u32 sw_pad_pulldown_val;
> +	u32 val = 0;
> +
> +	switch (ctrl->instance) {

Goodness no. A controller has one or more masters. It cannot have pins
as described in the SoundWire master specification.

> +	case ACP_SDW0:
> +		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
> +		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
> +		break;
> +	case ACP_SDW1:
> +		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
> +		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(ctrl->sdw_lock);
> +	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
> +	val |= sw_pad_enable_mask;
> +	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
> +	mutex_unlock(ctrl->sdw_lock);
> +	usleep_range(1000, 1500);
> +
> +	mutex_lock(ctrl->sdw_lock);
> +	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
> +	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
> +	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
> +	mutex_unlock(ctrl->sdw_lock);
> +	return 0;
> +}
> +
> +static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
> +	u32 val = 0;
> +	u32 timeout = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +
> +	/* Sdw Controller reset */
> +	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
> +			break;
> +		usleep_range(1, 5);
> +		timeout++;
> +	}

no test on timeout here to check if the bus was indeed reset?

If you are talking about bus_reset you are referring to a master btw.
The terms bus/master/link are interchangeable. A controller is not
defined in the SoundWire specification, this is part of the DisCo spec
to deal with enumeration when multiple bus/master/link are supported in
the platform.

> +	timeout = 0;
> +	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +	while (val) {
> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
> +			break;
> +		usleep_range(1, 5);
> +		timeout++;
> +	}
> +	if (timeout == AMD_DELAY_LOOP_ITERATION) {
> +		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +	retry_count = 0;
> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (!val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 acp_sw_en_reg;
> +	u32 acp_sw_en_stat_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 clk_resume_ctrl_reg;
> +	u32 acp_sw_en_reg;
> +	u32 acp_sw_en_stat_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
> +
> +	/*
> +	 * After invoking controller disable sequence, check whether
> +	 * controller has executed clock stop sequence. In this case,
> +	 * controller should ignore checking enable status register.

again clock stop is a sequence at the master/link/bus level, not the
controller.

> +	 */
> +	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +	if (val)
> +		return 0;
> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (!val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 val;
> +	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
> +	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;

should be renamed and end in CNTL0 if the other is CNTL1

And it's manager anyways, not controller.

> +		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
> +		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		sw_err_intr_mask = SW_ERROR_INTR_MASK;
> +		break;
> +	case ACP_SDW1:
> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
> +		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
> +		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	mutex_lock(ctrl->sdw_lock);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
> +	val |= acp_sdw_intr_mask;
> +	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
> +	mutex_unlock(ctrl->sdw_lock);
> +	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
> +	if (val)
> +		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
> +	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
> +	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
> +	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
> +	return 0;
> +}
> +

> +static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
> +{
> +	u64 resp = 0;
> +	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
> +	u32 imm_resp_uword_reg, imm_resp_lword_reg;
> +	u32 resp_lower, resp_high;
> +	u32 sts = 0;
> +	u32 timeout = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		imm_cmd_stat_reg = SW_IMM_CMD_STS;
> +		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
> +		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
> +		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
> +		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
> +		break;
> +	case ACP_SDW1:
> +		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
> +		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
> +		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
> +		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
> +		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;

naming consistency would be good, the P1 is sometimes a prefix,
sometimes a suffix, sometimes in the middle. Pick one.

> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +	while (sts & AMD_SDW_IMM_CMD_BUSY) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
> +				ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +
> +	timeout = 0;
> +	if (sts & AMD_SDW_IMM_RES_VALID) {
> +		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
> +		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
> +	}
> +	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
> +	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
> +
> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
> +	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
> +	timeout = 0;
> +	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
> +	while ((sts & AMD_SDW_IMM_RES_VALID)) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +	resp = resp_high;
> +	resp = (resp << 32) | resp_lower;
> +	return resp;
> +}
> +
> +static enum sdw_command_response
> +amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
> +{
> +	struct sdw_msg scp_msg = {0};
> +	u64 response_buf[2] = {0};
> +	u32 uword = 0, lword = 0;
> +	int nack = 0, no_ack = 0;
> +	int index, timeout = 0;
> +
> +	scp_msg.dev_num = msg->dev_num;
> +	scp_msg.addr = SDW_SCP_ADDRPAGE1;
> +	scp_msg.buf = &msg->addr_page1;
> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
> +	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +	scp_msg.addr = SDW_SCP_ADDRPAGE2;
> +	scp_msg.buf = &msg->addr_page2;
> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
> +	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +
> +	/* check response the writes */

response to the writes?  after the writes?

> +	for (index = 0; index < 2; index++) {
> +		if (response_buf[index] == -ETIMEDOUT) {
> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
> +			timeout = 1;
> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
> +			no_ack = 1;
> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
> +				nack = 1;
> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
> +			}

this is a copy of the cadence_master.c code... With the error added that
this is not for a controller but for a master...

> +		}
> +	}
> +
> +	if (timeout) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_TIMEOUT;
> +	}
> +
> +	if (nack) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_FAIL;
> +	}
> +
> +	if (no_ack) {
> +		dev_dbg_ratelimited(ctrl->dev,
> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_IGNORED;
> +	}
> +	return SDW_CMD_OK;

this should probably become a helper since the response is really the
same as in cadence_master.c

There's really room for optimization and reuse here.

> +}
> +
> +static int amd_prep_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
> +{
> +	int ret;
> +
> +	if (msg->page) {
> +		ret = amd_program_scp_addr(ctrl, msg);
> +		if (ret) {
> +			msg->len = 0;
> +			return ret;
> +		}
> +	}
> +	switch (msg->flags) {
> +	case SDW_MSG_FLAG_READ:
> +		*cmd = AMD_SDW_CMD_READ;
> +		break;
> +	case SDW_MSG_FLAG_WRITE:
> +		*cmd = AMD_SDW_CMD_WRITE;
> +		break;
> +	default:
> +		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}

this is the same code as in cadence_master.c

you just replaced sdw_cnds by amd_sdw_ctrl (which is a mistake) and
cdns->dev by ctrl->dev.

> +
> +static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
> +				       int cmd, int cmd_offset)
> +{
> +	u64 response = 0;
> +	u32 uword = 0, lword = 0;
> +	int nack = 0, no_ack = 0;
> +	int timeout = 0;
> +
> +	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +
> +	if (response & AMD_SDW_MCP_RESP_ACK) {
> +		if (cmd == AMD_SDW_CMD_READ)
> +			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
> +	} else {
> +		no_ack = 1;
> +		if (response == -ETIMEDOUT) {
> +			timeout = 1;
> +		} else if (response & AMD_SDW_MCP_RESP_NACK) {
> +			nack = 1;
> +			dev_err(ctrl->dev, "Program SCP NACK received\n");
> +		}
> +	}
> +
> +	if (timeout) {
> +		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_TIMEOUT;
> +	}
> +	if (nack) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "command response NACK received for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_FAIL;
> +	}
> +
> +	if (no_ack) {
> +		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_IGNORED;
> +	}
> +	return SDW_CMD_OK;
> +}
> +
> +static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	int ret, i;
> +	int cmd = 0;
> +
> +	ret = amd_prep_msg(ctrl, msg, &cmd);
> +	if (ret)
> +		return SDW_CMD_FAIL_OTHER;
> +	for (i = 0; i < msg->len; i++) {
> +		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
> +		if (ret)
> +			return ret;
> +	}
> +	return SDW_CMD_OK;
> +}
> +
> +static enum sdw_command_response
> +amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	struct sdw_msg msg;
> +
> +	/* Create dummy message with valid device number */
> +	memset(&msg, 0, sizeof(msg));
> +	msg.dev_num = dev_num;
> +	return amd_program_scp_addr(ctrl, &msg);
> +}
> +
> +static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u64 response;
> +	u32 slave_stat = 0;
> +
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
> +	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
> +	return slave_stat;
> +}
> +
> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
> +					 struct sdw_transport_data *t_data)
> +{
> +	struct sdw_slave_runtime *s_rt = NULL;
> +	struct sdw_port_runtime *p_rt;
> +	int port_bo, sample_int;
> +	unsigned int rate, bps, ch = 0;
> +	unsigned int slave_total_ch;
> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
> +
> +	port_bo = t_data->block_offset;
> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
> +		slave_total_ch = 0;
> +
> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
> +
> +			sdw_fill_xport_params(&p_rt->transport_params,
> +					      p_rt->num, false,
> +					      SDW_BLK_GRP_CNT_1,
> +					      sample_int, port_bo, port_bo >> 8,
> +					      t_data->hstart,
> +					      t_data->hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->s_data_mode);
> +
> +			port_bo += bps * ch;
> +			slave_total_ch += ch;
> +		}
> +
> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
> +		    m_rt->ch_count == slave_total_ch) {
> +			port_bo = t_data->block_offset;
> +		}
> +	}
> +}

ok, this is really bad.

This is a verbatim copy of the same function in
generic_bandwidth_allocation.c

see
https://elixir.bootlin.com/linux/latest/source/drivers/soundwire/generic_bandwidth_allocation.c#L38

You only removed the comments and renamed the function.

Seriously? Why would you do that?

And in addition, this has *NOTHING* to do with the master support.

Programming the ports on peripheral side is something that happens at
the stream level.

I am afraid it's a double NAK, or rather NAK^2 from me here.

> +
> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
> +{
> +	struct sdw_transport_data t_data = {0};
> +	struct sdw_master_runtime *m_rt;
> +	struct sdw_port_runtime *p_rt;
> +	struct sdw_bus_params *b_params = &bus->params;
> +	int port_bo, hstart, hstop, sample_int;
> +	unsigned int rate, bps;
> +
> +	port_bo  = 0;
> +	hstart = 1;
> +	hstop = bus->params.col - 1;
> +	t_data.hstop = hstop;
> +	t_data.hstart = hstart;
> +
> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (bus->params.curr_dr_freq / rate);
> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
> +			port_bo = (p_rt->num * 64) + 1;
> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
> +				p_rt->num, hstart, hstop, port_bo);
> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
> +					      port_bo, port_bo >> 8, hstart, hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->m_data_mode);
> +			t_data.hstart = hstart;
> +			t_data.hstop = hstop;
> +			t_data.block_offset = port_bo;
> +			t_data.sub_block_offset = 0;
> +		}
> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
> +	}
> +	return 0;
> +}

this is a variation on sdw_compute_master_ports() in generic_allocation.c

You would need a lot more comments to convince me that this is
intentional and needed.

> +
> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
> +
> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = p_params->num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:

you'll have to explain what you mean by 'channel_type'

This looks like the streams that can be supported by this master
implementation, with dailinks for each.

> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_HS_TX:
> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
> +	return 0;
> +}
> +
> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
> +				     struct sdw_transport_params *params,
> +				     enum sdw_reg_bank bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 ssp_counter_reg;
> +	u32 dpn_frame_fmt;
> +	u32 dpn_sampleinterval;
> +	u32 dpn_hctrl;
> +	u32 dpn_offsetctrl;
> +	u32 dpn_lanectrl;
> +	u32 channel_type;
> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
> +	u32 offset_reg, lane_ctrl_reg;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
> +		channel_type = params->port_num;
> +		break;
> +	case ACP_SDW1:
> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;

There's obviously a dependency between SDW0 and SDW1 managers that you
haven't described?

> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
> +		__func__, params->port_num, channel_type);
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

This is confusing. Is this about enabling a stream or selecting the lane
for this port? Same for all cases.

is this saying that the two cases are handled by the same register -
unlike what is normative for the peripherals where the two concepts are
handeld in DPN_ChannelEn and DPN_LaneCtrl registers?

> +		break;
> +	}
> +	case ACP_SDW0_HS_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
> +		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_BT_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
> +		offset_reg = ACP_SW_BT_TX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW1_BT_TX:
> +	{
> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
> +		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
> +		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_AUDIO_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
> +		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
> +		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_HS_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
> +		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_BT_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
> +		offset_reg = ACP_SW_BT_RX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW1_BT_RX:
> +	{
> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
> +		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
> +		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
> +	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
> +
> +	dpn_sampleinterval = params->sample_interval - 1;
> +	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
> +
> +	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
> +	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
> +	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
> +
> +	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
> +	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
> +	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
> +
> +	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
> +	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
> +	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
> +	return 0;
> +}
> +
> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
> +				struct sdw_enable_ch *enable_ch,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 dpn_ch_enable;
> +	u32 ch_enable_reg, channel_type;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = enable_ch->port_num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

in the function above, I commented on

		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

This looks really weird. You need to add comments is this is really
intentional.

> +		break;
> +	case ACP_SDW0_HS_TX:
> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +
> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
> +	if (enable_ch->enable)
> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
> +	else
> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
> +	return 0;
> +}
> +
> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
> +{
> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;

same quirk as Intel, nice :-)

> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;

And here too. Is this really needed or just-copy-pasted?

> +	fwnode_property_read_u32(link, "amd-sdw-wake-enable", &wake_en_mask);
> +	ctrl->wake_en_mask = wake_en_mask;
> +	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
> +	ctrl->power_mode_mask = power_mode_mask;
> +	return 0;
> +}
> +

> +static int amd_sdwc_probe(struct platform_device *pdev)

why not use an auxiliary device? we've moved away from platform devices
maybe two years ago.

> +{
> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	struct device *dev = &pdev->dev;
> +	struct sdw_master_prop *prop;
> +	struct sdw_bus_params *params;
> +	struct amd_sdwc_ctrl *ctrl;
> +	int ret;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
> +	if (!ctrl)
> +		return -ENOMEM;
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENOMEM;
> +	}
> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (IS_ERR(ctrl->mmio)) {
> +		dev_err(&pdev->dev, "mmio not found\n");
> +		return PTR_ERR(ctrl->mmio);
> +	}
> +	ctrl->instance = pdata->instance;
> +	ctrl->sdw_lock  = pdata->sdw_lock;
> +	ctrl->rows_index = sdw_find_row_index(50);
> +	ctrl->cols_index = sdw_find_col_index(10);
> +
> +	ctrl->dev = dev;
> +	dev_set_drvdata(&pdev->dev, ctrl);
> +
> +	ctrl->bus.ops = &amd_sdwc_ops;
> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
> +	ctrl->bus.clk_stop_timeout = 1;
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
> +		break;
> +	case ACP_SDW1:
> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	params = &ctrl->bus.params;
> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->col = 10;
> +	params->row = 50;
> +
> +	prop = &ctrl->bus.prop;
> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
> +	ctrl->bus.link_id = ctrl->instance;
> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
> +	if (ret) {
> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",

master. the confusion continues.

> +			ret);
> +		return ret;
> +	}
> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
> +	schedule_work(&ctrl->probe_work);
> +	return 0;
> +}
> +
> +static int amd_sdwc_remove(struct platform_device *pdev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +
> +	amd_disable_sdw_interrupts(ctrl);
> +	sdw_bus_master_delete(&ctrl->bus);
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +
> +static struct platform_driver amd_sdwc_driver = {
> +	.probe	= &amd_sdwc_probe,
> +	.remove = &amd_sdwc_remove,
> +	.driver = {
> +		.name	= "amd_sdw_controller",
> +	}
> +};
> +module_platform_driver(amd_sdwc_driver);
> +
> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
> +MODULE_DESCRIPTION("AMD soundwire driver");
> +MODULE_LICENSE("GPL v2");

"GPL" is enough

> +enum amd_sdw_channel {
> +	/* SDW0 */
> +	ACP_SDW0_AUDIO_TX = 0,
> +	ACP_SDW0_BT_TX,
> +	ACP_SDW0_HS_TX,
> +	ACP_SDW0_AUDIO_RX,
> +	ACP_SDW0_BT_RX,
> +	ACP_SDW0_HS_RX,
> +	/* SDW1 */
> +	ACP_SDW1_BT_TX,
> +	ACP_SDW1_BT_RX,
> +};

you really need to comment on this. It looks like you've special-cased
manager ports for specific usages? This is perfectly fine in closed
applications, but it's not clear how it might work with headset,
amplifier and mic codec devices.


> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
> index f0123815af46..5ec39f8c2f2e 100644
> --- a/include/linux/soundwire/sdw_amd.h
> +++ b/include/linux/soundwire/sdw_amd.h
> @@ -10,9 +10,30 @@
>  
>  #define AMD_SDW_CLK_STOP_MODE		1
>  #define AMD_SDW_POWER_OFF_MODE		2
> +#define ACP_SDW0	0
> +#define ACP_SDW1	1
> +#define ACP_SDW0_MAX_DAI	6

is this related to the definition of amd_sdw_channel or the number of
ports available?
>  
>  struct acp_sdw_pdata {
>  	u16 instance;
>  	struct mutex *sdw_lock;
>  };
> +
> +struct amd_sdwc_ctrl {
> +	struct sdw_bus bus;
> +	struct device *dev;
> +	void __iomem *mmio;
> +	struct work_struct probe_work;
> +	struct mutex *sdw_lock;

comment please.

> +	int num_din_ports;
> +	int num_dout_ports;
> +	int cols_index;
> +	int rows_index;
> +	u32 instance;
> +	u32 quirks;
> +	u32 wake_en_mask;
> +	int num_ports;
> +	bool startup_done;

ah this was an Intel definition. Due to power dependencies we had to
split the probe and startup step. Does AMD have a need for this? Is the
SoundWire master IP dependent on DSP boot or something?

> +	u32 power_mode_mask;
> +};
>  #endif

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-11 14:37     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 14:37 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> AMD ACP IP block has two soundwire controller devices.

s/controller/manager?

> Add support for
> - Master driver probe & remove sequence
> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>   enable sdw pads
> - Master driver sdw_master_ops & port_ops callbacks
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
>  drivers/soundwire/amd_master.h    |  279 ++++++++
>  include/linux/soundwire/sdw_amd.h |   21 +
>  3 files changed, 1375 insertions(+)
>  create mode 100644 drivers/soundwire/amd_master.c
>  create mode 100644 drivers/soundwire/amd_master.h
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> new file mode 100644
> index 000000000000..7e1f618254ac
> --- /dev/null
> +++ b/drivers/soundwire/amd_master.c
> @@ -0,0 +1,1075 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * SoundWire AMD Master driver
> + *
> + * Copyright 2023 Advanced Micro Devices, Inc.
> + */
> +
> +#include <linux/completion.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/soundwire/sdw_amd.h>
> +#include <linux/wait.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include "bus.h"
> +#include "amd_master.h"
> +
> +#define DRV_NAME "amd_sdw_controller"
> +
> +#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
> +
> +static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 sw_pad_enable_mask;
> +	u32 sw_pad_pulldown_mask;
> +	u32 sw_pad_pulldown_val;
> +	u32 val = 0;
> +
> +	switch (ctrl->instance) {

Goodness no. A controller has one or more masters. It cannot have pins
as described in the SoundWire master specification.

> +	case ACP_SDW0:
> +		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
> +		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
> +		break;
> +	case ACP_SDW1:
> +		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
> +		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(ctrl->sdw_lock);
> +	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
> +	val |= sw_pad_enable_mask;
> +	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
> +	mutex_unlock(ctrl->sdw_lock);
> +	usleep_range(1000, 1500);
> +
> +	mutex_lock(ctrl->sdw_lock);
> +	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
> +	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
> +	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
> +	mutex_unlock(ctrl->sdw_lock);
> +	return 0;
> +}
> +
> +static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
> +	u32 val = 0;
> +	u32 timeout = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +
> +	/* Sdw Controller reset */
> +	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
> +			break;
> +		usleep_range(1, 5);
> +		timeout++;
> +	}

no test on timeout here to check if the bus was indeed reset?

If you are talking about bus_reset you are referring to a master btw.
The terms bus/master/link are interchangeable. A controller is not
defined in the SoundWire specification, this is part of the DisCo spec
to deal with enumeration when multiple bus/master/link are supported in
the platform.

> +	timeout = 0;
> +	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +	while (val) {
> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
> +			break;
> +		usleep_range(1, 5);
> +		timeout++;
> +	}
> +	if (timeout == AMD_DELAY_LOOP_ITERATION) {
> +		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +	retry_count = 0;
> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (!val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 acp_sw_en_reg;
> +	u32 acp_sw_en_stat_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 clk_resume_ctrl_reg;
> +	u32 acp_sw_en_reg;
> +	u32 acp_sw_en_stat_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_sw_en_reg = ACP_SW_EN;
> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		acp_sw_en_reg = ACP_P1_SW_EN;
> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
> +
> +	/*
> +	 * After invoking controller disable sequence, check whether
> +	 * controller has executed clock stop sequence. In this case,
> +	 * controller should ignore checking enable status register.

again clock stop is a sequence at the master/link/bus level, not the
controller.

> +	 */
> +	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +	if (val)
> +		return 0;
> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
> +		if (!val)
> +			break;
> +		usleep_range(10, 50);
> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
> +
> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
> +		return -ETIMEDOUT;
> +	return 0;
> +}
> +
> +static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 val;
> +	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
> +	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;

should be renamed and end in CNTL0 if the other is CNTL1

And it's manager anyways, not controller.

> +		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
> +		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		sw_err_intr_mask = SW_ERROR_INTR_MASK;
> +		break;
> +	case ACP_SDW1:
> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
> +		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
> +		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	mutex_lock(ctrl->sdw_lock);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
> +	val |= acp_sdw_intr_mask;
> +	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
> +	mutex_unlock(ctrl->sdw_lock);
> +	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
> +	if (val)
> +		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
> +	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
> +	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
> +	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
> +	return 0;
> +}
> +

> +static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
> +{
> +	u64 resp = 0;
> +	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
> +	u32 imm_resp_uword_reg, imm_resp_lword_reg;
> +	u32 resp_lower, resp_high;
> +	u32 sts = 0;
> +	u32 timeout = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		imm_cmd_stat_reg = SW_IMM_CMD_STS;
> +		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
> +		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
> +		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
> +		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
> +		break;
> +	case ACP_SDW1:
> +		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
> +		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
> +		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
> +		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
> +		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;

naming consistency would be good, the P1 is sometimes a prefix,
sometimes a suffix, sometimes in the middle. Pick one.

> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +	while (sts & AMD_SDW_IMM_CMD_BUSY) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
> +				ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +
> +	timeout = 0;
> +	if (sts & AMD_SDW_IMM_RES_VALID) {
> +		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
> +		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
> +	}
> +	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
> +	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
> +
> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
> +	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
> +	timeout = 0;
> +	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
> +	while ((sts & AMD_SDW_IMM_RES_VALID)) {
> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
> +		if (timeout > AMD_SDW_RETRY_COUNT) {
> +			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
> +			return -ETIMEDOUT;
> +		}
> +		timeout++;
> +	}
> +	resp = resp_high;
> +	resp = (resp << 32) | resp_lower;
> +	return resp;
> +}
> +
> +static enum sdw_command_response
> +amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
> +{
> +	struct sdw_msg scp_msg = {0};
> +	u64 response_buf[2] = {0};
> +	u32 uword = 0, lword = 0;
> +	int nack = 0, no_ack = 0;
> +	int index, timeout = 0;
> +
> +	scp_msg.dev_num = msg->dev_num;
> +	scp_msg.addr = SDW_SCP_ADDRPAGE1;
> +	scp_msg.buf = &msg->addr_page1;
> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
> +	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +	scp_msg.addr = SDW_SCP_ADDRPAGE2;
> +	scp_msg.buf = &msg->addr_page2;
> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
> +	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +
> +	/* check response the writes */

response to the writes?  after the writes?

> +	for (index = 0; index < 2; index++) {
> +		if (response_buf[index] == -ETIMEDOUT) {
> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
> +			timeout = 1;
> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
> +			no_ack = 1;
> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
> +				nack = 1;
> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
> +			}

this is a copy of the cadence_master.c code... With the error added that
this is not for a controller but for a master...

> +		}
> +	}
> +
> +	if (timeout) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_TIMEOUT;
> +	}
> +
> +	if (nack) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_FAIL;
> +	}
> +
> +	if (no_ack) {
> +		dev_dbg_ratelimited(ctrl->dev,
> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_IGNORED;
> +	}
> +	return SDW_CMD_OK;

this should probably become a helper since the response is really the
same as in cadence_master.c

There's really room for optimization and reuse here.

> +}
> +
> +static int amd_prep_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
> +{
> +	int ret;
> +
> +	if (msg->page) {
> +		ret = amd_program_scp_addr(ctrl, msg);
> +		if (ret) {
> +			msg->len = 0;
> +			return ret;
> +		}
> +	}
> +	switch (msg->flags) {
> +	case SDW_MSG_FLAG_READ:
> +		*cmd = AMD_SDW_CMD_READ;
> +		break;
> +	case SDW_MSG_FLAG_WRITE:
> +		*cmd = AMD_SDW_CMD_WRITE;
> +		break;
> +	default:
> +		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}

this is the same code as in cadence_master.c

you just replaced sdw_cnds by amd_sdw_ctrl (which is a mistake) and
cdns->dev by ctrl->dev.

> +
> +static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
> +				       int cmd, int cmd_offset)
> +{
> +	u64 response = 0;
> +	u32 uword = 0, lword = 0;
> +	int nack = 0, no_ack = 0;
> +	int timeout = 0;
> +
> +	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
> +
> +	if (response & AMD_SDW_MCP_RESP_ACK) {
> +		if (cmd == AMD_SDW_CMD_READ)
> +			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
> +	} else {
> +		no_ack = 1;
> +		if (response == -ETIMEDOUT) {
> +			timeout = 1;
> +		} else if (response & AMD_SDW_MCP_RESP_NACK) {
> +			nack = 1;
> +			dev_err(ctrl->dev, "Program SCP NACK received\n");
> +		}
> +	}
> +
> +	if (timeout) {
> +		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_TIMEOUT;
> +	}
> +	if (nack) {
> +		dev_err_ratelimited(ctrl->dev,
> +				    "command response NACK received for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_FAIL;
> +	}
> +
> +	if (no_ack) {
> +		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
> +		return SDW_CMD_IGNORED;
> +	}
> +	return SDW_CMD_OK;
> +}
> +
> +static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	int ret, i;
> +	int cmd = 0;
> +
> +	ret = amd_prep_msg(ctrl, msg, &cmd);
> +	if (ret)
> +		return SDW_CMD_FAIL_OTHER;
> +	for (i = 0; i < msg->len; i++) {
> +		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
> +		if (ret)
> +			return ret;
> +	}
> +	return SDW_CMD_OK;
> +}
> +
> +static enum sdw_command_response
> +amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	struct sdw_msg msg;
> +
> +	/* Create dummy message with valid device number */
> +	memset(&msg, 0, sizeof(msg));
> +	msg.dev_num = dev_num;
> +	return amd_program_scp_addr(ctrl, &msg);
> +}
> +
> +static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u64 response;
> +	u32 slave_stat = 0;
> +
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
> +	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
> +	return slave_stat;
> +}
> +
> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
> +					 struct sdw_transport_data *t_data)
> +{
> +	struct sdw_slave_runtime *s_rt = NULL;
> +	struct sdw_port_runtime *p_rt;
> +	int port_bo, sample_int;
> +	unsigned int rate, bps, ch = 0;
> +	unsigned int slave_total_ch;
> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
> +
> +	port_bo = t_data->block_offset;
> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
> +		slave_total_ch = 0;
> +
> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
> +
> +			sdw_fill_xport_params(&p_rt->transport_params,
> +					      p_rt->num, false,
> +					      SDW_BLK_GRP_CNT_1,
> +					      sample_int, port_bo, port_bo >> 8,
> +					      t_data->hstart,
> +					      t_data->hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->s_data_mode);
> +
> +			port_bo += bps * ch;
> +			slave_total_ch += ch;
> +		}
> +
> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
> +		    m_rt->ch_count == slave_total_ch) {
> +			port_bo = t_data->block_offset;
> +		}
> +	}
> +}

ok, this is really bad.

This is a verbatim copy of the same function in
generic_bandwidth_allocation.c

see
https://elixir.bootlin.com/linux/latest/source/drivers/soundwire/generic_bandwidth_allocation.c#L38

You only removed the comments and renamed the function.

Seriously? Why would you do that?

And in addition, this has *NOTHING* to do with the master support.

Programming the ports on peripheral side is something that happens at
the stream level.

I am afraid it's a double NAK, or rather NAK^2 from me here.

> +
> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
> +{
> +	struct sdw_transport_data t_data = {0};
> +	struct sdw_master_runtime *m_rt;
> +	struct sdw_port_runtime *p_rt;
> +	struct sdw_bus_params *b_params = &bus->params;
> +	int port_bo, hstart, hstop, sample_int;
> +	unsigned int rate, bps;
> +
> +	port_bo  = 0;
> +	hstart = 1;
> +	hstop = bus->params.col - 1;
> +	t_data.hstop = hstop;
> +	t_data.hstart = hstart;
> +
> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
> +		rate = m_rt->stream->params.rate;
> +		bps = m_rt->stream->params.bps;
> +		sample_int = (bus->params.curr_dr_freq / rate);
> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
> +			port_bo = (p_rt->num * 64) + 1;
> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
> +				p_rt->num, hstart, hstop, port_bo);
> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
> +					      port_bo, port_bo >> 8, hstart, hstop,
> +					      SDW_BLK_PKG_PER_PORT, 0x0);
> +
> +			sdw_fill_port_params(&p_rt->port_params,
> +					     p_rt->num, bps,
> +					     SDW_PORT_FLOW_MODE_ISOCH,
> +					     b_params->m_data_mode);
> +			t_data.hstart = hstart;
> +			t_data.hstop = hstop;
> +			t_data.block_offset = port_bo;
> +			t_data.sub_block_offset = 0;
> +		}
> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
> +	}
> +	return 0;
> +}

this is a variation on sdw_compute_master_ports() in generic_allocation.c

You would need a lot more comments to convince me that this is
intentional and needed.

> +
> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
> +
> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = p_params->num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:

you'll have to explain what you mean by 'channel_type'

This looks like the streams that can be supported by this master
implementation, with dailinks for each.

> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_HS_TX:
> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
> +	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
> +	return 0;
> +}
> +
> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
> +				     struct sdw_transport_params *params,
> +				     enum sdw_reg_bank bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 ssp_counter_reg;
> +	u32 dpn_frame_fmt;
> +	u32 dpn_sampleinterval;
> +	u32 dpn_hctrl;
> +	u32 dpn_offsetctrl;
> +	u32 dpn_lanectrl;
> +	u32 channel_type;
> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
> +	u32 offset_reg, lane_ctrl_reg;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
> +		channel_type = params->port_num;
> +		break;
> +	case ACP_SDW1:
> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;

There's obviously a dependency between SDW0 and SDW1 managers that you
haven't described?

> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
> +		__func__, params->port_num, channel_type);
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

This is confusing. Is this about enabling a stream or selecting the lane
for this port? Same for all cases.

is this saying that the two cases are handled by the same register -
unlike what is normative for the peripherals where the two concepts are
handeld in DPN_ChannelEn and DPN_LaneCtrl registers?

> +		break;
> +	}
> +	case ACP_SDW0_HS_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
> +		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_BT_TX:
> +	{
> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
> +		offset_reg = ACP_SW_BT_TX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW1_BT_TX:
> +	{
> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
> +		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
> +		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
> +		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_AUDIO_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
> +		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
> +		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_HS_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
> +		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW0_BT_RX:
> +	{
> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
> +		offset_reg = ACP_SW_BT_RX_OFFSET;
> +		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	case ACP_SDW1_BT_RX:
> +	{
> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
> +		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
> +		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
> +		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
> +		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	}
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
> +	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
> +
> +	dpn_sampleinterval = params->sample_interval - 1;
> +	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
> +
> +	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
> +	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
> +	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
> +
> +	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
> +	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
> +	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
> +
> +	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
> +	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
> +	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
> +	return 0;
> +}
> +
> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
> +				struct sdw_enable_ch *enable_ch,
> +				unsigned int bank)
> +{
> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> +	u32 dpn_ch_enable;
> +	u32 ch_enable_reg, channel_type;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		channel_type = enable_ch->port_num;
> +		break;
> +	case ACP_SDW1:
> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	switch (channel_type) {
> +	case ACP_SDW0_AUDIO_TX:
> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

in the function above, I commented on

		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;

This looks really weird. You need to add comments is this is really
intentional.

> +		break;
> +	case ACP_SDW0_HS_TX:
> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_TX:
> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_AUDIO_RX:
> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_HS_RX:
> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW0_BT_RX:
> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
> +		break;
> +	default:
> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
> +		return -EINVAL;
> +	}
> +
> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
> +	if (enable_ch->enable)
> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
> +	else
> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
> +	return 0;
> +}
> +
> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
> +{
> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;

same quirk as Intel, nice :-)

> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;

And here too. Is this really needed or just-copy-pasted?

> +	fwnode_property_read_u32(link, "amd-sdw-wake-enable", &wake_en_mask);
> +	ctrl->wake_en_mask = wake_en_mask;
> +	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
> +	ctrl->power_mode_mask = power_mode_mask;
> +	return 0;
> +}
> +

> +static int amd_sdwc_probe(struct platform_device *pdev)

why not use an auxiliary device? we've moved away from platform devices
maybe two years ago.

> +{
> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
> +	struct resource *res;
> +	struct device *dev = &pdev->dev;
> +	struct sdw_master_prop *prop;
> +	struct sdw_bus_params *params;
> +	struct amd_sdwc_ctrl *ctrl;
> +	int ret;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
> +	if (!ctrl)
> +		return -ENOMEM;
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENOMEM;
> +	}
> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (IS_ERR(ctrl->mmio)) {
> +		dev_err(&pdev->dev, "mmio not found\n");
> +		return PTR_ERR(ctrl->mmio);
> +	}
> +	ctrl->instance = pdata->instance;
> +	ctrl->sdw_lock  = pdata->sdw_lock;
> +	ctrl->rows_index = sdw_find_row_index(50);
> +	ctrl->cols_index = sdw_find_col_index(10);
> +
> +	ctrl->dev = dev;
> +	dev_set_drvdata(&pdev->dev, ctrl);
> +
> +	ctrl->bus.ops = &amd_sdwc_ops;
> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
> +	ctrl->bus.clk_stop_timeout = 1;
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
> +		break;
> +	case ACP_SDW1:
> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	params = &ctrl->bus.params;
> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
> +	params->col = 10;
> +	params->row = 50;
> +
> +	prop = &ctrl->bus.prop;
> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
> +	ctrl->bus.link_id = ctrl->instance;
> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
> +	if (ret) {
> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",

master. the confusion continues.

> +			ret);
> +		return ret;
> +	}
> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
> +	schedule_work(&ctrl->probe_work);
> +	return 0;
> +}
> +
> +static int amd_sdwc_remove(struct platform_device *pdev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
> +	int ret;
> +
> +	amd_disable_sdw_interrupts(ctrl);
> +	sdw_bus_master_delete(&ctrl->bus);
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +
> +static struct platform_driver amd_sdwc_driver = {
> +	.probe	= &amd_sdwc_probe,
> +	.remove = &amd_sdwc_remove,
> +	.driver = {
> +		.name	= "amd_sdw_controller",
> +	}
> +};
> +module_platform_driver(amd_sdwc_driver);
> +
> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
> +MODULE_DESCRIPTION("AMD soundwire driver");
> +MODULE_LICENSE("GPL v2");

"GPL" is enough

> +enum amd_sdw_channel {
> +	/* SDW0 */
> +	ACP_SDW0_AUDIO_TX = 0,
> +	ACP_SDW0_BT_TX,
> +	ACP_SDW0_HS_TX,
> +	ACP_SDW0_AUDIO_RX,
> +	ACP_SDW0_BT_RX,
> +	ACP_SDW0_HS_RX,
> +	/* SDW1 */
> +	ACP_SDW1_BT_TX,
> +	ACP_SDW1_BT_RX,
> +};

you really need to comment on this. It looks like you've special-cased
manager ports for specific usages? This is perfectly fine in closed
applications, but it's not clear how it might work with headset,
amplifier and mic codec devices.


> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
> index f0123815af46..5ec39f8c2f2e 100644
> --- a/include/linux/soundwire/sdw_amd.h
> +++ b/include/linux/soundwire/sdw_amd.h
> @@ -10,9 +10,30 @@
>  
>  #define AMD_SDW_CLK_STOP_MODE		1
>  #define AMD_SDW_POWER_OFF_MODE		2
> +#define ACP_SDW0	0
> +#define ACP_SDW1	1
> +#define ACP_SDW0_MAX_DAI	6

is this related to the definition of amd_sdw_channel or the number of
ports available?
>  
>  struct acp_sdw_pdata {
>  	u16 instance;
>  	struct mutex *sdw_lock;
>  };
> +
> +struct amd_sdwc_ctrl {
> +	struct sdw_bus bus;
> +	struct device *dev;
> +	void __iomem *mmio;
> +	struct work_struct probe_work;
> +	struct mutex *sdw_lock;

comment please.

> +	int num_din_ports;
> +	int num_dout_ports;
> +	int cols_index;
> +	int rows_index;
> +	u32 instance;
> +	u32 quirks;
> +	u32 wake_en_mask;
> +	int num_ports;
> +	bool startup_done;

ah this was an Intel definition. Due to power dependencies we had to
split the probe and startup step. Does AMD have a need for this? Is the
SoundWire master IP dependent on DSP boot or something?

> +	u32 power_mode_mask;
> +};
>  #endif

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 14:58     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 14:58 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Register dai ops for two controller instances.

manager instances

> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 7e1f618254ac..93bffe6ff9e2 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
>  	.read_ping_status = amd_sdwc_read_ping_status,
>  };
>  
> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
> +			      struct snd_pcm_hw_params *params,
> +			      struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	struct sdw_stream_config sconfig;
> +	struct sdw_port_config *pconfig;
> +	int ch, dir;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
> +	dma->hw_params = params;
> +
> +	sconfig.direction = dir;
> +	sconfig.ch_count = ch;
> +	sconfig.frame_rate = params_rate(params);
> +	sconfig.type = dma->stream_type;
> +
> +	sconfig.bps = snd_pcm_format_width(params_format(params));
> +
> +	/* Port configuration */
> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
> +	if (!pconfig) {
> +		ret =  -ENOMEM;
> +		goto error;
> +	}
> +
> +	pconfig->num = dai->id;
> +	pconfig->ch_mask = (1 << ch) - 1;
> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
> +				    pconfig, 1, dma->stream);
> +	if (ret)
> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
> +
> +	kfree(pconfig);
> +error:
> +	return ret;
> +}

This looks inspired from intel.c, but you are not programming ANY
registers here. is this intentional?

> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		return -EIO;
> +
> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
> +			dma->stream->name, ret);
> +		return ret;
> +	}
> +	dma->hw_params = NULL;
> +	return 0;
> +}
> +
> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;

you want to avoid using dma_data and use your own runtime. We made that
change recently for cadence_runtime.c

> +
> +	if (stream) {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dma = dai->playback_dma_data;
> +		else
> +			dma = dai->capture_dma_data;
> +
> +		if (dma) {
> +			dev_err(dai->dev,
> +				"dma_data already allocated for dai %s\n",
> +				dai->name);
> +			return -EINVAL;
> +		}
> +
> +		/* allocate and set dma info */
> +		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
> +		if (!dma)
> +			return -ENOMEM;
> +		dma->stream_type = SDW_STREAM_PCM;
> +		dma->bus = &ctrl->bus;
> +		dma->link_id = ctrl->instance;
> +		dma->stream = stream;
> +
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dai->playback_dma_data = dma;
> +		else
> +			dai->capture_dma_data = dma;
> +	} else {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
> +			kfree(dai->playback_dma_data);
> +			dai->playback_dma_data = NULL;
> +		} else {
> +			kfree(dai->capture_dma_data);
> +			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
> +
> +	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +		dma = dai->playback_dma_data;
> +	else
> +		dma = dai->capture_dma_data;
> +
> +	if (!dma)
> +		return ERR_PTR(-EINVAL);
> +
> +	return dma->stream;
> +}
> +
> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
> +	.hw_params = amd_sdwc_hw_params,
> +	.hw_free = amd_sdwc_hw_free,
> +	.set_stream = amd_pcm_set_sdw_stream,

In the first patch there was support for PDM exposed, but here it's PDM
only?

> +	.get_stream = amd_get_sdw_stream,
> +};
> +
> +static const struct snd_soc_component_driver amd_sdwc_dai_component = {
> +	.name = "soundwire",
> +};
> +
> +static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
> +{
> +	struct snd_soc_dai_driver *dais;
> +	struct snd_soc_pcm_stream *stream;
> +	struct device *dev;
> +	int i, num_dais;
> +
> +	dev = ctrl->dev;
> +	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
> +	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
> +	if (!dais)
> +		return -ENOMEM;
> +	for (i = 0; i < num_dais; i++) {
> +		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
> +		if (!dais[i].name) {
> +			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");

remove, we don't add error logs on memory allocation issues.

> +			return -ENOMEM;
> +		}
> +
> +		if (i < ctrl->num_dout_ports)
> +			stream = &dais[i].playback;
> +		else
> +			stream = &dais[i].capture;
> +
> +		stream->channels_min = 2;
> +		stream->channels_max = 2;

Is this a port limitation or just a software definition?

> +		stream->rates = SNDRV_PCM_RATE_48000;
> +		stream->formats = SNDRV_PCM_FMTBIT_S16_LE;

Wondering if this is needed. I don't even recall why it's in the Intel
code, we tested with 32 bit data and 192kHz, that looks unnecessary to
me unless the hardware is really limited to those values.

> +
> +		dais[i].ops = &amd_sdwc_dai_ops;
> +		dais[i].id = i;
> +	}
> +
> +	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
> +					       dais, num_dais);
> +}
> +
>  static void amd_sdwc_probe_work(struct work_struct *work)
>  {
>  	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
> @@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>  			ret);
>  		return ret;
>  	}
> +	ret = amd_sdwc_register_dais(ctrl);
> +	if (ret) {
> +		dev_err(dev, "CPU DAI registration failed\n");
> +		sdw_bus_master_delete(&ctrl->bus);
> +		return ret;
> +	}
>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>  	schedule_work(&ctrl->probe_work);
>  	return 0;
> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
> index 5ec39f8c2f2e..7a99d782969f 100644
> --- a/include/linux/soundwire/sdw_amd.h
> +++ b/include/linux/soundwire/sdw_amd.h
> @@ -13,6 +13,7 @@
>  #define ACP_SDW0	0
>  #define ACP_SDW1	1
>  #define ACP_SDW0_MAX_DAI	6
> +#define AMD_SDW_MAX_DAIS	8

How does this work? 6 dais for the first master and 2 for the second?

>  
>  struct acp_sdw_pdata {
>  	u16 instance;
> @@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
>  	void __iomem *mmio;
>  	struct work_struct probe_work;
>  	struct mutex *sdw_lock;
> +	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];

well no, a stream runtime needs to be allocated per stream and usually
there's a 1:1 mapping between dailink and stream. A stream may use
multiple DAIs, possibly on different masters - just like a dailink can
rely on multiple cpu- and codec-dais.

You are conflating/confusing concepts I am afraid here.

>  	int num_din_ports;
>  	int num_dout_ports;
>  	int cols_index;
> @@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
>  	bool startup_done;
>  	u32 power_mode_mask;
>  };
> +
> +/**
> + * struct sdw_amd_dma_data: AMD DMA data
> + *
> + * @name: SoundWire stream name
> + * @stream: stream runtime
> + * @bus: Bus handle
> + * @stream_type: Stream type
> + * @link_id: Master link id
> + * @hw_params: hw_params to be applied in .prepare step
> + */
> +struct sdw_amd_dma_data {
> +	char *name;
> +	struct sdw_stream_runtime *stream;
> +	struct sdw_bus *bus;
> +	enum sdw_stream_type stream_type;
> +	int link_id;
> +	struct snd_pcm_hw_params *hw_params;
> +};
>  #endif

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
@ 2023-01-11 14:58     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 14:58 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Register dai ops for two controller instances.

manager instances

> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 7e1f618254ac..93bffe6ff9e2 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
>  	.read_ping_status = amd_sdwc_read_ping_status,
>  };
>  
> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
> +			      struct snd_pcm_hw_params *params,
> +			      struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	struct sdw_stream_config sconfig;
> +	struct sdw_port_config *pconfig;
> +	int ch, dir;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
> +	dma->hw_params = params;
> +
> +	sconfig.direction = dir;
> +	sconfig.ch_count = ch;
> +	sconfig.frame_rate = params_rate(params);
> +	sconfig.type = dma->stream_type;
> +
> +	sconfig.bps = snd_pcm_format_width(params_format(params));
> +
> +	/* Port configuration */
> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
> +	if (!pconfig) {
> +		ret =  -ENOMEM;
> +		goto error;
> +	}
> +
> +	pconfig->num = dai->id;
> +	pconfig->ch_mask = (1 << ch) - 1;
> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
> +				    pconfig, 1, dma->stream);
> +	if (ret)
> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
> +
> +	kfree(pconfig);
> +error:
> +	return ret;
> +}

This looks inspired from intel.c, but you are not programming ANY
registers here. is this intentional?

> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		return -EIO;
> +
> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
> +			dma->stream->name, ret);
> +		return ret;
> +	}
> +	dma->hw_params = NULL;
> +	return 0;
> +}
> +
> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;

you want to avoid using dma_data and use your own runtime. We made that
change recently for cadence_runtime.c

> +
> +	if (stream) {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dma = dai->playback_dma_data;
> +		else
> +			dma = dai->capture_dma_data;
> +
> +		if (dma) {
> +			dev_err(dai->dev,
> +				"dma_data already allocated for dai %s\n",
> +				dai->name);
> +			return -EINVAL;
> +		}
> +
> +		/* allocate and set dma info */
> +		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
> +		if (!dma)
> +			return -ENOMEM;
> +		dma->stream_type = SDW_STREAM_PCM;
> +		dma->bus = &ctrl->bus;
> +		dma->link_id = ctrl->instance;
> +		dma->stream = stream;
> +
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dai->playback_dma_data = dma;
> +		else
> +			dai->capture_dma_data = dma;
> +	} else {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
> +			kfree(dai->playback_dma_data);
> +			dai->playback_dma_data = NULL;
> +		} else {
> +			kfree(dai->capture_dma_data);
> +			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
> +
> +	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +		dma = dai->playback_dma_data;
> +	else
> +		dma = dai->capture_dma_data;
> +
> +	if (!dma)
> +		return ERR_PTR(-EINVAL);
> +
> +	return dma->stream;
> +}
> +
> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
> +	.hw_params = amd_sdwc_hw_params,
> +	.hw_free = amd_sdwc_hw_free,
> +	.set_stream = amd_pcm_set_sdw_stream,

In the first patch there was support for PDM exposed, but here it's PDM
only?

> +	.get_stream = amd_get_sdw_stream,
> +};
> +
> +static const struct snd_soc_component_driver amd_sdwc_dai_component = {
> +	.name = "soundwire",
> +};
> +
> +static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
> +{
> +	struct snd_soc_dai_driver *dais;
> +	struct snd_soc_pcm_stream *stream;
> +	struct device *dev;
> +	int i, num_dais;
> +
> +	dev = ctrl->dev;
> +	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
> +	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
> +	if (!dais)
> +		return -ENOMEM;
> +	for (i = 0; i < num_dais; i++) {
> +		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
> +		if (!dais[i].name) {
> +			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");

remove, we don't add error logs on memory allocation issues.

> +			return -ENOMEM;
> +		}
> +
> +		if (i < ctrl->num_dout_ports)
> +			stream = &dais[i].playback;
> +		else
> +			stream = &dais[i].capture;
> +
> +		stream->channels_min = 2;
> +		stream->channels_max = 2;

Is this a port limitation or just a software definition?

> +		stream->rates = SNDRV_PCM_RATE_48000;
> +		stream->formats = SNDRV_PCM_FMTBIT_S16_LE;

Wondering if this is needed. I don't even recall why it's in the Intel
code, we tested with 32 bit data and 192kHz, that looks unnecessary to
me unless the hardware is really limited to those values.

> +
> +		dais[i].ops = &amd_sdwc_dai_ops;
> +		dais[i].id = i;
> +	}
> +
> +	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
> +					       dais, num_dais);
> +}
> +
>  static void amd_sdwc_probe_work(struct work_struct *work)
>  {
>  	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
> @@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>  			ret);
>  		return ret;
>  	}
> +	ret = amd_sdwc_register_dais(ctrl);
> +	if (ret) {
> +		dev_err(dev, "CPU DAI registration failed\n");
> +		sdw_bus_master_delete(&ctrl->bus);
> +		return ret;
> +	}
>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>  	schedule_work(&ctrl->probe_work);
>  	return 0;
> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
> index 5ec39f8c2f2e..7a99d782969f 100644
> --- a/include/linux/soundwire/sdw_amd.h
> +++ b/include/linux/soundwire/sdw_amd.h
> @@ -13,6 +13,7 @@
>  #define ACP_SDW0	0
>  #define ACP_SDW1	1
>  #define ACP_SDW0_MAX_DAI	6
> +#define AMD_SDW_MAX_DAIS	8

How does this work? 6 dais for the first master and 2 for the second?

>  
>  struct acp_sdw_pdata {
>  	u16 instance;
> @@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
>  	void __iomem *mmio;
>  	struct work_struct probe_work;
>  	struct mutex *sdw_lock;
> +	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];

well no, a stream runtime needs to be allocated per stream and usually
there's a 1:1 mapping between dailink and stream. A stream may use
multiple DAIs, possibly on different masters - just like a dailink can
rely on multiple cpu- and codec-dais.

You are conflating/confusing concepts I am afraid here.

>  	int num_din_ports;
>  	int num_dout_ports;
>  	int cols_index;
> @@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
>  	bool startup_done;
>  	u32 power_mode_mask;
>  };
> +
> +/**
> + * struct sdw_amd_dma_data: AMD DMA data
> + *
> + * @name: SoundWire stream name
> + * @stream: stream runtime
> + * @bus: Bus handle
> + * @stream_type: Stream type
> + * @link_id: Master link id
> + * @hw_params: hw_params to be applied in .prepare step
> + */
> +struct sdw_amd_dma_data {
> +	char *name;
> +	struct sdw_stream_runtime *stream;
> +	struct sdw_bus *bus;
> +	enum sdw_stream_type stream_type;
> +	int link_id;
> +	struct snd_pcm_hw_params *hw_params;
> +};
>  #endif

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 14:59     ` Amadeusz Sławiński
  -1 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 14:59 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Pierre-Louis Bossart, open list, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> Register dai ops for two controller instances.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---

> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		return -EIO;
> +
> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
> +			dma->stream->name, ret);
> +		return ret;
> +	}
> +	dma->hw_params = NULL;
> +	return 0;
> +}
> +
> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +
> +	if (stream) {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dma = dai->playback_dma_data;
> +		else
> +			dma = dai->capture_dma_data;
> +

The patch itself looks ok, but I have generic ASoC API question. Could 
we perhaps change snd_soc_dai_get_dma_data() definition, so that instead 
of it being:
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai 
*dai, const struct snd_pcm_substream *ss)
it would be something like:
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai 
*dai, int direction)

it would require converting current calls from something like
dma = snd_soc_dai_get_dma_data(dai, substream);
to
dma = snd_soc_dai_get_dma_data(dai, substream->stream);
but would also allow for use in code like above?
It would become just:
dma = snd_soc_dai_get_dma_data(dai, direction);

The more I'm looking at the soc-dai.h header the more I like this idea, 
as other functions in the area seem to pass stream/direction explicitly 
instead of substream.

Mark, what do you think?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
@ 2023-01-11 14:59     ` Amadeusz Sławiński
  0 siblings, 0 replies; 170+ messages in thread
From: Amadeusz Sławiński @ 2023-01-11 14:59 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Pierre-Louis Bossart, Mario.Limonciello,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

On 1/11/2023 10:02 AM, Vijendar Mukunda wrote:
> Register dai ops for two controller instances.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---

> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +	int ret;
> +
> +	dma = snd_soc_dai_get_dma_data(dai, substream);
> +	if (!dma)
> +		return -EIO;
> +
> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
> +	if (ret < 0) {
> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
> +			dma->stream->name, ret);
> +		return ret;
> +	}
> +	dma->hw_params = NULL;
> +	return 0;
> +}
> +
> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
> +{
> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
> +	struct sdw_amd_dma_data *dma;
> +
> +	if (stream) {
> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
> +			dma = dai->playback_dma_data;
> +		else
> +			dma = dai->capture_dma_data;
> +

The patch itself looks ok, but I have generic ASoC API question. Could 
we perhaps change snd_soc_dai_get_dma_data() definition, so that instead 
of it being:
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai 
*dai, const struct snd_pcm_substream *ss)
it would be something like:
static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai 
*dai, int direction)

it would require converting current calls from something like
dma = snd_soc_dai_get_dma_data(dai, substream);
to
dma = snd_soc_dai_get_dma_data(dai, substream->stream);
but would also allow for use in code like above?
It would become just:
dma = snd_soc_dai_get_dma_data(dai, direction);

The more I'm looking at the soc-dai.h header the more I like this idea, 
as other functions in the area seem to pass stream/direction explicitly 
instead of substream.

Mark, what do you think?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:19     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:19 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list




>  
> +static void amd_sdwc_process_ping_status(u64 response, struct amd_sdwc_ctrl *ctrl)
> +{
> +	u64 slave_stat = 0;
> +	u32 val = 0;
> +	u16 dev_index;
> +
> +	/* slave status from ping response*/

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(ctrl->dev, "%s: slave_stat:0x%llx\n", __func__, 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(ctrl->dev, "%s val:0x%x\n", __func__, val);
> +		switch (val) {
> +		case SDW_SLAVE_ATTACHED:
> +			ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
> +			break;
> +		case SDW_SLAVE_UNATTACHED:
> +			ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
> +			break;
> +		case SDW_SLAVE_ALERT:
> +			ctrl->status[dev_index] = SDW_SLAVE_ALERT;
> +			break;
> +		default:
> +			ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
> +			break;
> +		}
> +	}
> +}
> +
> +static void amd_sdwc_read_and_process_ping_status(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u64 response = 0;
> +
> +	mutex_lock(&ctrl->bus.msg_lock);
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
> +	mutex_unlock(&ctrl->bus.msg_lock);
> +	amd_sdwc_process_ping_status(response, ctrl);

Is this saying that you actually need to send a PING frame manually
every time the manager needs to check the device status, including
interrupts?

> +}
> +
>  static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
>  {
>  	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> @@ -1132,6 +1173,119 @@ static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
>  					       dais, num_dais);
>  }
>  
> +static void amd_sdwc_update_slave_status_work(struct work_struct *work)
> +{
> +	struct amd_sdwc_ctrl *ctrl =
> +		container_of(work, struct amd_sdwc_ctrl, amd_sdw_work);
> +	u32 sw_status_change_mask_0to7_reg;
> +	u32 sw_status_change_mask_8to11_reg;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_status_change_mask_8to11_reg = SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		break;
> +	case ACP_SDW1:
> +		sw_status_change_mask_0to7_reg = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_status_change_mask_8to11_reg = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		break;
> +	default:
> +		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
> +		return;
> +	}
> +
> +	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
> +		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_0to7_reg);
> +		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_8to11_reg);
> +	}
> +
> +update_status:
> +	sdw_handle_slave_status(&ctrl->bus, ctrl->status);
> +	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
> +		acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_status_change_mask_0to7_reg);
> +		acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11,
> +			       ctrl->mmio + sw_status_change_mask_8to11_reg);
> +		amd_sdwc_read_and_process_ping_status(ctrl);
> +		goto update_status;
> +	}

well no, you have to use some sort of retry count. You cannot handle
interrupts in a loop like this, a faulty or chatty device would keep
signaling an issue and you would be stuck here for a while.

In addition, it's not clear if this is really needed. We added this loop
in cadence_master.c because of issues with multiple devices becoming
attached at the same time and how the hardware works. As it turns out,
this update_status loop seems to be a paranoid case, the actually cause
for devices de-attaching was found by Cirrus Logic and fixed in
"soundwire: cadence: fix updating slave status when a bus has multiple
peripherals"

You would need to explain how the status is detected and if any race
conditions can occur.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
@ 2023-01-11 15:19     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:19 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao




>  
> +static void amd_sdwc_process_ping_status(u64 response, struct amd_sdwc_ctrl *ctrl)
> +{
> +	u64 slave_stat = 0;
> +	u32 val = 0;
> +	u16 dev_index;
> +
> +	/* slave status from ping response*/

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(ctrl->dev, "%s: slave_stat:0x%llx\n", __func__, 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(ctrl->dev, "%s val:0x%x\n", __func__, val);
> +		switch (val) {
> +		case SDW_SLAVE_ATTACHED:
> +			ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
> +			break;
> +		case SDW_SLAVE_UNATTACHED:
> +			ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
> +			break;
> +		case SDW_SLAVE_ALERT:
> +			ctrl->status[dev_index] = SDW_SLAVE_ALERT;
> +			break;
> +		default:
> +			ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
> +			break;
> +		}
> +	}
> +}
> +
> +static void amd_sdwc_read_and_process_ping_status(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u64 response = 0;
> +
> +	mutex_lock(&ctrl->bus.msg_lock);
> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
> +	mutex_unlock(&ctrl->bus.msg_lock);
> +	amd_sdwc_process_ping_status(response, ctrl);

Is this saying that you actually need to send a PING frame manually
every time the manager needs to check the device status, including
interrupts?

> +}
> +
>  static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
>  {
>  	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
> @@ -1132,6 +1173,119 @@ static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
>  					       dais, num_dais);
>  }
>  
> +static void amd_sdwc_update_slave_status_work(struct work_struct *work)
> +{
> +	struct amd_sdwc_ctrl *ctrl =
> +		container_of(work, struct amd_sdwc_ctrl, amd_sdw_work);
> +	u32 sw_status_change_mask_0to7_reg;
> +	u32 sw_status_change_mask_8to11_reg;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		sw_status_change_mask_0to7_reg = SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_status_change_mask_8to11_reg = SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		break;
> +	case ACP_SDW1:
> +		sw_status_change_mask_0to7_reg = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
> +		sw_status_change_mask_8to11_reg = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
> +		break;
> +	default:
> +		dev_err(ctrl->dev, "Invalid Soundwire controller instance\n");
> +		return;
> +	}
> +
> +	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
> +		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_0to7_reg);
> +		acp_reg_writel(0, ctrl->mmio + sw_status_change_mask_8to11_reg);
> +	}
> +
> +update_status:
> +	sdw_handle_slave_status(&ctrl->bus, ctrl->status);
> +	if (ctrl->status[0] == SDW_SLAVE_ATTACHED) {
> +		acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_status_change_mask_0to7_reg);
> +		acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11,
> +			       ctrl->mmio + sw_status_change_mask_8to11_reg);
> +		amd_sdwc_read_and_process_ping_status(ctrl);
> +		goto update_status;
> +	}

well no, you have to use some sort of retry count. You cannot handle
interrupts in a loop like this, a faulty or chatty device would keep
signaling an issue and you would be stuck here for a while.

In addition, it's not clear if this is really needed. We added this loop
in cadence_master.c because of issues with multiple devices becoming
attached at the same time and how the hardware works. As it turns out,
this update_status loop seems to be a paranoid case, the actually cause
for devices de-attaching was found by Cirrus Logic and fixed in
"soundwire: cadence: fix updating slave status when a bus has multiple
peripherals"

You would need to explain how the status is detected and if any race
conditions can occur.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:22     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:22 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Soundwire DMA platform driver binds to the platform device created by
> ACP PCI device. Soundwire DMA driver registers ALSA DMA component
> with ASoC framework.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h      |  5 +++
>  sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
>  2 files changed, 77 insertions(+)
>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index 0bd9dc363461..b462320fdf2a 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -135,3 +135,8 @@ struct acp63_dev_data {
>  	bool is_sdw_dev;
>  	bool acp_sdw_power_off;
>  };
> +
> +struct sdw_dma_dev_data {
> +	void __iomem *acp_base;
> +	struct mutex *acp_lock;
> +};
> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
> new file mode 100644
> index 000000000000..388a4b7df715
> --- /dev/null
> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
> + *
> + * Copyright 2023 Advanced Micro Devices, Inc.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dai.h>
> +#include "acp63.h"
> +
> +#define DRV_NAME "amd_ps_sdw_dma"
> +
> +static const struct snd_soc_component_driver acp63_sdw_component = {
> +	.name		= DRV_NAME,
> +};
> +
> +static int acp63_sdw_platform_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct sdw_dma_dev_data *sdw_data;
> +	int status;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENODEV;
> +	}
> +
> +	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
> +	if (!sdw_data)
> +		return -ENOMEM;
> +
> +	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!sdw_data->acp_base)
> +		return -ENOMEM;
> +
> +	sdw_data->acp_lock = pdev->dev.platform_data;
> +	dev_set_drvdata(&pdev->dev, sdw_data);
> +	status = devm_snd_soc_register_component(&pdev->dev,
> +						 &acp63_sdw_component,
> +						 NULL, 0);
> +	if (status) {
> +		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");

not sure what this means? Are you registering a PDM component or a DMA one?

> +
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +static struct platform_driver acp63_sdw_dma_driver = {
> +	.probe = acp63_sdw_platform_probe,
> +	.driver = {
> +		.name = "amd_ps_sdw_dma",
> +	},
> +};
> +
> +module_platform_driver(acp63_sdw_dma_driver);
> +
> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
> +MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRV_NAME);

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
@ 2023-01-11 15:22     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:22 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Soundwire DMA platform driver binds to the platform device created by
> ACP PCI device. Soundwire DMA driver registers ALSA DMA component
> with ASoC framework.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h      |  5 +++
>  sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
>  2 files changed, 77 insertions(+)
>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index 0bd9dc363461..b462320fdf2a 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -135,3 +135,8 @@ struct acp63_dev_data {
>  	bool is_sdw_dev;
>  	bool acp_sdw_power_off;
>  };
> +
> +struct sdw_dma_dev_data {
> +	void __iomem *acp_base;
> +	struct mutex *acp_lock;
> +};
> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
> new file mode 100644
> index 000000000000..388a4b7df715
> --- /dev/null
> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
> + *
> + * Copyright 2023 Advanced Micro Devices, Inc.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +#include <sound/soc-dai.h>
> +#include "acp63.h"
> +
> +#define DRV_NAME "amd_ps_sdw_dma"
> +
> +static const struct snd_soc_component_driver acp63_sdw_component = {
> +	.name		= DRV_NAME,
> +};
> +
> +static int acp63_sdw_platform_probe(struct platform_device *pdev)
> +{
> +	struct resource *res;
> +	struct sdw_dma_dev_data *sdw_data;
> +	int status;
> +
> +	if (!pdev->dev.platform_data) {
> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
> +		return -ENODEV;
> +	}
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
> +		return -ENODEV;
> +	}
> +
> +	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
> +	if (!sdw_data)
> +		return -ENOMEM;
> +
> +	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
> +	if (!sdw_data->acp_base)
> +		return -ENOMEM;
> +
> +	sdw_data->acp_lock = pdev->dev.platform_data;
> +	dev_set_drvdata(&pdev->dev, sdw_data);
> +	status = devm_snd_soc_register_component(&pdev->dev,
> +						 &acp63_sdw_component,
> +						 NULL, 0);
> +	if (status) {
> +		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");

not sure what this means? Are you registering a PDM component or a DMA one?

> +
> +		return -ENODEV;
> +	}
> +	return 0;
> +}
> +
> +static struct platform_driver acp63_sdw_dma_driver = {
> +	.probe = acp63_sdw_platform_probe,
> +	.driver = {
> +		.name = "amd_ps_sdw_dma",
> +	},
> +};
> +
> +module_platform_driver(acp63_sdw_dma_driver);
> +
> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
> +MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRV_NAME);

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:34     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:34 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add Soundwire DMA driver dma ops for Pink Sardine platform.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h      |  61 ++++
>  sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
>  2 files changed, 592 insertions(+)
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index b462320fdf2a..8963cfb6120d 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -67,6 +67,38 @@
>  #define ACP_SDW0_IRQ_MASK	21
>  #define ACP_SDW1_IRQ_MASK	2
>  #define ACP_ERROR_IRQ_MASK      29
> +#define ACP_AUDIO_TX_THRESHOLD	28
> +#define ACP_BT_TX_THRESHOLD	26
> +#define ACP_HS_TX_THRESHOLD	24
> +#define ACP_AUDIO_RX_THRESHOLD	27
> +#define ACP_BT_RX_THRESHOLD	25
> +#define ACP_HS_RX_THRESHOLD	23
> +#define ACP_P1_BT_TX_THRESHOLD	6
> +#define ACP_P1_BT_RX_THRESHOLD	5
> +#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
> +#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
> +#define ACP63_SDW_MAX_STREAMS		8
> +
> +#define ACP_DELAY_US		5
> +#define SDW_MEM_WINDOW_START	0x4800000
> +#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
> +#define SDW_PTE_OFFSET		0x400
> +#define SDW_FIFO_SIZE		0x100
> +#define SDW_DMA_SIZE		0x40
> +#define ACP_SDW_FIFO_OFFSET	0x100
> +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
> +
> +#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
> +#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
> +#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
> +#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
> +#define SDW_CAPTURE_MIN_NUM_PERIODS     2
> +#define SDW_CAPTURE_MAX_NUM_PERIODS     8
> +#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
> +#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
> +
> +#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
> +#define SDW_MIN_BUFFER SDW_MAX_BUFFER
>  
>  enum acp_config {
>  	ACP_CONFIG_0 = 0,
> @@ -93,6 +125,17 @@ enum acp_pdev_mask {
>  	ACP63_SDW_PDM_DEV_MASK,
>  };
>  
> +enum channel_type {
> +	ACP_SDW_AUDIO_TX = 0,
> +	ACP_SDW_BT_TX,
> +	ACP_SDW_HS_TX,
> +	ACP_SDW_AUDIO_RX,
> +	ACP_SDW_BT_RX,
> +	ACP_SDW_HS_RX,
> +	ACP_SDW1_BT_TX,
> +	ACP_SDW1_BT_RX,
> +};

this was defined in another patch already?

> +
>  struct pdm_stream_instance {
>  	u16 num_pages;
>  	u16 channels;
> @@ -139,4 +182,22 @@ struct acp63_dev_data {
>  struct sdw_dma_dev_data {
>  	void __iomem *acp_base;
>  	struct mutex *acp_lock;
> +	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
> +};
> +
> +struct sdw_stream_instance {

sdw_stream is already a well-defined concept. Please use sdw_dma_stream
or something less confusing naming-wise.

> +	u16 num_pages;
> +	u16 channels;
> +	u32 stream_id;
> +	dma_addr_t dma_addr;
> +	u64 bytescount;
> +	void __iomem *acp_base;
> +};
> +
> +union acp_sdw_dma_count {
> +	struct {
> +	u32 low;
> +	u32 high;
> +	} bcount;
> +	u64 bytescount;
>  };
> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
> index 388a4b7df715..e94f76053c66 100644
> --- a/sound/soc/amd/ps/ps-sdw-dma.c
> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
> @@ -12,12 +12,543 @@
>  #include <sound/pcm_params.h>
>  #include <sound/soc.h>
>  #include <sound/soc-dai.h>
> +#include <linux/soundwire/sdw_amd.h>
>  #include "acp63.h"
>  
>  #define DRV_NAME "amd_ps_sdw_dma"
>  
> +static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,

is this really limited to 2ch 48kHz? This doesn't align with the
references to Bluetooth above?

> +	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
> +	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
> +	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
> +	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
> +	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
> +};
> +
> +static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID |
> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,

same here?

> +	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
> +	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
> +	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
> +	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
> +	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
> +};
> +
> +static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
> +{
> +	u16 page_idx;
> +	u32 low, high, val;
> +	dma_addr_t addr;
> +
> +	addr = sdw_ins->dma_addr;
> +	val = SDW_PTE_OFFSET + (stream_id * 256);
> +
> +	/* Group Enable */
> +	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
> +		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
> +	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
> +		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
> +	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
> +		/* Load the low address of page int ACP SRAM through SRBM */
> +		low = lower_32_bits(addr);
> +		high = upper_32_bits(addr);
> +
> +		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
> +		high |= BIT(31);
> +		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
> +		val += 8;
> +		addr += PAGE_SIZE;
> +	}
> +
> +	/*cache Invalidation added for Testing */

/* cache

> +	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
> +}

> +static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
> +				   struct snd_pcm_substream *substream,
> +				   struct snd_pcm_hw_params *params)
> +{
> +	struct sdw_stream_instance *sdw_stream_data;
> +	struct sdw_dma_dev_data *sdw_data;
> +	u32 period_bytes;
> +	u32 water_mark_size_reg;
> +	u32 irq_mask, ext_intr_ctrl;
> +	u64 size;
> +	u32 stream_id;
> +	u32 acp_ext_intr_cntl_reg;
> +	int ret;
> +
> +	stream_id = 0;

useless initialization...

> +	sdw_data = dev_get_drvdata(component->dev);
> +	sdw_stream_data = substream->runtime->private_data;
> +	if (!sdw_stream_data)
> +		return -EINVAL;
> +	stream_id = sdw_stream_data->stream_id;

... overriden here

> +	sdw_data->sdw_stream[stream_id] = substream;
> +	size = params_buffer_bytes(params);
> +	period_bytes = params_period_bytes(params);
> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
> +	acp63_config_dma(sdw_stream_data, stream_id);
> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
> +	if (ret) {
> +		dev_err(component->dev, "Invalid channel type\n");
> +		return -EINVAL;
> +	}
> +	switch (stream_id) {
> +	case ACP_SDW_AUDIO_TX:
> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;

so there's ONE resource to deal with external codecs? How does this work
if you have a headset codec and an amplifier?

> +	case ACP_SDW_BT_TX:
> +		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_HS_TX:
> +		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
> +		break;
> +	case ACP_SDW_AUDIO_RX:
> +		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_BT_RX:
> +		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_HS_RX:
> +		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
> +		break;
> +	default:
> +		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
> +	ext_intr_ctrl |= irq_mask;
> +	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
> +	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-11 15:34     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:34 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add Soundwire DMA driver dma ops for Pink Sardine platform.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h      |  61 ++++
>  sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
>  2 files changed, 592 insertions(+)
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index b462320fdf2a..8963cfb6120d 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -67,6 +67,38 @@
>  #define ACP_SDW0_IRQ_MASK	21
>  #define ACP_SDW1_IRQ_MASK	2
>  #define ACP_ERROR_IRQ_MASK      29
> +#define ACP_AUDIO_TX_THRESHOLD	28
> +#define ACP_BT_TX_THRESHOLD	26
> +#define ACP_HS_TX_THRESHOLD	24
> +#define ACP_AUDIO_RX_THRESHOLD	27
> +#define ACP_BT_RX_THRESHOLD	25
> +#define ACP_HS_RX_THRESHOLD	23
> +#define ACP_P1_BT_TX_THRESHOLD	6
> +#define ACP_P1_BT_RX_THRESHOLD	5
> +#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
> +#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
> +#define ACP63_SDW_MAX_STREAMS		8
> +
> +#define ACP_DELAY_US		5
> +#define SDW_MEM_WINDOW_START	0x4800000
> +#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
> +#define SDW_PTE_OFFSET		0x400
> +#define SDW_FIFO_SIZE		0x100
> +#define SDW_DMA_SIZE		0x40
> +#define ACP_SDW_FIFO_OFFSET	0x100
> +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
> +
> +#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
> +#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
> +#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
> +#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
> +#define SDW_CAPTURE_MIN_NUM_PERIODS     2
> +#define SDW_CAPTURE_MAX_NUM_PERIODS     8
> +#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
> +#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
> +
> +#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
> +#define SDW_MIN_BUFFER SDW_MAX_BUFFER
>  
>  enum acp_config {
>  	ACP_CONFIG_0 = 0,
> @@ -93,6 +125,17 @@ enum acp_pdev_mask {
>  	ACP63_SDW_PDM_DEV_MASK,
>  };
>  
> +enum channel_type {
> +	ACP_SDW_AUDIO_TX = 0,
> +	ACP_SDW_BT_TX,
> +	ACP_SDW_HS_TX,
> +	ACP_SDW_AUDIO_RX,
> +	ACP_SDW_BT_RX,
> +	ACP_SDW_HS_RX,
> +	ACP_SDW1_BT_TX,
> +	ACP_SDW1_BT_RX,
> +};

this was defined in another patch already?

> +
>  struct pdm_stream_instance {
>  	u16 num_pages;
>  	u16 channels;
> @@ -139,4 +182,22 @@ struct acp63_dev_data {
>  struct sdw_dma_dev_data {
>  	void __iomem *acp_base;
>  	struct mutex *acp_lock;
> +	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
> +};
> +
> +struct sdw_stream_instance {

sdw_stream is already a well-defined concept. Please use sdw_dma_stream
or something less confusing naming-wise.

> +	u16 num_pages;
> +	u16 channels;
> +	u32 stream_id;
> +	dma_addr_t dma_addr;
> +	u64 bytescount;
> +	void __iomem *acp_base;
> +};
> +
> +union acp_sdw_dma_count {
> +	struct {
> +	u32 low;
> +	u32 high;
> +	} bcount;
> +	u64 bytescount;
>  };
> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
> index 388a4b7df715..e94f76053c66 100644
> --- a/sound/soc/amd/ps/ps-sdw-dma.c
> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
> @@ -12,12 +12,543 @@
>  #include <sound/pcm_params.h>
>  #include <sound/soc.h>
>  #include <sound/soc-dai.h>
> +#include <linux/soundwire/sdw_amd.h>
>  #include "acp63.h"
>  
>  #define DRV_NAME "amd_ps_sdw_dma"
>  
> +static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,

is this really limited to 2ch 48kHz? This doesn't align with the
references to Bluetooth above?

> +	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
> +	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
> +	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
> +	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
> +	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
> +};
> +
> +static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
> +		SNDRV_PCM_INFO_MMAP |
> +		SNDRV_PCM_INFO_MMAP_VALID |
> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
> +	.channels_min = 2,
> +	.channels_max = 2,
> +	.rates = SNDRV_PCM_RATE_48000,
> +	.rate_min = 48000,
> +	.rate_max = 48000,

same here?

> +	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
> +	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
> +	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
> +	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
> +	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
> +};
> +
> +static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
> +{
> +	u16 page_idx;
> +	u32 low, high, val;
> +	dma_addr_t addr;
> +
> +	addr = sdw_ins->dma_addr;
> +	val = SDW_PTE_OFFSET + (stream_id * 256);
> +
> +	/* Group Enable */
> +	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
> +		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
> +	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
> +		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
> +	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
> +		/* Load the low address of page int ACP SRAM through SRBM */
> +		low = lower_32_bits(addr);
> +		high = upper_32_bits(addr);
> +
> +		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
> +		high |= BIT(31);
> +		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
> +		val += 8;
> +		addr += PAGE_SIZE;
> +	}
> +
> +	/*cache Invalidation added for Testing */

/* cache

> +	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
> +}

> +static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
> +				   struct snd_pcm_substream *substream,
> +				   struct snd_pcm_hw_params *params)
> +{
> +	struct sdw_stream_instance *sdw_stream_data;
> +	struct sdw_dma_dev_data *sdw_data;
> +	u32 period_bytes;
> +	u32 water_mark_size_reg;
> +	u32 irq_mask, ext_intr_ctrl;
> +	u64 size;
> +	u32 stream_id;
> +	u32 acp_ext_intr_cntl_reg;
> +	int ret;
> +
> +	stream_id = 0;

useless initialization...

> +	sdw_data = dev_get_drvdata(component->dev);
> +	sdw_stream_data = substream->runtime->private_data;
> +	if (!sdw_stream_data)
> +		return -EINVAL;
> +	stream_id = sdw_stream_data->stream_id;

... overriden here

> +	sdw_data->sdw_stream[stream_id] = substream;
> +	size = params_buffer_bytes(params);
> +	period_bytes = params_period_bytes(params);
> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
> +	acp63_config_dma(sdw_stream_data, stream_id);
> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
> +	if (ret) {
> +		dev_err(component->dev, "Invalid channel type\n");
> +		return -EINVAL;
> +	}
> +	switch (stream_id) {
> +	case ACP_SDW_AUDIO_TX:
> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;

so there's ONE resource to deal with external codecs? How does this work
if you have a headset codec and an amplifier?

> +	case ACP_SDW_BT_TX:
> +		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_HS_TX:
> +		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW1_BT_TX:
> +		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
> +		break;
> +	case ACP_SDW_AUDIO_RX:
> +		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_BT_RX:
> +		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW_HS_RX:
> +		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
> +		break;
> +	case ACP_SDW1_BT_RX:
> +		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
> +		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
> +		break;
> +	default:
> +		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
> +		return -EINVAL;
> +	}
> +
> +	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
> +	ext_intr_ctrl |= irq_mask;
> +	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
> +	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:38     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:38 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Liam Girdwood,
	Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list




> @@ -167,9 +167,11 @@ struct acp63_dev_data {
>  	struct platform_device *pdev[ACP63_DEVS];
>  	struct mutex acp_lock; /* protect shared registers */
>  	struct fwnode_handle *sdw_fw_node;
> +	struct work_struct acp_sdw_dma_work;
>  	u16 pdev_mask;
>  	u16 pdev_count;
>  	u16 pdm_dev_index;
> +	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];

streams and DMAs are different things in SoundWire. You can have a 1:N
mapping.

>  	u8 sdw_master_count;
>  	u16 sdw0_dev_index;
>  	u16 sdw1_dev_index;
> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index 0fbe5e27f3fb..5b82ee8e3ad8 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
>  	return 0;
>  }
>  
> +static void acp63_sdw_dma_workthread(struct work_struct *work)
> +{
> +	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
> +						    acp_sdw_dma_work);
> +	struct sdw_dma_dev_data *sdw_dma_data;
> +	u32 stream_index;
> +	u16 pdev_index;
> +
> +	pdev_index = adata->sdw_dma_dev_index;
> +	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
> +
> +	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
> +		if (adata->dma_intr_stat[stream_index]) {
> +			if (sdw_dma_data->sdw_stream[stream_index])
> +				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);

is there a reason why you do this in a work thread?

IIRC we did this in SOF because of an issue where during an xrun a stop
IPC would be sent while we were dealing with an IPC.

I don't quite see why it's needed for a DMA?

What am I missing?

> +			adata->dma_intr_stat[stream_index] = 0;
> +		}
> +	}
> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
@ 2023-01-11 15:38     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:38 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Syed Saba Kareem




> @@ -167,9 +167,11 @@ struct acp63_dev_data {
>  	struct platform_device *pdev[ACP63_DEVS];
>  	struct mutex acp_lock; /* protect shared registers */
>  	struct fwnode_handle *sdw_fw_node;
> +	struct work_struct acp_sdw_dma_work;
>  	u16 pdev_mask;
>  	u16 pdev_count;
>  	u16 pdm_dev_index;
> +	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];

streams and DMAs are different things in SoundWire. You can have a 1:N
mapping.

>  	u8 sdw_master_count;
>  	u16 sdw0_dev_index;
>  	u16 sdw1_dev_index;
> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
> index 0fbe5e27f3fb..5b82ee8e3ad8 100644
> --- a/sound/soc/amd/ps/pci-ps.c
> +++ b/sound/soc/amd/ps/pci-ps.c
> @@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
>  	return 0;
>  }
>  
> +static void acp63_sdw_dma_workthread(struct work_struct *work)
> +{
> +	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
> +						    acp_sdw_dma_work);
> +	struct sdw_dma_dev_data *sdw_dma_data;
> +	u32 stream_index;
> +	u16 pdev_index;
> +
> +	pdev_index = adata->sdw_dma_dev_index;
> +	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
> +
> +	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
> +		if (adata->dma_intr_stat[stream_index]) {
> +			if (sdw_dma_data->sdw_stream[stream_index])
> +				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);

is there a reason why you do this in a work thread?

IIRC we did this in SOF because of an issue where during an xrun a stop
IPC would be sent while we were dealing with an IPC.

I don't quite see why it's needed for a DMA?

What am I missing?

> +			adata->dma_intr_stat[stream_index] = 0;
> +		}
> +	}
> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:47     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:47 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add support for runtime pm ops for AMD master driver.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
>  drivers/soundwire/amd_master.h    |   3 +
>  include/linux/soundwire/sdw_amd.h |   1 +
>  3 files changed, 209 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index c7063b8bdd7b..d2d7f07de202 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -15,6 +15,7 @@
>  #include <linux/soundwire/sdw.h>
>  #include <linux/soundwire/sdw_registers.h>
>  #include <linux/soundwire/sdw_amd.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/wait.h>
>  #include <sound/pcm_params.h>
>  #include <sound/soc.h>
> @@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>  	return 0;
>  }
>  
> +static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	int ret;
> +
> +	ret = amd_disable_sdw_interrupts(ctrl);
> +	if (ret)
> +		return ret;
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +
>  static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
>  {
>  	u32 sdw_rows, sdw_cols, frame_size;
> @@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>  	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>  	schedule_work(&ctrl->probe_work);
> +	/* Enable runtime PM */
> +	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
>  	return 0;
>  }
>  
> @@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
>  	amd_disable_sdw_interrupts(ctrl);
>  	sdw_bus_master_delete(&ctrl->bus);
>  	ret = amd_disable_sdw_controller(ctrl);
> +	pm_runtime_disable(&pdev->dev);
>  	return ret;
>  }
>  
> +static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 clk_resume_ctrl_reg;
> +	u32 wake_en_reg;
> +	u32 val;
> +	u32 retry_count = 0;
> +	int ret;
> +
> +	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
> +	if (ret < 0 && ret != -ENODATA) {
> +		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
> +		return ret;
> +	}
> +	ret = sdw_bus_clk_stop(&ctrl->bus);
> +	if (ret < 0 && ret != -ENODATA) {
> +		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
> +		return ret;

You need to be very careful here, because returning an error may prevent
the device from suspending.

If it's safe and possible to recover during the resume step, you
probably want to log the error but let the suspend continue.

> +	}
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		wake_en_reg = ACP_SW_WAKE_EN;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		wake_en_reg = ACP_SW1_WAKE_EN;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

why not store these offsets during the probe and use them directly here?
You know at probe time which master you're using.

> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		if (val & AMD_SDW_CLK_STOP_DONE) {
> +			ctrl->clk_stopped = true;
> +			break;
> +		}
> +	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +
> +	if (!ctrl->clk_stopped) {
> +		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (ctrl->wake_en_mask)
> +		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
> +
> +	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
> +	return 0;
> +}
> +
> +static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
> +{
> +	int ret;
> +	u32 clk_resume_ctrl_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ctrl->clk_stopped) {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		val |= AMD_SDW_CLK_RESUME_REQ;
> +		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
> +		do {
> +			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +			if (val & AMD_SDW_CLK_RESUME_DONE)
> +				break;
> +			usleep_range(10, 100);
> +		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +		if (val & AMD_SDW_CLK_RESUME_DONE) {
> +			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
> +			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
> +			if (ret < 0)
> +				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
> +			ctrl->clk_stopped = false;
> +		}
> +	}
> +	if (ctrl->clk_stopped) {
> +		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +
> +	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {

do you have a case where the startup is not done? This was an
Intel-specific thing.

> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +		ret = amd_deinit_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int __maybe_unused amd_resume_runtime(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +	u32 clk_resume_ctrl_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {

same here

> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

select registers in the probe.

> +
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop_exit(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		if (val) {
> +			val |= AMD_SDW_CLK_RESUME_REQ;
> +			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
> +			do {
> +				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +				if (val & AMD_SDW_CLK_RESUME_DONE)
> +					break;
> +				usleep_range(10, 100);
> +			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +			if (val & AMD_SDW_CLK_RESUME_DONE) {
> +				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
> +				ctrl->clk_stopped = false;
> +			}
> +		}
> +		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
> +		amd_init_sdw_controller(ctrl);
> +		amd_enable_sdw_interrupts(ctrl);
> +		ret = amd_enable_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);

this should be defined at probe time, using magic numbers like this will
not work in all cases and totally depends on the frame rate and
bandwidth needs.

> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
@ 2023-01-11 15:47     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:47 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add support for runtime pm ops for AMD master driver.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
>  drivers/soundwire/amd_master.h    |   3 +
>  include/linux/soundwire/sdw_amd.h |   1 +
>  3 files changed, 209 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index c7063b8bdd7b..d2d7f07de202 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -15,6 +15,7 @@
>  #include <linux/soundwire/sdw.h>
>  #include <linux/soundwire/sdw_registers.h>
>  #include <linux/soundwire/sdw_amd.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/wait.h>
>  #include <sound/pcm_params.h>
>  #include <sound/soc.h>
> @@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>  	return 0;
>  }
>  
> +static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
> +{
> +	int ret;
> +
> +	ret = amd_disable_sdw_interrupts(ctrl);
> +	if (ret)
> +		return ret;
> +	ret = amd_disable_sdw_controller(ctrl);
> +	return ret;
> +}
> +
>  static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
>  {
>  	u32 sdw_rows, sdw_cols, frame_size;
> @@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>  	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>  	schedule_work(&ctrl->probe_work);
> +	/* Enable runtime PM */
> +	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
> +	pm_runtime_use_autosuspend(dev);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
>  	return 0;
>  }
>  
> @@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
>  	amd_disable_sdw_interrupts(ctrl);
>  	sdw_bus_master_delete(&ctrl->bus);
>  	ret = amd_disable_sdw_controller(ctrl);
> +	pm_runtime_disable(&pdev->dev);
>  	return ret;
>  }
>  
> +static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
> +{
> +	u32 clk_resume_ctrl_reg;
> +	u32 wake_en_reg;
> +	u32 val;
> +	u32 retry_count = 0;
> +	int ret;
> +
> +	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
> +	if (ret < 0 && ret != -ENODATA) {
> +		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
> +		return ret;
> +	}
> +	ret = sdw_bus_clk_stop(&ctrl->bus);
> +	if (ret < 0 && ret != -ENODATA) {
> +		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
> +		return ret;

You need to be very careful here, because returning an error may prevent
the device from suspending.

If it's safe and possible to recover during the resume step, you
probably want to log the error but let the suspend continue.

> +	}
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		wake_en_reg = ACP_SW_WAKE_EN;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		wake_en_reg = ACP_SW1_WAKE_EN;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

why not store these offsets during the probe and use them directly here?
You know at probe time which master you're using.

> +
> +	do {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		if (val & AMD_SDW_CLK_STOP_DONE) {
> +			ctrl->clk_stopped = true;
> +			break;
> +		}
> +	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +
> +	if (!ctrl->clk_stopped) {
> +		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +
> +	if (ctrl->wake_en_mask)
> +		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
> +
> +	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
> +	return 0;
> +}
> +
> +static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
> +{
> +	int ret;
> +	u32 clk_resume_ctrl_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	if (ctrl->clk_stopped) {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		val |= AMD_SDW_CLK_RESUME_REQ;
> +		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
> +		do {
> +			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +			if (val & AMD_SDW_CLK_RESUME_DONE)
> +				break;
> +			usleep_range(10, 100);
> +		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +		if (val & AMD_SDW_CLK_RESUME_DONE) {
> +			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
> +			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
> +			if (ret < 0)
> +				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
> +			ctrl->clk_stopped = false;
> +		}
> +	}
> +	if (ctrl->clk_stopped) {
> +		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
> +		return -ETIMEDOUT;
> +	}
> +
> +	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {

do you have a case where the startup is not done? This was an
Intel-specific thing.

> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +		ret = amd_deinit_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
> +static int __maybe_unused amd_resume_runtime(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +	u32 clk_resume_ctrl_reg;
> +	u32 val = 0;
> +	u32 retry_count = 0;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {

same here

> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +
> +	switch (ctrl->instance) {
> +	case ACP_SDW0:
> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
> +		break;
> +	case ACP_SDW1:
> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

select registers in the probe.

> +
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop_exit(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +		if (val) {
> +			val |= AMD_SDW_CLK_RESUME_REQ;
> +			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
> +			do {
> +				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
> +				if (val & AMD_SDW_CLK_RESUME_DONE)
> +					break;
> +				usleep_range(10, 100);
> +			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
> +			if (val & AMD_SDW_CLK_RESUME_DONE) {
> +				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
> +				ctrl->clk_stopped = false;
> +			}
> +		}
> +		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
> +		amd_init_sdw_controller(ctrl);
> +		amd_enable_sdw_interrupts(ctrl);
> +		ret = amd_enable_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);

this should be defined at probe time, using magic numbers like this will
not work in all cases and totally depends on the frame rate and
bandwidth needs.

> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:49     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:49 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add start up and shutdown dai ops for AMD Master driver.

I don't think those startup and shutdown ops are necessary, we removed
them for Intel, see "soundwire: intel: remove DAI startup/shutdown"


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
@ 2023-01-11 15:49     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:49 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add start up and shutdown dai ops for AMD Master driver.

I don't think those startup and shutdown ops are necessary, we removed
them for Intel, see "soundwire: intel: remove DAI startup/shutdown"


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 16/19] soundwire: amd: handle wake enable interrupt
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:54     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:54 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add wake enable interrupt support for both the soundwire controller

SoundWire.

> instances.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 9 +++++++++
>  drivers/soundwire/amd_master.h    | 1 +
>  include/linux/soundwire/sdw_amd.h | 1 +
>  3 files changed, 11 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 290c59ab7760..2fd77a673c22 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
>  	u32 sw_status_change_mask_0to7_reg;
>  	u32 sw_status_change_mask_8to11_reg;
>  
> +	if (ctrl->wake_event) {
> +		pm_runtime_resume(ctrl->dev);
> +		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
> +		ctrl->wake_event = false;
> +		return;
> +	}

this is surprising.

A wake event typically happens when the bus is in clock-stop mode.
You cannot deal with wake events while dealing with the peripheral
status update, because you can only get that status when the manager is
up-and-running. There's a conceptual miss here, no?

If the wake comes from the PCI side, then it's the same comment: why
would the wake be handled while updating the peripheral status.

What am I missing?


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 16/19] soundwire: amd: handle wake enable interrupt
@ 2023-01-11 15:54     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:54 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add wake enable interrupt support for both the soundwire controller

SoundWire.

> instances.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c    | 9 +++++++++
>  drivers/soundwire/amd_master.h    | 1 +
>  include/linux/soundwire/sdw_amd.h | 1 +
>  3 files changed, 11 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 290c59ab7760..2fd77a673c22 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
>  	u32 sw_status_change_mask_0to7_reg;
>  	u32 sw_status_change_mask_8to11_reg;
>  
> +	if (ctrl->wake_event) {
> +		pm_runtime_resume(ctrl->dev);
> +		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
> +		ctrl->wake_event = false;
> +		return;
> +	}

this is surprising.

A wake event typically happens when the bus is in clock-stop mode.
You cannot deal with wake events while dealing with the peripheral
status update, because you can only get that status when the manager is
up-and-running. There's a conceptual miss here, no?

If the wake comes from the PCI side, then it's the same comment: why
would the wake be handled while updating the peripheral status.

What am I missing?


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 15:58     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:58 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add pm_prepare callback and System level pm ops support for
> AMD master driver.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 2fd77a673c22..f4478cc17aac 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>  	return 0;
>  }
>  
> +static int amd_resume_child_device(struct device *dev, void *data)
> +{
> +	int ret;
> +	struct sdw_slave *slave = dev_to_sdw_dev(dev);
> +
> +	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;
> +	}
> +
> +	if (!pm_runtime_suspended(dev))
> +		return 0;
> +	ret = pm_request_resume(dev);
> +	if (ret < 0)
> +		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
> +
> +	return ret;
> +}
> +
> +static int __maybe_unused amd_pm_prepare(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
> +	if (ret < 0)
> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = pm_request_resume(dev);
> +		if (ret < 0) {
> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
> +			return 0;
> +		}
> +	}
> +	return 0;
> +}

This seems to be inspired by the Intel code, but is this necessary here?

For Intel, we saw cases where we had to pm_resume before doing a system
suspend, otherwise the hardware was in a bad state.

Do you actually need to do so, or is is possible to do a system suspend
when the clock is stopped.

And in the case where the bus is in 'power-off' mode, do you actually
need to resume at all?

> +
> +static int __maybe_unused amd_suspend(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);

do you actually need to stop the clock before powering-off? This seems
counter intuitive and not so useful?

> +		if (ret)
> +			return ret;
> +		ret = amd_deinit_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
>  static int __maybe_unused amd_suspend_runtime(struct device *dev)
>  {
>  	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> @@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
>  }
>  
>  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)
>  };
>  

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
@ 2023-01-11 15:58     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 15:58 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/11/23 03:02, Vijendar Mukunda wrote:
> Add pm_prepare callback and System level pm ops support for
> AMD master driver.
> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
> ---
>  drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
> 
> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
> index 2fd77a673c22..f4478cc17aac 100644
> --- a/drivers/soundwire/amd_master.c
> +++ b/drivers/soundwire/amd_master.c
> @@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>  	return 0;
>  }
>  
> +static int amd_resume_child_device(struct device *dev, void *data)
> +{
> +	int ret;
> +	struct sdw_slave *slave = dev_to_sdw_dev(dev);
> +
> +	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;
> +	}
> +
> +	if (!pm_runtime_suspended(dev))
> +		return 0;
> +	ret = pm_request_resume(dev);
> +	if (ret < 0)
> +		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
> +
> +	return ret;
> +}
> +
> +static int __maybe_unused amd_pm_prepare(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
> +	if (ret < 0)
> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = pm_request_resume(dev);
> +		if (ret < 0) {
> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
> +			return 0;
> +		}
> +	}
> +	return 0;
> +}

This seems to be inspired by the Intel code, but is this necessary here?

For Intel, we saw cases where we had to pm_resume before doing a system
suspend, otherwise the hardware was in a bad state.

Do you actually need to do so, or is is possible to do a system suspend
when the clock is stopped.

And in the case where the bus is in 'power-off' mode, do you actually
need to resume at all?

> +
> +static int __maybe_unused amd_suspend(struct device *dev)
> +{
> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> +	struct sdw_bus *bus = &ctrl->bus;
> +	int ret;
> +
> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
> +			bus->link_id);
> +		return 0;
> +	}
> +
> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);
> +		if (ret)
> +			return ret;
> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
> +		ret = amd_sdwc_clock_stop(ctrl);

do you actually need to stop the clock before powering-off? This seems
counter intuitive and not so useful?

> +		if (ret)
> +			return ret;
> +		ret = amd_deinit_sdw_controller(ctrl);
> +		if (ret)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
>  static int __maybe_unused amd_suspend_runtime(struct device *dev)
>  {
>  	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
> @@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
>  }
>  
>  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)
>  };
>  

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-11 16:02     ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 16:02 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> To avoid ACP entering into D3 state during slave enumeration and
> initialization on two soundwire controller instances for multiple codecs,
> increase the runtime suspend delay to 3 seconds.

You have a parent PCI device and a set of child devices for each
manager. The parent PCI device cannot suspend before all its children
are also suspended, so shouldn't the delay be modified at the manager level?

Not getting what this delay is and how this would deal with a lengthy
enumeration/initialization process.

> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index 833d0b5aa73d..6c8849f2bcec 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -51,7 +51,7 @@
>  #define MIN_BUFFER MAX_BUFFER
>  
>  /* time in ms for runtime suspend delay */
> -#define ACP_SUSPEND_DELAY_MS	2000
> +#define ACP_SUSPEND_DELAY_MS	3000
>  
>  #define ACP63_DMIC_ADDR		2
>  #define ACP63_PDM_MODE_DEVS		3

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-11 16:02     ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-11 16:02 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem



On 1/11/23 03:02, Vijendar Mukunda wrote:
> To avoid ACP entering into D3 state during slave enumeration and
> initialization on two soundwire controller instances for multiple codecs,
> increase the runtime suspend delay to 3 seconds.

You have a parent PCI device and a set of child devices for each
manager. The parent PCI device cannot suspend before all its children
are also suspended, so shouldn't the delay be modified at the manager level?

Not getting what this delay is and how this would deal with a lengthy
enumeration/initialization process.

> 
> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
> ---
>  sound/soc/amd/ps/acp63.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
> index 833d0b5aa73d..6c8849f2bcec 100644
> --- a/sound/soc/amd/ps/acp63.h
> +++ b/sound/soc/amd/ps/acp63.h
> @@ -51,7 +51,7 @@
>  #define MIN_BUFFER MAX_BUFFER
>  
>  /* time in ms for runtime suspend delay */
> -#define ACP_SUSPEND_DELAY_MS	2000
> +#define ACP_SUSPEND_DELAY_MS	3000
>  
>  #define ACP63_DMIC_ADDR		2
>  #define ACP63_PDM_MODE_DEVS		3

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 00/19] Add soundwire support for Pink Sardine platform
  2023-01-11 13:36 ` [PATCH 00/19] Add soundwire support for Pink Sardine platform Pierre-Louis Bossart
@ 2023-01-12  9:08   ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12  9:08 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: arungopal.kondaveeti, Mastan.Katragadda, Sunil-kumar.Dommati,
	Basavaraj.Hiregoudar, Mario.Limonciello

On 11/01/23 19:06, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Pink Sardine(ps) platform is based on ACP6.3 Architecture.
>> ACP6.3 IP has two soundwire controller instance support.
> After reviewing patch1, it looks like there's a confusion between
> controller and manager? the use of the 'sdw-master-count' property is
> not for controller count, it's defined within the scope of an ACPI
> controller device (usually a companion device to the PCI device).
Sorry for confusion. We are meant to refer Master/Manager instance
in throughout the code. we will replace the "Controller" reference
with "Manager". We will correct the cover letter.
>> This patchset add support for Soundwire controller on Pink Sardine
>> platform.
> That's great, thanks for sharing.
>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Vijendar Mukunda (19):
>>   ASoC: amd: ps: create platform devices based on acp config
>>   soundwire: amd: Add support for AMD Master driver
>>   soundwire: amd: register sdw controller dai ops
>>   soundwire: amd: enable build for AMD soundwire master driver
>>   soundwire: amd: add soundwire interrupt handling
>>   ASoC: amd: ps: add support for soundwire interrupts in acp pci driver
>>   ASoC: amd: ps: add soundwire dma driver for pink sardine platform
>>   ASoC: amd: ps: add soundwire dma driver dma ops
>>   ASoC: amd: ps: add support for Soundwire DMA interrupts
>>   ASoC: amd: ps: enable Soundwire DMA driver build
>>   ASoC: amd: update comments in Kconfig file
>>   ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops.
>>   ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver
>>   soundwire: amd: add runtime pm ops for AMD master driver
>>   soundwire: amd: add startup and shutdown dai ops
>>   soundwire: amd: handle wake enable interrupt
>>   soundwire: amd: add pm_prepare callback and pm ops support
>>   ASoC: amd: ps: implement system level pm ops for soundwire dma driver
>>   ASoC: amd: ps: increase runtime suspend delay
>>
>>  drivers/soundwire/Kconfig         |    9 +
>>  drivers/soundwire/Makefile        |    4 +
>>  drivers/soundwire/amd_master.c    | 1734 +++++++++++++++++++++++++++++
>>  drivers/soundwire/amd_master.h    |  284 +++++
>>  include/linux/soundwire/sdw_amd.h |   65 ++
>>  sound/soc/amd/Kconfig             |    3 +-
>>  sound/soc/amd/ps/Makefile         |    2 +
>>  sound/soc/amd/ps/acp63.h          |   98 +-
>>  sound/soc/amd/ps/pci-ps.c         |  383 ++++++-
>>  sound/soc/amd/ps/ps-sdw-dma.c     |  728 ++++++++++++
>>  10 files changed, 3287 insertions(+), 23 deletions(-)
>>  create mode 100644 drivers/soundwire/amd_master.c
>>  create mode 100644 drivers/soundwire/amd_master.h
>>  create mode 100644 include/linux/soundwire/sdw_amd.h
>>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
>>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
  2023-01-11 15:22     ` Pierre-Louis Bossart
@ 2023-01-12  9:10       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12  9:10 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 20:52, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Soundwire DMA platform driver binds to the platform device created by
>> ACP PCI device. Soundwire DMA driver registers ALSA DMA component
>> with ASoC framework.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h      |  5 +++
>>  sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
>>  2 files changed, 77 insertions(+)
>>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index 0bd9dc363461..b462320fdf2a 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -135,3 +135,8 @@ struct acp63_dev_data {
>>  	bool is_sdw_dev;
>>  	bool acp_sdw_power_off;
>>  };
>> +
>> +struct sdw_dma_dev_data {
>> +	void __iomem *acp_base;
>> +	struct mutex *acp_lock;
>> +};
>> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
>> new file mode 100644
>> index 000000000000..388a4b7df715
>> --- /dev/null
>> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
>> @@ -0,0 +1,72 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
>> + *
>> + * Copyright 2023 Advanced Micro Devices, Inc.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/soc.h>
>> +#include <sound/soc-dai.h>
>> +#include "acp63.h"
>> +
>> +#define DRV_NAME "amd_ps_sdw_dma"
>> +
>> +static const struct snd_soc_component_driver acp63_sdw_component = {
>> +	.name		= DRV_NAME,
>> +};
>> +
>> +static int acp63_sdw_platform_probe(struct platform_device *pdev)
>> +{
>> +	struct resource *res;
>> +	struct sdw_dma_dev_data *sdw_data;
>> +	int status;
>> +
>> +	if (!pdev->dev.platform_data) {
>> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +		return -ENODEV;
>> +	}
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
>> +	if (!sdw_data)
>> +		return -ENOMEM;
>> +
>> +	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +	if (!sdw_data->acp_base)
>> +		return -ENOMEM;
>> +
>> +	sdw_data->acp_lock = pdev->dev.platform_data;
>> +	dev_set_drvdata(&pdev->dev, sdw_data);
>> +	status = devm_snd_soc_register_component(&pdev->dev,
>> +						 &acp63_sdw_component,
>> +						 NULL, 0);
>> +	if (status) {
>> +		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
> not sure what this means? Are you registering a PDM component or a DMA one?
It's my bad. We will correct the log statement.
>
>> +
>> +		return -ENODEV;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver acp63_sdw_dma_driver = {
>> +	.probe = acp63_sdw_platform_probe,
>> +	.driver = {
>> +		.name = "amd_ps_sdw_dma",
>> +	},
>> +};
>> +
>> +module_platform_driver(acp63_sdw_dma_driver);
>> +
>> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
>> +MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DRV_NAME);


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform
@ 2023-01-12  9:10       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12  9:10 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 20:52, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Soundwire DMA platform driver binds to the platform device created by
>> ACP PCI device. Soundwire DMA driver registers ALSA DMA component
>> with ASoC framework.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h      |  5 +++
>>  sound/soc/amd/ps/ps-sdw-dma.c | 72 +++++++++++++++++++++++++++++++++++
>>  2 files changed, 77 insertions(+)
>>  create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index 0bd9dc363461..b462320fdf2a 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -135,3 +135,8 @@ struct acp63_dev_data {
>>  	bool is_sdw_dev;
>>  	bool acp_sdw_power_off;
>>  };
>> +
>> +struct sdw_dma_dev_data {
>> +	void __iomem *acp_base;
>> +	struct mutex *acp_lock;
>> +};
>> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
>> new file mode 100644
>> index 000000000000..388a4b7df715
>> --- /dev/null
>> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
>> @@ -0,0 +1,72 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * AMD ALSA SoC Pink Sardine Soundwire DMA Driver
>> + *
>> + * Copyright 2023 Advanced Micro Devices, Inc.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/soc.h>
>> +#include <sound/soc-dai.h>
>> +#include "acp63.h"
>> +
>> +#define DRV_NAME "amd_ps_sdw_dma"
>> +
>> +static const struct snd_soc_component_driver acp63_sdw_component = {
>> +	.name		= DRV_NAME,
>> +};
>> +
>> +static int acp63_sdw_platform_probe(struct platform_device *pdev)
>> +{
>> +	struct resource *res;
>> +	struct sdw_dma_dev_data *sdw_data;
>> +	int status;
>> +
>> +	if (!pdev->dev.platform_data) {
>> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +		return -ENODEV;
>> +	}
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	sdw_data = devm_kzalloc(&pdev->dev, sizeof(*sdw_data), GFP_KERNEL);
>> +	if (!sdw_data)
>> +		return -ENOMEM;
>> +
>> +	sdw_data->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +	if (!sdw_data->acp_base)
>> +		return -ENOMEM;
>> +
>> +	sdw_data->acp_lock = pdev->dev.platform_data;
>> +	dev_set_drvdata(&pdev->dev, sdw_data);
>> +	status = devm_snd_soc_register_component(&pdev->dev,
>> +						 &acp63_sdw_component,
>> +						 NULL, 0);
>> +	if (status) {
>> +		dev_err(&pdev->dev, "Fail to register acp pdm dai\n");
> not sure what this means? Are you registering a PDM component or a DMA one?
It's my bad. We will correct the log statement.
>
>> +
>> +		return -ENODEV;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static struct platform_driver acp63_sdw_dma_driver = {
>> +	.probe = acp63_sdw_platform_probe,
>> +	.driver = {
>> +		.name = "amd_ps_sdw_dma",
>> +	},
>> +};
>> +
>> +module_platform_driver(acp63_sdw_dma_driver);
>> +
>> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
>> +MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_ALIAS("platform:" DRV_NAME);


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
  2023-01-11 15:58     ` Pierre-Louis Bossart
@ 2023-01-12 10:14       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:14 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 21:28, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add pm_prepare callback and System level pm ops support for
>> AMD master driver.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 76 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 2fd77a673c22..f4478cc17aac 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>>  	return 0;
>>  }
>>  
>> +static int amd_resume_child_device(struct device *dev, void *data)
>> +{
>> +	int ret;
>> +	struct sdw_slave *slave = dev_to_sdw_dev(dev);
>> +
>> +	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;
>> +	}
>> +
>> +	if (!pm_runtime_suspended(dev))
>> +		return 0;
>> +	ret = pm_request_resume(dev);
>> +	if (ret < 0)
>> +		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int __maybe_unused amd_pm_prepare(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
>> +	if (ret < 0)
>> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
>> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = pm_request_resume(dev);
>> +		if (ret < 0) {
>> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
>> +			return 0;
>> +		}
>> +	}
>> +	return 0;
>> +}
> This seems to be inspired by the Intel code, but is this necessary here?
No It's not inspired by intel code. Initially, we haven't included
pm_prepare callback. We have observed issues without
pm_prepare callback.
> For Intel, we saw cases where we had to pm_resume before doing a system
> suspend, otherwise the hardware was in a bad state.
>
> Do you actually need to do so, or is is possible to do a system suspend
> when the clock is stopped.
>
> And in the case where the bus is in 'power-off' mode, do you actually
> need to resume at all?
Our platform supports different power modes. To support all
combinations, we have included pm_prepare callback.
>> +
>> +static int __maybe_unused amd_suspend(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
> do you actually need to stop the clock before powering-off? This seems
> counter intuitive and not so useful?
Yes, as per our design, we need to stop the clock
before powering off.
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_deinit_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>>  static int __maybe_unused amd_suspend_runtime(struct device *dev)
>>  {
>>  	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> @@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
>>  }
>>  
>>  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)
>>  };
>>  


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
@ 2023-01-12 10:14       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:14 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 21:28, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add pm_prepare callback and System level pm ops support for
>> AMD master driver.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c | 76 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 76 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 2fd77a673c22..f4478cc17aac 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -1552,6 +1552,80 @@ static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>>  	return 0;
>>  }
>>  
>> +static int amd_resume_child_device(struct device *dev, void *data)
>> +{
>> +	int ret;
>> +	struct sdw_slave *slave = dev_to_sdw_dev(dev);
>> +
>> +	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;
>> +	}
>> +
>> +	if (!pm_runtime_suspended(dev))
>> +		return 0;
>> +	ret = pm_request_resume(dev);
>> +	if (ret < 0)
>> +		dev_err(dev, "%s: pm_request_resume failed: %d\n", __func__, ret);
>> +
>> +	return ret;
>> +}
>> +
>> +static int __maybe_unused amd_pm_prepare(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
>> +	if (ret < 0)
>> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
>> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = pm_request_resume(dev);
>> +		if (ret < 0) {
>> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
>> +			return 0;
>> +		}
>> +	}
>> +	return 0;
>> +}
> This seems to be inspired by the Intel code, but is this necessary here?
No It's not inspired by intel code. Initially, we haven't included
pm_prepare callback. We have observed issues without
pm_prepare callback.
> For Intel, we saw cases where we had to pm_resume before doing a system
> suspend, otherwise the hardware was in a bad state.
>
> Do you actually need to do so, or is is possible to do a system suspend
> when the clock is stopped.
>
> And in the case where the bus is in 'power-off' mode, do you actually
> need to resume at all?
Our platform supports different power modes. To support all
combinations, we have included pm_prepare callback.
>> +
>> +static int __maybe_unused amd_suspend(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
> do you actually need to stop the clock before powering-off? This seems
> counter intuitive and not so useful?
Yes, as per our design, we need to stop the clock
before powering off.
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_deinit_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>>  static int __maybe_unused amd_suspend_runtime(struct device *dev)
>>  {
>>  	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> @@ -1638,6 +1712,8 @@ static int __maybe_unused amd_resume_runtime(struct device *dev)
>>  }
>>  
>>  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)
>>  };
>>  


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 16/19] soundwire: amd: handle wake enable interrupt
  2023-01-11 15:54     ` Pierre-Louis Bossart
@ 2023-01-12 10:21       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:21 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 21:24, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add wake enable interrupt support for both the soundwire controller
> SoundWire.
>
>> instances.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 9 +++++++++
>>  drivers/soundwire/amd_master.h    | 1 +
>>  include/linux/soundwire/sdw_amd.h | 1 +
>>  3 files changed, 11 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 290c59ab7760..2fd77a673c22 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
>>  	u32 sw_status_change_mask_0to7_reg;
>>  	u32 sw_status_change_mask_8to11_reg;
>>  
>> +	if (ctrl->wake_event) {
>> +		pm_runtime_resume(ctrl->dev);
>> +		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
>> +		ctrl->wake_event = false;
>> +		return;







>> +	}
> this is surprising.
>
> A wake event typically happens when the bus is in clock-stop mode.
> You cannot deal with wake events while dealing with the peripheral
> status update, because you can only get that status when the manager is
> up-and-running. There's a conceptual miss here, no?
>
> If the wake comes from the PCI side, then it's the same comment: why
> would the wake be handled while updating the peripheral status.
>
> What am I missing?
>
It's a miss. This should be moved out of slave_status_work() even
though when wake enable irq is received we are just returning
from API.
will move wake interrupt handling in to a separate helper function.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 16/19] soundwire: amd: handle wake enable interrupt
@ 2023-01-12 10:21       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:21 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 21:24, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add wake enable interrupt support for both the soundwire controller
> SoundWire.
>
>> instances.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 9 +++++++++
>>  drivers/soundwire/amd_master.h    | 1 +
>>  include/linux/soundwire/sdw_amd.h | 1 +
>>  3 files changed, 11 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 290c59ab7760..2fd77a673c22 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -1219,6 +1219,13 @@ static void amd_sdwc_update_slave_status_work(struct work_struct *work)
>>  	u32 sw_status_change_mask_0to7_reg;
>>  	u32 sw_status_change_mask_8to11_reg;
>>  
>> +	if (ctrl->wake_event) {
>> +		pm_runtime_resume(ctrl->dev);
>> +		acp_reg_writel(0x00, ctrl->mmio + ACP_SW_WAKE_EN);
>> +		ctrl->wake_event = false;
>> +		return;







>> +	}
> this is surprising.
>
> A wake event typically happens when the bus is in clock-stop mode.
> You cannot deal with wake events while dealing with the peripheral
> status update, because you can only get that status when the manager is
> up-and-running. There's a conceptual miss here, no?
>
> If the wake comes from the PCI side, then it's the same comment: why
> would the wake be handled while updating the peripheral status.
>
> What am I missing?
>
It's a miss. This should be moved out of slave_status_work() even
though when wake enable irq is received we are just returning
from API.
will move wake interrupt handling in to a separate helper function.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
  2023-01-11 15:49     ` Pierre-Louis Bossart
@ 2023-01-12 10:22       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:22 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 21:19, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add start up and shutdown dai ops for AMD Master driver.
> I don't think those startup and shutdown ops are necessary, we removed
> them for Intel, see "soundwire: intel: remove DAI startup/shutdown"
will drop this patch.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops
@ 2023-01-12 10:22       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:22 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 21:19, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add start up and shutdown dai ops for AMD Master driver.
> I don't think those startup and shutdown ops are necessary, we removed
> them for Intel, see "soundwire: intel: remove DAI startup/shutdown"
will drop this patch.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
  2023-01-11 15:47     ` Pierre-Louis Bossart
@ 2023-01-12 10:35       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:35 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 21:17, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add support for runtime pm ops for AMD master driver.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
>>  drivers/soundwire/amd_master.h    |   3 +
>>  include/linux/soundwire/sdw_amd.h |   1 +
>>  3 files changed, 209 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index c7063b8bdd7b..d2d7f07de202 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -15,6 +15,7 @@
>>  #include <linux/soundwire/sdw.h>
>>  #include <linux/soundwire/sdw_registers.h>
>>  #include <linux/soundwire/sdw_amd.h>
>> +#include <linux/pm_runtime.h>
>>  #include <linux/wait.h>
>>  #include <sound/pcm_params.h>
>>  #include <sound/soc.h>
>> @@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>>  	return 0;
>>  }
>>  
>> +static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	int ret;
>> +
>> +	ret = amd_disable_sdw_interrupts(ctrl);
>> +	if (ret)
>> +		return ret;
>> +	ret = amd_disable_sdw_controller(ctrl);
>> +	return ret;
>> +}
>> +
>>  static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
>>  {
>>  	u32 sdw_rows, sdw_cols, frame_size;
>> @@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>>  	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
>>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>>  	schedule_work(&ctrl->probe_work);
>> +	/* Enable runtime PM */
>> +	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
>> +	pm_runtime_use_autosuspend(dev);
>> +	pm_runtime_mark_last_busy(dev);
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>>  	return 0;
>>  }
>>  
>> @@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
>>  	amd_disable_sdw_interrupts(ctrl);
>>  	sdw_bus_master_delete(&ctrl->bus);
>>  	ret = amd_disable_sdw_controller(ctrl);
>> +	pm_runtime_disable(&pdev->dev);
>>  	return ret;
>>  }
>>  
>> +static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 wake_en_reg;
>> +	u32 val;
>> +	u32 retry_count = 0;
>> +	int ret;
>> +
>> +	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
>> +	if (ret < 0 && ret != -ENODATA) {
>> +		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
>> +		return ret;
>> +	}
>> +	ret = sdw_bus_clk_stop(&ctrl->bus);
>> +	if (ret < 0 && ret != -ENODATA) {
>> +		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
>> +		return ret;
> You need to be very careful here, because returning an error may prevent
> the device from suspending.
>
> If it's safe and possible to recover during the resume step, you
> probably want to log the error but let the suspend continue.
will fix it.
>> +	}
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		wake_en_reg = ACP_SW_WAKE_EN;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		wake_en_reg = ACP_SW1_WAKE_EN;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
> why not store these offsets during the probe and use them directly here?
> You know at probe time which master you're using.
will fix it.
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		if (val & AMD_SDW_CLK_STOP_DONE) {
>> +			ctrl->clk_stopped = true;
>> +			break;
>> +		}
>> +	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +
>> +	if (!ctrl->clk_stopped) {
>> +		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	if (ctrl->wake_en_mask)
>> +		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
>> +
>> +	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	int ret;
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ctrl->clk_stopped) {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		val |= AMD_SDW_CLK_RESUME_REQ;
>> +		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
>> +		do {
>> +			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +			if (val & AMD_SDW_CLK_RESUME_DONE)
>> +				break;
>> +			usleep_range(10, 100);
>> +		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +		if (val & AMD_SDW_CLK_RESUME_DONE) {
>> +			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
>> +			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
>> +			if (ret < 0)
>> +				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
>> +			ctrl->clk_stopped = false;
>> +		}
>> +	}
>> +	if (ctrl->clk_stopped) {
>> +		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> do you have a case where the startup is not done? This was an
> Intel-specific thing.
We have included startup_done flag in probe_work to check whether Manager
has started. In case if manager init sequence fails, then there is no need
to apply any PM ops.

>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_deinit_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused amd_resume_runtime(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> same here
>
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
> select registers in the probe.
will fix it.
>> +
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop_exit(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		if (val) {
>> +			val |= AMD_SDW_CLK_RESUME_REQ;
>> +			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
>> +			do {
>> +				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +				if (val & AMD_SDW_CLK_RESUME_DONE)
>> +					break;
>> +				usleep_range(10, 100);
>> +			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +			if (val & AMD_SDW_CLK_RESUME_DONE) {
>> +				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
>> +				ctrl->clk_stopped = false;
>> +			}
>> +		}
>> +		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
>> +		amd_init_sdw_controller(ctrl);
>> +		amd_enable_sdw_interrupts(ctrl);
>> +		ret = amd_enable_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
> this should be defined at probe time, using magic numbers like this will
> not work in all cases and totally depends on the frame rate and
> bandwidth needs.
Will fix it.
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
@ 2023-01-12 10:35       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:35 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 21:17, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add support for runtime pm ops for AMD master driver.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 205 ++++++++++++++++++++++++++++++
>>  drivers/soundwire/amd_master.h    |   3 +
>>  include/linux/soundwire/sdw_amd.h |   1 +
>>  3 files changed, 209 insertions(+)
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index c7063b8bdd7b..d2d7f07de202 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -15,6 +15,7 @@
>>  #include <linux/soundwire/sdw.h>
>>  #include <linux/soundwire/sdw_registers.h>
>>  #include <linux/soundwire/sdw_amd.h>
>> +#include <linux/pm_runtime.h>
>>  #include <linux/wait.h>
>>  #include <sound/pcm_params.h>
>>  #include <sound/soc.h>
>> @@ -290,6 +291,17 @@ static int amd_disable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>>  	return 0;
>>  }
>>  
>> +static int amd_deinit_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	int ret;
>> +
>> +	ret = amd_disable_sdw_interrupts(ctrl);
>> +	if (ret)
>> +		return ret;
>> +	ret = amd_disable_sdw_controller(ctrl);
>> +	return ret;
>> +}
>> +
>>  static int amd_sdwc_set_frameshape(struct amd_sdwc_ctrl *ctrl, u32 rows, u32 cols)
>>  {
>>  	u32 sdw_rows, sdw_cols, frame_size;
>> @@ -1387,6 +1399,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>>  	INIT_WORK(&ctrl->amd_sdw_work, amd_sdwc_update_slave_status_work);
>>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>>  	schedule_work(&ctrl->probe_work);
>> +	/* Enable runtime PM */
>> +	pm_runtime_set_autosuspend_delay(dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS);
>> +	pm_runtime_use_autosuspend(dev);
>> +	pm_runtime_mark_last_busy(dev);
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>>  	return 0;
>>  }
>>  
>> @@ -1398,14 +1416,201 @@ static int amd_sdwc_remove(struct platform_device *pdev)
>>  	amd_disable_sdw_interrupts(ctrl);
>>  	sdw_bus_master_delete(&ctrl->bus);
>>  	ret = amd_disable_sdw_controller(ctrl);
>> +	pm_runtime_disable(&pdev->dev);
>>  	return ret;
>>  }
>>  
>> +static int amd_sdwc_clock_stop(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 wake_en_reg;
>> +	u32 val;
>> +	u32 retry_count = 0;
>> +	int ret;
>> +
>> +	ret = sdw_bus_prep_clk_stop(&ctrl->bus);
>> +	if (ret < 0 && ret != -ENODATA) {
>> +		dev_err(ctrl->dev, "prepare clock stop failed %d", ret);
>> +		return ret;
>> +	}
>> +	ret = sdw_bus_clk_stop(&ctrl->bus);
>> +	if (ret < 0 && ret != -ENODATA) {
>> +		dev_err(ctrl->dev, "bus clock stop failed %d", ret);
>> +		return ret;
> You need to be very careful here, because returning an error may prevent
> the device from suspending.
>
> If it's safe and possible to recover during the resume step, you
> probably want to log the error but let the suspend continue.
will fix it.
>> +	}
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		wake_en_reg = ACP_SW_WAKE_EN;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		wake_en_reg = ACP_SW1_WAKE_EN;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
> why not store these offsets during the probe and use them directly here?
> You know at probe time which master you're using.
will fix it.
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		if (val & AMD_SDW_CLK_STOP_DONE) {
>> +			ctrl->clk_stopped = true;
>> +			break;
>> +		}
>> +	} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +
>> +	if (!ctrl->clk_stopped) {
>> +		dev_err(ctrl->dev, "SDW%x clock stop failed\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	if (ctrl->wake_en_mask)
>> +		acp_reg_writel(0x01, ctrl->mmio + wake_en_reg);
>> +
>> +	dev_dbg(ctrl->dev, "SDW%x clock stop successful\n", ctrl->instance);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_clock_stop_exit(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	int ret;
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	if (ctrl->clk_stopped) {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		val |= AMD_SDW_CLK_RESUME_REQ;
>> +		acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
>> +		do {
>> +			val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +			if (val & AMD_SDW_CLK_RESUME_DONE)
>> +				break;
>> +			usleep_range(10, 100);
>> +		} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +		if (val & AMD_SDW_CLK_RESUME_DONE) {
>> +			acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
>> +			ret = sdw_bus_exit_clk_stop(&ctrl->bus);
>> +			if (ret < 0)
>> +				dev_err(ctrl->dev, "bus failed to exit clock stop %d\n", ret);
>> +			ctrl->clk_stopped = false;
>> +		}
>> +	}
>> +	if (ctrl->clk_stopped) {
>> +		dev_err(ctrl->dev, "SDW%x clock stop exit failed\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	dev_dbg(ctrl->dev, "SDW%x clock stop exit successful\n", ctrl->instance);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> do you have a case where the startup is not done? This was an
> Intel-specific thing.
We have included startup_done flag in probe_work to check whether Manager
has started. In case if manager init sequence fails, then there is no need
to apply any PM ops.

>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		ret = amd_sdwc_clock_stop(ctrl);
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_deinit_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused amd_resume_runtime(struct device *dev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>> +	struct sdw_bus *bus = &ctrl->bus;
>> +	int ret;
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
> same here
>
>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>> +			bus->link_id);
>> +		return 0;
>> +	}
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
> select registers in the probe.
will fix it.
>> +
>> +	if (ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>> +		ret = amd_sdwc_clock_stop_exit(ctrl);
>> +		if (ret)
>> +			return ret;
>> +	} else if (ctrl->power_mode_mask & AMD_SDW_POWER_OFF_MODE) {
>> +		val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +		if (val) {
>> +			val |= AMD_SDW_CLK_RESUME_REQ;
>> +			acp_reg_writel(val, ctrl->mmio + clk_resume_ctrl_reg);
>> +			do {
>> +				val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +				if (val & AMD_SDW_CLK_RESUME_DONE)
>> +					break;
>> +				usleep_range(10, 100);
>> +			} while (retry_count++ < AMD_SDW_CLK_STOP_MAX_RETRY_COUNT);
>> +			if (val & AMD_SDW_CLK_RESUME_DONE) {
>> +				acp_reg_writel(0, ctrl->mmio + clk_resume_ctrl_reg);
>> +				ctrl->clk_stopped = false;
>> +			}
>> +		}
>> +		sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET);
>> +		amd_init_sdw_controller(ctrl);
>> +		amd_enable_sdw_interrupts(ctrl);
>> +		ret = amd_enable_sdw_controller(ctrl);
>> +		if (ret)
>> +			return ret;
>> +		ret = amd_sdwc_set_frameshape(ctrl, 50, 10);
> this should be defined at probe time, using magic numbers like this will
> not work in all cases and totally depends on the frame rate and
> bandwidth needs.
Will fix it.
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return 0;
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
  2023-01-11 15:38     ` Pierre-Louis Bossart
@ 2023-01-12 10:55       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:55 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Syed Saba Kareem

On 11/01/23 21:08, Pierre-Louis Bossart wrote:
>
>
>> @@ -167,9 +167,11 @@ struct acp63_dev_data {
>>  	struct platform_device *pdev[ACP63_DEVS];
>>  	struct mutex acp_lock; /* protect shared registers */
>>  	struct fwnode_handle *sdw_fw_node;
>> +	struct work_struct acp_sdw_dma_work;
>>  	u16 pdev_mask;
>>  	u16 pdev_count;
>>  	u16 pdm_dev_index;
>> +	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];
> streams and DMAs are different things in SoundWire. You can have a 1:N
> mapping.
>
>>  	u8 sdw_master_count;
>>  	u16 sdw0_dev_index;
>>  	u16 sdw1_dev_index;
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index 0fbe5e27f3fb..5b82ee8e3ad8 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
>>  	return 0;
>>  }
>>  
>> +static void acp63_sdw_dma_workthread(struct work_struct *work)
>> +{
>> +	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
>> +						    acp_sdw_dma_work);
>> +	struct sdw_dma_dev_data *sdw_dma_data;
>> +	u32 stream_index;
>> +	u16 pdev_index;
>> +
>> +	pdev_index = adata->sdw_dma_dev_index;
>> +	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
>> +
>> +	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
>> +		if (adata->dma_intr_stat[stream_index]) {
>> +			if (sdw_dma_data->sdw_stream[stream_index])
>> +				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);
> is there a reason why you do this in a work thread?
>
> IIRC we did this in SOF because of an issue where during an xrun a stop
> IPC would be sent while we were dealing with an IPC.
>
> I don't quite see why it's needed for a DMA?
>
> What am I missing?
Initially, we have used in atomic context. We have seen issues
during stream closure, in interrupt context , handling
period_elapsed causing sleep in atomic context.
To avoid that , we have declared dai_link as non-atomic and
moved period_elapsed code to work queue.
>> +			adata->dma_intr_stat[stream_index] = 0;
>> +		}
>> +	}
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts
@ 2023-01-12 10:55       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 10:55 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Liam Girdwood,
	Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

On 11/01/23 21:08, Pierre-Louis Bossart wrote:
>
>
>> @@ -167,9 +167,11 @@ struct acp63_dev_data {
>>  	struct platform_device *pdev[ACP63_DEVS];
>>  	struct mutex acp_lock; /* protect shared registers */
>>  	struct fwnode_handle *sdw_fw_node;
>> +	struct work_struct acp_sdw_dma_work;
>>  	u16 pdev_mask;
>>  	u16 pdev_count;
>>  	u16 pdm_dev_index;
>> +	u16 dma_intr_stat[ACP63_SDW_MAX_STREAMS];
> streams and DMAs are different things in SoundWire. You can have a 1:N
> mapping.
>
>>  	u8 sdw_master_count;
>>  	u16 sdw0_dev_index;
>>  	u16 sdw1_dev_index;
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index 0fbe5e27f3fb..5b82ee8e3ad8 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -113,14 +113,37 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
>>  	return 0;
>>  }
>>  
>> +static void acp63_sdw_dma_workthread(struct work_struct *work)
>> +{
>> +	struct acp63_dev_data *adata = container_of(work, struct acp63_dev_data,
>> +						    acp_sdw_dma_work);
>> +	struct sdw_dma_dev_data *sdw_dma_data;
>> +	u32 stream_index;
>> +	u16 pdev_index;
>> +
>> +	pdev_index = adata->sdw_dma_dev_index;
>> +	sdw_dma_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
>> +
>> +	for (stream_index = 0; stream_index < ACP63_SDW_MAX_STREAMS; stream_index++) {
>> +		if (adata->dma_intr_stat[stream_index]) {
>> +			if (sdw_dma_data->sdw_stream[stream_index])
>> +				snd_pcm_period_elapsed(sdw_dma_data->sdw_stream[stream_index]);
> is there a reason why you do this in a work thread?
>
> IIRC we did this in SOF because of an issue where during an xrun a stop
> IPC would be sent while we were dealing with an IPC.
>
> I don't quite see why it's needed for a DMA?
>
> What am I missing?
Initially, we have used in atomic context. We have seen issues
during stream closure, in interrupt context , handling
period_elapsed causing sleep in atomic context.
To avoid that , we have declared dai_link as non-atomic and
moved period_elapsed code to work queue.
>> +			adata->dma_intr_stat[stream_index] = 0;
>> +		}
>> +	}
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-11 16:02     ` Pierre-Louis Bossart
@ 2023-01-12 11:02       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 11:02 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 21:32, Pierre-Louis Bossart wrote:
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> To avoid ACP entering into D3 state during slave enumeration and
>> initialization on two soundwire controller instances for multiple codecs,
>> increase the runtime suspend delay to 3 seconds.
> You have a parent PCI device and a set of child devices for each
> manager. The parent PCI device cannot suspend before all its children
> are also suspended, so shouldn't the delay be modified at the manager level?
>
> Not getting what this delay is and how this would deal with a lengthy
> enumeration/initialization process.
Yes agreed. Until Child devices are suspended, parent device will
be in D0 state. We will rephrase the commit message.

Machine driver node will be created by ACP PCI driver.
We have added delay in machine driver to make sure
two manager instances completes codec enumeration and
peripheral initialization before registering the sound card.
Without adding delay in machine driver will result early card
registration before codec initialization is completed. Manager
will enter in to bad state due to codec read/write failures.
We are intended to keep the ACP in D0 state, till sound card
is created and jack controls are initialized. To handle, at manager
level increased runtime suspend delay.
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index 833d0b5aa73d..6c8849f2bcec 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -51,7 +51,7 @@
>>  #define MIN_BUFFER MAX_BUFFER
>>  
>>  /* time in ms for runtime suspend delay */
>> -#define ACP_SUSPEND_DELAY_MS	2000
>> +#define ACP_SUSPEND_DELAY_MS	3000
>>  
>>  #define ACP63_DMIC_ADDR		2
>>  #define ACP63_PDM_MODE_DEVS		3


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-12 11:02       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-12 11:02 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 21:32, Pierre-Louis Bossart wrote:
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> To avoid ACP entering into D3 state during slave enumeration and
>> initialization on two soundwire controller instances for multiple codecs,
>> increase the runtime suspend delay to 3 seconds.
> You have a parent PCI device and a set of child devices for each
> manager. The parent PCI device cannot suspend before all its children
> are also suspended, so shouldn't the delay be modified at the manager level?
>
> Not getting what this delay is and how this would deal with a lengthy
> enumeration/initialization process.
Yes agreed. Until Child devices are suspended, parent device will
be in D0 state. We will rephrase the commit message.

Machine driver node will be created by ACP PCI driver.
We have added delay in machine driver to make sure
two manager instances completes codec enumeration and
peripheral initialization before registering the sound card.
Without adding delay in machine driver will result early card
registration before codec initialization is completed. Manager
will enter in to bad state due to codec read/write failures.
We are intended to keep the ACP in D0 state, till sound card
is created and jack controls are initialized. To handle, at manager
level increased runtime suspend delay.
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h | 2 +-
>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index 833d0b5aa73d..6c8849f2bcec 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -51,7 +51,7 @@
>>  #define MIN_BUFFER MAX_BUFFER
>>  
>>  /* time in ms for runtime suspend delay */
>> -#define ACP_SUSPEND_DELAY_MS	2000
>> +#define ACP_SUSPEND_DELAY_MS	3000
>>  
>>  #define ACP63_DMIC_ADDR		2
>>  #define ACP63_PDM_MODE_DEVS		3


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
  2023-01-12 10:35       ` Mukunda,Vijendar
@ 2023-01-12 14:47         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:47 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



>>> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>>> +	struct sdw_bus *bus = &ctrl->bus;
>>> +	int ret;
>>> +
>>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> do you have a case where the startup is not done? This was an
>> Intel-specific thing.
> We have included startup_done flag in probe_work to check whether Manager
> has started. In case if manager init sequence fails, then there is no need
> to apply any PM ops.

Not following, sorry.

We introduced the .startup callback for intel because of a power
dependency where we could not access and initialize the registers at the
.probe time for the master driver.

Do you have a similar dependency, and if not why not remove this flag?



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver
@ 2023-01-12 14:47         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:47 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



>>> +static int __maybe_unused amd_suspend_runtime(struct device *dev)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>>> +	struct sdw_bus *bus = &ctrl->bus;
>>> +	int ret;
>>> +
>>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>> do you have a case where the startup is not done? This was an
>> Intel-specific thing.
> We have included startup_done flag in probe_work to check whether Manager
> has started. In case if manager init sequence fails, then there is no need
> to apply any PM ops.

Not following, sorry.

We introduced the .startup callback for intel because of a power
dependency where we could not access and initialize the registers at the
.probe time for the master driver.

Do you have a similar dependency, and if not why not remove this flag?



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
  2023-01-12 10:14       ` Mukunda,Vijendar
@ 2023-01-12 14:50         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:50 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao


>>> +static int __maybe_unused amd_pm_prepare(struct device *dev)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>>> +	struct sdw_bus *bus = &ctrl->bus;
>>> +	int ret;
>>> +
>>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>>> +			bus->link_id);
>>> +		return 0;
>>> +	}
>>> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
>>> +	if (ret < 0)
>>> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
>>> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>>> +		ret = pm_request_resume(dev);
>>> +		if (ret < 0) {
>>> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
>>> +			return 0;
>>> +		}
>>> +	}
>>> +	return 0;
>>> +}
>> This seems to be inspired by the Intel code, but is this necessary here?
> No It's not inspired by intel code. Initially, we haven't included
> pm_prepare callback. We have observed issues without
> pm_prepare callback.
>> For Intel, we saw cases where we had to pm_resume before doing a system
>> suspend, otherwise the hardware was in a bad state.
>>
>> Do you actually need to do so, or is is possible to do a system suspend
>> when the clock is stopped.
>>
>> And in the case where the bus is in 'power-off' mode, do you actually
>> need to resume at all?
> Our platform supports different power modes. To support all
> combinations, we have included pm_prepare callback.

>> do you actually need to stop the clock before powering-off? This seems
>> counter intuitive and not so useful?
> Yes, as per our design, we need to stop the clock
> before powering off.

It'd be good to add comments capturing these points, that would be
useful for new contributors and reviewers to know this is intentional
and required by the hardware programming sequences.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support
@ 2023-01-12 14:50         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:50 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list


>>> +static int __maybe_unused amd_pm_prepare(struct device *dev)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(dev);
>>> +	struct sdw_bus *bus = &ctrl->bus;
>>> +	int ret;
>>> +
>>> +	if (bus->prop.hw_disabled || !ctrl->startup_done) {
>>> +		dev_dbg(bus->dev, "SoundWire master %d is disabled or not-started, ignoring\n",
>>> +			bus->link_id);
>>> +		return 0;
>>> +	}
>>> +	ret = device_for_each_child(bus->dev, NULL, amd_resume_child_device);
>>> +	if (ret < 0)
>>> +		dev_err(dev, "%s: amd_resume_child_device failed: %d\n", __func__, ret);
>>> +	if (pm_runtime_suspended(dev) && ctrl->power_mode_mask & AMD_SDW_CLK_STOP_MODE) {
>>> +		ret = pm_request_resume(dev);
>>> +		if (ret < 0) {
>>> +			dev_err(bus->dev, "pm_request_resume failed: %d\n", ret);
>>> +			return 0;
>>> +		}
>>> +	}
>>> +	return 0;
>>> +}
>> This seems to be inspired by the Intel code, but is this necessary here?
> No It's not inspired by intel code. Initially, we haven't included
> pm_prepare callback. We have observed issues without
> pm_prepare callback.
>> For Intel, we saw cases where we had to pm_resume before doing a system
>> suspend, otherwise the hardware was in a bad state.
>>
>> Do you actually need to do so, or is is possible to do a system suspend
>> when the clock is stopped.
>>
>> And in the case where the bus is in 'power-off' mode, do you actually
>> need to resume at all?
> Our platform supports different power modes. To support all
> combinations, we have included pm_prepare callback.

>> do you actually need to stop the clock before powering-off? This seems
>> counter intuitive and not so useful?
> Yes, as per our design, we need to stop the clock
> before powering off.

It'd be good to add comments capturing these points, that would be
useful for new contributors and reviewers to know this is intentional
and required by the hardware programming sequences.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-12 11:02       ` Mukunda,Vijendar
@ 2023-01-12 14:54         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:54 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem



On 1/12/23 05:02, Mukunda,Vijendar wrote:
> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>> To avoid ACP entering into D3 state during slave enumeration and
>>> initialization on two soundwire controller instances for multiple codecs,
>>> increase the runtime suspend delay to 3 seconds.
>> You have a parent PCI device and a set of child devices for each
>> manager. The parent PCI device cannot suspend before all its children
>> are also suspended, so shouldn't the delay be modified at the manager level?
>>
>> Not getting what this delay is and how this would deal with a lengthy
>> enumeration/initialization process.
> Yes agreed. Until Child devices are suspended, parent device will
> be in D0 state. We will rephrase the commit message.
> 
> Machine driver node will be created by ACP PCI driver.
> We have added delay in machine driver to make sure
> two manager instances completes codec enumeration and
> peripheral initialization before registering the sound card.
> Without adding delay in machine driver will result early card
> registration before codec initialization is completed. Manager
> will enter in to bad state due to codec read/write failures.
> We are intended to keep the ACP in D0 state, till sound card
> is created and jack controls are initialized. To handle, at manager
> level increased runtime suspend delay.

This doesn't look too good. You should not assume any timing
dependencies in the machine driver probe. I made that mistake in earlier
versions and we had to revisit all this to make sure drivers could be
bound/unbound at any time.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-12 14:54         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 14:54 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem



On 1/12/23 05:02, Mukunda,Vijendar wrote:
> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>> To avoid ACP entering into D3 state during slave enumeration and
>>> initialization on two soundwire controller instances for multiple codecs,
>>> increase the runtime suspend delay to 3 seconds.
>> You have a parent PCI device and a set of child devices for each
>> manager. The parent PCI device cannot suspend before all its children
>> are also suspended, so shouldn't the delay be modified at the manager level?
>>
>> Not getting what this delay is and how this would deal with a lengthy
>> enumeration/initialization process.
> Yes agreed. Until Child devices are suspended, parent device will
> be in D0 state. We will rephrase the commit message.
> 
> Machine driver node will be created by ACP PCI driver.
> We have added delay in machine driver to make sure
> two manager instances completes codec enumeration and
> peripheral initialization before registering the sound card.
> Without adding delay in machine driver will result early card
> registration before codec initialization is completed. Manager
> will enter in to bad state due to codec read/write failures.
> We are intended to keep the ACP in D0 state, till sound card
> is created and jack controls are initialized. To handle, at manager
> level increased runtime suspend delay.

This doesn't look too good. You should not assume any timing
dependencies in the machine driver probe. I made that mistake in earlier
versions and we had to revisit all this to make sure drivers could be
bound/unbound at any time.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-12 14:54         ` Pierre-Louis Bossart
@ 2023-01-12 15:29           ` Limonciello, Mario
  -1 siblings, 0 replies; 170+ messages in thread
From: Limonciello, Mario @ 2023-01-12 15:29 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti

On 1/12/2023 08:54, Pierre-Louis Bossart wrote:
> 
> 
> On 1/12/23 05:02, Mukunda,Vijendar wrote:
>> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>>> To avoid ACP entering into D3 state during slave enumeration and
>>>> initialization on two soundwire controller instances for multiple codecs,
>>>> increase the runtime suspend delay to 3 seconds.
>>> You have a parent PCI device and a set of child devices for each
>>> manager. The parent PCI device cannot suspend before all its children
>>> are also suspended, so shouldn't the delay be modified at the manager level?
>>>
>>> Not getting what this delay is and how this would deal with a lengthy
>>> enumeration/initialization process.
>> Yes agreed. Until Child devices are suspended, parent device will
>> be in D0 state. We will rephrase the commit message.
>>
>> Machine driver node will be created by ACP PCI driver.
>> We have added delay in machine driver to make sure
>> two manager instances completes codec enumeration and
>> peripheral initialization before registering the sound card.
>> Without adding delay in machine driver will result early card
>> registration before codec initialization is completed. Manager
>> will enter in to bad state due to codec read/write failures.
>> We are intended to keep the ACP in D0 state, till sound card
>> is created and jack controls are initialized. To handle, at manager
>> level increased runtime suspend delay.
> 
> This doesn't look too good. You should not assume any timing
> dependencies in the machine driver probe. I made that mistake in earlier
> versions and we had to revisit all this to make sure drivers could be
> bound/unbound at any time.

Rather than a timing dependency, could you perhaps prohibit runtime PM 
and have a codec make a callback to indicate it's fully initialized and 
then allow runtime PM again?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-12 15:29           ` Limonciello, Mario
  0 siblings, 0 replies; 170+ messages in thread
From: Limonciello, Mario @ 2023-01-12 15:29 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	arungopal.kondaveeti, Syed Saba Kareem

On 1/12/2023 08:54, Pierre-Louis Bossart wrote:
> 
> 
> On 1/12/23 05:02, Mukunda,Vijendar wrote:
>> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>>> To avoid ACP entering into D3 state during slave enumeration and
>>>> initialization on two soundwire controller instances for multiple codecs,
>>>> increase the runtime suspend delay to 3 seconds.
>>> You have a parent PCI device and a set of child devices for each
>>> manager. The parent PCI device cannot suspend before all its children
>>> are also suspended, so shouldn't the delay be modified at the manager level?
>>>
>>> Not getting what this delay is and how this would deal with a lengthy
>>> enumeration/initialization process.
>> Yes agreed. Until Child devices are suspended, parent device will
>> be in D0 state. We will rephrase the commit message.
>>
>> Machine driver node will be created by ACP PCI driver.
>> We have added delay in machine driver to make sure
>> two manager instances completes codec enumeration and
>> peripheral initialization before registering the sound card.
>> Without adding delay in machine driver will result early card
>> registration before codec initialization is completed. Manager
>> will enter in to bad state due to codec read/write failures.
>> We are intended to keep the ACP in D0 state, till sound card
>> is created and jack controls are initialized. To handle, at manager
>> level increased runtime suspend delay.
> 
> This doesn't look too good. You should not assume any timing
> dependencies in the machine driver probe. I made that mistake in earlier
> versions and we had to revisit all this to make sure drivers could be
> bound/unbound at any time.

Rather than a timing dependency, could you perhaps prohibit runtime PM 
and have a codec make a callback to indicate it's fully initialized and 
then allow runtime PM again?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-12 15:29           ` Limonciello, Mario
  (?)
@ 2023-01-12 16:05           ` Pierre-Louis Bossart
  2023-01-13 10:58             ` Mukunda,Vijendar
  -1 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-12 16:05 UTC (permalink / raw)
  To: Limonciello, Mario, Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti



On 1/12/23 09:29, Limonciello, Mario wrote:
> On 1/12/2023 08:54, Pierre-Louis Bossart wrote:
>>
>>
>> On 1/12/23 05:02, Mukunda,Vijendar wrote:
>>> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>>>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>>>> To avoid ACP entering into D3 state during slave enumeration and
>>>>> initialization on two soundwire controller instances for multiple
>>>>> codecs,
>>>>> increase the runtime suspend delay to 3 seconds.
>>>> You have a parent PCI device and a set of child devices for each
>>>> manager. The parent PCI device cannot suspend before all its children
>>>> are also suspended, so shouldn't the delay be modified at the
>>>> manager level?
>>>>
>>>> Not getting what this delay is and how this would deal with a lengthy
>>>> enumeration/initialization process.
>>> Yes agreed. Until Child devices are suspended, parent device will
>>> be in D0 state. We will rephrase the commit message.
>>>
>>> Machine driver node will be created by ACP PCI driver.
>>> We have added delay in machine driver to make sure
>>> two manager instances completes codec enumeration and
>>> peripheral initialization before registering the sound card.
>>> Without adding delay in machine driver will result early card
>>> registration before codec initialization is completed. Manager
>>> will enter in to bad state due to codec read/write failures.
>>> We are intended to keep the ACP in D0 state, till sound card
>>> is created and jack controls are initialized. To handle, at manager
>>> level increased runtime suspend delay.
>>
>> This doesn't look too good. You should not assume any timing
>> dependencies in the machine driver probe. I made that mistake in earlier
>> versions and we had to revisit all this to make sure drivers could be
>> bound/unbound at any time.
> 
> Rather than a timing dependency, could you perhaps prohibit runtime PM
> and have a codec make a callback to indicate it's fully initialized and
> then allow runtime PM again?

We already have enumeration and initialization 'struct completion' that
are used by codec drivers to know if the hardware is usable. We also
have pm_runtime_get_sync() is the bus layer to make sure the codec is
resumed before being accessed.

The explanations above confuse card registration and manager
probe/initialization. These are two different things. Maybe there's
indeed a missing part in the SoundWire PM assumptions, but I am not
getting what the issue is.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-12 16:05           ` Pierre-Louis Bossart
@ 2023-01-13 10:58             ` Mukunda,Vijendar
  2023-01-13 17:33               ` Pierre-Louis Bossart
  0 siblings, 1 reply; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 10:58 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti

On 12/01/23 21:35, Pierre-Louis Bossart wrote:
>
> On 1/12/23 09:29, Limonciello, Mario wrote:
>> On 1/12/2023 08:54, Pierre-Louis Bossart wrote:
>>>
>>> On 1/12/23 05:02, Mukunda,Vijendar wrote:
>>>> On 11/01/23 21:32, Pierre-Louis Bossart wrote:
>>>>> On 1/11/23 03:02, Vijendar Mukunda wrote:
>>>>>> To avoid ACP entering into D3 state during slave enumeration and
>>>>>> initialization on two soundwire controller instances for multiple
>>>>>> codecs,
>>>>>> increase the runtime suspend delay to 3 seconds.
>>>>> You have a parent PCI device and a set of child devices for each
>>>>> manager. The parent PCI device cannot suspend before all its children
>>>>> are also suspended, so shouldn't the delay be modified at the
>>>>> manager level?
>>>>>
>>>>> Not getting what this delay is and how this would deal with a lengthy
>>>>> enumeration/initialization process.
>>>> Yes agreed. Until Child devices are suspended, parent device will
>>>> be in D0 state. We will rephrase the commit message.
>>>>
>>>> Machine driver node will be created by ACP PCI driver.
>>>> We have added delay in machine driver to make sure
>>>> two manager instances completes codec enumeration and
>>>> peripheral initialization before registering the sound card.
>>>> Without adding delay in machine driver will result early card
>>>> registration before codec initialization is completed. Manager
>>>> will enter in to bad state due to codec read/write failures.
>>>> We are intended to keep the ACP in D0 state, till sound card
>>>> is created and jack controls are initialized. To handle, at manager
>>>> level increased runtime suspend delay.
>>> This doesn't look too good. You should not assume any timing
>>> dependencies in the machine driver probe. I made that mistake in earlier
>>> versions and we had to revisit all this to make sure drivers could be
>>> bound/unbound at any time.
>> Rather than a timing dependency, could you perhaps prohibit runtime PM
>> and have a codec make a callback to indicate it's fully initialized and
>> then allow runtime PM again?
> We already have enumeration and initialization 'struct completion' that
> are used by codec drivers to know if the hardware is usable. We also
> have pm_runtime_get_sync() is the bus layer to make sure the codec is
> resumed before being accessed.
Instead of walking through codec list and checking completion status
for every codec over the link, can we have some solution where once
all codecs gets enumerated and initialized, a variable in bus instance
will be updated to know all peripherals initialized. So that we can
check this variable in machine driver.

>
> The explanations above confuse card registration and manager
> probe/initialization. These are two different things. Maybe there's
> indeed a missing part in the SoundWire PM assumptions, but I am not
> getting what the issue is.
We will rephrase the commit message.
At manager level we want to increase the delay to 3s.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-11 15:34     ` Pierre-Louis Bossart
@ 2023-01-13 11:16       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 11:16 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 21:04, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add Soundwire DMA driver dma ops for Pink Sardine platform.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h      |  61 ++++
>>  sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
>>  2 files changed, 592 insertions(+)
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index b462320fdf2a..8963cfb6120d 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -67,6 +67,38 @@
>>  #define ACP_SDW0_IRQ_MASK	21
>>  #define ACP_SDW1_IRQ_MASK	2
>>  #define ACP_ERROR_IRQ_MASK      29
>> +#define ACP_AUDIO_TX_THRESHOLD	28
>> +#define ACP_BT_TX_THRESHOLD	26
>> +#define ACP_HS_TX_THRESHOLD	24
>> +#define ACP_AUDIO_RX_THRESHOLD	27
>> +#define ACP_BT_RX_THRESHOLD	25
>> +#define ACP_HS_RX_THRESHOLD	23
>> +#define ACP_P1_BT_TX_THRESHOLD	6
>> +#define ACP_P1_BT_RX_THRESHOLD	5
>> +#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
>> +#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
>> +#define ACP63_SDW_MAX_STREAMS		8
>> +
>> +#define ACP_DELAY_US		5
>> +#define SDW_MEM_WINDOW_START	0x4800000
>> +#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
>> +#define SDW_PTE_OFFSET		0x400
>> +#define SDW_FIFO_SIZE		0x100
>> +#define SDW_DMA_SIZE		0x40
>> +#define ACP_SDW_FIFO_OFFSET	0x100
>> +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
>> +
>> +#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
>> +#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
>> +#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
>> +#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
>> +#define SDW_CAPTURE_MIN_NUM_PERIODS     2
>> +#define SDW_CAPTURE_MAX_NUM_PERIODS     8
>> +#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
>> +#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
>> +
>> +#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
>> +#define SDW_MIN_BUFFER SDW_MAX_BUFFER
>>  
>>  enum acp_config {
>>  	ACP_CONFIG_0 = 0,
>> @@ -93,6 +125,17 @@ enum acp_pdev_mask {
>>  	ACP63_SDW_PDM_DEV_MASK,
>>  };
>>  
>> +enum channel_type {
>> +	ACP_SDW_AUDIO_TX = 0,
>> +	ACP_SDW_BT_TX,
>> +	ACP_SDW_HS_TX,
>> +	ACP_SDW_AUDIO_RX,
>> +	ACP_SDW_BT_RX,
>> +	ACP_SDW_HS_RX,
>> +	ACP_SDW1_BT_TX,
>> +	ACP_SDW1_BT_RX,
>> +};
> this was defined in another patch already?
will drop this change.
>
>> +
>>  struct pdm_stream_instance {
>>  	u16 num_pages;
>>  	u16 channels;
>> @@ -139,4 +182,22 @@ struct acp63_dev_data {
>>  struct sdw_dma_dev_data {
>>  	void __iomem *acp_base;
>>  	struct mutex *acp_lock;
>> +	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
>> +};
>> +
>> +struct sdw_stream_instance {
> sdw_stream is already a well-defined concept. Please use sdw_dma_stream
> or something less confusing naming-wise.
will rename it.
>> +	u16 num_pages;
>> +	u16 channels;
>> +	u32 stream_id;
>> +	dma_addr_t dma_addr;
>> +	u64 bytescount;
>> +	void __iomem *acp_base;
>> +};
>> +
>> +union acp_sdw_dma_count {
>> +	struct {
>> +	u32 low;
>> +	u32 high;
>> +	} bcount;
>> +	u64 bytescount;
>>  };
>> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
>> index 388a4b7df715..e94f76053c66 100644
>> --- a/sound/soc/amd/ps/ps-sdw-dma.c
>> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
>> @@ -12,12 +12,543 @@
>>  #include <sound/pcm_params.h>
>>  #include <sound/soc.h>
>>  #include <sound/soc-dai.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>  #include "acp63.h"
>>  
>>  #define DRV_NAME "amd_ps_sdw_dma"
>>  
>> +static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
>> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
>> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
>> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
>> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
>> +	.channels_min = 2,
>> +	.channels_max = 2,
>> +	.rates = SNDRV_PCM_RATE_48000,
>> +	.rate_min = 48000,
>> +	.rate_max = 48000,
> is this really limited to 2ch 48kHz? This doesn't align with the
> references to Bluetooth above?
As of now we are enabling solution for 48khz, 2Ch, 16bit.
We will expand the coverage in the future.
>> +	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
>> +	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
>> +	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
>> +	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
>> +	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
>> +};
>> +
>> +static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
>> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +		SNDRV_PCM_INFO_MMAP |
>> +		SNDRV_PCM_INFO_MMAP_VALID |
>> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
>> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
>> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
>> +	.channels_min = 2,
>> +	.channels_max = 2,
>> +	.rates = SNDRV_PCM_RATE_48000,
>> +	.rate_min = 48000,
>> +	.rate_max = 48000,
> same here?
>
>> +	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
>> +	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
>> +	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
>> +	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
>> +	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
>> +};
>> +
>> +static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
>> +{
>> +	u16 page_idx;
>> +	u32 low, high, val;
>> +	dma_addr_t addr;
>> +
>> +	addr = sdw_ins->dma_addr;
>> +	val = SDW_PTE_OFFSET + (stream_id * 256);
>> +
>> +	/* Group Enable */
>> +	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
>> +		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
>> +	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
>> +		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
>> +	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
>> +		/* Load the low address of page int ACP SRAM through SRBM */
>> +		low = lower_32_bits(addr);
>> +		high = upper_32_bits(addr);
>> +
>> +		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
>> +		high |= BIT(31);
>> +		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
>> +		val += 8;
>> +		addr += PAGE_SIZE;
>> +	}
>> +
>> +	/*cache Invalidation added for Testing */
Below register programming is required. We will remove the comment.
> /* cache
>
>> +	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
>> +}
>> +static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
>> +				   struct snd_pcm_substream *substream,
>> +				   struct snd_pcm_hw_params *params)
>> +{
>> +	struct sdw_stream_instance *sdw_stream_data;
>> +	struct sdw_dma_dev_data *sdw_data;
>> +	u32 period_bytes;
>> +	u32 water_mark_size_reg;
>> +	u32 irq_mask, ext_intr_ctrl;
>> +	u64 size;
>> +	u32 stream_id;
>> +	u32 acp_ext_intr_cntl_reg;
>> +	int ret;
>> +
>> +	stream_id = 0;
> useless initialization...
Will remove it.
>> +	sdw_data = dev_get_drvdata(component->dev);
>> +	sdw_stream_data = substream->runtime->private_data;
>> +	if (!sdw_stream_data)
>> +		return -EINVAL;
>> +	stream_id = sdw_stream_data->stream_id;
> ... overriden here
>
>> +	sdw_data->sdw_stream[stream_id] = substream;
>> +	size = params_buffer_bytes(params);
>> +	period_bytes = params_period_bytes(params);
>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>> +	acp63_config_dma(sdw_stream_data, stream_id);
>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>> +	if (ret) {
>> +		dev_err(component->dev, "Invalid channel type\n");
>> +		return -EINVAL;
>> +	}
>> +	switch (stream_id) {
>> +	case ACP_SDW_AUDIO_TX:
>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
> so there's ONE resource to deal with external codecs? How does this work
> if you have a headset codec and an amplifier?
Are you referring to playing a same stream over headset codec and
amplifier?
It's all about channel selection from DMA perspective.
We have tested speaker aggregation and headset playback use cases.


>> +	case ACP_SDW_BT_TX:
>> +		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_HS_TX:
>> +		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
>> +		break;
>> +	case ACP_SDW_AUDIO_RX:
>> +		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_BT_RX:
>> +		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_HS_RX:
>> +		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
>> +		break;
>> +	default:
>> +		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
>> +	ext_intr_ctrl |= irq_mask;
>> +	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
>> +	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
>> +	return 0;
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-13 11:16       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 11:16 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

On 11/01/23 21:04, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Add Soundwire DMA driver dma ops for Pink Sardine platform.
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  sound/soc/amd/ps/acp63.h      |  61 ++++
>>  sound/soc/amd/ps/ps-sdw-dma.c | 531 ++++++++++++++++++++++++++++++++++
>>  2 files changed, 592 insertions(+)
>>
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index b462320fdf2a..8963cfb6120d 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -67,6 +67,38 @@
>>  #define ACP_SDW0_IRQ_MASK	21
>>  #define ACP_SDW1_IRQ_MASK	2
>>  #define ACP_ERROR_IRQ_MASK      29
>> +#define ACP_AUDIO_TX_THRESHOLD	28
>> +#define ACP_BT_TX_THRESHOLD	26
>> +#define ACP_HS_TX_THRESHOLD	24
>> +#define ACP_AUDIO_RX_THRESHOLD	27
>> +#define ACP_BT_RX_THRESHOLD	25
>> +#define ACP_HS_RX_THRESHOLD	23
>> +#define ACP_P1_BT_TX_THRESHOLD	6
>> +#define ACP_P1_BT_RX_THRESHOLD	5
>> +#define ACP_SDW_DMA_IRQ_MASK	0x1F800000
>> +#define ACP_P1_SDW_DMA_IRQ_MASK	0x60
>> +#define ACP63_SDW_MAX_STREAMS		8
>> +
>> +#define ACP_DELAY_US		5
>> +#define SDW_MEM_WINDOW_START	0x4800000
>> +#define ACP_SDW_SRAM_PTE_OFFSET	0x03800400
>> +#define SDW_PTE_OFFSET		0x400
>> +#define SDW_FIFO_SIZE		0x100
>> +#define SDW_DMA_SIZE		0x40
>> +#define ACP_SDW_FIFO_OFFSET	0x100
>> +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
>> +
>> +#define SDW_PLAYBACK_MIN_NUM_PERIODS    2
>> +#define SDW_PLAYBACK_MAX_NUM_PERIODS    8
>> +#define SDW_PLAYBACK_MAX_PERIOD_SIZE    8192
>> +#define SDW_PLAYBACK_MIN_PERIOD_SIZE    1024
>> +#define SDW_CAPTURE_MIN_NUM_PERIODS     2
>> +#define SDW_CAPTURE_MAX_NUM_PERIODS     8
>> +#define SDW_CAPTURE_MAX_PERIOD_SIZE     8192
>> +#define SDW_CAPTURE_MIN_PERIOD_SIZE     1024
>> +
>> +#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
>> +#define SDW_MIN_BUFFER SDW_MAX_BUFFER
>>  
>>  enum acp_config {
>>  	ACP_CONFIG_0 = 0,
>> @@ -93,6 +125,17 @@ enum acp_pdev_mask {
>>  	ACP63_SDW_PDM_DEV_MASK,
>>  };
>>  
>> +enum channel_type {
>> +	ACP_SDW_AUDIO_TX = 0,
>> +	ACP_SDW_BT_TX,
>> +	ACP_SDW_HS_TX,
>> +	ACP_SDW_AUDIO_RX,
>> +	ACP_SDW_BT_RX,
>> +	ACP_SDW_HS_RX,
>> +	ACP_SDW1_BT_TX,
>> +	ACP_SDW1_BT_RX,
>> +};
> this was defined in another patch already?
will drop this change.
>
>> +
>>  struct pdm_stream_instance {
>>  	u16 num_pages;
>>  	u16 channels;
>> @@ -139,4 +182,22 @@ struct acp63_dev_data {
>>  struct sdw_dma_dev_data {
>>  	void __iomem *acp_base;
>>  	struct mutex *acp_lock;
>> +	struct snd_pcm_substream *sdw_stream[ACP63_SDW_MAX_STREAMS];
>> +};
>> +
>> +struct sdw_stream_instance {
> sdw_stream is already a well-defined concept. Please use sdw_dma_stream
> or something less confusing naming-wise.
will rename it.
>> +	u16 num_pages;
>> +	u16 channels;
>> +	u32 stream_id;
>> +	dma_addr_t dma_addr;
>> +	u64 bytescount;
>> +	void __iomem *acp_base;
>> +};
>> +
>> +union acp_sdw_dma_count {
>> +	struct {
>> +	u32 low;
>> +	u32 high;
>> +	} bcount;
>> +	u64 bytescount;
>>  };
>> diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
>> index 388a4b7df715..e94f76053c66 100644
>> --- a/sound/soc/amd/ps/ps-sdw-dma.c
>> +++ b/sound/soc/amd/ps/ps-sdw-dma.c
>> @@ -12,12 +12,543 @@
>>  #include <sound/pcm_params.h>
>>  #include <sound/soc.h>
>>  #include <sound/soc-dai.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>  #include "acp63.h"
>>  
>>  #define DRV_NAME "amd_ps_sdw_dma"
>>  
>> +static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
>> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +		SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
>> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
>> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
>> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
>> +	.channels_min = 2,
>> +	.channels_max = 2,
>> +	.rates = SNDRV_PCM_RATE_48000,
>> +	.rate_min = 48000,
>> +	.rate_max = 48000,
> is this really limited to 2ch 48kHz? This doesn't align with the
> references to Bluetooth above?
As of now we are enabling solution for 48khz, 2Ch, 16bit.
We will expand the coverage in the future.
>> +	.buffer_bytes_max = SDW_PLAYBACK_MAX_NUM_PERIODS * SDW_PLAYBACK_MAX_PERIOD_SIZE,
>> +	.period_bytes_min = SDW_PLAYBACK_MIN_PERIOD_SIZE,
>> +	.period_bytes_max = SDW_PLAYBACK_MAX_PERIOD_SIZE,
>> +	.periods_min = SDW_PLAYBACK_MIN_NUM_PERIODS,
>> +	.periods_max = SDW_PLAYBACK_MAX_NUM_PERIODS,
>> +};
>> +
>> +static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
>> +	.info = SNDRV_PCM_INFO_INTERLEAVED |
>> +		SNDRV_PCM_INFO_BLOCK_TRANSFER |
>> +		SNDRV_PCM_INFO_MMAP |
>> +		SNDRV_PCM_INFO_MMAP_VALID |
>> +		SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
>> +	.formats = SNDRV_PCM_FMTBIT_S16_LE |  SNDRV_PCM_FMTBIT_S8 |
>> +		   SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
>> +	.channels_min = 2,
>> +	.channels_max = 2,
>> +	.rates = SNDRV_PCM_RATE_48000,
>> +	.rate_min = 48000,
>> +	.rate_max = 48000,
> same here?
>
>> +	.buffer_bytes_max = SDW_CAPTURE_MAX_NUM_PERIODS * SDW_CAPTURE_MAX_PERIOD_SIZE,
>> +	.period_bytes_min = SDW_CAPTURE_MIN_PERIOD_SIZE,
>> +	.period_bytes_max = SDW_CAPTURE_MAX_PERIOD_SIZE,
>> +	.periods_min = SDW_CAPTURE_MIN_NUM_PERIODS,
>> +	.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
>> +};
>> +
>> +static void acp63_config_dma(struct sdw_stream_instance *sdw_ins, u32 stream_id)
>> +{
>> +	u16 page_idx;
>> +	u32 low, high, val;
>> +	dma_addr_t addr;
>> +
>> +	addr = sdw_ins->dma_addr;
>> +	val = SDW_PTE_OFFSET + (stream_id * 256);
>> +
>> +	/* Group Enable */
>> +	acp63_writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), sdw_ins->acp_base +
>> +		     ACPAXI2AXI_ATU_BASE_ADDR_GRP_2);
>> +	acp63_writel(PAGE_SIZE_4K_ENABLE, sdw_ins->acp_base +
>> +		     ACPAXI2AXI_ATU_PAGE_SIZE_GRP_2);
>> +	for (page_idx = 0; page_idx < sdw_ins->num_pages; page_idx++) {
>> +		/* Load the low address of page int ACP SRAM through SRBM */
>> +		low = lower_32_bits(addr);
>> +		high = upper_32_bits(addr);
>> +
>> +		acp63_writel(low, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val);
>> +		high |= BIT(31);
>> +		acp63_writel(high, sdw_ins->acp_base + ACP_SCRATCH_REG_0 + val + 4);
>> +		val += 8;
>> +		addr += PAGE_SIZE;
>> +	}
>> +
>> +	/*cache Invalidation added for Testing */
Below register programming is required. We will remove the comment.
> /* cache
>
>> +	acp63_writel(0x1, sdw_ins->acp_base + ACPAXI2AXI_ATU_CTRL);
>> +}
>> +static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
>> +				   struct snd_pcm_substream *substream,
>> +				   struct snd_pcm_hw_params *params)
>> +{
>> +	struct sdw_stream_instance *sdw_stream_data;
>> +	struct sdw_dma_dev_data *sdw_data;
>> +	u32 period_bytes;
>> +	u32 water_mark_size_reg;
>> +	u32 irq_mask, ext_intr_ctrl;
>> +	u64 size;
>> +	u32 stream_id;
>> +	u32 acp_ext_intr_cntl_reg;
>> +	int ret;
>> +
>> +	stream_id = 0;
> useless initialization...
Will remove it.
>> +	sdw_data = dev_get_drvdata(component->dev);
>> +	sdw_stream_data = substream->runtime->private_data;
>> +	if (!sdw_stream_data)
>> +		return -EINVAL;
>> +	stream_id = sdw_stream_data->stream_id;
> ... overriden here
>
>> +	sdw_data->sdw_stream[stream_id] = substream;
>> +	size = params_buffer_bytes(params);
>> +	period_bytes = params_period_bytes(params);
>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>> +	acp63_config_dma(sdw_stream_data, stream_id);
>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>> +	if (ret) {
>> +		dev_err(component->dev, "Invalid channel type\n");
>> +		return -EINVAL;
>> +	}
>> +	switch (stream_id) {
>> +	case ACP_SDW_AUDIO_TX:
>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
> so there's ONE resource to deal with external codecs? How does this work
> if you have a headset codec and an amplifier?
Are you referring to playing a same stream over headset codec and
amplifier?
It's all about channel selection from DMA perspective.
We have tested speaker aggregation and headset playback use cases.


>> +	case ACP_SDW_BT_TX:
>> +		water_mark_size_reg = ACP_BT_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_BT_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_HS_TX:
>> +		water_mark_size_reg = ACP_HS_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_HS_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		water_mark_size_reg = ACP_P1_BT_TX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_P1_BT_TX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
>> +		break;
>> +	case ACP_SDW_AUDIO_RX:
>> +		water_mark_size_reg = ACP_AUDIO_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_AUDIO_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_BT_RX:
>> +		water_mark_size_reg = ACP_BT_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_BT_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW_HS_RX:
>> +		water_mark_size_reg = ACP_HS_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_HS_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		water_mark_size_reg = ACP_P1_BT_RX_INTR_WATERMARK_SIZE;
>> +		irq_mask = BIT(ACP_P1_BT_RX_THRESHOLD);
>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
>> +		break;
>> +	default:
>> +		dev_err(component->dev, "%s: Invalid channel type\n", __func__);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ext_intr_ctrl = acp63_readl(sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
>> +	ext_intr_ctrl |= irq_mask;
>> +	acp63_writel(ext_intr_ctrl, sdw_stream_data->acp_base + acp_ext_intr_cntl_reg);
>> +	acp63_writel(period_bytes, sdw_stream_data->acp_base + water_mark_size_reg);
>> +	return 0;
>> +}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
  2023-01-11 14:58     ` Pierre-Louis Bossart
@ 2023-01-13 11:31       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 11:31 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 20:28, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Register dai ops for two controller instances.
> manager instances
will change it.
>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 7e1f618254ac..93bffe6ff9e2 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
>>  	.read_ping_status = amd_sdwc_read_ping_status,
>>  };
>>  
>> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
>> +			      struct snd_pcm_hw_params *params,
>> +			      struct snd_soc_dai *dai)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
>> +	struct sdw_stream_config sconfig;
>> +	struct sdw_port_config *pconfig;
>> +	int ch, dir;
>> +	int ret;
>> +
>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>> +	if (!dma)
>> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
>> +	dma->hw_params = params;
>> +
>> +	sconfig.direction = dir;
>> +	sconfig.ch_count = ch;
>> +	sconfig.frame_rate = params_rate(params);
>> +	sconfig.type = dma->stream_type;
>> +
>> +	sconfig.bps = snd_pcm_format_width(params_format(params));
>> +
>> +	/* Port configuration */
>> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
>> +	if (!pconfig) {
>> +		ret =  -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	pconfig->num = dai->id;
>> +	pconfig->ch_mask = (1 << ch) - 1;
>> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
>> +				    pconfig, 1, dma->stream);
>> +	if (ret)
>> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
>> +
>> +	kfree(pconfig);
>> +error:
>> +	return ret;
>> +}
> This looks inspired from intel.c, but you are not programming ANY
> registers here. is this intentional?
We don't have any additional registers to be programmed like intel.
>
>> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
>> +	int ret;
>> +
>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>> +	if (!dma)
>> +		return -EIO;
>> +
>> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
>> +	if (ret < 0) {
>> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
>> +			dma->stream->name, ret);
>> +		return ret;
>> +	}
>> +	dma->hw_params = NULL;
>> +	return 0;
>> +}
>> +
>> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
> you want to avoid using dma_data and use your own runtime. We made that
> change recently for cadence_runtime.c
>
will check the implementation.
>> +
>> +	if (stream) {

>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +			dma = dai->playback_dma_data;
>> +		else
>> +			dma = dai->capture_dma_data;
>> +
>> +		if (dma) {
>> +			dev_err(dai->dev,
>> +				"dma_data already allocated for dai %s\n",
>> +				dai->name);
>> +			return -EINVAL;
>> +		}
>> +
>> +		/* allocate and set dma info */
>> +		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
>> +		if (!dma)
>> +			return -ENOMEM;
>> +		dma->stream_type = SDW_STREAM_PCM;
>> +		dma->bus = &ctrl->bus;
>> +		dma->link_id = ctrl->instance;
>> +		dma->stream = stream;
>> +
>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +			dai->playback_dma_data = dma;
>> +		else
>> +			dai->capture_dma_data = dma;
>> +	} else {
>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
>> +			kfree(dai->playback_dma_data);
>> +			dai->playback_dma_data = NULL;
>> +		} else {
>> +			kfree(dai->capture_dma_data);
>> +			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
>> +
>> +	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +		dma = dai->playback_dma_data;
>> +	else
>> +		dma = dai->capture_dma_data;
>> +
>> +	if (!dma)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	return dma->stream;
>> +}
>> +
>> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
>> +	.hw_params = amd_sdwc_hw_params,
>> +	.hw_free = amd_sdwc_hw_free,
>> +	.set_stream = amd_pcm_set_sdw_stream,
> In the first patch there was support for PDM exposed, but here it's PDM
> only?
Didn't get your question.
First patch talks about creating dev nodes for Soundwire managers and
ACP PDM controller based on ACP pin config.

Let us know if we are missing anything?
>
>> +	.get_stream = amd_get_sdw_stream,
>> +};
>> +
>> +static const struct snd_soc_component_driver amd_sdwc_dai_component = {
>> +	.name = "soundwire",
>> +};
>> +
>> +static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	struct snd_soc_dai_driver *dais;
>> +	struct snd_soc_pcm_stream *stream;
>> +	struct device *dev;
>> +	int i, num_dais;
>> +
>> +	dev = ctrl->dev;
>> +	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
>> +	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
>> +	if (!dais)
>> +		return -ENOMEM;
>> +	for (i = 0; i < num_dais; i++) {
>> +		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
>> +		if (!dais[i].name) {
>> +			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");
> remove, we don't add error logs on memory allocation issues.
>
>> +			return -ENOMEM;
>> +		}
>> +
>> +		if (i < ctrl->num_dout_ports)
>> +			stream = &dais[i].playback;
>> +		else
>> +			stream = &dais[i].capture;
>> +
>> +		stream->channels_min = 2;
>> +		stream->channels_max = 2;
> Is this a port limitation or just a software definition?
>
>> +		stream->rates = SNDRV_PCM_RATE_48000;
>> +		stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
> Wondering if this is needed. I don't even recall why it's in the Intel
> code, we tested with 32 bit data and 192kHz, that looks unnecessary to
> me unless the hardware is really limited to those values.
>
>> +
>> +		dais[i].ops = &amd_sdwc_dai_ops;
>> +		dais[i].id = i;
>> +	}
>> +
>> +	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
>> +					       dais, num_dais);
>> +}
>> +
>>  static void amd_sdwc_probe_work(struct work_struct *work)
>>  {
>>  	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
>> @@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>>  			ret);
>>  		return ret;
>>  	}
>> +	ret = amd_sdwc_register_dais(ctrl);
>> +	if (ret) {
>> +		dev_err(dev, "CPU DAI registration failed\n");
>> +		sdw_bus_master_delete(&ctrl->bus);
>> +		return ret;
>> +	}
>>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>>  	schedule_work(&ctrl->probe_work);
>>  	return 0;
>> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
>> index 5ec39f8c2f2e..7a99d782969f 100644
>> --- a/include/linux/soundwire/sdw_amd.h
>> +++ b/include/linux/soundwire/sdw_amd.h
>> @@ -13,6 +13,7 @@
>>  #define ACP_SDW0	0
>>  #define ACP_SDW1	1
>>  #define ACP_SDW0_MAX_DAI	6
>> +#define AMD_SDW_MAX_DAIS	8
> How does this work? 6 dais for the first master and 2 for the second?
>
>>  
>>  struct acp_sdw_pdata {
>>  	u16 instance;
>> @@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
>>  	void __iomem *mmio;
>>  	struct work_struct probe_work;
>>  	struct mutex *sdw_lock;
>> +	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
> well no, a stream runtime needs to be allocated per stream and usually
> there's a 1:1 mapping between dailink and stream. A stream may use
> multiple DAIs, possibly on different masters - just like a dailink can
> rely on multiple cpu- and codec-dais.
>
> You are conflating/confusing concepts I am afraid here.
>
>>  	int num_din_ports;
>>  	int num_dout_ports;
>>  	int cols_index;
>> @@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
>>  	bool startup_done;
>>  	u32 power_mode_mask;
>>  };
>> +
>> +/**
>> + * struct sdw_amd_dma_data: AMD DMA data
>> + *
>> + * @name: SoundWire stream name
>> + * @stream: stream runtime
>> + * @bus: Bus handle
>> + * @stream_type: Stream type
>> + * @link_id: Master link id
>> + * @hw_params: hw_params to be applied in .prepare step
>> + */
>> +struct sdw_amd_dma_data {
>> +	char *name;
>> +	struct sdw_stream_runtime *stream;
>> +	struct sdw_bus *bus;
>> +	enum sdw_stream_type stream_type;
>> +	int link_id;
>> +	struct snd_pcm_hw_params *hw_params;
>> +};
>>  #endif


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
@ 2023-01-13 11:31       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 11:31 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 20:28, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> Register dai ops for two controller instances.
> manager instances
will change it.
>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> index 7e1f618254ac..93bffe6ff9e2 100644
>> --- a/drivers/soundwire/amd_master.c
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -952,6 +952,186 @@ static const struct sdw_master_ops amd_sdwc_ops = {
>>  	.read_ping_status = amd_sdwc_read_ping_status,
>>  };
>>  
>> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
>> +			      struct snd_pcm_hw_params *params,
>> +			      struct snd_soc_dai *dai)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
>> +	struct sdw_stream_config sconfig;
>> +	struct sdw_port_config *pconfig;
>> +	int ch, dir;
>> +	int ret;
>> +
>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>> +	if (!dma)
>> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
>> +	dma->hw_params = params;
>> +
>> +	sconfig.direction = dir;
>> +	sconfig.ch_count = ch;
>> +	sconfig.frame_rate = params_rate(params);
>> +	sconfig.type = dma->stream_type;
>> +
>> +	sconfig.bps = snd_pcm_format_width(params_format(params));
>> +
>> +	/* Port configuration */
>> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
>> +	if (!pconfig) {
>> +		ret =  -ENOMEM;
>> +		goto error;
>> +	}
>> +
>> +	pconfig->num = dai->id;
>> +	pconfig->ch_mask = (1 << ch) - 1;
>> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
>> +				    pconfig, 1, dma->stream);
>> +	if (ret)
>> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
>> +
>> +	kfree(pconfig);
>> +error:
>> +	return ret;
>> +}
> This looks inspired from intel.c, but you are not programming ANY
> registers here. is this intentional?
We don't have any additional registers to be programmed like intel.
>
>> +static int amd_sdwc_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
>> +	int ret;
>> +
>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>> +	if (!dma)
>> +		return -EIO;
>> +
>> +	ret = sdw_stream_remove_master(&ctrl->bus, dma->stream);
>> +	if (ret < 0) {
>> +		dev_err(dai->dev, "remove master from stream %s failed: %d\n",
>> +			dma->stream->name, ret);
>> +		return ret;
>> +	}
>> +	dma->hw_params = NULL;
>> +	return 0;
>> +}
>> +
>> +static int amd_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>> +	struct sdw_amd_dma_data *dma;
> you want to avoid using dma_data and use your own runtime. We made that
> change recently for cadence_runtime.c
>
will check the implementation.
>> +
>> +	if (stream) {

>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +			dma = dai->playback_dma_data;
>> +		else
>> +			dma = dai->capture_dma_data;
>> +
>> +		if (dma) {
>> +			dev_err(dai->dev,
>> +				"dma_data already allocated for dai %s\n",
>> +				dai->name);
>> +			return -EINVAL;
>> +		}
>> +
>> +		/* allocate and set dma info */
>> +		dma = kzalloc(sizeof(*dma), GFP_KERNEL);
>> +		if (!dma)
>> +			return -ENOMEM;
>> +		dma->stream_type = SDW_STREAM_PCM;
>> +		dma->bus = &ctrl->bus;
>> +		dma->link_id = ctrl->instance;
>> +		dma->stream = stream;
>> +
>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +			dai->playback_dma_data = dma;
>> +		else
>> +			dai->capture_dma_data = dma;
>> +	} else {
>> +		if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
>> +			kfree(dai->playback_dma_data);
>> +			dai->playback_dma_data = NULL;
>> +		} else {
>> +			kfree(dai->capture_dma_data);
>> +			dai->capture_dma_data = 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 sdw_amd_dma_data *dma;
>> +
>> +	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
>> +		dma = dai->playback_dma_data;
>> +	else
>> +		dma = dai->capture_dma_data;
>> +
>> +	if (!dma)
>> +		return ERR_PTR(-EINVAL);
>> +
>> +	return dma->stream;
>> +}
>> +
>> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
>> +	.hw_params = amd_sdwc_hw_params,
>> +	.hw_free = amd_sdwc_hw_free,
>> +	.set_stream = amd_pcm_set_sdw_stream,
> In the first patch there was support for PDM exposed, but here it's PDM
> only?
Didn't get your question.
First patch talks about creating dev nodes for Soundwire managers and
ACP PDM controller based on ACP pin config.

Let us know if we are missing anything?
>
>> +	.get_stream = amd_get_sdw_stream,
>> +};
>> +
>> +static const struct snd_soc_component_driver amd_sdwc_dai_component = {
>> +	.name = "soundwire",
>> +};
>> +
>> +static int amd_sdwc_register_dais(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	struct snd_soc_dai_driver *dais;
>> +	struct snd_soc_pcm_stream *stream;
>> +	struct device *dev;
>> +	int i, num_dais;
>> +
>> +	dev = ctrl->dev;
>> +	num_dais = ctrl->num_dout_ports + ctrl->num_din_ports;
>> +	dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
>> +	if (!dais)
>> +		return -ENOMEM;
>> +	for (i = 0; i < num_dais; i++) {
>> +		dais[i].name = devm_kasprintf(dev, GFP_KERNEL, "SDW%d Pin%d", ctrl->instance, i);
>> +		if (!dais[i].name) {
>> +			dev_err(ctrl->dev, "-ENOMEM dai name allocation failed\n");
> remove, we don't add error logs on memory allocation issues.
>
>> +			return -ENOMEM;
>> +		}
>> +
>> +		if (i < ctrl->num_dout_ports)
>> +			stream = &dais[i].playback;
>> +		else
>> +			stream = &dais[i].capture;
>> +
>> +		stream->channels_min = 2;
>> +		stream->channels_max = 2;
> Is this a port limitation or just a software definition?
>
>> +		stream->rates = SNDRV_PCM_RATE_48000;
>> +		stream->formats = SNDRV_PCM_FMTBIT_S16_LE;
> Wondering if this is needed. I don't even recall why it's in the Intel
> code, we tested with 32 bit data and 192kHz, that looks unnecessary to
> me unless the hardware is really limited to those values.
>
>> +
>> +		dais[i].ops = &amd_sdwc_dai_ops;
>> +		dais[i].id = i;
>> +	}
>> +
>> +	return devm_snd_soc_register_component(ctrl->dev, &amd_sdwc_dai_component,
>> +					       dais, num_dais);
>> +}
>> +
>>  static void amd_sdwc_probe_work(struct work_struct *work)
>>  {
>>  	struct amd_sdwc_ctrl *ctrl  = container_of(work, struct amd_sdwc_ctrl, probe_work);
>> @@ -1043,6 +1223,12 @@ static int amd_sdwc_probe(struct platform_device *pdev)
>>  			ret);
>>  		return ret;
>>  	}
>> +	ret = amd_sdwc_register_dais(ctrl);
>> +	if (ret) {
>> +		dev_err(dev, "CPU DAI registration failed\n");
>> +		sdw_bus_master_delete(&ctrl->bus);
>> +		return ret;
>> +	}
>>  	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>>  	schedule_work(&ctrl->probe_work);
>>  	return 0;
>> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
>> index 5ec39f8c2f2e..7a99d782969f 100644
>> --- a/include/linux/soundwire/sdw_amd.h
>> +++ b/include/linux/soundwire/sdw_amd.h
>> @@ -13,6 +13,7 @@
>>  #define ACP_SDW0	0
>>  #define ACP_SDW1	1
>>  #define ACP_SDW0_MAX_DAI	6
>> +#define AMD_SDW_MAX_DAIS	8
> How does this work? 6 dais for the first master and 2 for the second?
>
>>  
>>  struct acp_sdw_pdata {
>>  	u16 instance;
>> @@ -25,6 +26,7 @@ struct amd_sdwc_ctrl {
>>  	void __iomem *mmio;
>>  	struct work_struct probe_work;
>>  	struct mutex *sdw_lock;
>> +	struct sdw_stream_runtime *sruntime[AMD_SDW_MAX_DAIS];
> well no, a stream runtime needs to be allocated per stream and usually
> there's a 1:1 mapping between dailink and stream. A stream may use
> multiple DAIs, possibly on different masters - just like a dailink can
> rely on multiple cpu- and codec-dais.
>
> You are conflating/confusing concepts I am afraid here.
>
>>  	int num_din_ports;
>>  	int num_dout_ports;
>>  	int cols_index;
>> @@ -36,4 +38,23 @@ struct amd_sdwc_ctrl {
>>  	bool startup_done;
>>  	u32 power_mode_mask;
>>  };
>> +
>> +/**
>> + * struct sdw_amd_dma_data: AMD DMA data
>> + *
>> + * @name: SoundWire stream name
>> + * @stream: stream runtime
>> + * @bus: Bus handle
>> + * @stream_type: Stream type
>> + * @link_id: Master link id
>> + * @hw_params: hw_params to be applied in .prepare step
>> + */
>> +struct sdw_amd_dma_data {
>> +	char *name;
>> +	struct sdw_stream_runtime *stream;
>> +	struct sdw_bus *bus;
>> +	enum sdw_stream_type stream_type;
>> +	int link_id;
>> +	struct snd_pcm_hw_params *hw_params;
>> +};
>>  #endif


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-11 13:32     ` Pierre-Louis Bossart
@ 2023-01-13 12:36       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 12:36 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem

 
On 11/01/23 19:02, Pierre-Louis Bossart wrote:
>
>
>> +#define AMD_SDW_CLK_STOP_MODE		1
> there are multiple modes for clock stop in SoundWire, and multiple ways
> for the link manager to deal with clock stop, you want a comment to
> describe what this define refers to.
will add comments about flags explanation.
>> +#define AMD_SDW_POWER_OFF_MODE		2
>> +
>> +struct acp_sdw_pdata {
>> +	u16 instance;
>> +	struct mutex *sdw_lock;
> need a comment on what this lock protects.
>
>> +};
>> +#endif
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index b7535c7d093f..ed979e6d0c1d 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -10,7 +10,7 @@
>>  #define ACP_DEVICE_ID 0x15E2
>>  #define ACP63_REG_START		0x1240000
>>  #define ACP63_REG_END		0x1250200
>> -#define ACP63_DEVS		3
>> +#define ACP63_DEVS		5
>>  
>>  #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
>>  #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
>> @@ -55,8 +55,14 @@
>>  
>>  #define ACP63_DMIC_ADDR		2
>>  #define ACP63_PDM_MODE_DEVS		3
>> -#define ACP63_PDM_DEV_MASK		1
>>  #define ACP_DMIC_DEV	2
>> +#define ACP63_SDW0_MODE_DEVS		2
>> +#define ACP63_SDW0_SDW1_MODE_DEVS	3
>> +#define ACP63_SDW0_PDM_MODE_DEVS	4
>> +#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
>> +#define ACP63_DMIC_ADDR			2
>> +#define ACP63_SDW_ADDR			5
>> +#define AMD_SDW_MAX_CONTROLLERS		2
>>  
>>  enum acp_config {
>>  	ACP_CONFIG_0 = 0,
>> @@ -77,6 +83,12 @@ enum acp_config {
>>  	ACP_CONFIG_15,
>>  };
>>  
>> +enum acp_pdev_mask {
>> +	ACP63_PDM_DEV_MASK = 1,
>> +	ACP63_SDW_DEV_MASK,
>> +	ACP63_SDW_PDM_DEV_MASK,
>> +};
>> +
>>  struct pdm_stream_instance {
>>  	u16 num_pages;
>>  	u16 channels;
>> @@ -107,7 +119,15 @@ struct acp63_dev_data {
>>  	struct resource *res;
>>  	struct platform_device *pdev[ACP63_DEVS];
>>  	struct mutex acp_lock; /* protect shared registers */
>> +	struct fwnode_handle *sdw_fw_node;
>>  	u16 pdev_mask;
>>  	u16 pdev_count;
>>  	u16 pdm_dev_index;
>> +	u8 sdw_master_count;
> for new contributions, it's recommended to use manager and peripheral.
will use manager and peripheral terminology.
>> +	u16 sdw0_dev_index;
>> +	u16 sdw1_dev_index;
> probably need a comment on what the 0 and 1 refer to, it's not clear if
> there's any sort of dependency/link with the 'sdw_master_count' above.
>
> If this is related to the two controllers mentioned in the cover letter,
> then an explanation of the sdw_master_count would be needed as well
> (single variable for two controllers?)
will add comments for dev_index variables.
>> +	u16 sdw_dma_dev_index;
>> +	bool is_dmic_dev;
>> +	bool is_sdw_dev;
>> +	bool acp_sdw_power_off;
>>  };
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index e86f23d97584..85154cf0b2a2 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -14,6 +14,7 @@
>>  #include <linux/interrupt.h>
>>  #include <sound/pcm_params.h>
>>  #include <linux/pm_runtime.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>  
>>  #include "acp63.h"
>>  
>> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>>  	return IRQ_NONE;
>>  }
>>  
>> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>> -				    struct acp63_dev_data *acp_data)
>> +static int sdw_amd_scan_controller(struct device *dev)
>> +{
>> +	struct acp63_dev_data *acp_data;
>> +	struct fwnode_handle *link;
>> +	char name[32];
>> +	u8 count = 0;
>> +	u32 acp_sdw_power_mode = 0;
>> +	int index;
>> +	int ret;
>> +
>> +	acp_data = dev_get_drvdata(dev);
>> +	acp_data->acp_sdw_power_off = true;
>> +	/* Found controller, find links supported */
>> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
>> +					    "mipi-sdw-master-count", &count, 1);
>> +
>> +	if (ret) {
>> +		dev_err(dev,
>> +			"Failed to read mipi-sdw-master-count: %d\n", ret);
> one line?
will fix it.
>
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Check count is within bounds */
>> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
>> +		dev_err(dev, "Controller count %d exceeds max %d\n",
>> +			count, AMD_SDW_MAX_CONTROLLERS);
> No. controllers and masters are different concepts, see the DisCo
> specification for SoundWire. A Controller can have multiple Masters.
Will correct it.
>
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!count) {
>> +		dev_warn(dev, "No SoundWire controllers detected\n");
>> +		return -EINVAL;
>> +	}
> is this really a warning, looks like a dev_dbg or info to me.
>
>> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
> the term device is incorrect here, the DisCo spec does not expose ACPI
> devices for each master.
>
> "ACPI reports %d Managers"
will correct it.
>> +	acp_data->sdw_master_count  = count;
>> +	for (index = 0; index < count; index++) {
>> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
>> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
>> +		if (!link) {
>> +			dev_err(dev, "Master node %s not found\n", name);
>> +			return -EIO;
>> +		}
>> +
>> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
>> +					 &acp_sdw_power_mode);
>> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
>> +			acp_data->acp_sdw_power_off = false;
> does power-off mean 'clock-stop'?
> No. We will add comment for acp_sdw_power_off flag.
>> +	}
>> +	return 0;
>> +}
>> +
>> +		if (is_dmic_dev && is_sdw_dev) {
>> +			switch (acp_data->sdw_master_count) {
>> +			case 1:
>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>> +				break;
>> +			case 2:
>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>> +				break;
> so the cover letter is indeed wrong and confuses two controllers for two
> managers.
ACP IP has two independent manager instances driven by separate controller
each which are connected in different power domains.

we should create two separate ACPI companion devices for separate
manager instance.  Currently we have limitations with BIOS.
we are going with single ACPI companion device.
We will update the changes later.
>
>> +			default:
>> +				return -EINVAL;
>> +			}
>> +		} else if (is_dmic_dev) {
>>  			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
>>  			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
>> +		} else if (is_sdw_dev) {
>> +			switch (acp_data->sdw_master_count) {
>> +			case 1:
>> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
>> +				break;
>> +			case 2:
>> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
>> +				break;
>> +			default:
>> +				return -EINVAL;
>> +			}
>>  		}
>>  		break;
>> +	default:
>> +		break;
>>  	}
>> +	return 0;
>>  }


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-13 12:36       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 12:36 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

 
On 11/01/23 19:02, Pierre-Louis Bossart wrote:
>
>
>> +#define AMD_SDW_CLK_STOP_MODE		1
> there are multiple modes for clock stop in SoundWire, and multiple ways
> for the link manager to deal with clock stop, you want a comment to
> describe what this define refers to.
will add comments about flags explanation.
>> +#define AMD_SDW_POWER_OFF_MODE		2
>> +
>> +struct acp_sdw_pdata {
>> +	u16 instance;
>> +	struct mutex *sdw_lock;
> need a comment on what this lock protects.
>
>> +};
>> +#endif
>> diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
>> index b7535c7d093f..ed979e6d0c1d 100644
>> --- a/sound/soc/amd/ps/acp63.h
>> +++ b/sound/soc/amd/ps/acp63.h
>> @@ -10,7 +10,7 @@
>>  #define ACP_DEVICE_ID 0x15E2
>>  #define ACP63_REG_START		0x1240000
>>  #define ACP63_REG_END		0x1250200
>> -#define ACP63_DEVS		3
>> +#define ACP63_DEVS		5
>>  
>>  #define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK	0x00010001
>>  #define ACP_PGFSM_CNTL_POWER_ON_MASK	1
>> @@ -55,8 +55,14 @@
>>  
>>  #define ACP63_DMIC_ADDR		2
>>  #define ACP63_PDM_MODE_DEVS		3
>> -#define ACP63_PDM_DEV_MASK		1
>>  #define ACP_DMIC_DEV	2
>> +#define ACP63_SDW0_MODE_DEVS		2
>> +#define ACP63_SDW0_SDW1_MODE_DEVS	3
>> +#define ACP63_SDW0_PDM_MODE_DEVS	4
>> +#define ACP63_SDW0_SDW1_PDM_MODE_DEVS   5
>> +#define ACP63_DMIC_ADDR			2
>> +#define ACP63_SDW_ADDR			5
>> +#define AMD_SDW_MAX_CONTROLLERS		2
>>  
>>  enum acp_config {
>>  	ACP_CONFIG_0 = 0,
>> @@ -77,6 +83,12 @@ enum acp_config {
>>  	ACP_CONFIG_15,
>>  };
>>  
>> +enum acp_pdev_mask {
>> +	ACP63_PDM_DEV_MASK = 1,
>> +	ACP63_SDW_DEV_MASK,
>> +	ACP63_SDW_PDM_DEV_MASK,
>> +};
>> +
>>  struct pdm_stream_instance {
>>  	u16 num_pages;
>>  	u16 channels;
>> @@ -107,7 +119,15 @@ struct acp63_dev_data {
>>  	struct resource *res;
>>  	struct platform_device *pdev[ACP63_DEVS];
>>  	struct mutex acp_lock; /* protect shared registers */
>> +	struct fwnode_handle *sdw_fw_node;
>>  	u16 pdev_mask;
>>  	u16 pdev_count;
>>  	u16 pdm_dev_index;
>> +	u8 sdw_master_count;
> for new contributions, it's recommended to use manager and peripheral.
will use manager and peripheral terminology.
>> +	u16 sdw0_dev_index;
>> +	u16 sdw1_dev_index;
> probably need a comment on what the 0 and 1 refer to, it's not clear if
> there's any sort of dependency/link with the 'sdw_master_count' above.
>
> If this is related to the two controllers mentioned in the cover letter,
> then an explanation of the sdw_master_count would be needed as well
> (single variable for two controllers?)
will add comments for dev_index variables.
>> +	u16 sdw_dma_dev_index;
>> +	bool is_dmic_dev;
>> +	bool is_sdw_dev;
>> +	bool acp_sdw_power_off;
>>  };
>> diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
>> index e86f23d97584..85154cf0b2a2 100644
>> --- a/sound/soc/amd/ps/pci-ps.c
>> +++ b/sound/soc/amd/ps/pci-ps.c
>> @@ -14,6 +14,7 @@
>>  #include <linux/interrupt.h>
>>  #include <sound/pcm_params.h>
>>  #include <linux/pm_runtime.h>
>> +#include <linux/soundwire/sdw_amd.h>
>>  
>>  #include "acp63.h"
>>  
>> @@ -134,12 +135,68 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
>>  	return IRQ_NONE;
>>  }
>>  
>> -static void get_acp63_device_config(u32 config, struct pci_dev *pci,
>> -				    struct acp63_dev_data *acp_data)
>> +static int sdw_amd_scan_controller(struct device *dev)
>> +{
>> +	struct acp63_dev_data *acp_data;
>> +	struct fwnode_handle *link;
>> +	char name[32];
>> +	u8 count = 0;
>> +	u32 acp_sdw_power_mode = 0;
>> +	int index;
>> +	int ret;
>> +
>> +	acp_data = dev_get_drvdata(dev);
>> +	acp_data->acp_sdw_power_off = true;
>> +	/* Found controller, find links supported */
>> +	ret = fwnode_property_read_u8_array((acp_data->sdw_fw_node),
>> +					    "mipi-sdw-master-count", &count, 1);
>> +
>> +	if (ret) {
>> +		dev_err(dev,
>> +			"Failed to read mipi-sdw-master-count: %d\n", ret);
> one line?
will fix it.
>
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Check count is within bounds */
>> +	if (count > AMD_SDW_MAX_CONTROLLERS) {
>> +		dev_err(dev, "Controller count %d exceeds max %d\n",
>> +			count, AMD_SDW_MAX_CONTROLLERS);
> No. controllers and masters are different concepts, see the DisCo
> specification for SoundWire. A Controller can have multiple Masters.
Will correct it.
>
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!count) {
>> +		dev_warn(dev, "No SoundWire controllers detected\n");
>> +		return -EINVAL;
>> +	}
> is this really a warning, looks like a dev_dbg or info to me.
>
>> +	dev_dbg(dev, "ACPI reports %d Soundwire Controller devices\n", count);
> the term device is incorrect here, the DisCo spec does not expose ACPI
> devices for each master.
>
> "ACPI reports %d Managers"
will correct it.
>> +	acp_data->sdw_master_count  = count;
>> +	for (index = 0; index < count; index++) {
>> +		snprintf(name, sizeof(name), "mipi-sdw-link-%d-subproperties", index);
>> +		link = fwnode_get_named_child_node(acp_data->sdw_fw_node, name);
>> +		if (!link) {
>> +			dev_err(dev, "Master node %s not found\n", name);
>> +			return -EIO;
>> +		}
>> +
>> +		fwnode_property_read_u32(link, "amd-sdw-power-mode",
>> +					 &acp_sdw_power_mode);
>> +		if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
>> +			acp_data->acp_sdw_power_off = false;
> does power-off mean 'clock-stop'?
> No. We will add comment for acp_sdw_power_off flag.
>> +	}
>> +	return 0;
>> +}
>> +
>> +		if (is_dmic_dev && is_sdw_dev) {
>> +			switch (acp_data->sdw_master_count) {
>> +			case 1:
>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>> +				break;
>> +			case 2:
>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>> +				break;
> so the cover letter is indeed wrong and confuses two controllers for two
> managers.
ACP IP has two independent manager instances driven by separate controller
each which are connected in different power domains.

we should create two separate ACPI companion devices for separate
manager instance.  Currently we have limitations with BIOS.
we are going with single ACPI companion device.
We will update the changes later.
>
>> +			default:
>> +				return -EINVAL;
>> +			}
>> +		} else if (is_dmic_dev) {
>>  			acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
>>  			acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
>> +		} else if (is_sdw_dev) {
>> +			switch (acp_data->sdw_master_count) {
>> +			case 1:
>> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_MODE_DEVS;
>> +				break;
>> +			case 2:
>> +				acp_data->pdev_mask = ACP63_SDW_DEV_MASK;
>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_MODE_DEVS;
>> +				break;
>> +			default:
>> +				return -EINVAL;
>> +			}
>>  		}
>>  		break;
>> +	default:
>> +		break;
>>  	}
>> +	return 0;
>>  }


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-13 11:16       ` Mukunda,Vijendar
@ 2023-01-13 17:05         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:05 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem


>>> +	sdw_data->sdw_stream[stream_id] = substream;
>>> +	size = params_buffer_bytes(params);
>>> +	period_bytes = params_period_bytes(params);
>>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>>> +	acp63_config_dma(sdw_stream_data, stream_id);
>>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>>> +	if (ret) {
>>> +		dev_err(component->dev, "Invalid channel type\n");
>>> +		return -EINVAL;
>>> +	}
>>> +	switch (stream_id) {
>>> +	case ACP_SDW_AUDIO_TX:
>>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>>> +		break;
>> so there's ONE resource to deal with external codecs? How does this work
>> if you have a headset codec and an amplifier?
> Are you referring to playing a same stream over headset codec and
> amplifier?
> It's all about channel selection from DMA perspective.
> We have tested speaker aggregation and headset playback use cases.

No, I wasn't asking about playing the same content to different sinks.

I was referring to playing/recording different content to/from different
devices.

Even when interfacing with a single device, there are interesting
topologies in the SDCA spec that would require multiple DMA transfers
conveying unrelated content (or processed content from the same source).


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-13 17:05         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:05 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem


>>> +	sdw_data->sdw_stream[stream_id] = substream;
>>> +	size = params_buffer_bytes(params);
>>> +	period_bytes = params_period_bytes(params);
>>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>>> +	acp63_config_dma(sdw_stream_data, stream_id);
>>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>>> +	if (ret) {
>>> +		dev_err(component->dev, "Invalid channel type\n");
>>> +		return -EINVAL;
>>> +	}
>>> +	switch (stream_id) {
>>> +	case ACP_SDW_AUDIO_TX:
>>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>>> +		break;
>> so there's ONE resource to deal with external codecs? How does this work
>> if you have a headset codec and an amplifier?
> Are you referring to playing a same stream over headset codec and
> amplifier?
> It's all about channel selection from DMA perspective.
> We have tested speaker aggregation and headset playback use cases.

No, I wasn't asking about playing the same content to different sinks.

I was referring to playing/recording different content to/from different
devices.

Even when interfacing with a single device, there are interesting
topologies in the SDCA spec that would require multiple DMA transfers
conveying unrelated content (or processed content from the same source).


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-13 12:36       ` Mukunda,Vijendar
@ 2023-01-13 17:11         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:11 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list


>>> +		if (is_dmic_dev && is_sdw_dev) {
>>> +			switch (acp_data->sdw_master_count) {
>>> +			case 1:
>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>> +				break;
>>> +			case 2:
>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>> +				break;
>> so the cover letter is indeed wrong and confuses two controllers for two
>> managers.
> ACP IP has two independent manager instances driven by separate controller
> each which are connected in different power domains.
> 
> we should create two separate ACPI companion devices for separate
> manager instance.  Currently we have limitations with BIOS.
> we are going with single ACPI companion device.
> We will update the changes later.

Humm, this is tricky. The BIOS interface isn't something that can be
changed at will on the kernel side, you'd have to maintain two solutions
with a means to detect which one to use.

Or is this is a temporary issue on development devices, then that part
should probably not be upstreamed.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-13 17:11         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:11 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem


>>> +		if (is_dmic_dev && is_sdw_dev) {
>>> +			switch (acp_data->sdw_master_count) {
>>> +			case 1:
>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>> +				break;
>>> +			case 2:
>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>> +				break;
>> so the cover letter is indeed wrong and confuses two controllers for two
>> managers.
> ACP IP has two independent manager instances driven by separate controller
> each which are connected in different power domains.
> 
> we should create two separate ACPI companion devices for separate
> manager instance.  Currently we have limitations with BIOS.
> we are going with single ACPI companion device.
> We will update the changes later.

Humm, this is tricky. The BIOS interface isn't something that can be
changed at will on the kernel side, you'd have to maintain two solutions
with a means to detect which one to use.

Or is this is a temporary issue on development devices, then that part
should probably not be upstreamed.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
  2023-01-13 11:31       ` Mukunda,Vijendar
@ 2023-01-13 17:13         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:13 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



>>> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
>>> +			      struct snd_pcm_hw_params *params,
>>> +			      struct snd_soc_dai *dai)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>>> +	struct sdw_amd_dma_data *dma;
>>> +	struct sdw_stream_config sconfig;
>>> +	struct sdw_port_config *pconfig;
>>> +	int ch, dir;
>>> +	int ret;
>>> +
>>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>>> +	if (!dma)
>>> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
>>> +	dma->hw_params = params;
>>> +
>>> +	sconfig.direction = dir;
>>> +	sconfig.ch_count = ch;
>>> +	sconfig.frame_rate = params_rate(params);
>>> +	sconfig.type = dma->stream_type;
>>> +
>>> +	sconfig.bps = snd_pcm_format_width(params_format(params));
>>> +
>>> +	/* Port configuration */
>>> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
>>> +	if (!pconfig) {
>>> +		ret =  -ENOMEM;
>>> +		goto error;
>>> +	}
>>> +
>>> +	pconfig->num = dai->id;
>>> +	pconfig->ch_mask = (1 << ch) - 1;
>>> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
>>> +				    pconfig, 1, dma->stream);
>>> +	if (ret)
>>> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
>>> +
>>> +	kfree(pconfig);
>>> +error:
>>> +	return ret;
>>> +}
>> This looks inspired from intel.c, but you are not programming ANY
>> registers here. is this intentional?
> We don't have any additional registers to be programmed like intel.

ok, this is worthy of a comment.


>>> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
>>> +	.hw_params = amd_sdwc_hw_params,
>>> +	.hw_free = amd_sdwc_hw_free,
>>> +	.set_stream = amd_pcm_set_sdw_stream,
>> In the first patch there was support for PDM exposed, but here it's PDM
>> only?
> Didn't get your question.
> First patch talks about creating dev nodes for Soundwire managers and
> ACP PDM controller based on ACP pin config.

Sorry, my comment has a typo.

I meant that the first patch exposed PDM support but here you only have PCM?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 03/19] soundwire: amd: register sdw controller dai ops
@ 2023-01-13 17:13         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:13 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



>>> +static int amd_sdwc_hw_params(struct snd_pcm_substream *substream,
>>> +			      struct snd_pcm_hw_params *params,
>>> +			      struct snd_soc_dai *dai)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = snd_soc_dai_get_drvdata(dai);
>>> +	struct sdw_amd_dma_data *dma;
>>> +	struct sdw_stream_config sconfig;
>>> +	struct sdw_port_config *pconfig;
>>> +	int ch, dir;
>>> +	int ret;
>>> +
>>> +	dma = snd_soc_dai_get_dma_data(dai, substream);
>>> +	if (!dma)
>>> +		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(ctrl->dev, "%s: dir:%d dai->id:0x%x\n", __func__, dir, dai->id);
>>> +	dma->hw_params = params;
>>> +
>>> +	sconfig.direction = dir;
>>> +	sconfig.ch_count = ch;
>>> +	sconfig.frame_rate = params_rate(params);
>>> +	sconfig.type = dma->stream_type;
>>> +
>>> +	sconfig.bps = snd_pcm_format_width(params_format(params));
>>> +
>>> +	/* Port configuration */
>>> +	pconfig = kzalloc(sizeof(*pconfig), GFP_KERNEL);
>>> +	if (!pconfig) {
>>> +		ret =  -ENOMEM;
>>> +		goto error;
>>> +	}
>>> +
>>> +	pconfig->num = dai->id;
>>> +	pconfig->ch_mask = (1 << ch) - 1;
>>> +	ret = sdw_stream_add_master(&ctrl->bus, &sconfig,
>>> +				    pconfig, 1, dma->stream);
>>> +	if (ret)
>>> +		dev_err(ctrl->dev, "add master to stream failed:%d\n", ret);
>>> +
>>> +	kfree(pconfig);
>>> +error:
>>> +	return ret;
>>> +}
>> This looks inspired from intel.c, but you are not programming ANY
>> registers here. is this intentional?
> We don't have any additional registers to be programmed like intel.

ok, this is worthy of a comment.


>>> +static const struct snd_soc_dai_ops amd_sdwc_dai_ops = {
>>> +	.hw_params = amd_sdwc_hw_params,
>>> +	.hw_free = amd_sdwc_hw_free,
>>> +	.set_stream = amd_pcm_set_sdw_stream,
>> In the first patch there was support for PDM exposed, but here it's PDM
>> only?
> Didn't get your question.
> First patch talks about creating dev nodes for Soundwire managers and
> ACP PDM controller based on ACP pin config.

Sorry, my comment has a typo.

I meant that the first patch exposed PDM support but here you only have PCM?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-13 10:58             ` Mukunda,Vijendar
@ 2023-01-13 17:33               ` Pierre-Louis Bossart
  2023-01-13 19:57                   ` Mark Brown
  0 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 17:33 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti


>>>>>>> increase the runtime suspend delay to 3 seconds.
>>>>>> You have a parent PCI device and a set of child devices for each
>>>>>> manager. The parent PCI device cannot suspend before all its children
>>>>>> are also suspended, so shouldn't the delay be modified at the
>>>>>> manager level?
>>>>>>
>>>>>> Not getting what this delay is and how this would deal with a lengthy
>>>>>> enumeration/initialization process.
>>>>> Yes agreed. Until Child devices are suspended, parent device will
>>>>> be in D0 state. We will rephrase the commit message.
>>>>>
>>>>> Machine driver node will be created by ACP PCI driver.
>>>>> We have added delay in machine driver to make sure
>>>>> two manager instances completes codec enumeration and
>>>>> peripheral initialization before registering the sound card.
>>>>> Without adding delay in machine driver will result early card
>>>>> registration before codec initialization is completed. Manager
>>>>> will enter in to bad state due to codec read/write failures.
>>>>> We are intended to keep the ACP in D0 state, till sound card
>>>>> is created and jack controls are initialized. To handle, at manager
>>>>> level increased runtime suspend delay.
>>>> This doesn't look too good. You should not assume any timing
>>>> dependencies in the machine driver probe. I made that mistake in earlier
>>>> versions and we had to revisit all this to make sure drivers could be
>>>> bound/unbound at any time.
>>> Rather than a timing dependency, could you perhaps prohibit runtime PM
>>> and have a codec make a callback to indicate it's fully initialized and
>>> then allow runtime PM again?
>> We already have enumeration and initialization 'struct completion' that
>> are used by codec drivers to know if the hardware is usable. We also
>> have pm_runtime_get_sync() is the bus layer to make sure the codec is
>> resumed before being accessed.
> Instead of walking through codec list and checking completion status
> for every codec over the link, can we have some solution where once
> all codecs gets enumerated and initialized, a variable in bus instance
> will be updated to know all peripherals initialized. So that we can
> check this variable in machine driver.

No, because the bus cannot know for sure what codecs to expect on the
platform.

This comes from the design, we first create a bunch of devices based on
ACPI information, which causes the drivers to probe. Then when the bus
starts, codecs that are physically present on the bus will attach and be
initialized in the update_status callback.

It's perfectly acceptable for devices to be exposed in ACPI and not be
present on a board. The bus wouldn't know what is needed.

I am still not clear on what the "early card registration" issue might be.

Can you clarify which codec registers are accessed in that case, are
those supposed to be managed with regmap? one possibility is that we
need to make sure the codec drivers are in regmap cache_only probe at
the probe time, that may prevent this sort of uncontrolled register
access. I had a PR on this that I haven't touched in a while, see [1]

I do recall some issues with the codec jacks, where if the card
registration happens too late the codec might have suspended. But we
added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
that was solved.

[1] https://github.com/thesofproject/linux/pull/3941

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-11 14:37     ` Pierre-Louis Bossart
@ 2023-01-13 18:21       ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 18:21 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 11/01/23 20:07, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> AMD ACP IP block has two soundwire controller devices.
> s/controller/manager?
will rephrase the commit message.
>
>> Add support for
>> - Master driver probe & remove sequence
>> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>>   enable sdw pads
>> - Master driver sdw_master_ops & port_ops callbacks
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
>>  drivers/soundwire/amd_master.h    |  279 ++++++++
>>  include/linux/soundwire/sdw_amd.h |   21 +
>>  3 files changed, 1375 insertions(+)
>>  create mode 100644 drivers/soundwire/amd_master.c
>>  create mode 100644 drivers/soundwire/amd_master.h
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> new file mode 100644
>> index 000000000000..7e1f618254ac
>> --- /dev/null
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -0,0 +1,1075 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * SoundWire AMD Master driver
>> + *
>> + * Copyright 2023 Advanced Micro Devices, Inc.
>> + */
>> +
>> +#include <linux/completion.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/soundwire/sdw_amd.h>
>> +#include <linux/wait.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/soc.h>
>> +#include "bus.h"
>> +#include "amd_master.h"
>> +
>> +#define DRV_NAME "amd_sdw_controller"
>> +
>> +#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
>> +
>> +static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 sw_pad_enable_mask;
>> +	u32 sw_pad_pulldown_mask;
>> +	u32 sw_pad_pulldown_val;
>> +	u32 val = 0;
>> +
>> +	switch (ctrl->instance) {
> Goodness no. A controller has one or more masters. It cannot have pins
> as described in the SoundWire master specification.
we are referring to manager instance only.
will modify the private data structure name.
>
>> +	case ACP_SDW0:
>> +		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
>> +		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
>> +		break;
>> +	case ACP_SDW1:
>> +		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
>> +		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(ctrl->sdw_lock);
>> +	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
>> +	val |= sw_pad_enable_mask;
>> +	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	usleep_range(1000, 1500);
>> +
>> +	mutex_lock(ctrl->sdw_lock);
>> +	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
>> +	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
>> +	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	return 0;
>> +}
>> +
>> +static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
>> +	u32 val = 0;
>> +	u32 timeout = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +
>> +	/* Sdw Controller reset */
>> +	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
>> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
>> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
>> +			break;
>> +		usleep_range(1, 5);
>> +		timeout++;
>> +	}
> no test on timeout here to check if the bus was indeed reset?
It's a miss. we will add the code.
>
> If you are talking about bus_reset you are referring to a master btw.
> The terms bus/master/link are interchangeable. A controller is not
> defined in the SoundWire specification, this is part of the DisCo spec
> to deal with enumeration when multiple bus/master/link are supported in
> the platform.
>
>> +	timeout = 0;
>> +	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
>> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +	while (val) {
>> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
>> +			break;
>> +		usleep_range(1, 5);
>> +		timeout++;
>> +	}
>> +	if (timeout == AMD_DELAY_LOOP_ITERATION) {
>> +		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +	retry_count = 0;
>> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (!val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 acp_sw_en_reg;
>> +	u32 acp_sw_en_stat_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 acp_sw_en_reg;
>> +	u32 acp_sw_en_stat_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
>> +
>> +	/*
>> +	 * After invoking controller disable sequence, check whether
>> +	 * controller has executed clock stop sequence. In this case,
>> +	 * controller should ignore checking enable status register.
> again clock stop is a sequence at the master/link/bus level, not the
> controller.
Throughout the code we are referring manager instance only.
>> +	 */
>> +	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +	if (val)
>> +		return 0;
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (!val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 val;
>> +	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
>> +	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;
> should be renamed and end in CNTL0 if the other is CNTL1
Its register header file macro. It's still good to go.
>
> And it's manager anyways, not controller.
It's manager only.
>> +		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
>> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
>> +		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
>> +		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
>> +		sw_err_intr_mask = SW_ERROR_INTR_MASK;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
>> +		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
>> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
>> +		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
>> +		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
>> +		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	mutex_lock(ctrl->sdw_lock);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
>> +	val |= acp_sdw_intr_mask;
>> +	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
>> +	if (val)
>> +		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
>> +	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
>> +	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
>> +	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
>> +	return 0;
>> +}
>> +
>> +static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
>> +{
>> +	u64 resp = 0;
>> +	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
>> +	u32 imm_resp_uword_reg, imm_resp_lword_reg;
>> +	u32 resp_lower, resp_high;
>> +	u32 sts = 0;
>> +	u32 timeout = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		imm_cmd_stat_reg = SW_IMM_CMD_STS;
>> +		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
>> +		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
>> +		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
>> +		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
>> +		break;
>> +	case ACP_SDW1:
>> +		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
>> +		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
>> +		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
>> +		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
>> +		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;
> naming consistency would be good, the P1 is sometimes a prefix,
> sometimes a suffix, sometimes in the middle. Pick one.
These are taken from our AMD register header file.
It's still good to go.
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +	while (sts & AMD_SDW_IMM_CMD_BUSY) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
>> +				ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +
>> +	timeout = 0;
>> +	if (sts & AMD_SDW_IMM_RES_VALID) {
>> +		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
>> +		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
>> +	}
>> +	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
>> +	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
>> +
>> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
>> +	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
>> +	timeout = 0;
>> +	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
>> +	while ((sts & AMD_SDW_IMM_RES_VALID)) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +	resp = resp_high;
>> +	resp = (resp << 32) | resp_lower;
>> +	return resp;
>> +}
>> +
>> +static enum sdw_command_response
>> +amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
>> +{
>> +	struct sdw_msg scp_msg = {0};
>> +	u64 response_buf[2] = {0};
>> +	u32 uword = 0, lword = 0;
>> +	int nack = 0, no_ack = 0;
>> +	int index, timeout = 0;
>> +
>> +	scp_msg.dev_num = msg->dev_num;
>> +	scp_msg.addr = SDW_SCP_ADDRPAGE1;
>> +	scp_msg.buf = &msg->addr_page1;
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
>> +	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +	scp_msg.addr = SDW_SCP_ADDRPAGE2;
>> +	scp_msg.buf = &msg->addr_page2;
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
>> +	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +
>> +	/* check response the writes */
> response to the writes?  after the writes?
will correct the comment.
>> +	for (index = 0; index < 2; index++) {
>> +		if (response_buf[index] == -ETIMEDOUT) {
>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>> +			timeout = 1;
>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>> +			no_ack = 1;
>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>> +				nack = 1;
>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>> +			}
> this is a copy of the cadence_master.c code... With the error added that
> this is not for a controller but for a master...
Its manager instance only. Our immediate command and response
mechanism allows sending commands over the link and get the
response for every command immediately, unlike as mentioned in
candence_master.c.
>> +		}
>> +	}
>> +
>> +	if (timeout) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_TIMEOUT;
>> +	}
>> +
>> +	if (nack) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_FAIL;
>> +	}
>> +
>> +	if (no_ack) {
>> +		dev_dbg_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_IGNORED;
>> +	}
>> +	return SDW_CMD_OK;
> this should probably become a helper since the response is really the
> same as in cadence_master.c
>
> There's really room for optimization and reuse here.
not really needed. Please refer above comment as command/response
mechanism differs from Intel's implementation.
>
>> +}
>> +
>> +static int amd_prep_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
>> +{
>> +	int ret;
>> +
>> +	if (msg->page) {
>> +		ret = amd_program_scp_addr(ctrl, msg);
>> +		if (ret) {
>> +			msg->len = 0;
>> +			return ret;
>> +		}
>> +	}
>> +	switch (msg->flags) {
>> +	case SDW_MSG_FLAG_READ:
>> +		*cmd = AMD_SDW_CMD_READ;
>> +		break;
>> +	case SDW_MSG_FLAG_WRITE:
>> +		*cmd = AMD_SDW_CMD_WRITE;
>> +		break;
>> +	default:
>> +		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
>> +		return -EINVAL;
>> +	}
>> +	return 0;
>> +}
> this is the same code as in cadence_master.c
>
> you just replaced sdw_cnds by amd_sdw_ctrl (which is a mistake) and
> cdns->dev by ctrl->dev.
>
>> +
>> +static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
>> +				       int cmd, int cmd_offset)
>> +{
>> +	u64 response = 0;
>> +	u32 uword = 0, lword = 0;
>> +	int nack = 0, no_ack = 0;
>> +	int timeout = 0;
>> +
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
>> +	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +
>> +	if (response & AMD_SDW_MCP_RESP_ACK) {
>> +		if (cmd == AMD_SDW_CMD_READ)
>> +			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
>> +	} else {
>> +		no_ack = 1;
>> +		if (response == -ETIMEDOUT) {
>> +			timeout = 1;
>> +		} else if (response & AMD_SDW_MCP_RESP_NACK) {
>> +			nack = 1;
>> +			dev_err(ctrl->dev, "Program SCP NACK received\n");
>> +		}
>> +	}
>> +
>> +	if (timeout) {
>> +		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_TIMEOUT;
>> +	}
>> +	if (nack) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "command response NACK received for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_FAIL;
>> +	}
>> +
>> +	if (no_ack) {
>> +		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_IGNORED;
>> +	}
>> +	return SDW_CMD_OK;
>> +}
>> +
>> +static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	int ret, i;
>> +	int cmd = 0;
>> +
>> +	ret = amd_prep_msg(ctrl, msg, &cmd);
>> +	if (ret)
>> +		return SDW_CMD_FAIL_OTHER;
>> +	for (i = 0; i < msg->len; i++) {
>> +		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return SDW_CMD_OK;
>> +}
>> +
>> +static enum sdw_command_response
>> +amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	struct sdw_msg msg;
>> +
>> +	/* Create dummy message with valid device number */
>> +	memset(&msg, 0, sizeof(msg));
>> +	msg.dev_num = dev_num;
>> +	return amd_program_scp_addr(ctrl, &msg);
>> +}
>> +
>> +static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u64 response;
>> +	u32 slave_stat = 0;
>> +
>> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
>> +	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
>> +	return slave_stat;
>> +}
>> +
>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>> +					 struct sdw_transport_data *t_data)
>> +{
>> +	struct sdw_slave_runtime *s_rt = NULL;
>> +	struct sdw_port_runtime *p_rt;
>> +	int port_bo, sample_int;
>> +	unsigned int rate, bps, ch = 0;
>> +	unsigned int slave_total_ch;
>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>> +
>> +	port_bo = t_data->block_offset;
>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>> +		rate = m_rt->stream->params.rate;
>> +		bps = m_rt->stream->params.bps;
>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>> +		slave_total_ch = 0;
>> +
>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>> +
>> +			sdw_fill_xport_params(&p_rt->transport_params,
>> +					      p_rt->num, false,
>> +					      SDW_BLK_GRP_CNT_1,
>> +					      sample_int, port_bo, port_bo >> 8,
>> +					      t_data->hstart,
>> +					      t_data->hstop,
>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +			sdw_fill_port_params(&p_rt->port_params,
>> +					     p_rt->num, bps,
>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>> +					     b_params->s_data_mode);
>> +
>> +			port_bo += bps * ch;
>> +			slave_total_ch += ch;
>> +		}
>> +
>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>> +		    m_rt->ch_count == slave_total_ch) {
>> +			port_bo = t_data->block_offset;
>> +		}
>> +	}
>> +}
> ok, this is really bad.
>
> This is a verbatim copy of the same function in
> generic_bandwidth_allocation.c
>
> see
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>
> You only removed the comments and renamed the function.
>
> Seriously? Why would you do that?
>
> And in addition, this has *NOTHING* to do with the master support.
>
> Programming the ports on peripheral side is something that happens at
> the stream level.
>
> I am afraid it's a double NAK, or rather NAK^2 from me here.
Our intention is to implement our own compute params callback.
Sorry, instead of making a copied one , we could have exported this
API.
>> +
>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>> +{
>> +	struct sdw_transport_data t_data = {0};
>> +	struct sdw_master_runtime *m_rt;
>> +	struct sdw_port_runtime *p_rt;
>> +	struct sdw_bus_params *b_params = &bus->params;
>> +	int port_bo, hstart, hstop, sample_int;
>> +	unsigned int rate, bps;
>> +
>> +	port_bo  = 0;
>> +	hstart = 1;
>> +	hstop = bus->params.col - 1;
>> +	t_data.hstop = hstop;
>> +	t_data.hstart = hstart;
>> +
>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>> +		rate = m_rt->stream->params.rate;
>> +		bps = m_rt->stream->params.bps;
>> +		sample_int = (bus->params.curr_dr_freq / rate);
>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>> +			port_bo = (p_rt->num * 64) + 1;
>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>> +				p_rt->num, hstart, hstop, port_bo);
>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>> +					      port_bo, port_bo >> 8, hstart, hstop,
>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +			sdw_fill_port_params(&p_rt->port_params,
>> +					     p_rt->num, bps,
>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>> +					     b_params->m_data_mode);
>> +			t_data.hstart = hstart;
>> +			t_data.hstop = hstop;
>> +			t_data.block_offset = port_bo;
>> +			t_data.sub_block_offset = 0;
>> +		}
>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>> +	}
>> +	return 0;
>> +}
> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>
> You would need a lot more comments to convince me that this is
> intentional and needed.
This is intentional. We have a HW bug, if we go it generic bdw allocation
API, when we launch multiple streams, we are observing noise for shorter
duration for active stream.
To avoid that, we have slightly modified the sdw_compute_master_ports()
API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
We will expand the coverage in the future.
 
>> +
>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>> +				unsigned int bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>> +
>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		channel_type = p_params->num;
>> +		break;
>> +	case ACP_SDW1:
>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
> you'll have to explain what you mean by 'channel_type'
>
> This looks like the streams that can be supported by this master
> implementation, with dailinks for each.
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)

Each port number on Manager side is mapped to a channel number.
i.e SDW0 Pin0 -> port number 0 -> Audio TX
    SDW0 Pin1 -> Port number 1 -> Headset TX
    SDW0 Pin2 -> Port number 2 -> BT TX
    SDW0 Pin3 -> port number 3 -> Audio RX
    SDW0 Pin4 -> Port number 4 -> Headset RX
    SDW0 Pin5 -> Port number 5 -> BT RX

Whereas for SDW1 instance

    SDW1 Pin0 -> port number 0 -> P1 BT TX
    SDW1 Pin1 -> Port number 1 -> P1 BT RX
    
We use this channel value to program register set for transport params,
port params and Channel enable for each manager instance.
We need to use same channel mapping for programming DMA controller
registers in Soundwire DMA driver.
i.e if AUDIO TX channel is selected then we need to use Audio TX registers
for DMA programming in Soundwire DMA driver.
>
>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_HS_TX:
>> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_BT_TX:
>> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_AUDIO_RX:
>> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_HS_RX:
>> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_BT_RX:
>> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
>> +		break;
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
>> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>> +				     struct sdw_transport_params *params,
>> +				     enum sdw_reg_bank bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 ssp_counter_reg;
>> +	u32 dpn_frame_fmt;
>> +	u32 dpn_sampleinterval;
>> +	u32 dpn_hctrl;
>> +	u32 dpn_offsetctrl;
>> +	u32 dpn_lanectrl;
>> +	u32 channel_type;
>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>> +	u32 offset_reg, lane_ctrl_reg;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>> +		channel_type = params->port_num;
>> +		break;
>> +	case ACP_SDW1:
>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
> There's obviously a dependency between SDW0 and SDW1 managers that you
> haven't described?
No, both are independent manager instances which are connected in
different power domains.
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>> +		__func__, params->port_num, channel_type);
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> This is confusing. Is this about enabling a stream or selecting the lane
> for this port? Same for all cases.
>
> is this saying that the two cases are handled by the same register -
> unlike what is normative for the peripherals where the two concepts are
> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
we have to refer the same register to program channel enable and lane
ctrl as per our soundwire register definition.
>
>> +		break;
>> +	}
>> +	case ACP_SDW0_HS_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
>> +		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_BT_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
>> +		offset_reg = ACP_SW_BT_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW1_BT_TX:
>> +	{
>> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
>> +		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_AUDIO_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
>> +		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
>> +		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_HS_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
>> +		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_BT_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
>> +		offset_reg = ACP_SW_BT_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW1_BT_RX:
>> +	{
>> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
>> +		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
>> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
>> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
>> +	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
>> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
>> +
>> +	dpn_sampleinterval = params->sample_interval - 1;
>> +	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
>> +
>> +	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
>> +	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
>> +	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
>> +
>> +	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
>> +	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
>> +	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
>> +
>> +	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
>> +	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
>> +	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
>> +				struct sdw_enable_ch *enable_ch,
>> +				unsigned int bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 dpn_ch_enable;
>> +	u32 ch_enable_reg, channel_type;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		channel_type = enable_ch->port_num;
>> +		break;
>> +	case ACP_SDW1:
>> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
>> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> in the function above, I commented on
>
> 		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>
> This looks really weird. You need to add comments is this is really
> intentional.
Please refer above comment reply. We will add comments.
>> +		break;
>> +	case ACP_SDW0_HS_TX:
>> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_BT_TX:
>> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_AUDIO_RX:
>> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_HS_RX:
>> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_BT_RX:
>> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +
>> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
>> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
>> +	if (enable_ch->enable)
>> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
>> +	else
>> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
>> +	return 0;
>> +}
>> +
>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
> same quirk as Intel, nice :-)
>
>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
> And here too. Is this really needed or just-copy-pasted?
No, It's not a copy and paste. We have seen issues bus clash/parity errors
during peripheral enumeration/initialization across the multiple links without
this quirk. We have also seen device alerts missed during peripheral initialization
sequence.

>> +	fwnode_property_read_u32(link, "amd-sdw-wake-enable", &wake_en_mask);
>> +	ctrl->wake_en_mask = wake_en_mask;
>> +	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
>> +	ctrl->power_mode_mask = power_mode_mask;
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_probe(struct platform_device *pdev)
> why not use an auxiliary device? we've moved away from platform devices
> maybe two years ago.
+{
>> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
>> +	struct resource *res;
>> +	struct device *dev = &pdev->dev;
>> +	struct sdw_master_prop *prop;
>> +	struct sdw_bus_params *params;
>> +	struct amd_sdwc_ctrl *ctrl;
>> +	int ret;
>> +
>> +	if (!pdev->dev.platform_data) {
>> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +		return -ENODEV;
>> +	}
>> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
>> +	if (!ctrl)
>> +		return -ENOMEM;
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +		return -ENOMEM;
>> +	}
>> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +	if (IS_ERR(ctrl->mmio)) {
>> +		dev_err(&pdev->dev, "mmio not found\n");
>> +		return PTR_ERR(ctrl->mmio);
>> +	}
>> +	ctrl->instance = pdata->instance;
>> +	ctrl->sdw_lock  = pdata->sdw_lock;
>> +	ctrl->rows_index = sdw_find_row_index(50);
>> +	ctrl->cols_index = sdw_find_col_index(10);
>> +
>> +	ctrl->dev = dev;
>> +	dev_set_drvdata(&pdev->dev, ctrl);
>> +
>> +	ctrl->bus.ops = &amd_sdwc_ops;
>> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
>> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
>> +	ctrl->bus.clk_stop_timeout = 1;
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
>> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
>> +		break;
>> +	case ACP_SDW1:
>> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
>> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	params = &ctrl->bus.params;
>> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +	params->col = 10;
>> +	params->row = 50;
>> +
>> +	prop = &ctrl->bus.prop;
>> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
>> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
>> +	ctrl->bus.link_id = ctrl->instance;
>> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
> master. the confusion continues.
It's manager instance.
>> +			ret);
>> +		return ret;
>> +	}
>> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>> +	schedule_work(&ctrl->probe_work);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_remove(struct platform_device *pdev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
>> +	int ret;
>> +
>> +	amd_disable_sdw_interrupts(ctrl);
>> +	sdw_bus_master_delete(&ctrl->bus);
>> +	ret = amd_disable_sdw_controller(ctrl);
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver amd_sdwc_driver = {
>> +	.probe	= &amd_sdwc_probe,
>> +	.remove = &amd_sdwc_remove,
>> +	.driver = {
>> +		.name	= "amd_sdw_controller",
>> +	}
>> +};
>> +module_platform_driver(amd_sdwc_driver);
>> +
>> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
>> +MODULE_DESCRIPTION("AMD soundwire driver");
>> +MODULE_LICENSE("GPL v2");
> "GPL" is enough
will modify the license.
>> +enum amd_sdw_channel {
>> +	/* SDW0 */
>> +	ACP_SDW0_AUDIO_TX = 0,
>> +	ACP_SDW0_BT_TX,
>> +	ACP_SDW0_HS_TX,
>> +	ACP_SDW0_AUDIO_RX,
>> +	ACP_SDW0_BT_RX,
>> +	ACP_SDW0_HS_RX,
>> +	/* SDW1 */
>> +	ACP_SDW1_BT_TX,
>> +	ACP_SDW1_BT_RX,
>> +};
> you really need to comment on this. It looks like you've special-cased
> manager ports for specific usages? This is perfectly fine in closed
> applications, but it's not clear how it might work with headset,
> amplifier and mic codec devices.
will add comments.
>
>> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
>> index f0123815af46..5ec39f8c2f2e 100644
>> --- a/include/linux/soundwire/sdw_amd.h
>> +++ b/include/linux/soundwire/sdw_amd.h
>> @@ -10,9 +10,30 @@
>>  
>>  #define AMD_SDW_CLK_STOP_MODE		1
>>  #define AMD_SDW_POWER_OFF_MODE		2
>> +#define ACP_SDW0	0
>> +#define ACP_SDW1	1
>> +#define ACP_SDW0_MAX_DAI	6
> is this related to the definition of amd_sdw_channel or the number of
> ports available?
port number and channel count is same for SDW0 instance.
Please go through channel mapping explanation mentioned in
one of the above content.
>>  
>>  struct acp_sdw_pdata {
>>  	u16 instance;
>>  	struct mutex *sdw_lock;
>>  };
>> +
>> +struct amd_sdwc_ctrl {
>> +	struct sdw_bus bus;
>> +	struct device *dev;
>> +	void __iomem *mmio;
>> +	struct work_struct probe_work;
>> +	struct mutex *sdw_lock;
> comment please.
will add comment.
>
>> +	int num_din_ports;
>> +	int num_dout_ports;
>> +	int cols_index;
>> +	int rows_index;
>> +	u32 instance;
>> +	u32 quirks;
>> +	u32 wake_en_mask;
>> +	int num_ports;
>> +	bool startup_done;
> ah this was an Intel definition. Due to power dependencies we had to
> split the probe and startup step. Does AMD have a need for this? Is the
> SoundWire master IP dependent on DSP boot or something?
if you are referring to startup_done flag, we have already explained
in another patch reply.
>> +	u32 power_mode_mask;
>> +};
>>  #endif


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-13 18:21       ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-13 18:21 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 11/01/23 20:07, Pierre-Louis Bossart wrote:
>
> On 1/11/23 03:02, Vijendar Mukunda wrote:
>> AMD ACP IP block has two soundwire controller devices.
> s/controller/manager?
will rephrase the commit message.
>
>> Add support for
>> - Master driver probe & remove sequence
>> - Helper functions to enable/disable interrupts, Initialize sdw controller,
>>   enable sdw pads
>> - Master driver sdw_master_ops & port_ops callbacks
>>
>> Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
>> ---
>>  drivers/soundwire/amd_master.c    | 1075 +++++++++++++++++++++++++++++
>>  drivers/soundwire/amd_master.h    |  279 ++++++++
>>  include/linux/soundwire/sdw_amd.h |   21 +
>>  3 files changed, 1375 insertions(+)
>>  create mode 100644 drivers/soundwire/amd_master.c
>>  create mode 100644 drivers/soundwire/amd_master.h
>>
>> diff --git a/drivers/soundwire/amd_master.c b/drivers/soundwire/amd_master.c
>> new file mode 100644
>> index 000000000000..7e1f618254ac
>> --- /dev/null
>> +++ b/drivers/soundwire/amd_master.c
>> @@ -0,0 +1,1075 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * SoundWire AMD Master driver
>> + *
>> + * Copyright 2023 Advanced Micro Devices, Inc.
>> + */
>> +
>> +#include <linux/completion.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/soundwire/sdw_amd.h>
>> +#include <linux/wait.h>
>> +#include <sound/pcm_params.h>
>> +#include <sound/soc.h>
>> +#include "bus.h"
>> +#include "amd_master.h"
>> +
>> +#define DRV_NAME "amd_sdw_controller"
>> +
>> +#define to_amd_sdw(b)	container_of(b, struct amd_sdwc_ctrl, bus)
>> +
>> +static int amd_enable_sdw_pads(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 sw_pad_enable_mask;
>> +	u32 sw_pad_pulldown_mask;
>> +	u32 sw_pad_pulldown_val;
>> +	u32 val = 0;
>> +
>> +	switch (ctrl->instance) {
> Goodness no. A controller has one or more masters. It cannot have pins
> as described in the SoundWire master specification.
we are referring to manager instance only.
will modify the private data structure name.
>
>> +	case ACP_SDW0:
>> +		sw_pad_enable_mask = AMD_SDW0_PAD_KEEPER_EN_MASK;
>> +		sw_pad_pulldown_mask = AMD_SDW0_PAD_PULLDOWN_CTRL_ENABLE_MASK;
>> +		break;
>> +	case ACP_SDW1:
>> +		sw_pad_enable_mask = AMD_SDW1_PAD_KEEPER_EN_MASK;
>> +		sw_pad_pulldown_mask = AMD_SDW1_PAD_PULLDOWN_CTRL_ENABLE_MASK;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_lock(ctrl->sdw_lock);
>> +	val = acp_reg_readl(ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
>> +	val |= sw_pad_enable_mask;
>> +	acp_reg_writel(val, ctrl->mmio + ACP_SW_PAD_KEEPER_EN);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	usleep_range(1000, 1500);
>> +
>> +	mutex_lock(ctrl->sdw_lock);
>> +	sw_pad_pulldown_val  = acp_reg_readl(ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
>> +	sw_pad_pulldown_val &= sw_pad_pulldown_mask;
>> +	acp_reg_writel(sw_pad_pulldown_val, ctrl->mmio + ACP_PAD_PULLDOWN_CTRL);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	return 0;
>> +}
>> +
>> +static int amd_init_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 acp_sw_en_reg, acp_sw_en_stat_reg, sw_bus_reset_reg;
>> +	u32 val = 0;
>> +	u32 timeout = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		sw_bus_reset_reg = ACP_SW_BUS_RESET_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		sw_bus_reset_reg = ACP_P1_SW_BUS_RESET_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +
>> +	/* Sdw Controller reset */
>> +	acp_reg_writel(AMD_SDW_BUS_RESET_REQ, ctrl->mmio + sw_bus_reset_reg);
>> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +	while (!(val & AMD_SDW_BUS_RESET_DONE)) {
>> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
>> +			break;
>> +		usleep_range(1, 5);
>> +		timeout++;
>> +	}
> no test on timeout here to check if the bus was indeed reset?
It's a miss. we will add the code.
>
> If you are talking about bus_reset you are referring to a master btw.
> The terms bus/master/link are interchangeable. A controller is not
> defined in the SoundWire specification, this is part of the DisCo spec
> to deal with enumeration when multiple bus/master/link are supported in
> the platform.
>
>> +	timeout = 0;
>> +	acp_reg_writel(AMD_SDW_BUS_RESET_CLEAR_REQ, ctrl->mmio + sw_bus_reset_reg);
>> +	val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +	while (val) {
>> +		val = acp_reg_readl(ctrl->mmio + sw_bus_reset_reg);
>> +		if (timeout > AMD_DELAY_LOOP_ITERATION)
>> +			break;
>> +		usleep_range(1, 5);
>> +		timeout++;
>> +	}
>> +	if (timeout == AMD_DELAY_LOOP_ITERATION) {
>> +		dev_err(ctrl->dev, "Failed to reset SW%x Soundwire Controller\n", ctrl->instance);
>> +		return -ETIMEDOUT;
>> +	}
>> +	retry_count = 0;
>> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (!val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_enable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 acp_sw_en_reg;
>> +	u32 acp_sw_en_stat_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_ENABLE, ctrl->mmio + acp_sw_en_reg);
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_disable_sdw_controller(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 clk_resume_ctrl_reg;
>> +	u32 acp_sw_en_reg;
>> +	u32 acp_sw_en_stat_reg;
>> +	u32 val = 0;
>> +	u32 retry_count = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_sw_en_reg = ACP_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_SW_EN_STATUS;
>> +		clk_resume_ctrl_reg = ACP_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_sw_en_reg = ACP_P1_SW_EN;
>> +		acp_sw_en_stat_reg = ACP_P1_SW_EN_STATUS;
>> +		clk_resume_ctrl_reg = ACP_P1_SW_CLK_RESUME_CTRL;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_DISABLE, ctrl->mmio + acp_sw_en_reg);
>> +
>> +	/*
>> +	 * After invoking controller disable sequence, check whether
>> +	 * controller has executed clock stop sequence. In this case,
>> +	 * controller should ignore checking enable status register.
> again clock stop is a sequence at the master/link/bus level, not the
> controller.
Throughout the code we are referring manager instance only.
>> +	 */
>> +	val = acp_reg_readl(ctrl->mmio + clk_resume_ctrl_reg);
>> +	if (val)
>> +		return 0;
>> +
>> +	do {
>> +		val = acp_reg_readl(ctrl->mmio + acp_sw_en_stat_reg);
>> +		if (!val)
>> +			break;
>> +		usleep_range(10, 50);
>> +	} while (retry_count++ < AMD_SDW_STAT_MAX_RETRY_COUNT);
>> +
>> +	if (retry_count > AMD_SDW_STAT_MAX_RETRY_COUNT)
>> +		return -ETIMEDOUT;
>> +	return 0;
>> +}
>> +
>> +static int amd_enable_sdw_interrupts(struct amd_sdwc_ctrl *ctrl)
>> +{
>> +	u32 val;
>> +	u32 acp_ext_intr_stat, acp_ext_intr_ctrl, acp_sdw_intr_mask;
>> +	u32 sw_stat_mask_0to7, sw_stat_mask_8to11, sw_err_intr_mask;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL;
> should be renamed and end in CNTL0 if the other is CNTL1
Its register header file macro. It's still good to go.
>
> And it's manager anyways, not controller.
It's manager only.
>> +		acp_sdw_intr_mask = AMD_SDW0_EXT_INTR_MASK;
>> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT;
>> +		sw_stat_mask_0to7 = SW_STATE_CHANGE_STATUS_MASK_0TO7;
>> +		sw_stat_mask_8to11 = SW_STATE_CHANGE_STATUS_MASK_8TO11;
>> +		sw_err_intr_mask = SW_ERROR_INTR_MASK;
>> +		break;
>> +	case ACP_SDW1:
>> +		acp_ext_intr_ctrl = ACP_EXTERNAL_INTR_CNTL1;
>> +		acp_sdw_intr_mask = AMD_SDW1_EXT_INTR_MASK;
>> +		acp_ext_intr_stat = ACP_EXTERNAL_INTR_STAT1;
>> +		sw_stat_mask_0to7 = P1_SW_STATE_CHANGE_STATUS_MASK_0TO7;
>> +		sw_stat_mask_8to11 = P1_SW_STATE_CHANGE_STATUS_MASK_8TO11;
>> +		sw_err_intr_mask = P1_SW_ERROR_INTR_MASK;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	mutex_lock(ctrl->sdw_lock);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
>> +	val |= acp_sdw_intr_mask;
>> +	acp_reg_writel(val, ctrl->mmio + acp_ext_intr_ctrl);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_ctrl);
>> +	mutex_unlock(ctrl->sdw_lock);
>> +	dev_dbg(ctrl->dev, "%s: acp_ext_intr_ctrl[0x%x]:0x%x\n", __func__, acp_ext_intr_ctrl, val);
>> +	val = acp_reg_readl(ctrl->mmio + acp_ext_intr_stat);
>> +	if (val)
>> +		acp_reg_writel(val, ctrl->mmio + acp_ext_intr_stat);
>> +	acp_reg_writel(AMD_SDW_IRQ_MASK_0TO7, ctrl->mmio + sw_stat_mask_0to7);
>> +	acp_reg_writel(AMD_SDW_IRQ_MASK_8TO11, ctrl->mmio + sw_stat_mask_8to11);
>> +	acp_reg_writel(AMD_SDW_IRQ_ERROR_MASK, ctrl->mmio + sw_err_intr_mask);
>> +	return 0;
>> +}
>> +
>> +static u64 amd_sdwc_send_cmd_get_resp(struct amd_sdwc_ctrl *ctrl, u32 lword, u32 uword)
>> +{
>> +	u64 resp = 0;
>> +	u32 imm_cmd_stat_reg, imm_cmd_uword_reg, imm_cmd_lword_reg;
>> +	u32 imm_resp_uword_reg, imm_resp_lword_reg;
>> +	u32 resp_lower, resp_high;
>> +	u32 sts = 0;
>> +	u32 timeout = 0;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		imm_cmd_stat_reg = SW_IMM_CMD_STS;
>> +		imm_cmd_uword_reg = SW_IMM_CMD_UPPER_WORD;
>> +		imm_cmd_lword_reg = SW_IMM_CMD_LOWER_QWORD;
>> +		imm_resp_uword_reg = SW_IMM_RESP_UPPER_WORD;
>> +		imm_resp_lword_reg = SW_IMM_RESP_LOWER_QWORD;
>> +		break;
>> +	case ACP_SDW1:
>> +		imm_cmd_stat_reg = P1_SW_IMM_CMD_STS;
>> +		imm_cmd_uword_reg = P1_SW_IMM_CMD_UPPER_WORD;
>> +		imm_cmd_lword_reg = P1_SW_IMM_CMD_LOWER_QWORD;
>> +		imm_resp_uword_reg = P1_SW_IMM_RESP_UPPER_WORD;
>> +		imm_resp_lword_reg = P1_SW_IMM_RESP_LOWER_QWORD;
> naming consistency would be good, the P1 is sometimes a prefix,
> sometimes a suffix, sometimes in the middle. Pick one.
These are taken from our AMD register header file.
It's still good to go.
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +	while (sts & AMD_SDW_IMM_CMD_BUSY) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x previous cmd status clear failed\n",
>> +				ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +
>> +	timeout = 0;
>> +	if (sts & AMD_SDW_IMM_RES_VALID) {
>> +		dev_err(ctrl->dev, "SDW%x controller is in bad state\n", ctrl->instance);
>> +		acp_reg_writel(0x00, ctrl->mmio + imm_cmd_stat_reg);
>> +	}
>> +	acp_reg_writel(uword, ctrl->mmio + imm_cmd_uword_reg);
>> +	acp_reg_writel(lword, ctrl->mmio + imm_cmd_lword_reg);
>> +
>> +	sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +	while (!(sts & AMD_SDW_IMM_RES_VALID)) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x cmd response timeout occurred\n", ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +	resp_high = acp_reg_readl(ctrl->mmio + imm_resp_uword_reg);
>> +	resp_lower = acp_reg_readl(ctrl->mmio + imm_resp_lword_reg);
>> +	timeout = 0;
>> +	acp_reg_writel(AMD_SDW_IMM_RES_VALID, ctrl->mmio + imm_cmd_stat_reg);
>> +	while ((sts & AMD_SDW_IMM_RES_VALID)) {
>> +		sts = acp_reg_readl(ctrl->mmio + imm_cmd_stat_reg);
>> +		if (timeout > AMD_SDW_RETRY_COUNT) {
>> +			dev_err(ctrl->dev, "SDW%x cmd status retry failed\n", ctrl->instance);
>> +			return -ETIMEDOUT;
>> +		}
>> +		timeout++;
>> +	}
>> +	resp = resp_high;
>> +	resp = (resp << 32) | resp_lower;
>> +	return resp;
>> +}
>> +
>> +static enum sdw_command_response
>> +amd_program_scp_addr(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg)
>> +{
>> +	struct sdw_msg scp_msg = {0};
>> +	u64 response_buf[2] = {0};
>> +	u32 uword = 0, lword = 0;
>> +	int nack = 0, no_ack = 0;
>> +	int index, timeout = 0;
>> +
>> +	scp_msg.dev_num = msg->dev_num;
>> +	scp_msg.addr = SDW_SCP_ADDRPAGE1;
>> +	scp_msg.buf = &msg->addr_page1;
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
>> +	response_buf[0] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +	scp_msg.addr = SDW_SCP_ADDRPAGE2;
>> +	scp_msg.buf = &msg->addr_page2;
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, AMD_SDW_CMD_WRITE, &scp_msg, 0);
>> +	response_buf[1] = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +
>> +	/* check response the writes */
> response to the writes?  after the writes?
will correct the comment.
>> +	for (index = 0; index < 2; index++) {
>> +		if (response_buf[index] == -ETIMEDOUT) {
>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>> +			timeout = 1;
>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>> +			no_ack = 1;
>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>> +				nack = 1;
>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>> +			}
> this is a copy of the cadence_master.c code... With the error added that
> this is not for a controller but for a master...
Its manager instance only. Our immediate command and response
mechanism allows sending commands over the link and get the
response for every command immediately, unlike as mentioned in
candence_master.c.
>> +		}
>> +	}
>> +
>> +	if (timeout) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_TIMEOUT;
>> +	}
>> +
>> +	if (nack) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_FAIL;
>> +	}
>> +
>> +	if (no_ack) {
>> +		dev_dbg_ratelimited(ctrl->dev,
>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_IGNORED;
>> +	}
>> +	return SDW_CMD_OK;
> this should probably become a helper since the response is really the
> same as in cadence_master.c
>
> There's really room for optimization and reuse here.
not really needed. Please refer above comment as command/response
mechanism differs from Intel's implementation.
>
>> +}
>> +
>> +static int amd_prep_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg, int *cmd)
>> +{
>> +	int ret;
>> +
>> +	if (msg->page) {
>> +		ret = amd_program_scp_addr(ctrl, msg);
>> +		if (ret) {
>> +			msg->len = 0;
>> +			return ret;
>> +		}
>> +	}
>> +	switch (msg->flags) {
>> +	case SDW_MSG_FLAG_READ:
>> +		*cmd = AMD_SDW_CMD_READ;
>> +		break;
>> +	case SDW_MSG_FLAG_WRITE:
>> +		*cmd = AMD_SDW_CMD_WRITE;
>> +		break;
>> +	default:
>> +		dev_err(ctrl->dev, "Invalid msg cmd: %d\n", msg->flags);
>> +		return -EINVAL;
>> +	}
>> +	return 0;
>> +}
> this is the same code as in cadence_master.c
>
> you just replaced sdw_cnds by amd_sdw_ctrl (which is a mistake) and
> cdns->dev by ctrl->dev.
>
>> +
>> +static unsigned int _amd_sdwc_xfer_msg(struct amd_sdwc_ctrl *ctrl, struct sdw_msg *msg,
>> +				       int cmd, int cmd_offset)
>> +{
>> +	u64 response = 0;
>> +	u32 uword = 0, lword = 0;
>> +	int nack = 0, no_ack = 0;
>> +	int timeout = 0;
>> +
>> +	amd_sdwc_ctl_word_prep(&lword, &uword, cmd, msg, cmd_offset);
>> +	response = amd_sdwc_send_cmd_get_resp(ctrl, lword, uword);
>> +
>> +	if (response & AMD_SDW_MCP_RESP_ACK) {
>> +		if (cmd == AMD_SDW_CMD_READ)
>> +			msg->buf[cmd_offset] = FIELD_GET(AMD_SDW_MCP_RESP_RDATA, response);
>> +	} else {
>> +		no_ack = 1;
>> +		if (response == -ETIMEDOUT) {
>> +			timeout = 1;
>> +		} else if (response & AMD_SDW_MCP_RESP_NACK) {
>> +			nack = 1;
>> +			dev_err(ctrl->dev, "Program SCP NACK received\n");
>> +		}
>> +	}
>> +
>> +	if (timeout) {
>> +		dev_err_ratelimited(ctrl->dev, "command timeout for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_TIMEOUT;
>> +	}
>> +	if (nack) {
>> +		dev_err_ratelimited(ctrl->dev,
>> +				    "command response NACK received for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_FAIL;
>> +	}
>> +
>> +	if (no_ack) {
>> +		dev_err_ratelimited(ctrl->dev, "command is ignored for Slave %d\n", msg->dev_num);
>> +		return SDW_CMD_IGNORED;
>> +	}
>> +	return SDW_CMD_OK;
>> +}
>> +
>> +static enum sdw_command_response amd_sdwc_xfer_msg(struct sdw_bus *bus, struct sdw_msg *msg)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	int ret, i;
>> +	int cmd = 0;
>> +
>> +	ret = amd_prep_msg(ctrl, msg, &cmd);
>> +	if (ret)
>> +		return SDW_CMD_FAIL_OTHER;
>> +	for (i = 0; i < msg->len; i++) {
>> +		ret = _amd_sdwc_xfer_msg(ctrl, msg, cmd, i);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +	return SDW_CMD_OK;
>> +}
>> +
>> +static enum sdw_command_response
>> +amd_reset_page_addr(struct sdw_bus *bus, unsigned int dev_num)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	struct sdw_msg msg;
>> +
>> +	/* Create dummy message with valid device number */
>> +	memset(&msg, 0, sizeof(msg));
>> +	msg.dev_num = dev_num;
>> +	return amd_program_scp_addr(ctrl, &msg);
>> +}
>> +
>> +static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u64 response;
>> +	u32 slave_stat = 0;
>> +
>> +	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
>> +	/* slave status from ping 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(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
>> +	return slave_stat;
>> +}
>> +
>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>> +					 struct sdw_transport_data *t_data)
>> +{
>> +	struct sdw_slave_runtime *s_rt = NULL;
>> +	struct sdw_port_runtime *p_rt;
>> +	int port_bo, sample_int;
>> +	unsigned int rate, bps, ch = 0;
>> +	unsigned int slave_total_ch;
>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>> +
>> +	port_bo = t_data->block_offset;
>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>> +		rate = m_rt->stream->params.rate;
>> +		bps = m_rt->stream->params.bps;
>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>> +		slave_total_ch = 0;
>> +
>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>> +
>> +			sdw_fill_xport_params(&p_rt->transport_params,
>> +					      p_rt->num, false,
>> +					      SDW_BLK_GRP_CNT_1,
>> +					      sample_int, port_bo, port_bo >> 8,
>> +					      t_data->hstart,
>> +					      t_data->hstop,
>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +			sdw_fill_port_params(&p_rt->port_params,
>> +					     p_rt->num, bps,
>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>> +					     b_params->s_data_mode);
>> +
>> +			port_bo += bps * ch;
>> +			slave_total_ch += ch;
>> +		}
>> +
>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>> +		    m_rt->ch_count == slave_total_ch) {
>> +			port_bo = t_data->block_offset;
>> +		}
>> +	}
>> +}
> ok, this is really bad.
>
> This is a verbatim copy of the same function in
> generic_bandwidth_allocation.c
>
> see
> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>
> You only removed the comments and renamed the function.
>
> Seriously? Why would you do that?
>
> And in addition, this has *NOTHING* to do with the master support.
>
> Programming the ports on peripheral side is something that happens at
> the stream level.
>
> I am afraid it's a double NAK, or rather NAK^2 from me here.
Our intention is to implement our own compute params callback.
Sorry, instead of making a copied one , we could have exported this
API.
>> +
>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>> +{
>> +	struct sdw_transport_data t_data = {0};
>> +	struct sdw_master_runtime *m_rt;
>> +	struct sdw_port_runtime *p_rt;
>> +	struct sdw_bus_params *b_params = &bus->params;
>> +	int port_bo, hstart, hstop, sample_int;
>> +	unsigned int rate, bps;
>> +
>> +	port_bo  = 0;
>> +	hstart = 1;
>> +	hstop = bus->params.col - 1;
>> +	t_data.hstop = hstop;
>> +	t_data.hstart = hstart;
>> +
>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>> +		rate = m_rt->stream->params.rate;
>> +		bps = m_rt->stream->params.bps;
>> +		sample_int = (bus->params.curr_dr_freq / rate);
>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>> +			port_bo = (p_rt->num * 64) + 1;
>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>> +				p_rt->num, hstart, hstop, port_bo);
>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>> +					      port_bo, port_bo >> 8, hstart, hstop,
>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>> +
>> +			sdw_fill_port_params(&p_rt->port_params,
>> +					     p_rt->num, bps,
>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>> +					     b_params->m_data_mode);
>> +			t_data.hstart = hstart;
>> +			t_data.hstop = hstop;
>> +			t_data.block_offset = port_bo;
>> +			t_data.sub_block_offset = 0;
>> +		}
>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>> +	}
>> +	return 0;
>> +}
> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>
> You would need a lot more comments to convince me that this is
> intentional and needed.
This is intentional. We have a HW bug, if we go it generic bdw allocation
API, when we launch multiple streams, we are observing noise for shorter
duration for active stream.
To avoid that, we have slightly modified the sdw_compute_master_ports()
API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
We will expand the coverage in the future.
 
>> +
>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>> +				unsigned int bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>> +
>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		channel_type = p_params->num;
>> +		break;
>> +	case ACP_SDW1:
>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
> you'll have to explain what you mean by 'channel_type'
>
> This looks like the streams that can be supported by this master
> implementation, with dailinks for each.
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)

Each port number on Manager side is mapped to a channel number.
i.e SDW0 Pin0 -> port number 0 -> Audio TX
    SDW0 Pin1 -> Port number 1 -> Headset TX
    SDW0 Pin2 -> Port number 2 -> BT TX
    SDW0 Pin3 -> port number 3 -> Audio RX
    SDW0 Pin4 -> Port number 4 -> Headset RX
    SDW0 Pin5 -> Port number 5 -> BT RX

Whereas for SDW1 instance

    SDW1 Pin0 -> port number 0 -> P1 BT TX
    SDW1 Pin1 -> Port number 1 -> P1 BT RX
    
We use this channel value to program register set for transport params,
port params and Channel enable for each manager instance.
We need to use same channel mapping for programming DMA controller
registers in Soundwire DMA driver.
i.e if AUDIO TX channel is selected then we need to use Audio TX registers
for DMA programming in Soundwire DMA driver.
>
>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_HS_TX:
>> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_BT_TX:
>> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_AUDIO_RX:
>> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_HS_RX:
>> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW0_BT_RX:
>> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
>> +		break;
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->flow_mode, AMD_DPN_FRAME_FMT_PFM);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->data_mode, AMD_DPN_FRAME_FMT_PDM);
>> +	u32p_replace_bits(&dpn_frame_fmt, p_params->bps - 1, AMD_DPN_FRAME_FMT_WORD_LEN);
>> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>> +				     struct sdw_transport_params *params,
>> +				     enum sdw_reg_bank bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 ssp_counter_reg;
>> +	u32 dpn_frame_fmt;
>> +	u32 dpn_sampleinterval;
>> +	u32 dpn_hctrl;
>> +	u32 dpn_offsetctrl;
>> +	u32 dpn_lanectrl;
>> +	u32 channel_type;
>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>> +	u32 offset_reg, lane_ctrl_reg;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>> +		channel_type = params->port_num;
>> +		break;
>> +	case ACP_SDW1:
>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
> There's obviously a dependency between SDW0 and SDW1 managers that you
> haven't described?
No, both are independent manager instances which are connected in
different power domains.
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>> +		__func__, params->port_num, channel_type);
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> This is confusing. Is this about enabling a stream or selecting the lane
> for this port? Same for all cases.
>
> is this saying that the two cases are handled by the same register -
> unlike what is normative for the peripherals where the two concepts are
> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
we have to refer the same register to program channel enable and lane
ctrl as per our soundwire register definition.
>
>> +		break;
>> +	}
>> +	case ACP_SDW0_HS_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_HEADSET_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_HEADSET_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_HEADSET_TX_HCTRL;
>> +		offset_reg = ACP_SW_HEADSET_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_BT_TX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_BT_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_BT_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_BT_TX_HCTRL;
>> +		offset_reg = ACP_SW_BT_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW1_BT_TX:
>> +	{
>> +		frame_fmt_reg = ACP_P1_SW_BT_TX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_P1_SW_BT_TX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_P1_SW_BT_TX_HCTRL;
>> +		offset_reg = ACP_P1_SW_BT_TX_OFFSET;
>> +		lane_ctrl_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_AUDIO_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_AUDIO_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_AUDIO_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_AUDIO_RX_HCTRL_DP0;
>> +		offset_reg = ACP_SW_AUDIO_RX_OFFSET_DP0;
>> +		lane_ctrl_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_HS_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_HEADSET_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_HEADSET_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_HEADSET_RX_HCTRL;
>> +		offset_reg = ACP_SW_HEADSET_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW0_BT_RX:
>> +	{
>> +		frame_fmt_reg = ACP_SW_BT_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_SW_BT_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_SW_BT_RX_HCTRL;
>> +		offset_reg = ACP_SW_BT_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	case ACP_SDW1_BT_RX:
>> +	{
>> +		frame_fmt_reg = ACP_P1_SW_BT_RX_FRAME_FORMAT;
>> +		sample_int_reg = ACP_P1_SW_BT_RX_SAMPLEINTERVAL;
>> +		hctrl_dp0_reg = ACP_P1_SW_BT_RX_HCTRL;
>> +		offset_reg = ACP_P1_SW_BT_RX_OFFSET;
>> +		lane_ctrl_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	}
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +	dpn_frame_fmt = acp_reg_readl(ctrl->mmio + frame_fmt_reg);
>> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_pkg_mode, AMD_DPN_FRAME_FMT_BLK_PKG_MODE);
>> +	u32p_replace_bits(&dpn_frame_fmt, params->blk_grp_ctrl, AMD_DPN_FRAME_FMT_BLK_GRP_CTRL);
>> +	u32p_replace_bits(&dpn_frame_fmt, SDW_STREAM_PCM, AMD_DPN_FRAME_FMT_PCM_OR_PDM);
>> +	acp_reg_writel(dpn_frame_fmt, ctrl->mmio + frame_fmt_reg);
>> +
>> +	dpn_sampleinterval = params->sample_interval - 1;
>> +	acp_reg_writel(dpn_sampleinterval, ctrl->mmio + sample_int_reg);
>> +
>> +	dpn_hctrl = FIELD_PREP(AMD_DPN_HCTRL_HSTOP, params->hstop);
>> +	dpn_hctrl |= FIELD_PREP(AMD_DPN_HCTRL_HSTART, params->hstart);
>> +	acp_reg_writel(dpn_hctrl, ctrl->mmio + hctrl_dp0_reg);
>> +
>> +	dpn_offsetctrl = FIELD_PREP(AMD_DPN_OFFSET_CTRL_1, params->offset1);
>> +	dpn_offsetctrl |= FIELD_PREP(AMD_DPN_OFFSET_CTRL_2, params->offset2);
>> +	acp_reg_writel(dpn_offsetctrl, ctrl->mmio + offset_reg);
>> +
>> +	dpn_lanectrl = acp_reg_readl(ctrl->mmio + lane_ctrl_reg);
>> +	u32p_replace_bits(&dpn_lanectrl, params->lane_ctrl, AMD_DPN_CH_EN_LCTRL);
>> +	acp_reg_writel(dpn_lanectrl, ctrl->mmio + lane_ctrl_reg);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_port_enable(struct sdw_bus *bus,
>> +				struct sdw_enable_ch *enable_ch,
>> +				unsigned int bank)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>> +	u32 dpn_ch_enable;
>> +	u32 ch_enable_reg, channel_type;
>> +
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		channel_type = enable_ch->port_num;
>> +		break;
>> +	case ACP_SDW1:
>> +		channel_type = enable_ch->port_num + ACP_SDW0_MAX_DAI;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +	switch (channel_type) {
>> +	case ACP_SDW0_AUDIO_TX:
>> +		ch_enable_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
> in the function above, I commented on
>
> 		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>
> This looks really weird. You need to add comments is this is really
> intentional.
Please refer above comment reply. We will add comments.
>> +		break;
>> +	case ACP_SDW0_HS_TX:
>> +		ch_enable_reg = ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_BT_TX:
>> +		ch_enable_reg = ACP_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW1_BT_TX:
>> +		ch_enable_reg = ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_AUDIO_RX:
>> +		ch_enable_reg = ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_HS_RX:
>> +		ch_enable_reg = ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW0_BT_RX:
>> +		ch_enable_reg = ACP_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	case ACP_SDW1_BT_RX:
>> +		ch_enable_reg = ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0;
>> +		break;
>> +	default:
>> +		dev_err(bus->dev, "%s:Invalid channel:%d\n", __func__, channel_type);
>> +		return -EINVAL;
>> +	}
>> +
>> +	dpn_ch_enable =  acp_reg_readl(ctrl->mmio + ch_enable_reg);
>> +	u32p_replace_bits(&dpn_ch_enable, enable_ch->ch_mask, AMD_DPN_CH_EN_CHMASK);
>> +	if (enable_ch->enable)
>> +		acp_reg_writel(dpn_ch_enable, ctrl->mmio + ch_enable_reg);
>> +	else
>> +		acp_reg_writel(0, ctrl->mmio + ch_enable_reg);
>> +	return 0;
>> +}
>> +
>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
> same quirk as Intel, nice :-)
>
>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
> And here too. Is this really needed or just-copy-pasted?
No, It's not a copy and paste. We have seen issues bus clash/parity errors
during peripheral enumeration/initialization across the multiple links without
this quirk. We have also seen device alerts missed during peripheral initialization
sequence.

>> +	fwnode_property_read_u32(link, "amd-sdw-wake-enable", &wake_en_mask);
>> +	ctrl->wake_en_mask = wake_en_mask;
>> +	fwnode_property_read_u32(link, "amd-sdw-power-mode", &power_mode_mask);
>> +	ctrl->power_mode_mask = power_mode_mask;
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_probe(struct platform_device *pdev)
> why not use an auxiliary device? we've moved away from platform devices
> maybe two years ago.
+{
>> +	const struct acp_sdw_pdata *pdata = pdev->dev.platform_data;
>> +	struct resource *res;
>> +	struct device *dev = &pdev->dev;
>> +	struct sdw_master_prop *prop;
>> +	struct sdw_bus_params *params;
>> +	struct amd_sdwc_ctrl *ctrl;
>> +	int ret;
>> +
>> +	if (!pdev->dev.platform_data) {
>> +		dev_err(&pdev->dev, "platform_data not retrieved\n");
>> +		return -ENODEV;
>> +	}
>> +	ctrl = devm_kzalloc(&pdev->dev, sizeof(struct amd_sdwc_ctrl), GFP_KERNEL);
>> +	if (!ctrl)
>> +		return -ENOMEM;
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
>> +		return -ENOMEM;
>> +	}
>> +	ctrl->mmio = devm_ioremap(&pdev->dev, res->start, resource_size(res));
>> +	if (IS_ERR(ctrl->mmio)) {
>> +		dev_err(&pdev->dev, "mmio not found\n");
>> +		return PTR_ERR(ctrl->mmio);
>> +	}
>> +	ctrl->instance = pdata->instance;
>> +	ctrl->sdw_lock  = pdata->sdw_lock;
>> +	ctrl->rows_index = sdw_find_row_index(50);
>> +	ctrl->cols_index = sdw_find_col_index(10);
>> +
>> +	ctrl->dev = dev;
>> +	dev_set_drvdata(&pdev->dev, ctrl);
>> +
>> +	ctrl->bus.ops = &amd_sdwc_ops;
>> +	ctrl->bus.port_ops = &amd_sdwc_port_ops;
>> +	ctrl->bus.compute_params = &amd_sdwc_compute_params;
>> +	ctrl->bus.clk_stop_timeout = 1;
>> +	switch (ctrl->instance) {
>> +	case ACP_SDW0:
>> +		ctrl->num_dout_ports =  AMD_SDW0_MAX_TX_PORTS;
>> +		ctrl->num_din_ports = AMD_SDW0_MAX_RX_PORTS;
>> +		break;
>> +	case ACP_SDW1:
>> +		ctrl->num_dout_ports = AMD_SDW1_MAX_TX_PORTS;
>> +		ctrl->num_din_ports = AMD_SDW1_MAX_RX_PORTS;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	params = &ctrl->bus.params;
>> +	params->max_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +	params->curr_dr_freq = AMD_SDW_DEFAULT_CLK_FREQ * 2;
>> +	params->col = 10;
>> +	params->row = 50;
>> +
>> +	prop = &ctrl->bus.prop;
>> +	prop->clk_freq = &amd_sdwc_freq_tbl[0];
>> +	prop->mclk_freq = AMD_SDW_BUS_BASE_FREQ;
>> +	ctrl->bus.link_id = ctrl->instance;
>> +	ret = sdw_bus_master_add(&ctrl->bus, dev, dev->fwnode);
>> +	if (ret) {
>> +		dev_err(dev, "Failed to register Soundwire controller (%d)\n",
> master. the confusion continues.
It's manager instance.
>> +			ret);
>> +		return ret;
>> +	}
>> +	INIT_WORK(&ctrl->probe_work, amd_sdwc_probe_work);
>> +	schedule_work(&ctrl->probe_work);
>> +	return 0;
>> +}
>> +
>> +static int amd_sdwc_remove(struct platform_device *pdev)
>> +{
>> +	struct amd_sdwc_ctrl *ctrl = dev_get_drvdata(&pdev->dev);
>> +	int ret;
>> +
>> +	amd_disable_sdw_interrupts(ctrl);
>> +	sdw_bus_master_delete(&ctrl->bus);
>> +	ret = amd_disable_sdw_controller(ctrl);
>> +	return ret;
>> +}
>> +
>> +static struct platform_driver amd_sdwc_driver = {
>> +	.probe	= &amd_sdwc_probe,
>> +	.remove = &amd_sdwc_remove,
>> +	.driver = {
>> +		.name	= "amd_sdw_controller",
>> +	}
>> +};
>> +module_platform_driver(amd_sdwc_driver);
>> +
>> +MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
>> +MODULE_DESCRIPTION("AMD soundwire driver");
>> +MODULE_LICENSE("GPL v2");
> "GPL" is enough
will modify the license.
>> +enum amd_sdw_channel {
>> +	/* SDW0 */
>> +	ACP_SDW0_AUDIO_TX = 0,
>> +	ACP_SDW0_BT_TX,
>> +	ACP_SDW0_HS_TX,
>> +	ACP_SDW0_AUDIO_RX,
>> +	ACP_SDW0_BT_RX,
>> +	ACP_SDW0_HS_RX,
>> +	/* SDW1 */
>> +	ACP_SDW1_BT_TX,
>> +	ACP_SDW1_BT_RX,
>> +};
> you really need to comment on this. It looks like you've special-cased
> manager ports for specific usages? This is perfectly fine in closed
> applications, but it's not clear how it might work with headset,
> amplifier and mic codec devices.
will add comments.
>
>> diff --git a/include/linux/soundwire/sdw_amd.h b/include/linux/soundwire/sdw_amd.h
>> index f0123815af46..5ec39f8c2f2e 100644
>> --- a/include/linux/soundwire/sdw_amd.h
>> +++ b/include/linux/soundwire/sdw_amd.h
>> @@ -10,9 +10,30 @@
>>  
>>  #define AMD_SDW_CLK_STOP_MODE		1
>>  #define AMD_SDW_POWER_OFF_MODE		2
>> +#define ACP_SDW0	0
>> +#define ACP_SDW1	1
>> +#define ACP_SDW0_MAX_DAI	6
> is this related to the definition of amd_sdw_channel or the number of
> ports available?
port number and channel count is same for SDW0 instance.
Please go through channel mapping explanation mentioned in
one of the above content.
>>  
>>  struct acp_sdw_pdata {
>>  	u16 instance;
>>  	struct mutex *sdw_lock;
>>  };
>> +
>> +struct amd_sdwc_ctrl {
>> +	struct sdw_bus bus;
>> +	struct device *dev;
>> +	void __iomem *mmio;
>> +	struct work_struct probe_work;
>> +	struct mutex *sdw_lock;
> comment please.
will add comment.
>
>> +	int num_din_ports;
>> +	int num_dout_ports;
>> +	int cols_index;
>> +	int rows_index;
>> +	u32 instance;
>> +	u32 quirks;
>> +	u32 wake_en_mask;
>> +	int num_ports;
>> +	bool startup_done;
> ah this was an Intel definition. Due to power dependencies we had to
> split the probe and startup step. Does AMD have a need for this? Is the
> SoundWire master IP dependent on DSP boot or something?
if you are referring to startup_done flag, we have already explained
in another patch reply.
>> +	u32 power_mode_mask;
>> +};
>>  #endif


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-13 18:21       ` Mukunda,Vijendar
@ 2023-01-13 18:41         ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 18:41 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list


>>> +	for (index = 0; index < 2; index++) {
>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>> +			timeout = 1;
>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>> +			no_ack = 1;
>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>> +				nack = 1;
>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>> +			}
>> this is a copy of the cadence_master.c code... With the error added that
>> this is not for a controller but for a master...
> Its manager instance only. Our immediate command and response
> mechanism allows sending commands over the link and get the
> response for every command immediately, unlike as mentioned in
> candence_master.c.

I don't get the reply. The Cadence IP also has the ability to get the
response immediately. There's limited scope for creativity, the commands
are defined in the spec and the responses as well.

>>> +		}
>>> +	}
>>> +
>>> +	if (timeout) {
>>> +		dev_err_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_TIMEOUT;
>>> +	}
>>> +
>>> +	if (nack) {
>>> +		dev_err_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_FAIL;
>>> +	}
>>> +
>>> +	if (no_ack) {
>>> +		dev_dbg_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_IGNORED;
>>> +	}
>>> +	return SDW_CMD_OK;
>> this should probably become a helper since the response is really the
>> same as in cadence_master.c
>>
>> There's really room for optimization and reuse here.
> not really needed. Please refer above comment as command/response
> mechanism differs from Intel's implementation.

how? there's a buffer of responses in both cases. please clarify.

>>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>>> +					 struct sdw_transport_data *t_data)
>>> +{
>>> +	struct sdw_slave_runtime *s_rt = NULL;
>>> +	struct sdw_port_runtime *p_rt;
>>> +	int port_bo, sample_int;
>>> +	unsigned int rate, bps, ch = 0;
>>> +	unsigned int slave_total_ch;
>>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>>> +
>>> +	port_bo = t_data->block_offset;
>>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>>> +		rate = m_rt->stream->params.rate;
>>> +		bps = m_rt->stream->params.bps;
>>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>>> +		slave_total_ch = 0;
>>> +
>>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>>> +
>>> +			sdw_fill_xport_params(&p_rt->transport_params,
>>> +					      p_rt->num, false,
>>> +					      SDW_BLK_GRP_CNT_1,
>>> +					      sample_int, port_bo, port_bo >> 8,
>>> +					      t_data->hstart,
>>> +					      t_data->hstop,
>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>> +
>>> +			sdw_fill_port_params(&p_rt->port_params,
>>> +					     p_rt->num, bps,
>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>> +					     b_params->s_data_mode);
>>> +
>>> +			port_bo += bps * ch;
>>> +			slave_total_ch += ch;
>>> +		}
>>> +
>>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>>> +		    m_rt->ch_count == slave_total_ch) {
>>> +			port_bo = t_data->block_offset;
>>> +		}
>>> +	}
>>> +}
>> ok, this is really bad.
>>
>> This is a verbatim copy of the same function in
>> generic_bandwidth_allocation.c
>>
>> see
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>>
>> You only removed the comments and renamed the function.
>>
>> Seriously? Why would you do that?
>>
>> And in addition, this has *NOTHING* to do with the master support.
>>
>> Programming the ports on peripheral side is something that happens at
>> the stream level.
>>
>> I am afraid it's a double NAK, or rather NAK^2 from me here.
> Our intention is to implement our own compute params callback.
> Sorry, instead of making a copied one , we could have exported this
> API.

ok.

>>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>>> +{
>>> +	struct sdw_transport_data t_data = {0};
>>> +	struct sdw_master_runtime *m_rt;
>>> +	struct sdw_port_runtime *p_rt;
>>> +	struct sdw_bus_params *b_params = &bus->params;
>>> +	int port_bo, hstart, hstop, sample_int;
>>> +	unsigned int rate, bps;
>>> +
>>> +	port_bo  = 0;
>>> +	hstart = 1;
>>> +	hstop = bus->params.col - 1;
>>> +	t_data.hstop = hstop;
>>> +	t_data.hstart = hstart;
>>> +
>>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>>> +		rate = m_rt->stream->params.rate;
>>> +		bps = m_rt->stream->params.bps;
>>> +		sample_int = (bus->params.curr_dr_freq / rate);
>>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>>> +			port_bo = (p_rt->num * 64) + 1;
>>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>>> +				p_rt->num, hstart, hstop, port_bo);
>>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>>> +					      port_bo, port_bo >> 8, hstart, hstop,
>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>> +
>>> +			sdw_fill_port_params(&p_rt->port_params,
>>> +					     p_rt->num, bps,
>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>> +					     b_params->m_data_mode);
>>> +			t_data.hstart = hstart;
>>> +			t_data.hstop = hstop;
>>> +			t_data.block_offset = port_bo;
>>> +			t_data.sub_block_offset = 0;
>>> +		}
>>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>>> +	}
>>> +	return 0;
>>> +}
>> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>>
>> You would need a lot more comments to convince me that this is
>> intentional and needed.
> This is intentional. We have a HW bug, if we go it generic bdw allocation
> API, when we launch multiple streams, we are observing noise for shorter
> duration for active stream.
> To avoid that, we have slightly modified the sdw_compute_master_ports()
> API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
> We will expand the coverage in the future.

That's fine, it's perfectly ok to have different strategies on the host
side. Exporting additional functions from generic_bandwidth_allocation.c
would help, you can pick what you need.
  
>>> +
>>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>>> +				unsigned int bank)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>>> +
>>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>>> +	switch (ctrl->instance) {
>>> +	case ACP_SDW0:
>>> +		channel_type = p_params->num;
>>> +		break;
>>> +	case ACP_SDW1:
>>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	switch (channel_type) {
>>> +	case ACP_SDW0_AUDIO_TX:
>> you'll have to explain what you mean by 'channel_type'
>>
>> This looks like the streams that can be supported by this master
>> implementation, with dailinks for each.
> 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)
> 
> Each port number on Manager side is mapped to a channel number.
> i.e SDW0 Pin0 -> port number 0 -> Audio TX
>     SDW0 Pin1 -> Port number 1 -> Headset TX
>     SDW0 Pin2 -> Port number 2 -> BT TX
>     SDW0 Pin3 -> port number 3 -> Audio RX
>     SDW0 Pin4 -> Port number 4 -> Headset RX
>     SDW0 Pin5 -> Port number 5 -> BT RX
> 
> Whereas for SDW1 instance
> 
>     SDW1 Pin0 -> port number 0 -> P1 BT TX
>     SDW1 Pin1 -> Port number 1 -> P1 BT RX
>     
> We use this channel value to program register set for transport params,
> port params and Channel enable for each manager instance.
> We need to use same channel mapping for programming DMA controller
> registers in Soundwire DMA driver.
> i.e if AUDIO TX channel is selected then we need to use Audio TX registers
> for DMA programming in Soundwire DMA driver.

Ah, that's an important piece of information that should probably be
captured to help reviewers. On the Intel side the assignment from stream
types to ports is handled at the machine driver + topology level.


>>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>>> +				     struct sdw_transport_params *params,
>>> +				     enum sdw_reg_bank bank)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>> +	u32 ssp_counter_reg;
>>> +	u32 dpn_frame_fmt;
>>> +	u32 dpn_sampleinterval;
>>> +	u32 dpn_hctrl;
>>> +	u32 dpn_offsetctrl;
>>> +	u32 dpn_lanectrl;
>>> +	u32 channel_type;
>>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>>> +	u32 offset_reg, lane_ctrl_reg;
>>> +
>>> +	switch (ctrl->instance) {
>>> +	case ACP_SDW0:
>>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>>> +		channel_type = params->port_num;
>>> +		break;
>>> +	case ACP_SDW1:
>>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
>> There's obviously a dependency between SDW0 and SDW1 managers that you
>> haven't described?
> No, both are independent manager instances which are connected in
> different power domains.

if they are independent, then why does the channel type for SDW1 depends
on SDW0_MAX_DAI?

>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>>> +		__func__, params->port_num, channel_type);
>>> +
>>> +	switch (channel_type) {
>>> +	case ACP_SDW0_AUDIO_TX:
>>> +	{
>>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>> This is confusing. Is this about enabling a stream or selecting the lane
>> for this port? Same for all cases.
>>
>> is this saying that the two cases are handled by the same register -
>> unlike what is normative for the peripherals where the two concepts are
>> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
> we have to refer the same register to program channel enable and lane
> ctrl as per our soundwire register definition.

ok, please clarify with a comment. It's fine but different from other
implementations on device and host sides.

>>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
>> same quirk as Intel, nice :-)
>>
>>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
>> And here too. Is this really needed or just-copy-pasted?
> No, It's not a copy and paste. We have seen issues bus clash/parity errors
> during peripheral enumeration/initialization across the multiple links without
> this quirk. We have also seen device alerts missed during peripheral initialization
> sequence.

ah, that's good to some extent that it wasn't the Intel IP behaving :-)


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-13 18:41         ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-13 18:41 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao


>>> +	for (index = 0; index < 2; index++) {
>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>> +			timeout = 1;
>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>> +			no_ack = 1;
>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>> +				nack = 1;
>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>> +			}
>> this is a copy of the cadence_master.c code... With the error added that
>> this is not for a controller but for a master...
> Its manager instance only. Our immediate command and response
> mechanism allows sending commands over the link and get the
> response for every command immediately, unlike as mentioned in
> candence_master.c.

I don't get the reply. The Cadence IP also has the ability to get the
response immediately. There's limited scope for creativity, the commands
are defined in the spec and the responses as well.

>>> +		}
>>> +	}
>>> +
>>> +	if (timeout) {
>>> +		dev_err_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_TIMEOUT;
>>> +	}
>>> +
>>> +	if (nack) {
>>> +		dev_err_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_FAIL;
>>> +	}
>>> +
>>> +	if (no_ack) {
>>> +		dev_dbg_ratelimited(ctrl->dev,
>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>> +		return SDW_CMD_IGNORED;
>>> +	}
>>> +	return SDW_CMD_OK;
>> this should probably become a helper since the response is really the
>> same as in cadence_master.c
>>
>> There's really room for optimization and reuse here.
> not really needed. Please refer above comment as command/response
> mechanism differs from Intel's implementation.

how? there's a buffer of responses in both cases. please clarify.

>>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>>> +					 struct sdw_transport_data *t_data)
>>> +{
>>> +	struct sdw_slave_runtime *s_rt = NULL;
>>> +	struct sdw_port_runtime *p_rt;
>>> +	int port_bo, sample_int;
>>> +	unsigned int rate, bps, ch = 0;
>>> +	unsigned int slave_total_ch;
>>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>>> +
>>> +	port_bo = t_data->block_offset;
>>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>>> +		rate = m_rt->stream->params.rate;
>>> +		bps = m_rt->stream->params.bps;
>>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>>> +		slave_total_ch = 0;
>>> +
>>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>>> +
>>> +			sdw_fill_xport_params(&p_rt->transport_params,
>>> +					      p_rt->num, false,
>>> +					      SDW_BLK_GRP_CNT_1,
>>> +					      sample_int, port_bo, port_bo >> 8,
>>> +					      t_data->hstart,
>>> +					      t_data->hstop,
>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>> +
>>> +			sdw_fill_port_params(&p_rt->port_params,
>>> +					     p_rt->num, bps,
>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>> +					     b_params->s_data_mode);
>>> +
>>> +			port_bo += bps * ch;
>>> +			slave_total_ch += ch;
>>> +		}
>>> +
>>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>>> +		    m_rt->ch_count == slave_total_ch) {
>>> +			port_bo = t_data->block_offset;
>>> +		}
>>> +	}
>>> +}
>> ok, this is really bad.
>>
>> This is a verbatim copy of the same function in
>> generic_bandwidth_allocation.c
>>
>> see
>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>>
>> You only removed the comments and renamed the function.
>>
>> Seriously? Why would you do that?
>>
>> And in addition, this has *NOTHING* to do with the master support.
>>
>> Programming the ports on peripheral side is something that happens at
>> the stream level.
>>
>> I am afraid it's a double NAK, or rather NAK^2 from me here.
> Our intention is to implement our own compute params callback.
> Sorry, instead of making a copied one , we could have exported this
> API.

ok.

>>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>>> +{
>>> +	struct sdw_transport_data t_data = {0};
>>> +	struct sdw_master_runtime *m_rt;
>>> +	struct sdw_port_runtime *p_rt;
>>> +	struct sdw_bus_params *b_params = &bus->params;
>>> +	int port_bo, hstart, hstop, sample_int;
>>> +	unsigned int rate, bps;
>>> +
>>> +	port_bo  = 0;
>>> +	hstart = 1;
>>> +	hstop = bus->params.col - 1;
>>> +	t_data.hstop = hstop;
>>> +	t_data.hstart = hstart;
>>> +
>>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>>> +		rate = m_rt->stream->params.rate;
>>> +		bps = m_rt->stream->params.bps;
>>> +		sample_int = (bus->params.curr_dr_freq / rate);
>>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>>> +			port_bo = (p_rt->num * 64) + 1;
>>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>>> +				p_rt->num, hstart, hstop, port_bo);
>>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>>> +					      port_bo, port_bo >> 8, hstart, hstop,
>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>> +
>>> +			sdw_fill_port_params(&p_rt->port_params,
>>> +					     p_rt->num, bps,
>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>> +					     b_params->m_data_mode);
>>> +			t_data.hstart = hstart;
>>> +			t_data.hstop = hstop;
>>> +			t_data.block_offset = port_bo;
>>> +			t_data.sub_block_offset = 0;
>>> +		}
>>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>>> +	}
>>> +	return 0;
>>> +}
>> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>>
>> You would need a lot more comments to convince me that this is
>> intentional and needed.
> This is intentional. We have a HW bug, if we go it generic bdw allocation
> API, when we launch multiple streams, we are observing noise for shorter
> duration for active stream.
> To avoid that, we have slightly modified the sdw_compute_master_ports()
> API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
> We will expand the coverage in the future.

That's fine, it's perfectly ok to have different strategies on the host
side. Exporting additional functions from generic_bandwidth_allocation.c
would help, you can pick what you need.
  
>>> +
>>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>>> +				unsigned int bank)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>>> +
>>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>>> +	switch (ctrl->instance) {
>>> +	case ACP_SDW0:
>>> +		channel_type = p_params->num;
>>> +		break;
>>> +	case ACP_SDW1:
>>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	switch (channel_type) {
>>> +	case ACP_SDW0_AUDIO_TX:
>> you'll have to explain what you mean by 'channel_type'
>>
>> This looks like the streams that can be supported by this master
>> implementation, with dailinks for each.
> 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)
> 
> Each port number on Manager side is mapped to a channel number.
> i.e SDW0 Pin0 -> port number 0 -> Audio TX
>     SDW0 Pin1 -> Port number 1 -> Headset TX
>     SDW0 Pin2 -> Port number 2 -> BT TX
>     SDW0 Pin3 -> port number 3 -> Audio RX
>     SDW0 Pin4 -> Port number 4 -> Headset RX
>     SDW0 Pin5 -> Port number 5 -> BT RX
> 
> Whereas for SDW1 instance
> 
>     SDW1 Pin0 -> port number 0 -> P1 BT TX
>     SDW1 Pin1 -> Port number 1 -> P1 BT RX
>     
> We use this channel value to program register set for transport params,
> port params and Channel enable for each manager instance.
> We need to use same channel mapping for programming DMA controller
> registers in Soundwire DMA driver.
> i.e if AUDIO TX channel is selected then we need to use Audio TX registers
> for DMA programming in Soundwire DMA driver.

Ah, that's an important piece of information that should probably be
captured to help reviewers. On the Intel side the assignment from stream
types to ports is handled at the machine driver + topology level.


>>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>>> +				     struct sdw_transport_params *params,
>>> +				     enum sdw_reg_bank bank)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>> +	u32 ssp_counter_reg;
>>> +	u32 dpn_frame_fmt;
>>> +	u32 dpn_sampleinterval;
>>> +	u32 dpn_hctrl;
>>> +	u32 dpn_offsetctrl;
>>> +	u32 dpn_lanectrl;
>>> +	u32 channel_type;
>>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>>> +	u32 offset_reg, lane_ctrl_reg;
>>> +
>>> +	switch (ctrl->instance) {
>>> +	case ACP_SDW0:
>>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>>> +		channel_type = params->port_num;
>>> +		break;
>>> +	case ACP_SDW1:
>>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
>> There's obviously a dependency between SDW0 and SDW1 managers that you
>> haven't described?
> No, both are independent manager instances which are connected in
> different power domains.

if they are independent, then why does the channel type for SDW1 depends
on SDW0_MAX_DAI?

>>> +		break;
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>>> +		__func__, params->port_num, channel_type);
>>> +
>>> +	switch (channel_type) {
>>> +	case ACP_SDW0_AUDIO_TX:
>>> +	{
>>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>> This is confusing. Is this about enabling a stream or selecting the lane
>> for this port? Same for all cases.
>>
>> is this saying that the two cases are handled by the same register -
>> unlike what is normative for the peripherals where the two concepts are
>> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
> we have to refer the same register to program channel enable and lane
> ctrl as per our soundwire register definition.

ok, please clarify with a comment. It's fine but different from other
implementations on device and host sides.

>>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>>> +{
>>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
>> same quirk as Intel, nice :-)
>>
>>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
>> And here too. Is this really needed or just-copy-pasted?
> No, It's not a copy and paste. We have seen issues bus clash/parity errors
> during peripheral enumeration/initialization across the multiple links without
> this quirk. We have also seen device alerts missed during peripheral initialization
> sequence.

ah, that's good to some extent that it wasn't the Intel IP behaving :-)


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-13 17:33               ` Pierre-Louis Bossart
@ 2023-01-13 19:57                   ` Mark Brown
  0 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-13 19:57 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Mukunda,Vijendar, Limonciello, Mario, vkoul, alsa-devel,
	Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti

[-- Attachment #1: Type: text/plain, Size: 558 bytes --]

On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:

> I do recall some issues with the codec jacks, where if the card
> registration happens too late the codec might have suspended. But we
> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
> that was solved.

Right, I would expect that whatever needs the device to be powered on
would be explicitly ensuring that this is done rather than tweaking
timeouts - the timeouts should be more of a performance thing to avoid
bouncing power too much, not a correctness thing.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-13 19:57                   ` Mark Brown
  0 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-13 19:57 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, Mukunda, Vijendar, arungopal.kondaveeti,
	Syed Saba Kareem

[-- Attachment #1: Type: text/plain, Size: 558 bytes --]

On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:

> I do recall some issues with the codec jacks, where if the card
> registration happens too late the codec might have suspended. But we
> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
> that was solved.

Right, I would expect that whatever needs the device to be powered on
would be explicitly ensuring that this is done rather than tweaking
timeouts - the timeouts should be more of a performance thing to avoid
bouncing power too much, not a correctness thing.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
  2023-01-13 17:05         ` Pierre-Louis Bossart
@ 2023-01-16  6:59           ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  6:59 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Mario.Limonciello, arungopal.kondaveeti, Syed Saba Kareem

On 13/01/23 22:35, Pierre-Louis Bossart wrote:
>>>> +	sdw_data->sdw_stream[stream_id] = substream;
>>>> +	size = params_buffer_bytes(params);
>>>> +	period_bytes = params_period_bytes(params);
>>>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>>>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>>>> +	acp63_config_dma(sdw_stream_data, stream_id);
>>>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>>>> +	if (ret) {
>>>> +		dev_err(component->dev, "Invalid channel type\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	switch (stream_id) {
>>>> +	case ACP_SDW_AUDIO_TX:
>>>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>>>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>>>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>>>> +		break;
>>> so there's ONE resource to deal with external codecs? How does this work
>>> if you have a headset codec and an amplifier?
>> Are you referring to playing a same stream over headset codec and
>> amplifier?
>> It's all about channel selection from DMA perspective.
>> We have tested speaker aggregation and headset playback use cases.
> No, I wasn't asking about playing the same content to different sinks.
>
> I was referring to playing/recording different content to/from different
> devices.
Yes , it's possible. We have tested parallel RX and TX streams.
Please refer patch 2 review comments reply.

For ex, speaker playback we are using Audio_TX Channel whereas for
Headset playback we are using Headset_Tx channel.
>
> Even when interfacing with a single device, there are interesting
> topologies in the SDCA spec that would require multiple DMA transfers
> conveying unrelated content (or processed content from the same source).
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops
@ 2023-01-16  6:59           ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  6:59 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Mario.Limonciello,
	arungopal.kondaveeti, Syed Saba Kareem

On 13/01/23 22:35, Pierre-Louis Bossart wrote:
>>>> +	sdw_data->sdw_stream[stream_id] = substream;
>>>> +	size = params_buffer_bytes(params);
>>>> +	period_bytes = params_period_bytes(params);
>>>> +	sdw_stream_data->dma_addr = substream->runtime->dma_addr;
>>>> +	sdw_stream_data->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
>>>> +	acp63_config_dma(sdw_stream_data, stream_id);
>>>> +	ret = acp63_configure_sdw_ringbuffer(sdw_stream_data->acp_base, stream_id, size);
>>>> +	if (ret) {
>>>> +		dev_err(component->dev, "Invalid channel type\n");
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	switch (stream_id) {
>>>> +	case ACP_SDW_AUDIO_TX:
>>>> +		water_mark_size_reg = ACP_AUDIO_TX_INTR_WATERMARK_SIZE;
>>>> +		irq_mask = BIT(ACP_AUDIO_TX_THRESHOLD);
>>>> +		acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
>>>> +		break;
>>> so there's ONE resource to deal with external codecs? How does this work
>>> if you have a headset codec and an amplifier?
>> Are you referring to playing a same stream over headset codec and
>> amplifier?
>> It's all about channel selection from DMA perspective.
>> We have tested speaker aggregation and headset playback use cases.
> No, I wasn't asking about playing the same content to different sinks.
>
> I was referring to playing/recording different content to/from different
> devices.
Yes , it's possible. We have tested parallel RX and TX streams.
Please refer patch 2 review comments reply.

For ex, speaker playback we are using Audio_TX Channel whereas for
Headset playback we are using Headset_Tx channel.
>
> Even when interfacing with a single device, there are interesting
> topologies in the SDCA spec that would require multiple DMA transfers
> conveying unrelated content (or processed content from the same source).
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-13 18:41         ` Pierre-Louis Bossart
@ 2023-01-16  7:53           ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  7:53 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>> +	for (index = 0; index < 2; index++) {
>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>> +			timeout = 1;
>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>> +			no_ack = 1;
>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>> +				nack = 1;
>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>> +			}
>>> this is a copy of the cadence_master.c code... With the error added that
>>> this is not for a controller but for a master...
>> Its manager instance only. Our immediate command and response
>> mechanism allows sending commands over the link and get the
>> response for every command immediately, unlike as mentioned in
>> candence_master.c.
> I don't get the reply. The Cadence IP also has the ability to get the
> response immediately. There's limited scope for creativity, the commands
> are defined in the spec and the responses as well.
As per our understanding in Intel code, responses are processed
after sending all commands.
In our case, we send the command and process the response
immediately before invoking the next command.
>>>> +		}
>>>> +	}
>>>> +
>>>> +	if (timeout) {
>>>> +		dev_err_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_TIMEOUT;
>>>> +	}
>>>> +
>>>> +	if (nack) {
>>>> +		dev_err_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_FAIL;
>>>> +	}
>>>> +
>>>> +	if (no_ack) {
>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_IGNORED;
>>>> +	}
>>>> +	return SDW_CMD_OK;
>>> this should probably become a helper since the response is really the
>>> same as in cadence_master.c
>>>
>>> There's really room for optimization and reuse here.
>> not really needed. Please refer above comment as command/response
>> mechanism differs from Intel's implementation.
> how? there's a buffer of responses in both cases. please clarify.
Ours implementation is not interrupt driven like Intel.
When we send command over the link, we will wait for command's
response in polling method and process the response immediately
before issuing the new command.

>>>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>>>> +					 struct sdw_transport_data *t_data)
>>>> +{
>>>> +	struct sdw_slave_runtime *s_rt = NULL;
>>>> +	struct sdw_port_runtime *p_rt;
>>>> +	int port_bo, sample_int;
>>>> +	unsigned int rate, bps, ch = 0;
>>>> +	unsigned int slave_total_ch;
>>>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>>>> +
>>>> +	port_bo = t_data->block_offset;
>>>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>>>> +		rate = m_rt->stream->params.rate;
>>>> +		bps = m_rt->stream->params.bps;
>>>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>>>> +		slave_total_ch = 0;
>>>> +
>>>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>>>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>>>> +
>>>> +			sdw_fill_xport_params(&p_rt->transport_params,
>>>> +					      p_rt->num, false,
>>>> +					      SDW_BLK_GRP_CNT_1,
>>>> +					      sample_int, port_bo, port_bo >> 8,
>>>> +					      t_data->hstart,
>>>> +					      t_data->hstop,
>>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>>> +
>>>> +			sdw_fill_port_params(&p_rt->port_params,
>>>> +					     p_rt->num, bps,
>>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>>> +					     b_params->s_data_mode);
>>>> +
>>>> +			port_bo += bps * ch;
>>>> +			slave_total_ch += ch;
>>>> +		}
>>>> +
>>>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>>>> +		    m_rt->ch_count == slave_total_ch) {
>>>> +			port_bo = t_data->block_offset;
>>>> +		}
>>>> +	}
>>>> +}
>>> ok, this is really bad.
>>>
>>> This is a verbatim copy of the same function in
>>> generic_bandwidth_allocation.c
>>>
>>> see
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>>>
>>> You only removed the comments and renamed the function.
>>>
>>> Seriously? Why would you do that?
>>>
>>> And in addition, this has *NOTHING* to do with the master support.
>>>
>>> Programming the ports on peripheral side is something that happens at
>>> the stream level.
>>>
>>> I am afraid it's a double NAK, or rather NAK^2 from me here.
>> Our intention is to implement our own compute params callback.
>> Sorry, instead of making a copied one , we could have exported this
>> API.
> ok.
>
>>>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>>>> +{
>>>> +	struct sdw_transport_data t_data = {0};
>>>> +	struct sdw_master_runtime *m_rt;
>>>> +	struct sdw_port_runtime *p_rt;
>>>> +	struct sdw_bus_params *b_params = &bus->params;
>>>> +	int port_bo, hstart, hstop, sample_int;
>>>> +	unsigned int rate, bps;
>>>> +
>>>> +	port_bo  = 0;
>>>> +	hstart = 1;
>>>> +	hstop = bus->params.col - 1;
>>>> +	t_data.hstop = hstop;
>>>> +	t_data.hstart = hstart;
>>>> +
>>>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>>>> +		rate = m_rt->stream->params.rate;
>>>> +		bps = m_rt->stream->params.bps;
>>>> +		sample_int = (bus->params.curr_dr_freq / rate);
>>>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>>>> +			port_bo = (p_rt->num * 64) + 1;
>>>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>>>> +				p_rt->num, hstart, hstop, port_bo);
>>>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>>>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>>>> +					      port_bo, port_bo >> 8, hstart, hstop,
>>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>>> +
>>>> +			sdw_fill_port_params(&p_rt->port_params,
>>>> +					     p_rt->num, bps,
>>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>>> +					     b_params->m_data_mode);
>>>> +			t_data.hstart = hstart;
>>>> +			t_data.hstop = hstop;
>>>> +			t_data.block_offset = port_bo;
>>>> +			t_data.sub_block_offset = 0;
>>>> +		}
>>>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>>>> +	}
>>>> +	return 0;
>>>> +}
>>> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>>>
>>> You would need a lot more comments to convince me that this is
>>> intentional and needed.
>> This is intentional. We have a HW bug, if we go it generic bdw allocation
>> API, when we launch multiple streams, we are observing noise for shorter
>> duration for active stream.
>> To avoid that, we have slightly modified the sdw_compute_master_ports()
>> API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
>> We will expand the coverage in the future.
> That's fine, it's perfectly ok to have different strategies on the host
> side. Exporting additional functions from generic_bandwidth_allocation.c
> would help, you can pick what you need.
>   
>>>> +
>>>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>>>> +				unsigned int bank)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>>>> +
>>>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>>>> +	switch (ctrl->instance) {
>>>> +	case ACP_SDW0:
>>>> +		channel_type = p_params->num;
>>>> +		break;
>>>> +	case ACP_SDW1:
>>>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>>>> +		break;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	switch (channel_type) {
>>>> +	case ACP_SDW0_AUDIO_TX:
>>> you'll have to explain what you mean by 'channel_type'
>>>
>>> This looks like the streams that can be supported by this master
>>> implementation, with dailinks for each.
>> 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)
>>
>> Each port number on Manager side is mapped to a channel number.
>> i.e SDW0 Pin0 -> port number 0 -> Audio TX
>>     SDW0 Pin1 -> Port number 1 -> Headset TX
>>     SDW0 Pin2 -> Port number 2 -> BT TX
>>     SDW0 Pin3 -> port number 3 -> Audio RX
>>     SDW0 Pin4 -> Port number 4 -> Headset RX
>>     SDW0 Pin5 -> Port number 5 -> BT RX
>>
>> Whereas for SDW1 instance
>>
>>     SDW1 Pin0 -> port number 0 -> P1 BT TX
>>     SDW1 Pin1 -> Port number 1 -> P1 BT RX
>>     
>> We use this channel value to program register set for transport params,
>> port params and Channel enable for each manager instance.
>> We need to use same channel mapping for programming DMA controller
>> registers in Soundwire DMA driver.
>> i.e if AUDIO TX channel is selected then we need to use Audio TX registers
>> for DMA programming in Soundwire DMA driver.
> Ah, that's an important piece of information that should probably be
> captured to help reviewers. On the Intel side the assignment from stream
> types to ports is handled at the machine driver + topology level.
We will add comments in the code.
>
>
>>>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>>>> +				     struct sdw_transport_params *params,
>>>> +				     enum sdw_reg_bank bank)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>>> +	u32 ssp_counter_reg;
>>>> +	u32 dpn_frame_fmt;
>>>> +	u32 dpn_sampleinterval;
>>>> +	u32 dpn_hctrl;
>>>> +	u32 dpn_offsetctrl;
>>>> +	u32 dpn_lanectrl;
>>>> +	u32 channel_type;
>>>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>>>> +	u32 offset_reg, lane_ctrl_reg;
>>>> +
>>>> +	switch (ctrl->instance) {
>>>> +	case ACP_SDW0:
>>>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>>>> +		channel_type = params->port_num;
>>>> +		break;
>>>> +	case ACP_SDW1:
>>>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>>>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
>>> There's obviously a dependency between SDW0 and SDW1 managers that you
>>> haven't described?
>> No, both are independent manager instances which are connected in
>> different power domains.
> if they are independent, then why does the channel type for SDW1 depends
> on SDW0_MAX_DAI?
There is no hard dependency for SDW1 on SDW0_MAX_DAI.
We will modify the code.
>>>> +		break;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>>>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>>>> +		__func__, params->port_num, channel_type);
>>>> +
>>>> +	switch (channel_type) {
>>>> +	case ACP_SDW0_AUDIO_TX:
>>>> +	{
>>>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>>>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>>>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>>>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>>>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>>> This is confusing. Is this about enabling a stream or selecting the lane
>>> for this port? Same for all cases.
>>>
>>> is this saying that the two cases are handled by the same register -
>>> unlike what is normative for the peripherals where the two concepts are
>>> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
>> we have to refer the same register to program channel enable and lane
>> ctrl as per our soundwire register definition.
> ok, please clarify with a comment. It's fine but different from other
> implementations on device and host sides.
Will add comment.
>
>>>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
>>> same quirk as Intel, nice :-)
>>>
>>>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>>>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
>>> And here too. Is this really needed or just-copy-pasted?
>> No, It's not a copy and paste. We have seen issues bus clash/parity errors
>> during peripheral enumeration/initialization across the multiple links without
>> this quirk. We have also seen device alerts missed during peripheral initialization
>> sequence.
> ah, that's good to some extent that it wasn't the Intel IP behaving :-)
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-16  7:53           ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  7:53 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>> +	for (index = 0; index < 2; index++) {
>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>> +			timeout = 1;
>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>> +			no_ack = 1;
>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>> +				nack = 1;
>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>> +			}
>>> this is a copy of the cadence_master.c code... With the error added that
>>> this is not for a controller but for a master...
>> Its manager instance only. Our immediate command and response
>> mechanism allows sending commands over the link and get the
>> response for every command immediately, unlike as mentioned in
>> candence_master.c.
> I don't get the reply. The Cadence IP also has the ability to get the
> response immediately. There's limited scope for creativity, the commands
> are defined in the spec and the responses as well.
As per our understanding in Intel code, responses are processed
after sending all commands.
In our case, we send the command and process the response
immediately before invoking the next command.
>>>> +		}
>>>> +	}
>>>> +
>>>> +	if (timeout) {
>>>> +		dev_err_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_TIMEOUT;
>>>> +	}
>>>> +
>>>> +	if (nack) {
>>>> +		dev_err_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_FAIL;
>>>> +	}
>>>> +
>>>> +	if (no_ack) {
>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>> +		return SDW_CMD_IGNORED;
>>>> +	}
>>>> +	return SDW_CMD_OK;
>>> this should probably become a helper since the response is really the
>>> same as in cadence_master.c
>>>
>>> There's really room for optimization and reuse here.
>> not really needed. Please refer above comment as command/response
>> mechanism differs from Intel's implementation.
> how? there's a buffer of responses in both cases. please clarify.
Ours implementation is not interrupt driven like Intel.
When we send command over the link, we will wait for command's
response in polling method and process the response immediately
before issuing the new command.

>>>> +static void amd_sdwc_compute_slave_ports(struct sdw_master_runtime *m_rt,
>>>> +					 struct sdw_transport_data *t_data)
>>>> +{
>>>> +	struct sdw_slave_runtime *s_rt = NULL;
>>>> +	struct sdw_port_runtime *p_rt;
>>>> +	int port_bo, sample_int;
>>>> +	unsigned int rate, bps, ch = 0;
>>>> +	unsigned int slave_total_ch;
>>>> +	struct sdw_bus_params *b_params = &m_rt->bus->params;
>>>> +
>>>> +	port_bo = t_data->block_offset;
>>>> +	list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) {
>>>> +		rate = m_rt->stream->params.rate;
>>>> +		bps = m_rt->stream->params.bps;
>>>> +		sample_int = (m_rt->bus->params.curr_dr_freq / rate);
>>>> +		slave_total_ch = 0;
>>>> +
>>>> +		list_for_each_entry(p_rt, &s_rt->port_list, port_node) {
>>>> +			ch = sdw_ch_mask_to_ch(p_rt->ch_mask);
>>>> +
>>>> +			sdw_fill_xport_params(&p_rt->transport_params,
>>>> +					      p_rt->num, false,
>>>> +					      SDW_BLK_GRP_CNT_1,
>>>> +					      sample_int, port_bo, port_bo >> 8,
>>>> +					      t_data->hstart,
>>>> +					      t_data->hstop,
>>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>>> +
>>>> +			sdw_fill_port_params(&p_rt->port_params,
>>>> +					     p_rt->num, bps,
>>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>>> +					     b_params->s_data_mode);
>>>> +
>>>> +			port_bo += bps * ch;
>>>> +			slave_total_ch += ch;
>>>> +		}
>>>> +
>>>> +		if (m_rt->direction == SDW_DATA_DIR_TX &&
>>>> +		    m_rt->ch_count == slave_total_ch) {
>>>> +			port_bo = t_data->block_offset;
>>>> +		}
>>>> +	}
>>>> +}
>>> ok, this is really bad.
>>>
>>> This is a verbatim copy of the same function in
>>> generic_bandwidth_allocation.c
>>>
>>> see
>>> https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Felixir.bootlin.com%2Flinux%2Flatest%2Fsource%2Fdrivers%2Fsoundwire%2Fgeneric_bandwidth_allocation.c%23L38&data=05%7C01%7CVijendar.Mukunda%40amd.com%7Ccac3e7985a9347a69be908daf3f1ea8a%7C3dd8961fe4884e608e11a82d994e183d%7C0%7C0%7C638090517594233520%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=MhN0eJYtggAnkUyb6mDIWW8LvwGeS8JJ%2F2UBHkHHOYo%3D&reserved=0
>>>
>>> You only removed the comments and renamed the function.
>>>
>>> Seriously? Why would you do that?
>>>
>>> And in addition, this has *NOTHING* to do with the master support.
>>>
>>> Programming the ports on peripheral side is something that happens at
>>> the stream level.
>>>
>>> I am afraid it's a double NAK, or rather NAK^2 from me here.
>> Our intention is to implement our own compute params callback.
>> Sorry, instead of making a copied one , we could have exported this
>> API.
> ok.
>
>>>> +static int amd_sdwc_compute_params(struct sdw_bus *bus)
>>>> +{
>>>> +	struct sdw_transport_data t_data = {0};
>>>> +	struct sdw_master_runtime *m_rt;
>>>> +	struct sdw_port_runtime *p_rt;
>>>> +	struct sdw_bus_params *b_params = &bus->params;
>>>> +	int port_bo, hstart, hstop, sample_int;
>>>> +	unsigned int rate, bps;
>>>> +
>>>> +	port_bo  = 0;
>>>> +	hstart = 1;
>>>> +	hstop = bus->params.col - 1;
>>>> +	t_data.hstop = hstop;
>>>> +	t_data.hstart = hstart;
>>>> +
>>>> +	list_for_each_entry(m_rt, &bus->m_rt_list, bus_node) {
>>>> +		rate = m_rt->stream->params.rate;
>>>> +		bps = m_rt->stream->params.bps;
>>>> +		sample_int = (bus->params.curr_dr_freq / rate);
>>>> +		list_for_each_entry(p_rt, &m_rt->port_list, port_node) {
>>>> +			port_bo = (p_rt->num * 64) + 1;
>>>> +			dev_dbg(bus->dev, "p_rt->num=%d hstart=%d hstop=%d port_bo=%d\n",
>>>> +				p_rt->num, hstart, hstop, port_bo);
>>>> +			sdw_fill_xport_params(&p_rt->transport_params, p_rt->num,
>>>> +					      false, SDW_BLK_GRP_CNT_1, sample_int,
>>>> +					      port_bo, port_bo >> 8, hstart, hstop,
>>>> +					      SDW_BLK_PKG_PER_PORT, 0x0);
>>>> +
>>>> +			sdw_fill_port_params(&p_rt->port_params,
>>>> +					     p_rt->num, bps,
>>>> +					     SDW_PORT_FLOW_MODE_ISOCH,
>>>> +					     b_params->m_data_mode);
>>>> +			t_data.hstart = hstart;
>>>> +			t_data.hstop = hstop;
>>>> +			t_data.block_offset = port_bo;
>>>> +			t_data.sub_block_offset = 0;
>>>> +		}
>>>> +		amd_sdwc_compute_slave_ports(m_rt, &t_data);
>>>> +	}
>>>> +	return 0;
>>>> +}
>>> this is a variation on sdw_compute_master_ports() in generic_allocation.c
>>>
>>> You would need a lot more comments to convince me that this is
>>> intentional and needed.
>> This is intentional. We have a HW bug, if we go it generic bdw allocation
>> API, when we launch multiple streams, we are observing noise for shorter
>> duration for active stream.
>> To avoid that, we have slightly modified the sdw_compute_master_ports()
>> API. As of now we are enabling solution for 48khz, 2Ch, 16bit.
>> We will expand the coverage in the future.
> That's fine, it's perfectly ok to have different strategies on the host
> side. Exporting additional functions from generic_bandwidth_allocation.c
> would help, you can pick what you need.
>   
>>>> +
>>>> +static int amd_sdwc_port_params(struct sdw_bus *bus, struct sdw_port_params *p_params,
>>>> +				unsigned int bank)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>>> +	u32 channel_type, frame_fmt_reg, dpn_frame_fmt;
>>>> +
>>>> +	dev_dbg(ctrl->dev, "%s: p_params->num:0x%x\n", __func__, p_params->num);
>>>> +	switch (ctrl->instance) {
>>>> +	case ACP_SDW0:
>>>> +		channel_type = p_params->num;
>>>> +		break;
>>>> +	case ACP_SDW1:
>>>> +		channel_type = p_params->num + ACP_SDW0_MAX_DAI;
>>>> +		break;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +
>>>> +	switch (channel_type) {
>>>> +	case ACP_SDW0_AUDIO_TX:
>>> you'll have to explain what you mean by 'channel_type'
>>>
>>> This looks like the streams that can be supported by this master
>>> implementation, with dailinks for each.
>> 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)
>>
>> Each port number on Manager side is mapped to a channel number.
>> i.e SDW0 Pin0 -> port number 0 -> Audio TX
>>     SDW0 Pin1 -> Port number 1 -> Headset TX
>>     SDW0 Pin2 -> Port number 2 -> BT TX
>>     SDW0 Pin3 -> port number 3 -> Audio RX
>>     SDW0 Pin4 -> Port number 4 -> Headset RX
>>     SDW0 Pin5 -> Port number 5 -> BT RX
>>
>> Whereas for SDW1 instance
>>
>>     SDW1 Pin0 -> port number 0 -> P1 BT TX
>>     SDW1 Pin1 -> Port number 1 -> P1 BT RX
>>     
>> We use this channel value to program register set for transport params,
>> port params and Channel enable for each manager instance.
>> We need to use same channel mapping for programming DMA controller
>> registers in Soundwire DMA driver.
>> i.e if AUDIO TX channel is selected then we need to use Audio TX registers
>> for DMA programming in Soundwire DMA driver.
> Ah, that's an important piece of information that should probably be
> captured to help reviewers. On the Intel side the assignment from stream
> types to ports is handled at the machine driver + topology level.
We will add comments in the code.
>
>
>>>> +static int amd_sdwc_transport_params(struct sdw_bus *bus,
>>>> +				     struct sdw_transport_params *params,
>>>> +				     enum sdw_reg_bank bank)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
>>>> +	u32 ssp_counter_reg;
>>>> +	u32 dpn_frame_fmt;
>>>> +	u32 dpn_sampleinterval;
>>>> +	u32 dpn_hctrl;
>>>> +	u32 dpn_offsetctrl;
>>>> +	u32 dpn_lanectrl;
>>>> +	u32 channel_type;
>>>> +	u32 frame_fmt_reg, sample_int_reg, hctrl_dp0_reg;
>>>> +	u32 offset_reg, lane_ctrl_reg;
>>>> +
>>>> +	switch (ctrl->instance) {
>>>> +	case ACP_SDW0:
>>>> +		ssp_counter_reg = ACP_SW_SSP_COUNTER;
>>>> +		channel_type = params->port_num;
>>>> +		break;
>>>> +	case ACP_SDW1:
>>>> +		ssp_counter_reg = ACP_P1_SW_SSP_COUNTER;
>>>> +		channel_type = params->port_num + ACP_SDW0_MAX_DAI;
>>> There's obviously a dependency between SDW0 and SDW1 managers that you
>>> haven't described?
>> No, both are independent manager instances which are connected in
>> different power domains.
> if they are independent, then why does the channel type for SDW1 depends
> on SDW0_MAX_DAI?
There is no hard dependency for SDW1 on SDW0_MAX_DAI.
We will modify the code.
>>>> +		break;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	acp_reg_writel(AMD_SDW_SSP_COUNTER_VAL, ctrl->mmio + ssp_counter_reg);
>>>> +	dev_dbg(bus->dev, "%s: p_params->num:0x%x entry channel_type:0x%x\n",
>>>> +		__func__, params->port_num, channel_type);
>>>> +
>>>> +	switch (channel_type) {
>>>> +	case ACP_SDW0_AUDIO_TX:
>>>> +	{
>>>> +		frame_fmt_reg = ACP_SW_AUDIO_TX_FRAME_FORMAT;
>>>> +		sample_int_reg = ACP_SW_AUDIO_TX_SAMPLEINTERVAL;
>>>> +		hctrl_dp0_reg = ACP_SW_AUDIO_TX_HCTRL_DP0;
>>>> +		offset_reg = ACP_SW_AUDIO_TX_OFFSET_DP0;
>>>> +		lane_ctrl_reg = ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0;
>>> This is confusing. Is this about enabling a stream or selecting the lane
>>> for this port? Same for all cases.
>>>
>>> is this saying that the two cases are handled by the same register -
>>> unlike what is normative for the peripherals where the two concepts are
>>> handeld in DPN_ChannelEn and DPN_LaneCtrl registers?
>> we have to refer the same register to program channel enable and lane
>> ctrl as per our soundwire register definition.
> ok, please clarify with a comment. It's fine but different from other
> implementations on device and host sides.
Will add comment.
>
>>>> +static int sdw_master_read_amd_prop(struct sdw_bus *bus)
>>>> +{
>>>> +	struct amd_sdwc_ctrl *ctrl = 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 master 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, "Master 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;
>>> same quirk as Intel, nice :-)
>>>
>>>> +	prop->quirks = SDW_MASTER_QUIRKS_CLEAR_INITIAL_CLASH |
>>>> +		       SDW_MASTER_QUIRKS_CLEAR_INITIAL_PARITY;
>>> And here too. Is this really needed or just-copy-pasted?
>> No, It's not a copy and paste. We have seen issues bus clash/parity errors
>> during peripheral enumeration/initialization across the multiple links without
>> this quirk. We have also seen device alerts missed during peripheral initialization
>> sequence.
> ah, that's good to some extent that it wasn't the Intel IP behaving :-)
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-13 17:11         ` Pierre-Louis Bossart
@ 2023-01-16  8:02           ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  8:02 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>> +			switch (acp_data->sdw_master_count) {
>>>> +			case 1:
>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>> +				break;
>>>> +			case 2:
>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>> +				break;
>>> so the cover letter is indeed wrong and confuses two controllers for two
>>> managers.
>> ACP IP has two independent manager instances driven by separate controller
>> each which are connected in different power domains.
>>
>> we should create two separate ACPI companion devices for separate
>> manager instance.  Currently we have limitations with BIOS.
>> we are going with single ACPI companion device.
>> We will update the changes later.
> Humm, this is tricky. The BIOS interface isn't something that can be
> changed at will on the kernel side, you'd have to maintain two solutions
> with a means to detect which one to use.
>
> Or is this is a temporary issue on development devices, then that part
> should probably not be upstreamed.
It's a temporary issue on development devices.
We had discussion with Windows dev team and BIOS team.
They have agreed to modify ACPI companion device logic.
We will update the two companion devices logic for two manager
instances in V2 version.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-16  8:02           ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  8:02 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem

On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>> +			switch (acp_data->sdw_master_count) {
>>>> +			case 1:
>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>> +				break;
>>>> +			case 2:
>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>> +				break;
>>> so the cover letter is indeed wrong and confuses two controllers for two
>>> managers.
>> ACP IP has two independent manager instances driven by separate controller
>> each which are connected in different power domains.
>>
>> we should create two separate ACPI companion devices for separate
>> manager instance.  Currently we have limitations with BIOS.
>> we are going with single ACPI companion device.
>> We will update the changes later.
> Humm, this is tricky. The BIOS interface isn't something that can be
> changed at will on the kernel side, you'd have to maintain two solutions
> with a means to detect which one to use.
>
> Or is this is a temporary issue on development devices, then that part
> should probably not be upstreamed.
It's a temporary issue on development devices.
We had discussion with Windows dev team and BIOS team.
They have agreed to modify ACPI companion device logic.
We will update the two companion devices logic for two manager
instances in V2 version.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-13 19:57                   ` Mark Brown
@ 2023-01-16  8:35                     ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  8:35 UTC (permalink / raw)
  To: Mark Brown, Pierre-Louis Bossart
  Cc: Limonciello, Mario, vkoul, alsa-devel, Mastan.Katragadda,
	Sunil-kumar.Dommati, Basavaraj.Hiregoudar, Takashi Iwai,
	Liam Girdwood, open list, Syed Saba Kareem, arungopal.kondaveeti

On 14/01/23 01:27, Mark Brown wrote:
> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>
>> I do recall some issues with the codec jacks, where if the card
>> registration happens too late the codec might have suspended. But we
>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>> that was solved.
> Right, I would expect that whatever needs the device to be powered on
> would be explicitly ensuring that this is done rather than tweaking
> timeouts - the timeouts should be more of a performance thing to avoid
> bouncing power too much, not a correctness thing.
Machine driver probe is executed in parallel with Manager driver
probe sequence. Because of it, before completion of all peripherals
enumeration across the multiple links, if card registration is
completed, codec register writes will fail as Codec device numbers
are not assigned.

If we understood correctly, as per your suggestion, We shouldn't use any
time bounds in machine driver probe sequence and before registering the
sound card, need to traverses through all peripheral initialization completion
status for all the managers.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-16  8:35                     ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-16  8:35 UTC (permalink / raw)
  To: Mark Brown, Pierre-Louis Bossart
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, arungopal.kondaveeti, Syed Saba Kareem

On 14/01/23 01:27, Mark Brown wrote:
> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>
>> I do recall some issues with the codec jacks, where if the card
>> registration happens too late the codec might have suspended. But we
>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>> that was solved.
> Right, I would expect that whatever needs the device to be powered on
> would be explicitly ensuring that this is done rather than tweaking
> timeouts - the timeouts should be more of a performance thing to avoid
> bouncing power too much, not a correctness thing.
Machine driver probe is executed in parallel with Manager driver
probe sequence. Because of it, before completion of all peripherals
enumeration across the multiple links, if card registration is
completed, codec register writes will fail as Codec device numbers
are not assigned.

If we understood correctly, as per your suggestion, We shouldn't use any
time bounds in machine driver probe sequence and before registering the
sound card, need to traverses through all peripheral initialization completion
status for all the managers.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-16  7:53           ` Mukunda,Vijendar
@ 2023-01-16 14:57             ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-16 14:57 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao



On 1/16/23 01:53, Mukunda,Vijendar wrote:
> On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>>> +	for (index = 0; index < 2; index++) {
>>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>>> +			timeout = 1;
>>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>>> +			no_ack = 1;
>>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>>> +				nack = 1;
>>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>>> +			}
>>>> this is a copy of the cadence_master.c code... With the error added that
>>>> this is not for a controller but for a master...
>>> Its manager instance only. Our immediate command and response
>>> mechanism allows sending commands over the link and get the
>>> response for every command immediately, unlike as mentioned in
>>> candence_master.c.
>> I don't get the reply. The Cadence IP also has the ability to get the
>> response immediately. There's limited scope for creativity, the commands
>> are defined in the spec and the responses as well.
> As per our understanding in Intel code, responses are processed
> after sending all commands.
> In our case, we send the command and process the response
> immediately before invoking the next command.

The Cadence IP can queue a number of commands, I think 8 off the top of
my head. But the response is provided immediately after each command.

Maybe the disconnect is that there's an ability to define a watermark on
the response buffer, so that the software can decide to process the
command responses in one shot.

>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	if (timeout) {
>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_TIMEOUT;
>>>>> +	}
>>>>> +
>>>>> +	if (nack) {
>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_FAIL;
>>>>> +	}
>>>>> +
>>>>> +	if (no_ack) {
>>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_IGNORED;
>>>>> +	}
>>>>> +	return SDW_CMD_OK;
>>>> this should probably become a helper since the response is really the
>>>> same as in cadence_master.c
>>>>
>>>> There's really room for optimization and reuse here.
>>> not really needed. Please refer above comment as command/response
>>> mechanism differs from Intel's implementation.
>> how? there's a buffer of responses in both cases. please clarify.
> Ours implementation is not interrupt driven like Intel.
> When we send command over the link, we will wait for command's
> response in polling method and process the response immediately
> before issuing the new command.

On the Intel side we use an interrupt to avoid polling, and in case of N
commands the watermark will be set to N to reduce the overhead. That
said, most users only use 1 command at a time, it's only recently that
Cirrus Logic experimented with multiple commands to speed-up transfers.

Even if there are differences in the way the responses are processed,
whether one-at-a-time or in a batch, the point remains that each command
response can be individually analyzed and that could be using a helper -
moving code from cadence_master.c into the bus layer.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-16 14:57             ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-16 14:57 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list



On 1/16/23 01:53, Mukunda,Vijendar wrote:
> On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>>> +	for (index = 0; index < 2; index++) {
>>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>>> +			timeout = 1;
>>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>>> +			no_ack = 1;
>>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>>> +				nack = 1;
>>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>>> +			}
>>>> this is a copy of the cadence_master.c code... With the error added that
>>>> this is not for a controller but for a master...
>>> Its manager instance only. Our immediate command and response
>>> mechanism allows sending commands over the link and get the
>>> response for every command immediately, unlike as mentioned in
>>> candence_master.c.
>> I don't get the reply. The Cadence IP also has the ability to get the
>> response immediately. There's limited scope for creativity, the commands
>> are defined in the spec and the responses as well.
> As per our understanding in Intel code, responses are processed
> after sending all commands.
> In our case, we send the command and process the response
> immediately before invoking the next command.

The Cadence IP can queue a number of commands, I think 8 off the top of
my head. But the response is provided immediately after each command.

Maybe the disconnect is that there's an ability to define a watermark on
the response buffer, so that the software can decide to process the
command responses in one shot.

>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	if (timeout) {
>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_TIMEOUT;
>>>>> +	}
>>>>> +
>>>>> +	if (nack) {
>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_FAIL;
>>>>> +	}
>>>>> +
>>>>> +	if (no_ack) {
>>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>>> +		return SDW_CMD_IGNORED;
>>>>> +	}
>>>>> +	return SDW_CMD_OK;
>>>> this should probably become a helper since the response is really the
>>>> same as in cadence_master.c
>>>>
>>>> There's really room for optimization and reuse here.
>>> not really needed. Please refer above comment as command/response
>>> mechanism differs from Intel's implementation.
>> how? there's a buffer of responses in both cases. please clarify.
> Ours implementation is not interrupt driven like Intel.
> When we send command over the link, we will wait for command's
> response in polling method and process the response immediately
> before issuing the new command.

On the Intel side we use an interrupt to avoid polling, and in case of N
commands the watermark will be set to N to reduce the overhead. That
said, most users only use 1 command at a time, it's only recently that
Cirrus Logic experimented with multiple commands to speed-up transfers.

Even if there are differences in the way the responses are processed,
whether one-at-a-time or in a batch, the point remains that each command
response can be individually analyzed and that could be using a helper -
moving code from cadence_master.c into the bus layer.



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-16  8:35                     ` Mukunda,Vijendar
@ 2023-01-16 15:02                       ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-16 15:02 UTC (permalink / raw)
  To: Mukunda,Vijendar, Mark Brown
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, arungopal.kondaveeti, Syed Saba Kareem



On 1/16/23 02:35, Mukunda,Vijendar wrote:
> On 14/01/23 01:27, Mark Brown wrote:
>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>
>>> I do recall some issues with the codec jacks, where if the card
>>> registration happens too late the codec might have suspended. But we
>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>> that was solved.
>> Right, I would expect that whatever needs the device to be powered on
>> would be explicitly ensuring that this is done rather than tweaking
>> timeouts - the timeouts should be more of a performance thing to avoid
>> bouncing power too much, not a correctness thing.
> Machine driver probe is executed in parallel with Manager driver
> probe sequence. Because of it, before completion of all peripherals
> enumeration across the multiple links, if card registration is
> completed, codec register writes will fail as Codec device numbers
> are not assigned.
> 
> If we understood correctly, as per your suggestion, We shouldn't use any
> time bounds in machine driver probe sequence and before registering the
> sound card, need to traverses through all peripheral initialization completion
> status for all the managers.

What's not clear in your reply is this:

What codec registers are accessed as a result of the machine driver
probe and card registration, and in what part of the card registration?

Are we talking about SoundWire 'standard' registers for device/port
management, about vendor specific ones that are exposed to userspace, or
vendor-specific ones entirely configured by the driver/regmap.

You've got to give us more data or understanding of the sequence to
help. Saying there's a race condition doesn't really help if there's
nothing that explains what codec registers are accessed and when.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-16 15:02                       ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-16 15:02 UTC (permalink / raw)
  To: Mukunda,Vijendar, Mark Brown
  Cc: Limonciello, Mario, vkoul, alsa-devel, Mastan.Katragadda,
	Sunil-kumar.Dommati, Basavaraj.Hiregoudar, Takashi Iwai,
	Liam Girdwood, open list, Syed Saba Kareem, arungopal.kondaveeti



On 1/16/23 02:35, Mukunda,Vijendar wrote:
> On 14/01/23 01:27, Mark Brown wrote:
>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>
>>> I do recall some issues with the codec jacks, where if the card
>>> registration happens too late the codec might have suspended. But we
>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>> that was solved.
>> Right, I would expect that whatever needs the device to be powered on
>> would be explicitly ensuring that this is done rather than tweaking
>> timeouts - the timeouts should be more of a performance thing to avoid
>> bouncing power too much, not a correctness thing.
> Machine driver probe is executed in parallel with Manager driver
> probe sequence. Because of it, before completion of all peripherals
> enumeration across the multiple links, if card registration is
> completed, codec register writes will fail as Codec device numbers
> are not assigned.
> 
> If we understood correctly, as per your suggestion, We shouldn't use any
> time bounds in machine driver probe sequence and before registering the
> sound card, need to traverses through all peripheral initialization completion
> status for all the managers.

What's not clear in your reply is this:

What codec registers are accessed as a result of the machine driver
probe and card registration, and in what part of the card registration?

Are we talking about SoundWire 'standard' registers for device/port
management, about vendor specific ones that are exposed to userspace, or
vendor-specific ones entirely configured by the driver/regmap.

You've got to give us more data or understanding of the sequence to
help. Saying there's a race condition doesn't really help if there's
nothing that explains what codec registers are accessed and when.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-16 15:02                       ` Pierre-Louis Bossart
@ 2023-01-17 11:33                         ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-17 11:33 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mark Brown
  Cc: Limonciello, Mario, vkoul, alsa-devel, Mastan.Katragadda,
	Sunil-kumar.Dommati, Basavaraj.Hiregoudar, Takashi Iwai,
	Liam Girdwood, open list, Syed Saba Kareem, arungopal.kondaveeti

On 16/01/23 20:32, Pierre-Louis Bossart wrote:
>
> On 1/16/23 02:35, Mukunda,Vijendar wrote:
>> On 14/01/23 01:27, Mark Brown wrote:
>>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>>
>>>> I do recall some issues with the codec jacks, where if the card
>>>> registration happens too late the codec might have suspended. But we
>>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>>> that was solved.
>>> Right, I would expect that whatever needs the device to be powered on
>>> would be explicitly ensuring that this is done rather than tweaking
>>> timeouts - the timeouts should be more of a performance thing to avoid
>>> bouncing power too much, not a correctness thing.
>> Machine driver probe is executed in parallel with Manager driver
>> probe sequence. Because of it, before completion of all peripherals
>> enumeration across the multiple links, if card registration is
>> completed, codec register writes will fail as Codec device numbers
>> are not assigned.
>>
>> If we understood correctly, as per your suggestion, We shouldn't use any
>> time bounds in machine driver probe sequence and before registering the
>> sound card, need to traverses through all peripheral initialization completion
>> status for all the managers.
> What's not clear in your reply is this:
>
> What codec registers are accessed as a result of the machine driver
> probe and card registration, and in what part of the card registration?
>
> Are we talking about SoundWire 'standard' registers for device/port
> management, about vendor specific ones that are exposed to userspace, or
> vendor-specific ones entirely configured by the driver/regmap.
>
> You've got to give us more data or understanding of the sequence to
> help. Saying there's a race condition doesn't really help if there's
> nothing that explains what codec registers are accessed and when.
We have come across a race condition, where sound card registration
is successful before codec enumerations across all the links gets completed
and our manager instance going into bad state.

Please refer below link for error logs.
https://pastebin.com/ZYEN928S



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-17 11:33                         ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-17 11:33 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mark Brown
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, arungopal.kondaveeti, Syed Saba Kareem

On 16/01/23 20:32, Pierre-Louis Bossart wrote:
>
> On 1/16/23 02:35, Mukunda,Vijendar wrote:
>> On 14/01/23 01:27, Mark Brown wrote:
>>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>>
>>>> I do recall some issues with the codec jacks, where if the card
>>>> registration happens too late the codec might have suspended. But we
>>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>>> that was solved.
>>> Right, I would expect that whatever needs the device to be powered on
>>> would be explicitly ensuring that this is done rather than tweaking
>>> timeouts - the timeouts should be more of a performance thing to avoid
>>> bouncing power too much, not a correctness thing.
>> Machine driver probe is executed in parallel with Manager driver
>> probe sequence. Because of it, before completion of all peripherals
>> enumeration across the multiple links, if card registration is
>> completed, codec register writes will fail as Codec device numbers
>> are not assigned.
>>
>> If we understood correctly, as per your suggestion, We shouldn't use any
>> time bounds in machine driver probe sequence and before registering the
>> sound card, need to traverses through all peripheral initialization completion
>> status for all the managers.
> What's not clear in your reply is this:
>
> What codec registers are accessed as a result of the machine driver
> probe and card registration, and in what part of the card registration?
>
> Are we talking about SoundWire 'standard' registers for device/port
> management, about vendor specific ones that are exposed to userspace, or
> vendor-specific ones entirely configured by the driver/regmap.
>
> You've got to give us more data or understanding of the sequence to
> help. Saying there's a race condition doesn't really help if there's
> nothing that explains what codec registers are accessed and when.
We have come across a race condition, where sound card registration
is successful before codec enumerations across all the links gets completed
and our manager instance going into bad state.

Please refer below link for error logs.
https://pastebin.com/ZYEN928S



^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
  2023-01-16 14:57             ` Pierre-Louis Bossart
@ 2023-01-17 11:37               ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-17 11:37 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	open list

On 16/01/23 20:27, Pierre-Louis Bossart wrote:
>
> On 1/16/23 01:53, Mukunda,Vijendar wrote:
>> On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>>>> +	for (index = 0; index < 2; index++) {
>>>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>>>> +			timeout = 1;
>>>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>>>> +			no_ack = 1;
>>>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>>>> +				nack = 1;
>>>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>>>> +			}
>>>>> this is a copy of the cadence_master.c code... With the error added that
>>>>> this is not for a controller but for a master...
>>>> Its manager instance only. Our immediate command and response
>>>> mechanism allows sending commands over the link and get the
>>>> response for every command immediately, unlike as mentioned in
>>>> candence_master.c.
>>> I don't get the reply. The Cadence IP also has the ability to get the
>>> response immediately. There's limited scope for creativity, the commands
>>> are defined in the spec and the responses as well.
>> As per our understanding in Intel code, responses are processed
>> after sending all commands.
>> In our case, we send the command and process the response
>> immediately before invoking the next command.
> The Cadence IP can queue a number of commands, I think 8 off the top of
> my head. But the response is provided immediately after each command.
>
> Maybe the disconnect is that there's an ability to define a watermark on
> the response buffer, so that the software can decide to process the
> command responses in one shot.
>
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	if (timeout) {
>>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_TIMEOUT;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (nack) {
>>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_FAIL;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (no_ack) {
>>>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_IGNORED;
>>>>>> +	}
>>>>>> +	return SDW_CMD_OK;
>>>>> this should probably become a helper since the response is really the
>>>>> same as in cadence_master.c
>>>>>
>>>>> There's really room for optimization and reuse here.
>>>> not really needed. Please refer above comment as command/response
>>>> mechanism differs from Intel's implementation.
>>> how? there's a buffer of responses in both cases. please clarify.
>> Ours implementation is not interrupt driven like Intel.
>> When we send command over the link, we will wait for command's
>> response in polling method and process the response immediately
>> before issuing the new command.
> On the Intel side we use an interrupt to avoid polling, and in case of N
> commands the watermark will be set to N to reduce the overhead. That
> said, most users only use 1 command at a time, it's only recently that
> Cirrus Logic experimented with multiple commands to speed-up transfers.
>
> Even if there are differences in the way the responses are processed,
> whether one-at-a-time or in a batch, the point remains that each command
> response can be individually analyzed and that could be using a helper -
> moving code from cadence_master.c into the bus layer.
>
> will implement a helper function to analyze the response.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 02/19] soundwire: amd: Add support for AMD Master driver
@ 2023-01-17 11:37               ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-17 11:37 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	open list, Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale,
	Bard Liao

On 16/01/23 20:27, Pierre-Louis Bossart wrote:
>
> On 1/16/23 01:53, Mukunda,Vijendar wrote:
>> On 14/01/23 00:11, Pierre-Louis Bossart wrote:
>>>>>> +	for (index = 0; index < 2; index++) {
>>>>>> +		if (response_buf[index] == -ETIMEDOUT) {
>>>>>> +			dev_err(ctrl->dev, "Program SCP cmd timeout\n");
>>>>>> +			timeout = 1;
>>>>>> +		} else if (!(response_buf[index] & AMD_SDW_MCP_RESP_ACK)) {
>>>>>> +			no_ack = 1;
>>>>>> +			if (response_buf[index] & AMD_SDW_MCP_RESP_NACK) {
>>>>>> +				nack = 1;
>>>>>> +				dev_err(ctrl->dev, "Program SCP NACK received\n");
>>>>>> +			}
>>>>> this is a copy of the cadence_master.c code... With the error added that
>>>>> this is not for a controller but for a master...
>>>> Its manager instance only. Our immediate command and response
>>>> mechanism allows sending commands over the link and get the
>>>> response for every command immediately, unlike as mentioned in
>>>> candence_master.c.
>>> I don't get the reply. The Cadence IP also has the ability to get the
>>> response immediately. There's limited scope for creativity, the commands
>>> are defined in the spec and the responses as well.
>> As per our understanding in Intel code, responses are processed
>> after sending all commands.
>> In our case, we send the command and process the response
>> immediately before invoking the next command.
> The Cadence IP can queue a number of commands, I think 8 off the top of
> my head. But the response is provided immediately after each command.
>
> Maybe the disconnect is that there's an ability to define a watermark on
> the response buffer, so that the software can decide to process the
> command responses in one shot.
>
>>>>>> +		}
>>>>>> +	}
>>>>>> +
>>>>>> +	if (timeout) {
>>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage command timeout for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_TIMEOUT;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (nack) {
>>>>>> +		dev_err_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage NACKed for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_FAIL;
>>>>>> +	}
>>>>>> +
>>>>>> +	if (no_ack) {
>>>>>> +		dev_dbg_ratelimited(ctrl->dev,
>>>>>> +				    "SCP_addrpage ignored for Slave %d\n", msg->dev_num);
>>>>>> +		return SDW_CMD_IGNORED;
>>>>>> +	}
>>>>>> +	return SDW_CMD_OK;
>>>>> this should probably become a helper since the response is really the
>>>>> same as in cadence_master.c
>>>>>
>>>>> There's really room for optimization and reuse here.
>>>> not really needed. Please refer above comment as command/response
>>>> mechanism differs from Intel's implementation.
>>> how? there's a buffer of responses in both cases. please clarify.
>> Ours implementation is not interrupt driven like Intel.
>> When we send command over the link, we will wait for command's
>> response in polling method and process the response immediately
>> before issuing the new command.
> On the Intel side we use an interrupt to avoid polling, and in case of N
> commands the watermark will be set to N to reduce the overhead. That
> said, most users only use 1 command at a time, it's only recently that
> Cirrus Logic experimented with multiple commands to speed-up transfers.
>
> Even if there are differences in the way the responses are processed,
> whether one-at-a-time or in a batch, the point remains that each command
> response can be individually analyzed and that could be using a helper -
> moving code from cadence_master.c into the bus layer.
>
> will implement a helper function to analyze the response.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-17 11:33                         ` Mukunda,Vijendar
@ 2023-01-17 11:51                           ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-17 11:51 UTC (permalink / raw)
  To: Mukunda,Vijendar, Mark Brown
  Cc: Limonciello, Mario, vkoul, alsa-devel, Mastan.Katragadda,
	Sunil-kumar.Dommati, Basavaraj.Hiregoudar, Takashi Iwai,
	Liam Girdwood, open list, Syed Saba Kareem, arungopal.kondaveeti



On 1/17/23 05:33, Mukunda,Vijendar wrote:
> On 16/01/23 20:32, Pierre-Louis Bossart wrote:
>>
>> On 1/16/23 02:35, Mukunda,Vijendar wrote:
>>> On 14/01/23 01:27, Mark Brown wrote:
>>>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>>>
>>>>> I do recall some issues with the codec jacks, where if the card
>>>>> registration happens too late the codec might have suspended. But we
>>>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>>>> that was solved.
>>>> Right, I would expect that whatever needs the device to be powered on
>>>> would be explicitly ensuring that this is done rather than tweaking
>>>> timeouts - the timeouts should be more of a performance thing to avoid
>>>> bouncing power too much, not a correctness thing.
>>> Machine driver probe is executed in parallel with Manager driver
>>> probe sequence. Because of it, before completion of all peripherals
>>> enumeration across the multiple links, if card registration is
>>> completed, codec register writes will fail as Codec device numbers
>>> are not assigned.
>>>
>>> If we understood correctly, as per your suggestion, We shouldn't use any
>>> time bounds in machine driver probe sequence and before registering the
>>> sound card, need to traverses through all peripheral initialization completion
>>> status for all the managers.
>> What's not clear in your reply is this:
>>
>> What codec registers are accessed as a result of the machine driver
>> probe and card registration, and in what part of the card registration?
>>
>> Are we talking about SoundWire 'standard' registers for device/port
>> management, about vendor specific ones that are exposed to userspace, or
>> vendor-specific ones entirely configured by the driver/regmap.
>>
>> You've got to give us more data or understanding of the sequence to
>> help. Saying there's a race condition doesn't really help if there's
>> nothing that explains what codec registers are accessed and when.
> We have come across a race condition, where sound card registration
> is successful before codec enumerations across all the links gets completed
> and our manager instance going into bad state.
> 
> Please refer below link for error logs.
> https://pastebin.com/ZYEN928S

You have two RT1316 register areas that are accessed while the codec is
not even enumerated:

[    2.755828] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
[0x41080100] -22

[    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
[0x00003004] -110

The last one is clearly listed in the regmap list.

You probably want to reverse-engineer what causes these accesses.
I see this suspicious kcontrol definition that might be related:

	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-17 11:51                           ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-17 11:51 UTC (permalink / raw)
  To: Mukunda,Vijendar, Mark Brown
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, arungopal.kondaveeti, Syed Saba Kareem



On 1/17/23 05:33, Mukunda,Vijendar wrote:
> On 16/01/23 20:32, Pierre-Louis Bossart wrote:
>>
>> On 1/16/23 02:35, Mukunda,Vijendar wrote:
>>> On 14/01/23 01:27, Mark Brown wrote:
>>>> On Fri, Jan 13, 2023 at 11:33:09AM -0600, Pierre-Louis Bossart wrote:
>>>>
>>>>> I do recall some issues with the codec jacks, where if the card
>>>>> registration happens too late the codec might have suspended. But we
>>>>> added pm_runtime_resume_and_get in the set_jack_detect callbacks, so
>>>>> that was solved.
>>>> Right, I would expect that whatever needs the device to be powered on
>>>> would be explicitly ensuring that this is done rather than tweaking
>>>> timeouts - the timeouts should be more of a performance thing to avoid
>>>> bouncing power too much, not a correctness thing.
>>> Machine driver probe is executed in parallel with Manager driver
>>> probe sequence. Because of it, before completion of all peripherals
>>> enumeration across the multiple links, if card registration is
>>> completed, codec register writes will fail as Codec device numbers
>>> are not assigned.
>>>
>>> If we understood correctly, as per your suggestion, We shouldn't use any
>>> time bounds in machine driver probe sequence and before registering the
>>> sound card, need to traverses through all peripheral initialization completion
>>> status for all the managers.
>> What's not clear in your reply is this:
>>
>> What codec registers are accessed as a result of the machine driver
>> probe and card registration, and in what part of the card registration?
>>
>> Are we talking about SoundWire 'standard' registers for device/port
>> management, about vendor specific ones that are exposed to userspace, or
>> vendor-specific ones entirely configured by the driver/regmap.
>>
>> You've got to give us more data or understanding of the sequence to
>> help. Saying there's a race condition doesn't really help if there's
>> nothing that explains what codec registers are accessed and when.
> We have come across a race condition, where sound card registration
> is successful before codec enumerations across all the links gets completed
> and our manager instance going into bad state.
> 
> Please refer below link for error logs.
> https://pastebin.com/ZYEN928S

You have two RT1316 register areas that are accessed while the codec is
not even enumerated:

[    2.755828] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
[0x41080100] -22

[    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
[0x00003004] -110

The last one is clearly listed in the regmap list.

You probably want to reverse-engineer what causes these accesses.
I see this suspicious kcontrol definition that might be related:

	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-17 11:51                           ` Pierre-Louis Bossart
@ 2023-01-17 12:16                             ` Mark Brown
  -1 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-17 12:16 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Mukunda,Vijendar, Limonciello, Mario, vkoul, alsa-devel,
	Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti

[-- Attachment #1: Type: text/plain, Size: 578 bytes --]

On Tue, Jan 17, 2023 at 05:51:03AM -0600, Pierre-Louis Bossart wrote:
> On 1/17/23 05:33, Mukunda,Vijendar wrote:

> [    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
> snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
> [0x00003004] -110

> The last one is clearly listed in the regmap list.

> You probably want to reverse-engineer what causes these accesses.
> I see this suspicious kcontrol definition that might be related:

> 	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),

Looks like a case for putting the CODEC in cache only mode...

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-17 12:16                             ` Mark Brown
  0 siblings, 0 replies; 170+ messages in thread
From: Mark Brown @ 2023-01-17 12:16 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, Mukunda, Vijendar, arungopal.kondaveeti,
	Syed Saba Kareem

[-- Attachment #1: Type: text/plain, Size: 578 bytes --]

On Tue, Jan 17, 2023 at 05:51:03AM -0600, Pierre-Louis Bossart wrote:
> On 1/17/23 05:33, Mukunda,Vijendar wrote:

> [    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
> snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
> [0x00003004] -110

> The last one is clearly listed in the regmap list.

> You probably want to reverse-engineer what causes these accesses.
> I see this suspicious kcontrol definition that might be related:

> 	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),

Looks like a case for putting the CODEC in cache only mode...

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
  2023-01-17 12:16                             ` Mark Brown
@ 2023-01-17 12:36                               ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-17 12:36 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mukunda,Vijendar, Limonciello, Mario, vkoul, alsa-devel,
	Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Syed Saba Kareem,
	arungopal.kondaveeti



On 1/17/23 06:16, Mark Brown wrote:
> On Tue, Jan 17, 2023 at 05:51:03AM -0600, Pierre-Louis Bossart wrote:
>> On 1/17/23 05:33, Mukunda,Vijendar wrote:
> 
>> [    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
>> snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
>> [0x00003004] -110
> 
>> The last one is clearly listed in the regmap list.
> 
>> You probably want to reverse-engineer what causes these accesses.
>> I see this suspicious kcontrol definition that might be related:
> 
>> 	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),
> 
> Looks like a case for putting the CODEC in cache only mode...

Right, and I think we'd need to do this during the probe instead of the
hardware initialization (which could happen at a later time).

I started a PR to try and improve regmap handling, see
https://github.com/thesofproject/linux/pull/3941

I was trying to solve the case where codecs become unattached, but
apparently the problem is hardware-related. One of the suggested
improvements was to move the cache_only part earlier to prevent such
accesses. Unfortunately the work isn't complete so that PR is just a
draft at the moment.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay
@ 2023-01-17 12:36                               ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-17 12:36 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Liam Girdwood, open list,
	Basavaraj.Hiregoudar, alsa-devel, Takashi Iwai, vkoul,
	Limonciello, Mario, Mukunda, Vijendar, arungopal.kondaveeti,
	Syed Saba Kareem



On 1/17/23 06:16, Mark Brown wrote:
> On Tue, Jan 17, 2023 at 05:51:03AM -0600, Pierre-Louis Bossart wrote:
>> On 1/17/23 05:33, Mukunda,Vijendar wrote:
> 
>> [    2.758904] rt1316-sdca sdw:0:025d:1316:01:0: ASoC: error at
>> snd_soc_component_update_bits on sdw:0:025d:1316:01:0 for register:
>> [0x00003004] -110
> 
>> The last one is clearly listed in the regmap list.
> 
>> You probably want to reverse-engineer what causes these accesses.
>> I see this suspicious kcontrol definition that might be related:
> 
>> 	SOC_SINGLE("Left I Tag Select", 0x3004, 4, 7, 0),
> 
> Looks like a case for putting the CODEC in cache only mode...

Right, and I think we'd need to do this during the probe instead of the
hardware initialization (which could happen at a later time).

I started a PR to try and improve regmap handling, see
https://github.com/thesofproject/linux/pull/3941

I was trying to solve the case where codecs become unattached, but
apparently the problem is hardware-related. One of the suggested
improvements was to move the cache_only part earlier to prevent such
accesses. Unfortunately the work isn't complete so that PR is just a
draft at the moment.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-19 18:35     ` kernel test robot
  -1 siblings, 0 replies; 170+ messages in thread
From: kernel test robot @ 2023-01-19 18:35 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: llvm, oe-kbuild-all, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
	Mario.Limonciello, Mastan.Katragadda, arungopal.kondaveeti,
	Vijendar Mukunda, Bard Liao, Pierre-Louis Bossart, Sanyog Kale,
	open list

Hi Vijendar,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on next-20230119]
[cannot apply to vkoul-dmaengine/next linus/master v6.2-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20230111090222.2016499-5-Vijendar.Mukunda%40amd.com
patch subject: [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301200258.mTFnFc1h-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/52f32d2187adf9e16e4d4a3108e8ca47efa26aa2
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
        git checkout 52f32d2187adf9e16e4d4a3108e8ca47efa26aa2
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/soundwire/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 4 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 4 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:30: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                              ^~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:50: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                                         ~~~~~~~~~^~~~~~
   include/linux/bitfield.h:45:38: note: expanded from macro '__bf_shf'
   #define __bf_shf(x) (__builtin_ffsll(x) - 1)
                                        ^
   11 warnings generated.


vim +569 drivers/soundwire/amd_master.c

1e349784c25702 Vijendar Mukunda 2023-01-11  559  
1e349784c25702 Vijendar Mukunda 2023-01-11  560  static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
1e349784c25702 Vijendar Mukunda 2023-01-11  561  {
1e349784c25702 Vijendar Mukunda 2023-01-11  562  	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
1e349784c25702 Vijendar Mukunda 2023-01-11  563  	u64 response;
1e349784c25702 Vijendar Mukunda 2023-01-11  564  	u32 slave_stat = 0;
1e349784c25702 Vijendar Mukunda 2023-01-11  565  
1e349784c25702 Vijendar Mukunda 2023-01-11  566  	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
1e349784c25702 Vijendar Mukunda 2023-01-11  567  	/* slave status from ping response*/
1e349784c25702 Vijendar Mukunda 2023-01-11  568  	slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
1e349784c25702 Vijendar Mukunda 2023-01-11 @569  	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
1e349784c25702 Vijendar Mukunda 2023-01-11  570  	dev_dbg(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
1e349784c25702 Vijendar Mukunda 2023-01-11  571  	return slave_stat;
1e349784c25702 Vijendar Mukunda 2023-01-11  572  }
1e349784c25702 Vijendar Mukunda 2023-01-11  573  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
@ 2023-01-19 18:35     ` kernel test robot
  0 siblings, 0 replies; 170+ messages in thread
From: kernel test robot @ 2023-01-19 18:35 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, llvm, Vijendar Mukunda,
	Pierre-Louis Bossart, Mario.Limonciello, oe-kbuild-all,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

Hi Vijendar,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on next-20230119]
[cannot apply to vkoul-dmaengine/next linus/master v6.2-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20230111090222.2016499-5-Vijendar.Mukunda%40amd.com
patch subject: [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301200258.mTFnFc1h-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/52f32d2187adf9e16e4d4a3108e8ca47efa26aa2
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
        git checkout 52f32d2187adf9e16e4d4a3108e8ca47efa26aa2
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/soundwire/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 3 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 4 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 4 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:30: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                              ^~~~~
>> drivers/soundwire/amd_master.c:569:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:50: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                                         ~~~~~~~~~^~~~~~
   include/linux/bitfield.h:45:38: note: expanded from macro '__bf_shf'
   #define __bf_shf(x) (__builtin_ffsll(x) - 1)
                                        ^
   11 warnings generated.


vim +569 drivers/soundwire/amd_master.c

1e349784c25702 Vijendar Mukunda 2023-01-11  559  
1e349784c25702 Vijendar Mukunda 2023-01-11  560  static u32 amd_sdwc_read_ping_status(struct sdw_bus *bus)
1e349784c25702 Vijendar Mukunda 2023-01-11  561  {
1e349784c25702 Vijendar Mukunda 2023-01-11  562  	struct amd_sdwc_ctrl *ctrl = to_amd_sdw(bus);
1e349784c25702 Vijendar Mukunda 2023-01-11  563  	u64 response;
1e349784c25702 Vijendar Mukunda 2023-01-11  564  	u32 slave_stat = 0;
1e349784c25702 Vijendar Mukunda 2023-01-11  565  
1e349784c25702 Vijendar Mukunda 2023-01-11  566  	response = amd_sdwc_send_cmd_get_resp(ctrl, 0, 0);
1e349784c25702 Vijendar Mukunda 2023-01-11  567  	/* slave status from ping response*/
1e349784c25702 Vijendar Mukunda 2023-01-11  568  	slave_stat = FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_0_3, response);
1e349784c25702 Vijendar Mukunda 2023-01-11 @569  	slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
1e349784c25702 Vijendar Mukunda 2023-01-11  570  	dev_dbg(ctrl->dev, "%s: slave_stat:0x%x\n", __func__, slave_stat);
1e349784c25702 Vijendar Mukunda 2023-01-11  571  	return slave_stat;
1e349784c25702 Vijendar Mukunda 2023-01-11  572  }
1e349784c25702 Vijendar Mukunda 2023-01-11  573  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
  2023-01-11  9:02   ` Vijendar Mukunda
@ 2023-01-19 22:00     ` kernel test robot
  -1 siblings, 0 replies; 170+ messages in thread
From: kernel test robot @ 2023-01-19 22:00 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: llvm, oe-kbuild-all, Basavaraj.Hiregoudar, Sunil-kumar.Dommati,
	Mario.Limonciello, Mastan.Katragadda, arungopal.kondaveeti,
	Vijendar Mukunda, Bard Liao, Pierre-Louis Bossart, Sanyog Kale,
	open list

Hi Vijendar,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on next-20230119]
[cannot apply to vkoul-dmaengine/next linus/master v6.2-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20230111090222.2016499-6-Vijendar.Mukunda%40amd.com
patch subject: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301200537.eS27M0By-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/ee04e2b3a1ee45081b430ae161c53aa8964d5c36
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
        git checkout ee04e2b3a1ee45081b430ae161c53aa8964d5c36
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/soundwire/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:30: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                              ^~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:50: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                                         ~~~~~~~~~^~~~~~
   include/linux/bitfield.h:45:38: note: expanded from macro '__bf_shf'
   #define __bf_shf(x) (__builtin_ffsll(x) - 1)
                                        ^
>> drivers/soundwire/amd_master.c:1223:80: warning: shift count >= width of type [-Wshift-count-overflow]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
                                                                                         ^  ~~
   23 warnings generated.


vim +1223 drivers/soundwire/amd_master.c

  1212	
  1213	static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
  1214						 struct amd_sdwc_ctrl *ctrl)
  1215	{
  1216		u64 slave_stat = 0;
  1217		u32 val = 0;
  1218		int dev_index;
  1219	
  1220		if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
  1221			memset(ctrl->status, 0, sizeof(ctrl->status));
  1222		slave_stat = status_change_0to7;
> 1223		slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
  1224		dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
  1225			__func__, status_change_0to7, status_change_8to11);
  1226		if (slave_stat) {
  1227			for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
  1228				if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) {
  1229					val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) &
  1230					      AMD_SDW_MCP_SLAVE_STATUS_MASK;
  1231					switch (val) {
  1232					case SDW_SLAVE_ATTACHED:
  1233						ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
  1234						break;
  1235					case SDW_SLAVE_UNATTACHED:
  1236						ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
  1237						break;
  1238					case SDW_SLAVE_ALERT:
  1239						ctrl->status[dev_index] = SDW_SLAVE_ALERT;
  1240						break;
  1241					default:
  1242						ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
  1243						break;
  1244					}
  1245				}
  1246			}
  1247		}
  1248	}
  1249	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
@ 2023-01-19 22:00     ` kernel test robot
  0 siblings, 0 replies; 170+ messages in thread
From: kernel test robot @ 2023-01-19 22:00 UTC (permalink / raw)
  To: Vijendar Mukunda, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, llvm, Vijendar Mukunda,
	Pierre-Louis Bossart, Mario.Limonciello, oe-kbuild-all,
	arungopal.kondaveeti, Sanyog Kale, Bard Liao

Hi Vijendar,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on next-20230119]
[cannot apply to vkoul-dmaengine/next linus/master v6.2-rc4]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
base:   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link:    https://lore.kernel.org/r/20230111090222.2016499-6-Vijendar.Mukunda%40amd.com
patch subject: [PATCH 05/19] soundwire: amd: add soundwire interrupt handling
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20230120/202301200537.eS27M0By-lkp@intel.com/config)
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/ee04e2b3a1ee45081b430ae161c53aa8964d5c36
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Vijendar-Mukunda/ASoC-amd-ps-create-platform-devices-based-on-acp-config/20230111-170749
        git checkout ee04e2b3a1ee45081b430ae161c53aa8964d5c36
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/soundwire/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 5 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   note: (skipping 6 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
   include/linux/compiler_types.h:358:22: note: expanded from macro 'compiletime_assert'
           _compiletime_assert(condition, msg, __compiletime_assert_, __COUNTER__)
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:346:23: note: expanded from macro '_compiletime_assert'
           __compiletime_assert(condition, msg, prefix, suffix)
           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:338:9: note: expanded from macro '__compiletime_assert'
                   if (!(condition))                                       \
                         ^~~~~~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:30: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                              ^~~~~
   drivers/soundwire/amd_master.c:610:26: warning: shift count is negative [-Wshift-count-negative]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STAT_4_11, response) << 8;
                         ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/soundwire/amd_master.h:184:38: note: expanded from macro 'AMD_SDW_MCP_SLAVE_STAT_4_11'
   #define AMD_SDW_MCP_SLAVE_STAT_4_11             GENMASK(39, 24)
                                                   ^
   include/linux/bits.h:38:31: note: expanded from macro 'GENMASK'
           (GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
                                        ^
   include/linux/bits.h:36:11: note: expanded from macro '__GENMASK'
            (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
                    ^
   include/linux/bitfield.h:129:50: note: expanded from macro 'FIELD_GET'
                   (typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
                                                         ~~~~~~~~~^~~~~~
   include/linux/bitfield.h:45:38: note: expanded from macro '__bf_shf'
   #define __bf_shf(x) (__builtin_ffsll(x) - 1)
                                        ^
>> drivers/soundwire/amd_master.c:1223:80: warning: shift count >= width of type [-Wshift-count-overflow]
           slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
                                                                                         ^  ~~
   23 warnings generated.


vim +1223 drivers/soundwire/amd_master.c

  1212	
  1213	static void amd_sdwc_update_slave_status(u32 status_change_0to7, u32 status_change_8to11,
  1214						 struct amd_sdwc_ctrl *ctrl)
  1215	{
  1216		u64 slave_stat = 0;
  1217		u32 val = 0;
  1218		int dev_index;
  1219	
  1220		if (status_change_0to7 == AMD_SDW_SLAVE_0_ATTACHED)
  1221			memset(ctrl->status, 0, sizeof(ctrl->status));
  1222		slave_stat = status_change_0to7;
> 1223		slave_stat |= FIELD_GET(AMD_SDW_MCP_SLAVE_STATUS_8TO_11, status_change_8to11) << 32;
  1224		dev_dbg(ctrl->dev, "%s: status_change_0to7:0x%x status_change_8to11:0x%x\n",
  1225			__func__, status_change_0to7, status_change_8to11);
  1226		if (slave_stat) {
  1227			for (dev_index = 0; dev_index <= SDW_MAX_DEVICES; ++dev_index) {
  1228				if (slave_stat & AMD_SDW_MCP_SLAVE_STATUS_VALID_MASK(dev_index)) {
  1229					val = (slave_stat >> AMD_SDW_MCP_SLAVE_STAT_SHIFT_MASK(dev_index)) &
  1230					      AMD_SDW_MCP_SLAVE_STATUS_MASK;
  1231					switch (val) {
  1232					case SDW_SLAVE_ATTACHED:
  1233						ctrl->status[dev_index] = SDW_SLAVE_ATTACHED;
  1234						break;
  1235					case SDW_SLAVE_UNATTACHED:
  1236						ctrl->status[dev_index] = SDW_SLAVE_UNATTACHED;
  1237						break;
  1238					case SDW_SLAVE_ALERT:
  1239						ctrl->status[dev_index] = SDW_SLAVE_ALERT;
  1240						break;
  1241					default:
  1242						ctrl->status[dev_index] = SDW_SLAVE_RESERVED;
  1243						break;
  1244					}
  1245				}
  1246			}
  1247		}
  1248	}
  1249	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-16  8:02           ` Mukunda,Vijendar
@ 2023-01-31 13:09             ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-31 13:09 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mario.Limonciello,
	Mastan.Katragadda, arungopal.kondaveeti, Bard Liao, Sanyog Kale,
	Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

On 16/01/23 13:32, Mukunda,Vijendar wrote:
> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>> +			switch (acp_data->sdw_master_count) {
>>>>> +			case 1:
>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>> +				break;
>>>>> +			case 2:
>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>> +				break;
>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>> managers.
>>> ACP IP has two independent manager instances driven by separate controller
>>> each which are connected in different power domains.
>>>
>>> we should create two separate ACPI companion devices for separate
>>> manager instance.  Currently we have limitations with BIOS.
>>> we are going with single ACPI companion device.
>>> We will update the changes later.
>> Humm, this is tricky. The BIOS interface isn't something that can be
>> changed at will on the kernel side, you'd have to maintain two solutions
>> with a means to detect which one to use.
>>
>> Or is this is a temporary issue on development devices, then that part
>> should probably not be upstreamed.
> It's a temporary issue on development devices.
> We had discussion with Windows dev team and BIOS team.
> They have agreed to modify ACPI companion device logic.
> We will update the two companion devices logic for two manager
> instances in V2 version.
After experimenting, two ACPI companion devices approach,
we got an update from Windows team, there is a limitation
on windows stack. For current platform, we can't proceed
with two ACPI companion devices.

Even on Linux side, if we create two ACPI companion devices
followed by creating a single soundwire manager instance per
Soundwire controller, we have observed an issue in a scenario,
where similar codec parts(UID are also same) are connected on
both soundwire manager instances.

As per MIPI Disco spec, for single link controllers Link ID should
be set to zero.
If we use Link ID as zero, for the soundwire manager which is on
the second soundwire controller ACPI device scope, then soundwire
framework is not allowing to create peripheral device node as its
duplicate one.

If we want to support two ACPI companion device approach
on our future platforms, how to proceed?
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-31 13:09             ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-01-31 13:09 UTC (permalink / raw)
  To: Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem

On 16/01/23 13:32, Mukunda,Vijendar wrote:
> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>> +			switch (acp_data->sdw_master_count) {
>>>>> +			case 1:
>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>> +				break;
>>>>> +			case 2:
>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>> +				break;
>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>> managers.
>>> ACP IP has two independent manager instances driven by separate controller
>>> each which are connected in different power domains.
>>>
>>> we should create two separate ACPI companion devices for separate
>>> manager instance.  Currently we have limitations with BIOS.
>>> we are going with single ACPI companion device.
>>> We will update the changes later.
>> Humm, this is tricky. The BIOS interface isn't something that can be
>> changed at will on the kernel side, you'd have to maintain two solutions
>> with a means to detect which one to use.
>>
>> Or is this is a temporary issue on development devices, then that part
>> should probably not be upstreamed.
> It's a temporary issue on development devices.
> We had discussion with Windows dev team and BIOS team.
> They have agreed to modify ACPI companion device logic.
> We will update the two companion devices logic for two manager
> instances in V2 version.
After experimenting, two ACPI companion devices approach,
we got an update from Windows team, there is a limitation
on windows stack. For current platform, we can't proceed
with two ACPI companion devices.

Even on Linux side, if we create two ACPI companion devices
followed by creating a single soundwire manager instance per
Soundwire controller, we have observed an issue in a scenario,
where similar codec parts(UID are also same) are connected on
both soundwire manager instances.

As per MIPI Disco spec, for single link controllers Link ID should
be set to zero.
If we use Link ID as zero, for the soundwire manager which is on
the second soundwire controller ACPI device scope, then soundwire
framework is not allowing to create peripheral device node as its
duplicate one.

If we want to support two ACPI companion device approach
on our future platforms, how to proceed?
>
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-31 13:09             ` Mukunda,Vijendar
@ 2023-01-31 13:24               ` Mario Limonciello
  -1 siblings, 0 replies; 170+ messages in thread
From: Mario Limonciello @ 2023-01-31 13:24 UTC (permalink / raw)
  To: Mukunda,Vijendar, Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Basavaraj.Hiregoudar, Sunil-kumar.Dommati, Mastan.Katragadda,
	arungopal.kondaveeti, Bard Liao, Sanyog Kale, Liam Girdwood,
	Jaroslav Kysela, Takashi Iwai, Syed Saba Kareem,
	Nathan Chancellor, open list

On 1/31/23 07:09, Mukunda,Vijendar wrote:
> On 16/01/23 13:32, Mukunda,Vijendar wrote:
>> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>>> +			switch (acp_data->sdw_master_count) {
>>>>>> +			case 1:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>>> +			case 2:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>>> managers.
>>>> ACP IP has two independent manager instances driven by separate controller
>>>> each which are connected in different power domains.
>>>>
>>>> we should create two separate ACPI companion devices for separate
>>>> manager instance.  Currently we have limitations with BIOS.
>>>> we are going with single ACPI companion device.
>>>> We will update the changes later.
>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>> changed at will on the kernel side, you'd have to maintain two solutions
>>> with a means to detect which one to use.
>>>
>>> Or is this is a temporary issue on development devices, then that part
>>> should probably not be upstreamed.
>> It's a temporary issue on development devices.
>> We had discussion with Windows dev team and BIOS team.
>> They have agreed to modify ACPI companion device logic.
>> We will update the two companion devices logic for two manager
>> instances in V2 version.
> After experimenting, two ACPI companion devices approach,
> we got an update from Windows team, there is a limitation
> on windows stack. For current platform, we can't proceed
> with two ACPI companion devices.
> 
> Even on Linux side, if we create two ACPI companion devices
> followed by creating a single soundwire manager instance per
> Soundwire controller, we have observed an issue in a scenario,
> where similar codec parts(UID are also same) are connected on
> both soundwire manager instances.

If I'm not mistaken, the specific failure in the Linux stack is because 
of the duplicated sysfs files since the same UID is used on both manager 
instances, right?

At least with how the kernel handles it today I don't see how this 
should be handled.  You can't disambiguate between the two different 
ACPI devices when they would be identical unless you had another property.

> 
> As per MIPI Disco spec, for single link controllers Link ID should
> be set to zero.
> If we use Link ID as zero, for the soundwire manager which is on
> the second soundwire controller ACPI device scope, then soundwire
> framework is not allowing to create peripheral device node as its
> duplicate one.
> 
> If we want to support two ACPI companion device approach
> on our future platforms, how to proceed?

 From my understanding I would think this should be an exception for ps 
platform, but this should be discussed for a future spec revision.

Maybe in a future spec revision another property can be utilized in 
conjunction with ACPI device to disambiguate this case.

>>
>>
> 


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-31 13:24               ` Mario Limonciello
  0 siblings, 0 replies; 170+ messages in thread
From: Mario Limonciello @ 2023-01-31 13:24 UTC (permalink / raw)
  To: Mukunda,Vijendar, Pierre-Louis Bossart, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Syed Saba Kareem, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao

On 1/31/23 07:09, Mukunda,Vijendar wrote:
> On 16/01/23 13:32, Mukunda,Vijendar wrote:
>> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>>> +			switch (acp_data->sdw_master_count) {
>>>>>> +			case 1:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>>> +			case 2:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>>> managers.
>>>> ACP IP has two independent manager instances driven by separate controller
>>>> each which are connected in different power domains.
>>>>
>>>> we should create two separate ACPI companion devices for separate
>>>> manager instance.  Currently we have limitations with BIOS.
>>>> we are going with single ACPI companion device.
>>>> We will update the changes later.
>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>> changed at will on the kernel side, you'd have to maintain two solutions
>>> with a means to detect which one to use.
>>>
>>> Or is this is a temporary issue on development devices, then that part
>>> should probably not be upstreamed.
>> It's a temporary issue on development devices.
>> We had discussion with Windows dev team and BIOS team.
>> They have agreed to modify ACPI companion device logic.
>> We will update the two companion devices logic for two manager
>> instances in V2 version.
> After experimenting, two ACPI companion devices approach,
> we got an update from Windows team, there is a limitation
> on windows stack. For current platform, we can't proceed
> with two ACPI companion devices.
> 
> Even on Linux side, if we create two ACPI companion devices
> followed by creating a single soundwire manager instance per
> Soundwire controller, we have observed an issue in a scenario,
> where similar codec parts(UID are also same) are connected on
> both soundwire manager instances.

If I'm not mistaken, the specific failure in the Linux stack is because 
of the duplicated sysfs files since the same UID is used on both manager 
instances, right?

At least with how the kernel handles it today I don't see how this 
should be handled.  You can't disambiguate between the two different 
ACPI devices when they would be identical unless you had another property.

> 
> As per MIPI Disco spec, for single link controllers Link ID should
> be set to zero.
> If we use Link ID as zero, for the soundwire manager which is on
> the second soundwire controller ACPI device scope, then soundwire
> framework is not allowing to create peripheral device node as its
> duplicate one.
> 
> If we want to support two ACPI companion device approach
> on our future platforms, how to proceed?

 From my understanding I would think this should be an exception for ps 
platform, but this should be discussed for a future spec revision.

Maybe in a future spec revision another property can be utilized in 
conjunction with ACPI device to disambiguate this case.

>>
>>
> 


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-31 13:09             ` Mukunda,Vijendar
@ 2023-01-31 16:00               ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-31 16:00 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, open list,
	Basavaraj.Hiregoudar, Takashi Iwai, Liam Girdwood,
	Nathan Chancellor, Mario.Limonciello, arungopal.kondaveeti,
	Sanyog Kale, Bard Liao, Syed Saba Kareem



On 1/31/23 07:09, Mukunda,Vijendar wrote:
> On 16/01/23 13:32, Mukunda,Vijendar wrote:
>> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>>> +			switch (acp_data->sdw_master_count) {
>>>>>> +			case 1:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>>> +			case 2:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>>> managers.
>>>> ACP IP has two independent manager instances driven by separate controller
>>>> each which are connected in different power domains.
>>>>
>>>> we should create two separate ACPI companion devices for separate
>>>> manager instance.  Currently we have limitations with BIOS.
>>>> we are going with single ACPI companion device.
>>>> We will update the changes later.
>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>> changed at will on the kernel side, you'd have to maintain two solutions
>>> with a means to detect which one to use.
>>>
>>> Or is this is a temporary issue on development devices, then that part
>>> should probably not be upstreamed.
>> It's a temporary issue on development devices.
>> We had discussion with Windows dev team and BIOS team.
>> They have agreed to modify ACPI companion device logic.
>> We will update the two companion devices logic for two manager
>> instances in V2 version.
> After experimenting, two ACPI companion devices approach,
> we got an update from Windows team, there is a limitation
> on windows stack. For current platform, we can't proceed
> with two ACPI companion devices.

so how would the two controllers be declared then in the DSDT used by
Windows? There's a contradiction between having a single companion
device and the ability to set the 'manager-number' to one.

You probably want to give an example of what you have, otherwise we
probably will talk past each other.
> 
> Even on Linux side, if we create two ACPI companion devices
> followed by creating a single soundwire manager instance per
> Soundwire controller, we have observed an issue in a scenario,
> where similar codec parts(UID are also same) are connected on
> both soundwire manager instances.

We've been handling this case of two identical amplifiers on two
different links for the last 3 years. I don't see how this could be a
problem, the codecs are declared in the scope of the companion device
and the _ADR defines in bits [51..48] which link the codec is connected to.

see example below from a TigerLake device with two identical amsp on
link 1 and 2.

   Scope (_SB.PC00.HDAS.SNDW)
    {
       Device (SWD1)
        {
            Name (_ADR, 0x000131025D131601)  // _ADR: Address

	Device (SWD2)
        {
            Name (_ADR, 0x000230025D131601)  // _ADR: Address

> As per MIPI Disco spec, for single link controllers Link ID should
> be set to zero.
> If we use Link ID as zero, for the soundwire manager which is on
> the second soundwire controller ACPI device scope, then soundwire
> framework is not allowing to create peripheral device node as its
> duplicate one.

I still don't see how it's possible. There is an IDA used in the bus
allocation

static int sdw_get_id(struct sdw_bus *bus)
{
	int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL);

	if (rc < 0)
		return rc;

	bus->id = rc;
	return 0;
}

and that's used for debugfs

	/* create the debugfs master-N */
	snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus->link_id);

as well as in sdw_master_device_add():
	dev_set_name(&md->dev, "sdw-master-%d", bus->id);

can you clarify what part of the 'SoundWire framework' is problematic? I
guess the problem is that you have identical devices with the same _ADR
under the same manager, which is problematic indeed, but that's not a
SoundWire framework issue, just not a supported configuration.

> If we want to support two ACPI companion device approach
> on our future platforms, how to proceed?

Well how about dealing with a single companion device first, cause
that's what you have now and that's already problematic.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-31 16:00               ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-01-31 16:00 UTC (permalink / raw)
  To: Mukunda,Vijendar, broonie, vkoul, alsa-devel
  Cc: Mastan.Katragadda, Sunil-kumar.Dommati, Basavaraj.Hiregoudar,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Mario.Limonciello, arungopal.kondaveeti, Sanyog Kale, Bard Liao,
	Syed Saba Kareem



On 1/31/23 07:09, Mukunda,Vijendar wrote:
> On 16/01/23 13:32, Mukunda,Vijendar wrote:
>> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
>>>>>> +		if (is_dmic_dev && is_sdw_dev) {
>>>>>> +			switch (acp_data->sdw_master_count) {
>>>>>> +			case 1:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>>> +			case 2:
>>>>>> +				acp_data->pdev_mask = ACP63_SDW_PDM_DEV_MASK;
>>>>>> +				acp_data->pdev_count = ACP63_SDW0_SDW1_PDM_MODE_DEVS;
>>>>>> +				break;
>>>>> so the cover letter is indeed wrong and confuses two controllers for two
>>>>> managers.
>>>> ACP IP has two independent manager instances driven by separate controller
>>>> each which are connected in different power domains.
>>>>
>>>> we should create two separate ACPI companion devices for separate
>>>> manager instance.  Currently we have limitations with BIOS.
>>>> we are going with single ACPI companion device.
>>>> We will update the changes later.
>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>> changed at will on the kernel side, you'd have to maintain two solutions
>>> with a means to detect which one to use.
>>>
>>> Or is this is a temporary issue on development devices, then that part
>>> should probably not be upstreamed.
>> It's a temporary issue on development devices.
>> We had discussion with Windows dev team and BIOS team.
>> They have agreed to modify ACPI companion device logic.
>> We will update the two companion devices logic for two manager
>> instances in V2 version.
> After experimenting, two ACPI companion devices approach,
> we got an update from Windows team, there is a limitation
> on windows stack. For current platform, we can't proceed
> with two ACPI companion devices.

so how would the two controllers be declared then in the DSDT used by
Windows? There's a contradiction between having a single companion
device and the ability to set the 'manager-number' to one.

You probably want to give an example of what you have, otherwise we
probably will talk past each other.
> 
> Even on Linux side, if we create two ACPI companion devices
> followed by creating a single soundwire manager instance per
> Soundwire controller, we have observed an issue in a scenario,
> where similar codec parts(UID are also same) are connected on
> both soundwire manager instances.

We've been handling this case of two identical amplifiers on two
different links for the last 3 years. I don't see how this could be a
problem, the codecs are declared in the scope of the companion device
and the _ADR defines in bits [51..48] which link the codec is connected to.

see example below from a TigerLake device with two identical amsp on
link 1 and 2.

   Scope (_SB.PC00.HDAS.SNDW)
    {
       Device (SWD1)
        {
            Name (_ADR, 0x000131025D131601)  // _ADR: Address

	Device (SWD2)
        {
            Name (_ADR, 0x000230025D131601)  // _ADR: Address

> As per MIPI Disco spec, for single link controllers Link ID should
> be set to zero.
> If we use Link ID as zero, for the soundwire manager which is on
> the second soundwire controller ACPI device scope, then soundwire
> framework is not allowing to create peripheral device node as its
> duplicate one.

I still don't see how it's possible. There is an IDA used in the bus
allocation

static int sdw_get_id(struct sdw_bus *bus)
{
	int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL);

	if (rc < 0)
		return rc;

	bus->id = rc;
	return 0;
}

and that's used for debugfs

	/* create the debugfs master-N */
	snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus->link_id);

as well as in sdw_master_device_add():
	dev_set_name(&md->dev, "sdw-master-%d", bus->id);

can you clarify what part of the 'SoundWire framework' is problematic? I
guess the problem is that you have identical devices with the same _ADR
under the same manager, which is problematic indeed, but that's not a
SoundWire framework issue, just not a supported configuration.

> If we want to support two ACPI companion device approach
> on our future platforms, how to proceed?

Well how about dealing with a single companion device first, cause
that's what you have now and that's already problematic.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* RE: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-31 16:00               ` Pierre-Louis Bossart
@ 2023-01-31 22:57                 ` Limonciello, Mario
  -1 siblings, 0 replies; 170+ messages in thread
From: Limonciello, Mario @ 2023-01-31 22:57 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mukunda, Vijendar, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, open list, Hiregoudar,
	Basavaraj, Takashi Iwai, Liam Girdwood, Nathan Chancellor,
	kondaveeti, Arungopal, Sanyog Kale, Bard Liao, Saba Kareem, Syed

[Public]



> -----Original Message-----
> From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> Sent: Tuesday, January 31, 2023 10:01
> To: Mukunda, Vijendar <Vijendar.Mukunda@amd.com>;
> broonie@kernel.org; vkoul@kernel.org; alsa-devel@alsa-project.org
> Cc: Katragadda, Mastan <Mastan.Katragadda@amd.com>; Dommati, Sunil-
> kumar <Sunil-kumar.Dommati@amd.com>; open list <linux-
> kernel@vger.kernel.org>; Hiregoudar, Basavaraj
> <Basavaraj.Hiregoudar@amd.com>; Takashi Iwai <tiwai@suse.com>; Liam
> Girdwood <lgirdwood@gmail.com>; Nathan Chancellor
> <nathan@kernel.org>; Limonciello, Mario <Mario.Limonciello@amd.com>;
> kondaveeti, Arungopal <Arungopal.kondaveeti@amd.com>; Sanyog Kale
> <sanyog.r.kale@intel.com>; Bard Liao <yung-chuan.liao@linux.intel.com>;
> Saba Kareem, Syed <Syed.SabaKareem@amd.com>
> Subject: Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on
> acp config
> 
> 
> 
> On 1/31/23 07:09, Mukunda,Vijendar wrote:
> > On 16/01/23 13:32, Mukunda,Vijendar wrote:
> >> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
> >>>>>> +		if (is_dmic_dev && is_sdw_dev) {
> >>>>>> +			switch (acp_data->sdw_master_count) {
> >>>>>> +			case 1:
> >>>>>> +				acp_data->pdev_mask =
> ACP63_SDW_PDM_DEV_MASK;
> >>>>>> +				acp_data->pdev_count =
> ACP63_SDW0_PDM_MODE_DEVS;
> >>>>>> +				break;
> >>>>>> +			case 2:
> >>>>>> +				acp_data->pdev_mask =
> ACP63_SDW_PDM_DEV_MASK;
> >>>>>> +				acp_data->pdev_count =
> ACP63_SDW0_SDW1_PDM_MODE_DEVS;
> >>>>>> +				break;
> >>>>> so the cover letter is indeed wrong and confuses two controllers for
> two
> >>>>> managers.
> >>>> ACP IP has two independent manager instances driven by separate
> controller
> >>>> each which are connected in different power domains.
> >>>>
> >>>> we should create two separate ACPI companion devices for separate
> >>>> manager instance.  Currently we have limitations with BIOS.
> >>>> we are going with single ACPI companion device.
> >>>> We will update the changes later.
> >>> Humm, this is tricky. The BIOS interface isn't something that can be
> >>> changed at will on the kernel side, you'd have to maintain two solutions
> >>> with a means to detect which one to use.
> >>>
> >>> Or is this is a temporary issue on development devices, then that part
> >>> should probably not be upstreamed.
> >> It's a temporary issue on development devices.
> >> We had discussion with Windows dev team and BIOS team.
> >> They have agreed to modify ACPI companion device logic.
> >> We will update the two companion devices logic for two manager
> >> instances in V2 version.
> > After experimenting, two ACPI companion devices approach,
> > we got an update from Windows team, there is a limitation
> > on windows stack. For current platform, we can't proceed
> > with two ACPI companion devices.
> 
> so how would the two controllers be declared then in the DSDT used by
> Windows? There's a contradiction between having a single companion
> device and the ability to set the 'manager-number' to one.
> 
> You probably want to give an example of what you have, otherwise we
> probably will talk past each other.
> >
> > Even on Linux side, if we create two ACPI companion devices
> > followed by creating a single soundwire manager instance per
> > Soundwire controller, we have observed an issue in a scenario,
> > where similar codec parts(UID are also same) are connected on
> > both soundwire manager instances.
> 
> We've been handling this case of two identical amplifiers on two
> different links for the last 3 years. I don't see how this could be a
> problem, the codecs are declared in the scope of the companion device
> and the _ADR defines in bits [51..48] which link the codec is connected to.
> 

The problem is that there are two managers in the specified AMD design, and
the codecs are both on "Link 0" for each manager.

So the _ADR really is identical for both.

> see example below from a TigerLake device with two identical amsp on
> link 1 and 2.
> 
>    Scope (_SB.PC00.HDAS.SNDW)
>     {
>        Device (SWD1)
>         {
>             Name (_ADR, 0x000131025D131601)  // _ADR: Address
> 
> 	Device (SWD2)
>         {
>             Name (_ADR, 0x000230025D131601)  // _ADR: Address
> 
> > As per MIPI Disco spec, for single link controllers Link ID should
> > be set to zero.
> > If we use Link ID as zero, for the soundwire manager which is on
> > the second soundwire controller ACPI device scope, then soundwire
> > framework is not allowing to create peripheral device node as its
> > duplicate one.
> 
> I still don't see how it's possible. There is an IDA used in the bus
> allocation
> 
> static int sdw_get_id(struct sdw_bus *bus)
> {
> 	int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL);
> 
> 	if (rc < 0)
> 		return rc;
> 
> 	bus->id = rc;
> 	return 0;
> }
> 
> and that's used for debugfs
> 
> 	/* create the debugfs master-N */
> 	snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus-
> >link_id);
> 
> as well as in sdw_master_device_add():
> 	dev_set_name(&md->dev, "sdw-master-%d", bus->id);
> 
> can you clarify what part of the 'SoundWire framework' is problematic? I
> guess the problem is that you have identical devices with the same _ADR
> under the same manager, which is problematic indeed, but that's not a
> SoundWire framework issue, just not a supported configuration.
> 
> > If we want to support two ACPI companion device approach
> > on our future platforms, how to proceed?
> 
> Well how about dealing with a single companion device first, cause
> that's what you have now and that's already problematic.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* RE: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-01-31 22:57                 ` Limonciello, Mario
  0 siblings, 0 replies; 170+ messages in thread
From: Limonciello, Mario @ 2023-01-31 22:57 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Mukunda, Vijendar, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

[Public]



> -----Original Message-----
> From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> Sent: Tuesday, January 31, 2023 10:01
> To: Mukunda, Vijendar <Vijendar.Mukunda@amd.com>;
> broonie@kernel.org; vkoul@kernel.org; alsa-devel@alsa-project.org
> Cc: Katragadda, Mastan <Mastan.Katragadda@amd.com>; Dommati, Sunil-
> kumar <Sunil-kumar.Dommati@amd.com>; open list <linux-
> kernel@vger.kernel.org>; Hiregoudar, Basavaraj
> <Basavaraj.Hiregoudar@amd.com>; Takashi Iwai <tiwai@suse.com>; Liam
> Girdwood <lgirdwood@gmail.com>; Nathan Chancellor
> <nathan@kernel.org>; Limonciello, Mario <Mario.Limonciello@amd.com>;
> kondaveeti, Arungopal <Arungopal.kondaveeti@amd.com>; Sanyog Kale
> <sanyog.r.kale@intel.com>; Bard Liao <yung-chuan.liao@linux.intel.com>;
> Saba Kareem, Syed <Syed.SabaKareem@amd.com>
> Subject: Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on
> acp config
> 
> 
> 
> On 1/31/23 07:09, Mukunda,Vijendar wrote:
> > On 16/01/23 13:32, Mukunda,Vijendar wrote:
> >> On 13/01/23 22:41, Pierre-Louis Bossart wrote:
> >>>>>> +		if (is_dmic_dev && is_sdw_dev) {
> >>>>>> +			switch (acp_data->sdw_master_count) {
> >>>>>> +			case 1:
> >>>>>> +				acp_data->pdev_mask =
> ACP63_SDW_PDM_DEV_MASK;
> >>>>>> +				acp_data->pdev_count =
> ACP63_SDW0_PDM_MODE_DEVS;
> >>>>>> +				break;
> >>>>>> +			case 2:
> >>>>>> +				acp_data->pdev_mask =
> ACP63_SDW_PDM_DEV_MASK;
> >>>>>> +				acp_data->pdev_count =
> ACP63_SDW0_SDW1_PDM_MODE_DEVS;
> >>>>>> +				break;
> >>>>> so the cover letter is indeed wrong and confuses two controllers for
> two
> >>>>> managers.
> >>>> ACP IP has two independent manager instances driven by separate
> controller
> >>>> each which are connected in different power domains.
> >>>>
> >>>> we should create two separate ACPI companion devices for separate
> >>>> manager instance.  Currently we have limitations with BIOS.
> >>>> we are going with single ACPI companion device.
> >>>> We will update the changes later.
> >>> Humm, this is tricky. The BIOS interface isn't something that can be
> >>> changed at will on the kernel side, you'd have to maintain two solutions
> >>> with a means to detect which one to use.
> >>>
> >>> Or is this is a temporary issue on development devices, then that part
> >>> should probably not be upstreamed.
> >> It's a temporary issue on development devices.
> >> We had discussion with Windows dev team and BIOS team.
> >> They have agreed to modify ACPI companion device logic.
> >> We will update the two companion devices logic for two manager
> >> instances in V2 version.
> > After experimenting, two ACPI companion devices approach,
> > we got an update from Windows team, there is a limitation
> > on windows stack. For current platform, we can't proceed
> > with two ACPI companion devices.
> 
> so how would the two controllers be declared then in the DSDT used by
> Windows? There's a contradiction between having a single companion
> device and the ability to set the 'manager-number' to one.
> 
> You probably want to give an example of what you have, otherwise we
> probably will talk past each other.
> >
> > Even on Linux side, if we create two ACPI companion devices
> > followed by creating a single soundwire manager instance per
> > Soundwire controller, we have observed an issue in a scenario,
> > where similar codec parts(UID are also same) are connected on
> > both soundwire manager instances.
> 
> We've been handling this case of two identical amplifiers on two
> different links for the last 3 years. I don't see how this could be a
> problem, the codecs are declared in the scope of the companion device
> and the _ADR defines in bits [51..48] which link the codec is connected to.
> 

The problem is that there are two managers in the specified AMD design, and
the codecs are both on "Link 0" for each manager.

So the _ADR really is identical for both.

> see example below from a TigerLake device with two identical amsp on
> link 1 and 2.
> 
>    Scope (_SB.PC00.HDAS.SNDW)
>     {
>        Device (SWD1)
>         {
>             Name (_ADR, 0x000131025D131601)  // _ADR: Address
> 
> 	Device (SWD2)
>         {
>             Name (_ADR, 0x000230025D131601)  // _ADR: Address
> 
> > As per MIPI Disco spec, for single link controllers Link ID should
> > be set to zero.
> > If we use Link ID as zero, for the soundwire manager which is on
> > the second soundwire controller ACPI device scope, then soundwire
> > framework is not allowing to create peripheral device node as its
> > duplicate one.
> 
> I still don't see how it's possible. There is an IDA used in the bus
> allocation
> 
> static int sdw_get_id(struct sdw_bus *bus)
> {
> 	int rc = ida_alloc(&sdw_bus_ida, GFP_KERNEL);
> 
> 	if (rc < 0)
> 		return rc;
> 
> 	bus->id = rc;
> 	return 0;
> }
> 
> and that's used for debugfs
> 
> 	/* create the debugfs master-N */
> 	snprintf(name, sizeof(name), "master-%d-%d", bus->id, bus-
> >link_id);
> 
> as well as in sdw_master_device_add():
> 	dev_set_name(&md->dev, "sdw-master-%d", bus->id);
> 
> can you clarify what part of the 'SoundWire framework' is problematic? I
> guess the problem is that you have identical devices with the same _ADR
> under the same manager, which is problematic indeed, but that's not a
> SoundWire framework issue, just not a supported configuration.
> 
> > If we want to support two ACPI companion device approach
> > on our future platforms, how to proceed?
> 
> Well how about dealing with a single companion device first, cause
> that's what you have now and that's already problematic.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-01-31 22:57                 ` Limonciello, Mario
@ 2023-02-01  0:51                   ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01  0:51 UTC (permalink / raw)
  To: Limonciello, Mario, Mukunda, Vijendar, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, open list, Hiregoudar,
	Basavaraj, Takashi Iwai, Liam Girdwood, Nathan Chancellor,
	kondaveeti, Arungopal, Sanyog Kale, Bard Liao, Saba Kareem, Syed


>>>>>> we should create two separate ACPI companion devices for separate
>>>>>> manager instance.  Currently we have limitations with BIOS.
>>>>>> we are going with single ACPI companion device.
>>>>>> We will update the changes later.
>>>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>>>> changed at will on the kernel side, you'd have to maintain two solutions
>>>>> with a means to detect which one to use.
>>>>>
>>>>> Or is this is a temporary issue on development devices, then that part
>>>>> should probably not be upstreamed.
>>>> It's a temporary issue on development devices.
>>>> We had discussion with Windows dev team and BIOS team.
>>>> They have agreed to modify ACPI companion device logic.
>>>> We will update the two companion devices logic for two manager
>>>> instances in V2 version.
>>> After experimenting, two ACPI companion devices approach,
>>> we got an update from Windows team, there is a limitation
>>> on windows stack. For current platform, we can't proceed
>>> with two ACPI companion devices.
>>
>> so how would the two controllers be declared then in the DSDT used by
>> Windows? There's a contradiction between having a single companion
>> device and the ability to set the 'manager-number' to one.
>>
>> You probably want to give an example of what you have, otherwise we
>> probably will talk past each other.
>>>
>>> Even on Linux side, if we create two ACPI companion devices
>>> followed by creating a single soundwire manager instance per
>>> Soundwire controller, we have observed an issue in a scenario,
>>> where similar codec parts(UID are also same) are connected on
>>> both soundwire manager instances.
>>
>> We've been handling this case of two identical amplifiers on two
>> different links for the last 3 years. I don't see how this could be a
>> problem, the codecs are declared in the scope of the companion device
>> and the _ADR defines in bits [51..48] which link the codec is connected to.
>>
> 
> The problem is that there are two managers in the specified AMD design, and
> the codecs are both on "Link 0" for each manager.

You're confusing Controller and Manager.

A Manager is the same as a 'Link', the two terms are interchangeable. It
makes no sense to refer to a link number for a manager because there is
no such concept.

Only a Controller can have multiple links or managers. And each
Controller needs to be declared as an ACPI device if you want to use the
DisCo properties.

The Managers/Links are not described as ACPI devices, that's a
regrettable design decision made in MIPI circles many moons ago, that's
why in the Intel code we have to manually create auxiliary devices based
on the 'mipi-sdw-master-count' property.

> So the _ADR really is identical for both.

That cannot possible work, even for Windows. You need to have a
controller scope, and the _ADR can then be identical for different
peripherals as long as this ADR is local to a controller scope.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-02-01  0:51                   ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01  0:51 UTC (permalink / raw)
  To: Limonciello, Mario, Mukunda, Vijendar, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao


>>>>>> we should create two separate ACPI companion devices for separate
>>>>>> manager instance.  Currently we have limitations with BIOS.
>>>>>> we are going with single ACPI companion device.
>>>>>> We will update the changes later.
>>>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>>>> changed at will on the kernel side, you'd have to maintain two solutions
>>>>> with a means to detect which one to use.
>>>>>
>>>>> Or is this is a temporary issue on development devices, then that part
>>>>> should probably not be upstreamed.
>>>> It's a temporary issue on development devices.
>>>> We had discussion with Windows dev team and BIOS team.
>>>> They have agreed to modify ACPI companion device logic.
>>>> We will update the two companion devices logic for two manager
>>>> instances in V2 version.
>>> After experimenting, two ACPI companion devices approach,
>>> we got an update from Windows team, there is a limitation
>>> on windows stack. For current platform, we can't proceed
>>> with two ACPI companion devices.
>>
>> so how would the two controllers be declared then in the DSDT used by
>> Windows? There's a contradiction between having a single companion
>> device and the ability to set the 'manager-number' to one.
>>
>> You probably want to give an example of what you have, otherwise we
>> probably will talk past each other.
>>>
>>> Even on Linux side, if we create two ACPI companion devices
>>> followed by creating a single soundwire manager instance per
>>> Soundwire controller, we have observed an issue in a scenario,
>>> where similar codec parts(UID are also same) are connected on
>>> both soundwire manager instances.
>>
>> We've been handling this case of two identical amplifiers on two
>> different links for the last 3 years. I don't see how this could be a
>> problem, the codecs are declared in the scope of the companion device
>> and the _ADR defines in bits [51..48] which link the codec is connected to.
>>
> 
> The problem is that there are two managers in the specified AMD design, and
> the codecs are both on "Link 0" for each manager.

You're confusing Controller and Manager.

A Manager is the same as a 'Link', the two terms are interchangeable. It
makes no sense to refer to a link number for a manager because there is
no such concept.

Only a Controller can have multiple links or managers. And each
Controller needs to be declared as an ACPI device if you want to use the
DisCo properties.

The Managers/Links are not described as ACPI devices, that's a
regrettable design decision made in MIPI circles many moons ago, that's
why in the Intel code we have to manually create auxiliary devices based
on the 'mipi-sdw-master-count' property.

> So the _ADR really is identical for both.

That cannot possible work, even for Windows. You need to have a
controller scope, and the _ADR can then be identical for different
peripherals as long as this ADR is local to a controller scope.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  0:51                   ` Pierre-Louis Bossart
@ 2023-02-01  1:45                     ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-01  1:45 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, open list, Hiregoudar,
	Basavaraj, Takashi Iwai, Liam Girdwood, Nathan Chancellor,
	kondaveeti, Arungopal, Sanyog Kale, Bard Liao, Saba Kareem, Syed

On 01/02/23 06:21, Pierre-Louis Bossart wrote:
>>>>>>> we should create two separate ACPI companion devices for separate
>>>>>>> manager instance.  Currently we have limitations with BIOS.
>>>>>>> we are going with single ACPI companion device.
>>>>>>> We will update the changes later.
>>>>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>>>>> changed at will on the kernel side, you'd have to maintain two solutions
>>>>>> with a means to detect which one to use.
>>>>>>
>>>>>> Or is this is a temporary issue on development devices, then that part
>>>>>> should probably not be upstreamed.
>>>>> It's a temporary issue on development devices.
>>>>> We had discussion with Windows dev team and BIOS team.
>>>>> They have agreed to modify ACPI companion device logic.
>>>>> We will update the two companion devices logic for two manager
>>>>> instances in V2 version.
>>>> After experimenting, two ACPI companion devices approach,
>>>> we got an update from Windows team, there is a limitation
>>>> on windows stack. For current platform, we can't proceed
>>>> with two ACPI companion devices.
>>> so how would the two controllers be declared then in the DSDT used by
>>> Windows? There's a contradiction between having a single companion
>>> device and the ability to set the 'manager-number' to one.
>>>
>>> You probably want to give an example of what you have, otherwise we
>>> probably will talk past each other.
>>>> Even on Linux side, if we create two ACPI companion devices
>>>> followed by creating a single soundwire manager instance per
>>>> Soundwire controller, we have observed an issue in a scenario,
>>>> where similar codec parts(UID are also same) are connected on
>>>> both soundwire manager instances.
>>> We've been handling this case of two identical amplifiers on two
>>> different links for the last 3 years. I don't see how this could be a
>>> problem, the codecs are declared in the scope of the companion device
>>> and the _ADR defines in bits [51..48] which link the codec is connected to.
>>>
>> The problem is that there are two managers in the specified AMD design, and
>> the codecs are both on "Link 0" for each manager.
> You're confusing Controller and Manager.
>
> A Manager is the same as a 'Link', the two terms are interchangeable. It
> makes no sense to refer to a link number for a manager because there is
> no such concept.
>
> Only a Controller can have multiple links or managers. And each
> Controller needs to be declared as an ACPI device if you want to use the
> DisCo properties.
>
> The Managers/Links are not described as ACPI devices, that's a
> regrettable design decision made in MIPI circles many moons ago, that's
> why in the Intel code we have to manually create auxiliary devices based
> on the 'mipi-sdw-master-count' property.
>
>> So the _ADR really is identical for both.
Yes Controller has ACPI scope. Under controller based on
"mipi-sdw-manager-list" property manager instances will be created.
Manager and Link terms are interchangeable.

Below is the sample DSDT file if we go with two ACPI companion
devices.

Scope (\_SB.ACP)
    {

        Device (SWC0)
        {
            Name (_ADR, 0x05)  // _ADR: Address
        Name(_DSD, Package() {
                                        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                        Package () {
                                        Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                        Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
                                        },
                                        ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
                                        Package () {
                                        Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
                                        }
                                        }) // End _DSD
        Name(SWM0, Package() {
                                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                // ... place holder for SWM0 additional properties
                                }
                                }) // End SWM0.SWM

    Device (SLV0) { // SoundWire Slave 0
                        Name(_ADR, 0x000032025D131601)
                  } // END SLV0   

        } // END SWC0

     Device (SWC1)
        {
            Name (_ADR, 0x09)  // _ADR: Address
        Name(_DSD, Package() {
                                        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                        Package () {
                                        Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                        Package (2) {"mipi-sdw-manager-list", 1},
                                        },
                                        ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
                                        Package () {
                                        Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
                                      
                                        }
                                        }) // End _DSD
        Name(SWM0, Package() {
                                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                // ... place holder for SWM0 additional properties
                                }
                                }) // End SWM0.SWM

    Device (SLV0) { // SoundWire Slave 0
                        Name(_ADR, 0x000032025D131601)
                  } // END SLV0

        } // END SWC1
    }
}



In above case, two manager instances will be created.
When manager under SWC1 scope tries to add peripheral
device, In sdw_slave_add() API its failing because peripheral
device descriptor uses link id followed by 48bit encoded address.
In above scenarios, both the manager's link id is zero only.



> That cannot possible work, even for Windows. You need to have a
> controller scope, and the _ADR can then be identical for different
> peripherals as long as this ADR is local to a controller scope.
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-02-01  1:45                     ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-01  1:45 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

On 01/02/23 06:21, Pierre-Louis Bossart wrote:
>>>>>>> we should create two separate ACPI companion devices for separate
>>>>>>> manager instance.  Currently we have limitations with BIOS.
>>>>>>> we are going with single ACPI companion device.
>>>>>>> We will update the changes later.
>>>>>> Humm, this is tricky. The BIOS interface isn't something that can be
>>>>>> changed at will on the kernel side, you'd have to maintain two solutions
>>>>>> with a means to detect which one to use.
>>>>>>
>>>>>> Or is this is a temporary issue on development devices, then that part
>>>>>> should probably not be upstreamed.
>>>>> It's a temporary issue on development devices.
>>>>> We had discussion with Windows dev team and BIOS team.
>>>>> They have agreed to modify ACPI companion device logic.
>>>>> We will update the two companion devices logic for two manager
>>>>> instances in V2 version.
>>>> After experimenting, two ACPI companion devices approach,
>>>> we got an update from Windows team, there is a limitation
>>>> on windows stack. For current platform, we can't proceed
>>>> with two ACPI companion devices.
>>> so how would the two controllers be declared then in the DSDT used by
>>> Windows? There's a contradiction between having a single companion
>>> device and the ability to set the 'manager-number' to one.
>>>
>>> You probably want to give an example of what you have, otherwise we
>>> probably will talk past each other.
>>>> Even on Linux side, if we create two ACPI companion devices
>>>> followed by creating a single soundwire manager instance per
>>>> Soundwire controller, we have observed an issue in a scenario,
>>>> where similar codec parts(UID are also same) are connected on
>>>> both soundwire manager instances.
>>> We've been handling this case of two identical amplifiers on two
>>> different links for the last 3 years. I don't see how this could be a
>>> problem, the codecs are declared in the scope of the companion device
>>> and the _ADR defines in bits [51..48] which link the codec is connected to.
>>>
>> The problem is that there are two managers in the specified AMD design, and
>> the codecs are both on "Link 0" for each manager.
> You're confusing Controller and Manager.
>
> A Manager is the same as a 'Link', the two terms are interchangeable. It
> makes no sense to refer to a link number for a manager because there is
> no such concept.
>
> Only a Controller can have multiple links or managers. And each
> Controller needs to be declared as an ACPI device if you want to use the
> DisCo properties.
>
> The Managers/Links are not described as ACPI devices, that's a
> regrettable design decision made in MIPI circles many moons ago, that's
> why in the Intel code we have to manually create auxiliary devices based
> on the 'mipi-sdw-master-count' property.
>
>> So the _ADR really is identical for both.
Yes Controller has ACPI scope. Under controller based on
"mipi-sdw-manager-list" property manager instances will be created.
Manager and Link terms are interchangeable.

Below is the sample DSDT file if we go with two ACPI companion
devices.

Scope (\_SB.ACP)
    {

        Device (SWC0)
        {
            Name (_ADR, 0x05)  // _ADR: Address
        Name(_DSD, Package() {
                                        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                        Package () {
                                        Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                        Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
                                        },
                                        ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
                                        Package () {
                                        Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
                                        }
                                        }) // End _DSD
        Name(SWM0, Package() {
                                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                // ... place holder for SWM0 additional properties
                                }
                                }) // End SWM0.SWM

    Device (SLV0) { // SoundWire Slave 0
                        Name(_ADR, 0x000032025D131601)
                  } // END SLV0   

        } // END SWC0

     Device (SWC1)
        {
            Name (_ADR, 0x09)  // _ADR: Address
        Name(_DSD, Package() {
                                        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                        Package () {
                                        Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                        Package (2) {"mipi-sdw-manager-list", 1},
                                        },
                                        ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
                                        Package () {
                                        Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
                                      
                                        }
                                        }) // End _DSD
        Name(SWM0, Package() {
                                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                // ... place holder for SWM0 additional properties
                                }
                                }) // End SWM0.SWM

    Device (SLV0) { // SoundWire Slave 0
                        Name(_ADR, 0x000032025D131601)
                  } // END SLV0

        } // END SWC1
    }
}



In above case, two manager instances will be created.
When manager under SWC1 scope tries to add peripheral
device, In sdw_slave_add() API its failing because peripheral
device descriptor uses link id followed by 48bit encoded address.
In above scenarios, both the manager's link id is zero only.



> That cannot possible work, even for Windows. You need to have a
> controller scope, and the _ADR can then be identical for different
> peripherals as long as this ADR is local to a controller scope.
>


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  1:45                     ` Mukunda,Vijendar
@ 2023-02-01  2:03                       ` Pierre-Louis Bossart
  -1 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01  2:03 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, open list, Hiregoudar,
	Basavaraj, Takashi Iwai, Liam Girdwood, Nathan Chancellor,
	kondaveeti, Arungopal, Sanyog Kale, Bard Liao, Saba Kareem, Syed



> Yes Controller has ACPI scope. Under controller based on
> "mipi-sdw-manager-list" property manager instances will be created.
> Manager and Link terms are interchangeable.
> 
> Below is the sample DSDT file if we go with two ACPI companion
> devices.
> 
> Scope (\_SB.ACP)
>     {
> 
>         Device (SWC0)
>         {
>             Name (_ADR, 0x05)  // _ADR: Address
>         Name(_DSD, Package() {
>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                         Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
>                                         },
>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
>                                         Package () {
>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>                                         }
>                                         }) // End _DSD
>         Name(SWM0, Package() {
>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                 // ... place holder for SWM0 additional properties
>                                 }
>                                 }) // End SWM0.SWM
> 
>     Device (SLV0) { // SoundWire Slave 0
>                         Name(_ADR, 0x000032025D131601)
>                   } // END SLV0   
> 
>         } // END SWC0
> 
>      Device (SWC1)
>         {
>             Name (_ADR, 0x09)  // _ADR: Address
>         Name(_DSD, Package() {
>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                         Package (2) {"mipi-sdw-manager-list", 1},
>                                         },
>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>                                       
>                                         }
>                                         }) // End _DSD
>         Name(SWM0, Package() {
>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                 // ... place holder for SWM0 additional properties
>                                 }
>                                 }) // End SWM0.SWM
> 
>     Device (SLV0) { // SoundWire Slave 0
>                         Name(_ADR, 0x000032025D131601)
>                   } // END SLV0
> 
>         } // END SWC1
>     }
> }

that looks good to me.

> In above case, two manager instances will be created.
> When manager under SWC1 scope tries to add peripheral
> device, In sdw_slave_add() API its failing because peripheral
> device descriptor uses link id followed by 48bit encoded address.
> In above scenarios, both the manager's link id is zero only.

what fails exactly? The device_register() ?

If yes, what the issue. the device name?

I wonder if we need to use something like

"name shall be sdw:bus_id:link:mfg:part:class"

so as to uniquify the device name, if that was the problem.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-02-01  2:03                       ` Pierre-Louis Bossart
  0 siblings, 0 replies; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01  2:03 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao



> Yes Controller has ACPI scope. Under controller based on
> "mipi-sdw-manager-list" property manager instances will be created.
> Manager and Link terms are interchangeable.
> 
> Below is the sample DSDT file if we go with two ACPI companion
> devices.
> 
> Scope (\_SB.ACP)
>     {
> 
>         Device (SWC0)
>         {
>             Name (_ADR, 0x05)  // _ADR: Address
>         Name(_DSD, Package() {
>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                         Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
>                                         },
>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
>                                         Package () {
>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>                                         }
>                                         }) // End _DSD
>         Name(SWM0, Package() {
>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                 // ... place holder for SWM0 additional properties
>                                 }
>                                 }) // End SWM0.SWM
> 
>     Device (SLV0) { // SoundWire Slave 0
>                         Name(_ADR, 0x000032025D131601)
>                   } // END SLV0   
> 
>         } // END SWC0
> 
>      Device (SWC1)
>         {
>             Name (_ADR, 0x09)  // _ADR: Address
>         Name(_DSD, Package() {
>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                         Package (2) {"mipi-sdw-manager-list", 1},
>                                         },
>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>                                       
>                                         }
>                                         }) // End _DSD
>         Name(SWM0, Package() {
>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                 // ... place holder for SWM0 additional properties
>                                 }
>                                 }) // End SWM0.SWM
> 
>     Device (SLV0) { // SoundWire Slave 0
>                         Name(_ADR, 0x000032025D131601)
>                   } // END SLV0
> 
>         } // END SWC1
>     }
> }

that looks good to me.

> In above case, two manager instances will be created.
> When manager under SWC1 scope tries to add peripheral
> device, In sdw_slave_add() API its failing because peripheral
> device descriptor uses link id followed by 48bit encoded address.
> In above scenarios, both the manager's link id is zero only.

what fails exactly? The device_register() ?

If yes, what the issue. the device name?

I wonder if we need to use something like

"name shall be sdw:bus_id:link:mfg:part:class"

so as to uniquify the device name, if that was the problem.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  2:03                       ` Pierre-Louis Bossart
@ 2023-02-01  2:10                         ` Mukunda,Vijendar
  -1 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-01  2:10 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, open list, Hiregoudar,
	Basavaraj, Takashi Iwai, Liam Girdwood, Nathan Chancellor,
	kondaveeti, Arungopal, Sanyog Kale, Bard Liao, Saba Kareem, Syed

On 01/02/23 07:33, Pierre-Louis Bossart wrote:
>
>> Yes Controller has ACPI scope. Under controller based on
>> "mipi-sdw-manager-list" property manager instances will be created.
>> Manager and Link terms are interchangeable.
>>
>> Below is the sample DSDT file if we go with two ACPI companion
>> devices.
>>
>> Scope (\_SB.ACP)
>>     {
>>
>>         Device (SWC0)
>>         {
>>             Name (_ADR, 0x05)  // _ADR: Address
>>         Name(_DSD, Package() {
>>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                         Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
>>                                         },
>>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>>                                         }
>>                                         }) // End _DSD
>>         Name(SWM0, Package() {
>>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                 // ... place holder for SWM0 additional properties
>>                                 }
>>                                 }) // End SWM0.SWM
>>
>>     Device (SLV0) { // SoundWire Slave 0
>>                         Name(_ADR, 0x000032025D131601)
>>                   } // END SLV0   
>>
>>         } // END SWC0
>>
>>      Device (SWC1)
>>         {
>>             Name (_ADR, 0x09)  // _ADR: Address
>>         Name(_DSD, Package() {
>>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                         Package (2) {"mipi-sdw-manager-list", 1},
>>                                         },
>>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>>                                       
>>                                         }
>>                                         }) // End _DSD
>>         Name(SWM0, Package() {
>>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                 // ... place holder for SWM0 additional properties
>>                                 }
>>                                 }) // End SWM0.SWM
>>
>>     Device (SLV0) { // SoundWire Slave 0
>>                         Name(_ADR, 0x000032025D131601)
>>                   } // END SLV0
>>
>>         } // END SWC1
>>     }
>> }
> that looks good to me.
>
>> In above case, two manager instances will be created.
>> When manager under SWC1 scope tries to add peripheral
>> device, In sdw_slave_add() API its failing because peripheral
>> device descriptor uses link id followed by 48bit encoded address.
>> In above scenarios, both the manager's link id is zero only.
> what fails exactly? The device_register() ?
>
> If yes, what the issue. the device name?
device_register() is failing because of duplication of
device name.
> I wonder if we need to use something like
>
> "name shall be sdw:bus_id:link:mfg:part:class"
>
> so as to uniquify the device name, if that was the problem.
Yes correct.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
@ 2023-02-01  2:10                         ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-01  2:10 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

On 01/02/23 07:33, Pierre-Louis Bossart wrote:
>
>> Yes Controller has ACPI scope. Under controller based on
>> "mipi-sdw-manager-list" property manager instances will be created.
>> Manager and Link terms are interchangeable.
>>
>> Below is the sample DSDT file if we go with two ACPI companion
>> devices.
>>
>> Scope (\_SB.ACP)
>>     {
>>
>>         Device (SWC0)
>>         {
>>             Name (_ADR, 0x05)  // _ADR: Address
>>         Name(_DSD, Package() {
>>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                         Package (2) {"mipi-sdw-manager-list", 1}, // v 1.0
>>                                         },
>>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"), // Hierarchical Extension
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>>                                         }
>>                                         }) // End _DSD
>>         Name(SWM0, Package() {
>>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                 // ... place holder for SWM0 additional properties
>>                                 }
>>                                 }) // End SWM0.SWM
>>
>>     Device (SLV0) { // SoundWire Slave 0
>>                         Name(_ADR, 0x000032025D131601)
>>                   } // END SLV0   
>>
>>         } // END SWC0
>>
>>      Device (SWC1)
>>         {
>>             Name (_ADR, 0x09)  // _ADR: Address
>>         Name(_DSD, Package() {
>>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                         Package (2) {"mipi-sdw-manager-list", 1},
>>                                         },
>>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>>                                       
>>                                         }
>>                                         }) // End _DSD
>>         Name(SWM0, Package() {
>>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                 // ... place holder for SWM0 additional properties
>>                                 }
>>                                 }) // End SWM0.SWM
>>
>>     Device (SLV0) { // SoundWire Slave 0
>>                         Name(_ADR, 0x000032025D131601)
>>                   } // END SLV0
>>
>>         } // END SWC1
>>     }
>> }
> that looks good to me.
>
>> In above case, two manager instances will be created.
>> When manager under SWC1 scope tries to add peripheral
>> device, In sdw_slave_add() API its failing because peripheral
>> device descriptor uses link id followed by 48bit encoded address.
>> In above scenarios, both the manager's link id is zero only.
> what fails exactly? The device_register() ?
>
> If yes, what the issue. the device name?
device_register() is failing because of duplication of
device name.
> I wonder if we need to use something like
>
> "name shall be sdw:bus_id:link:mfg:part:class"
>
> so as to uniquify the device name, if that was the problem.
Yes correct.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  2:10                         ` Mukunda,Vijendar
  (?)
@ 2023-02-01  3:52                         ` Pierre-Louis Bossart
  2023-02-01  6:01                           ` Mukunda,Vijendar
  -1 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01  3:52 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao




>>> In above case, two manager instances will be created.
>>> When manager under SWC1 scope tries to add peripheral
>>> device, In sdw_slave_add() API its failing because peripheral
>>> device descriptor uses link id followed by 48bit encoded address.
>>> In above scenarios, both the manager's link id is zero only.
>> what fails exactly? The device_register() ?
>>
>> If yes, what the issue. the device name?
> device_register() is failing because of duplication of
> device name.
>> I wonder if we need to use something like
>>
>> "name shall be sdw:bus_id:link:mfg:part:class"
>>
>> so as to uniquify the device name, if that was the problem.
> Yes correct.

can you check https://github.com/thesofproject/linux/pull/4165 and see
if this works for you? I tested it on Intel platforms.

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  3:52                         ` Pierre-Louis Bossart
@ 2023-02-01  6:01                           ` Mukunda,Vijendar
  2023-02-01 23:08                             ` Pierre-Louis Bossart
  0 siblings, 1 reply; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-01  6:01 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

On 01/02/23 09:22, Pierre-Louis Bossart wrote:
>
>
>>>> In above case, two manager instances will be created.
>>>> When manager under SWC1 scope tries to add peripheral
>>>> device, In sdw_slave_add() API its failing because peripheral
>>>> device descriptor uses link id followed by 48bit encoded address.
>>>> In above scenarios, both the manager's link id is zero only.
>>> what fails exactly? The device_register() ?
>>>
>>> If yes, what the issue. the device name?
>> device_register() is failing because of duplication of
>> device name.
>>> I wonder if we need to use something like
>>>
>>> "name shall be sdw:bus_id:link:mfg:part:class"
>>>
>>> so as to uniquify the device name, if that was the problem.
>> Yes correct.
> can you check https://github.com/thesofproject/linux/pull/4165 and see
> if this works for you? I tested it on Intel platforms.
It's working fine on our platform. As mentioned earlier in this thread,
we can't go with two ACPI companion device approach due to
limitations on windows stack for current platform.


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01  6:01                           ` Mukunda,Vijendar
@ 2023-02-01 23:08                             ` Pierre-Louis Bossart
  2023-02-06  6:30                               ` Mukunda,Vijendar
  0 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-01 23:08 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao



On 2/1/23 00:01, Mukunda,Vijendar wrote:
> On 01/02/23 09:22, Pierre-Louis Bossart wrote:
>>
>>
>>>>> In above case, two manager instances will be created.
>>>>> When manager under SWC1 scope tries to add peripheral
>>>>> device, In sdw_slave_add() API its failing because peripheral
>>>>> device descriptor uses link id followed by 48bit encoded address.
>>>>> In above scenarios, both the manager's link id is zero only.
>>>> what fails exactly? The device_register() ?
>>>>
>>>> If yes, what the issue. the device name?
>>> device_register() is failing because of duplication of
>>> device name.
>>>> I wonder if we need to use something like
>>>>
>>>> "name shall be sdw:bus_id:link:mfg:part:class"
>>>>
>>>> so as to uniquify the device name, if that was the problem.
>>> Yes correct.
>> can you check https://github.com/thesofproject/linux/pull/4165 and see
>> if this works for you? I tested it on Intel platforms.
> It's working fine on our platform. As mentioned earlier in this thread,
> we can't go with two ACPI companion device approach due to
> limitations on windows stack for current platform.

Thanks for testing.

So if you can't go with 2 ACPI companion devices, what does the
'Windows' DSDT look like and how would you identify that there are two
controllers on the platform?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-01 23:08                             ` Pierre-Louis Bossart
@ 2023-02-06  6:30                               ` Mukunda,Vijendar
  2023-02-06 14:50                                 ` Pierre-Louis Bossart
  0 siblings, 1 reply; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-06  6:30 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

On 02/02/23 04:38, Pierre-Louis Bossart wrote:
>
> On 2/1/23 00:01, Mukunda,Vijendar wrote:
>> On 01/02/23 09:22, Pierre-Louis Bossart wrote:
>>>
>>>>>> In above case, two manager instances will be created.
>>>>>> When manager under SWC1 scope tries to add peripheral
>>>>>> device, In sdw_slave_add() API its failing because peripheral
>>>>>> device descriptor uses link id followed by 48bit encoded address.
>>>>>> In above scenarios, both the manager's link id is zero only.
>>>>> what fails exactly? The device_register() ?
>>>>>
>>>>> If yes, what the issue. the device name?
>>>> device_register() is failing because of duplication of
>>>> device name.
>>>>> I wonder if we need to use something like
>>>>>
>>>>> "name shall be sdw:bus_id:link:mfg:part:class"
>>>>>
>>>>> so as to uniquify the device name, if that was the problem.
>>>> Yes correct.
>>> can you check https://github.com/thesofproject/linux/pull/4165 and see
>>> if this works for you? I tested it on Intel platforms.
>> It's working fine on our platform. As mentioned earlier in this thread,
>> we can't go with two ACPI companion device approach due to
>> limitations on windows stack for current platform.
> Thanks for testing.
>
> So if you can't go with 2 ACPI companion devices, what does the
> 'Windows' DSDT look like and how would you identify that there are two
> controllers on the platform?
We are not populating two controller devices. Instead of it, we are populating
single controller device with two independent manager instances under the same
ACPI device scope.
We have configuration register to identify sound wire manager instances on the platform.
Below is the sample DSDT for Windows & Linux.

Scope (\_SB.ACP)
    {
    
        Device (SDWC)
        {
            Name (_ADR, 0x05)  // _ADR: Address
        Name(_DSD, Package() {
                                        ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                        Package () {
                                        Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
                                        Package (2) {"mipi-sdw-manager-list", 2},
                                        },
                                        ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
                                        Package () {
                                        Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
                                        Package (2) {"mipi-sdw-link-1-subproperties", "SWM1"},
                                        }
                                        }) // End _DSD
        Name(SWM0, Package() {
                                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                 
                                
                                // ... place holder for SWM0 additional properties
                                }
                                }) // End SWM0.SWM
       Name(SWM1,Package(){
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                                Package () {
                                Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                
                                
                                // ... place holder for SWM1 additional properties
                                }
                                }) // End SWM1.SWM

    Device (SLV0) { // SoundWire Slave 0
                        Name(_ADR, 0x000032025D131601)
        } // END SLV0

    Device (SLV1) { // SoundWire Slave 1
                        Name(_ADR, 0x000130025D131601)
            } // END SLV1   

    } // END SDWC
}


^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-06  6:30                               ` Mukunda,Vijendar
@ 2023-02-06 14:50                                 ` Pierre-Louis Bossart
  2023-02-06 16:38                                   ` Mukunda,Vijendar
  0 siblings, 1 reply; 170+ messages in thread
From: Pierre-Louis Bossart @ 2023-02-06 14:50 UTC (permalink / raw)
  To: Mukunda,Vijendar, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao


>>>>>>> In above case, two manager instances will be created.
>>>>>>> When manager under SWC1 scope tries to add peripheral
>>>>>>> device, In sdw_slave_add() API its failing because peripheral
>>>>>>> device descriptor uses link id followed by 48bit encoded address.
>>>>>>> In above scenarios, both the manager's link id is zero only.

So here you're reporting that the issue is that all devices use link0 ...

>>>>>> what fails exactly? The device_register() ?
>>>>>>
>>>>>> If yes, what the issue. the device name?
>>>>> device_register() is failing because of duplication of
>>>>> device name.
>>>>>> I wonder if we need to use something like
>>>>>>
>>>>>> "name shall be sdw:bus_id:link:mfg:part:class"
>>>>>>
>>>>>> so as to uniquify the device name, if that was the problem.
>>>>> Yes correct.
>>>> can you check https://github.com/thesofproject/linux/pull/4165 and see
>>>> if this works for you? I tested it on Intel platforms.
>>> It's working fine on our platform. As mentioned earlier in this thread,
>>> we can't go with two ACPI companion device approach due to
>>> limitations on windows stack for current platform.
>> Thanks for testing.
>>
>> So if you can't go with 2 ACPI companion devices, what does the
>> 'Windows' DSDT look like and how would you identify that there are two
>> controllers on the platform?
> We are not populating two controller devices. Instead of it, we are populating
> single controller device with two independent manager instances under the same
> ACPI device scope.
> We have configuration register to identify sound wire manager instances on the platform.
> Below is the sample DSDT for Windows & Linux.
> 
> Scope (\_SB.ACP)
>     {
>     
>         Device (SDWC)
>         {
>             Name (_ADR, 0x05)  // _ADR: Address
>         Name(_DSD, Package() {
>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>                                         Package (2) {"mipi-sdw-manager-list", 2},
>                                         },
>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>                                         Package () {
>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>                                         Package (2) {"mipi-sdw-link-1-subproperties", "SWM1"},
>                                         }
>                                         }) // End _DSD
>         Name(SWM0, Package() {
>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                 
>                                 
>                                 // ... place holder for SWM0 additional properties
>                                 }
>                                 }) // End SWM0.SWM
>        Name(SWM1,Package(){
>                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>                                 Package () {
>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                
>                                 
>                                 // ... place holder for SWM1 additional properties
>                                 }
>                                 }) // End SWM1.SWM
> 
>     Device (SLV0) { // SoundWire Slave 0
>                         Name(_ADR, 0x000032025D131601)
>         } // END SLV0
> 
>     Device (SLV1) { // SoundWire Slave 1
>                         Name(_ADR, 0x000130025D131601)
>             } // END SLV1   

... but here you have two different link numbers.

I interpret this as SLV0 on link0 and SLV1 on link1.

So what's the issue?

^ permalink raw reply	[flat|nested] 170+ messages in thread

* Re: [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config
  2023-02-06 14:50                                 ` Pierre-Louis Bossart
@ 2023-02-06 16:38                                   ` Mukunda,Vijendar
  0 siblings, 0 replies; 170+ messages in thread
From: Mukunda,Vijendar @ 2023-02-06 16:38 UTC (permalink / raw)
  To: Pierre-Louis Bossart, Limonciello, Mario, broonie, vkoul, alsa-devel
  Cc: Katragadda, Mastan, Dommati, Sunil-kumar, Hiregoudar, Basavaraj,
	Takashi Iwai, Liam Girdwood, open list, Nathan Chancellor,
	Saba Kareem, Syed, kondaveeti, Arungopal, Sanyog Kale, Bard Liao

On 06/02/23 20:20, Pierre-Louis Bossart wrote:
>>>>>>>> In above case, two manager instances will be created.
>>>>>>>> When manager under SWC1 scope tries to add peripheral
>>>>>>>> device, In sdw_slave_add() API its failing because peripheral
>>>>>>>> device descriptor uses link id followed by 48bit encoded address.
>>>>>>>> In above scenarios, both the manager's link id is zero only.
> So here you're reporting that the issue is that all devices use link0 ...
>
>>>>>>> what fails exactly? The device_register() ?
>>>>>>>
>>>>>>> If yes, what the issue. the device name?
>>>>>> device_register() is failing because of duplication of
>>>>>> device name.
>>>>>>> I wonder if we need to use something like
>>>>>>>
>>>>>>> "name shall be sdw:bus_id:link:mfg:part:class"
>>>>>>>
>>>>>>> so as to uniquify the device name, if that was the problem.
>>>>>> Yes correct.
>>>>> can you check https://github.com/thesofproject/linux/pull/4165 and see
>>>>> if this works for you? I tested it on Intel platforms.
>>>> It's working fine on our platform. As mentioned earlier in this thread,
>>>> we can't go with two ACPI companion device approach due to
>>>> limitations on windows stack for current platform.
>>> Thanks for testing.
>>>
>>> So if you can't go with 2 ACPI companion devices, what does the
>>> 'Windows' DSDT look like and how would you identify that there are two
>>> controllers on the platform?
>> We are not populating two controller devices. Instead of it, we are populating
>> single controller device with two independent manager instances under the same
>> ACPI device scope.
>> We have configuration register to identify sound wire manager instances on the platform.
>> Below is the sample DSDT for Windows & Linux.
>>
>> Scope (\_SB.ACP)
>>     {
>>     
>>         Device (SDWC)
>>         {
>>             Name (_ADR, 0x05)  // _ADR: Address
>>         Name(_DSD, Package() {
>>                                         ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},
>>                                         Package (2) {"mipi-sdw-manager-list", 2},
>>                                         },
>>                                         ToUUID("dbb8e3e6-5886-4ba6-8795-1319f52a966b"),
>>                                         Package () {
>>                                         Package (2) {"mipi-sdw-link-0-subproperties", "SWM0"},
>>                                         Package (2) {"mipi-sdw-link-1-subproperties", "SWM1"},
>>                                         }
>>                                         }) // End _DSD
>>         Name(SWM0, Package() {
>>                                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                 
>>                                 
>>                                 // ... place holder for SWM0 additional properties
>>                                 }
>>                                 }) // End SWM0.SWM
>>        Name(SWM1,Package(){
>>                 ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
>>                                 Package () {
>>                                 Package (2) {"mipi-sdw-sw-interface-revision", 0x00010000},                                
>>                                 
>>                                 // ... place holder for SWM1 additional properties
>>                                 }
>>                                 }) // End SWM1.SWM
>>
>>     Device (SLV0) { // SoundWire Slave 0
>>                         Name(_ADR, 0x000032025D131601)
>>         } // END SLV0
>>
>>     Device (SLV1) { // SoundWire Slave 1
>>                         Name(_ADR, 0x000130025D131601)
>>             } // END SLV1   
> ... but here you have two different link numbers.
>
> I interpret this as SLV0 on link0 and SLV1 on link1.
>
> So what's the issue?
This solution works fine for us. We have shared sample DSDT for reference.
By reading the ACP configuration register, controller count information is retrieved.
Each Controller device scope has a single manager instance.
To support this design, we need to create two ACPI companion devices.
Under each controller device, one manager instance device is scoped.
In this case, we will read "mipi-sdw-manager-list" as 1 for each controller.
As per your review comment, we can't go with two ACPI companion devices
approach due to Windows stack limitation.
Windows DSDT implementation will refer to single ACPI companion device with two
manager instances as mentioned in earlier reply. We are going to use the same
for Linux.



^ permalink raw reply	[flat|nested] 170+ messages in thread

end of thread, other threads:[~2023-02-06 16:35 UTC | newest]

Thread overview: 170+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-11  9:02 [PATCH 00/19] Add soundwire support for Pink Sardine platform Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 01/19] ASoC: amd: ps: create platform devices based on acp config Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 13:27   ` Amadeusz Sławiński
2023-01-11 13:27     ` Amadeusz Sławiński
2023-01-11 14:13     ` Mukunda,Vijendar
2023-01-11 14:13       ` Mukunda,Vijendar
2023-01-11 13:32   ` Pierre-Louis Bossart
2023-01-11 13:32     ` Pierre-Louis Bossart
2023-01-13 12:36     ` Mukunda,Vijendar
2023-01-13 12:36       ` Mukunda,Vijendar
2023-01-13 17:11       ` Pierre-Louis Bossart
2023-01-13 17:11         ` Pierre-Louis Bossart
2023-01-16  8:02         ` Mukunda,Vijendar
2023-01-16  8:02           ` Mukunda,Vijendar
2023-01-31 13:09           ` Mukunda,Vijendar
2023-01-31 13:09             ` Mukunda,Vijendar
2023-01-31 13:24             ` Mario Limonciello
2023-01-31 13:24               ` Mario Limonciello
2023-01-31 16:00             ` Pierre-Louis Bossart
2023-01-31 16:00               ` Pierre-Louis Bossart
2023-01-31 22:57               ` Limonciello, Mario
2023-01-31 22:57                 ` Limonciello, Mario
2023-02-01  0:51                 ` Pierre-Louis Bossart
2023-02-01  0:51                   ` Pierre-Louis Bossart
2023-02-01  1:45                   ` Mukunda,Vijendar
2023-02-01  1:45                     ` Mukunda,Vijendar
2023-02-01  2:03                     ` Pierre-Louis Bossart
2023-02-01  2:03                       ` Pierre-Louis Bossart
2023-02-01  2:10                       ` Mukunda,Vijendar
2023-02-01  2:10                         ` Mukunda,Vijendar
2023-02-01  3:52                         ` Pierre-Louis Bossart
2023-02-01  6:01                           ` Mukunda,Vijendar
2023-02-01 23:08                             ` Pierre-Louis Bossart
2023-02-06  6:30                               ` Mukunda,Vijendar
2023-02-06 14:50                                 ` Pierre-Louis Bossart
2023-02-06 16:38                                   ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 02/19] soundwire: amd: Add support for AMD Master driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 13:59   ` Amadeusz Sławiński
2023-01-11 13:59     ` Amadeusz Sławiński
2023-01-11 14:16     ` Mukunda,Vijendar
2023-01-11 14:16       ` Mukunda,Vijendar
2023-01-11 14:37   ` Pierre-Louis Bossart
2023-01-11 14:37     ` Pierre-Louis Bossart
2023-01-13 18:21     ` Mukunda,Vijendar
2023-01-13 18:21       ` Mukunda,Vijendar
2023-01-13 18:41       ` Pierre-Louis Bossart
2023-01-13 18:41         ` Pierre-Louis Bossart
2023-01-16  7:53         ` Mukunda,Vijendar
2023-01-16  7:53           ` Mukunda,Vijendar
2023-01-16 14:57           ` Pierre-Louis Bossart
2023-01-16 14:57             ` Pierre-Louis Bossart
2023-01-17 11:37             ` Mukunda,Vijendar
2023-01-17 11:37               ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 03/19] soundwire: amd: register sdw controller dai ops Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 14:58   ` Pierre-Louis Bossart
2023-01-11 14:58     ` Pierre-Louis Bossart
2023-01-13 11:31     ` Mukunda,Vijendar
2023-01-13 11:31       ` Mukunda,Vijendar
2023-01-13 17:13       ` Pierre-Louis Bossart
2023-01-13 17:13         ` Pierre-Louis Bossart
2023-01-11 14:59   ` Amadeusz Sławiński
2023-01-11 14:59     ` Amadeusz Sławiński
2023-01-11  9:02 ` [PATCH 04/19] soundwire: amd: enable build for AMD soundwire master driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-19 18:35   ` kernel test robot
2023-01-19 18:35     ` kernel test robot
2023-01-11  9:02 ` [PATCH 05/19] soundwire: amd: add soundwire interrupt handling Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:19   ` Pierre-Louis Bossart
2023-01-11 15:19     ` Pierre-Louis Bossart
2023-01-19 22:00   ` kernel test robot
2023-01-19 22:00     ` kernel test robot
2023-01-11  9:02 ` [PATCH 06/19] ASoC: amd: ps: add support for soundwire interrupts in acp pci driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 07/19] ASoC: amd: ps: add soundwire dma driver for pink sardine platform Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:22   ` Pierre-Louis Bossart
2023-01-11 15:22     ` Pierre-Louis Bossart
2023-01-12  9:10     ` Mukunda,Vijendar
2023-01-12  9:10       ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 08/19] ASoC: amd: ps: add soundwire dma driver dma ops Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 13:04   ` Mark Brown
2023-01-11 13:04     ` Mark Brown
2023-01-11 14:08     ` Mukunda,Vijendar
2023-01-11 14:08       ` Mukunda,Vijendar
2023-01-11 15:34   ` Pierre-Louis Bossart
2023-01-11 15:34     ` Pierre-Louis Bossart
2023-01-13 11:16     ` Mukunda,Vijendar
2023-01-13 11:16       ` Mukunda,Vijendar
2023-01-13 17:05       ` Pierre-Louis Bossart
2023-01-13 17:05         ` Pierre-Louis Bossart
2023-01-16  6:59         ` Mukunda,Vijendar
2023-01-16  6:59           ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 09/19] ASoC: amd: ps: add support for Soundwire DMA interrupts Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:38   ` Pierre-Louis Bossart
2023-01-11 15:38     ` Pierre-Louis Bossart
2023-01-12 10:55     ` Mukunda,Vijendar
2023-01-12 10:55       ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 10/19] ASoC: amd: ps: enable Soundwire DMA driver build Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 11/19] ASoC: amd: update comments in Kconfig file Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 12/19] ASoC: amd: ps: Add soundwire specific checks in pci driver in pm ops Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 13/19] ASoC: amd: ps: add support for runtime pm ops for soundwire dma driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 14/19] soundwire: amd: add runtime pm ops for AMD master driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:47   ` Pierre-Louis Bossart
2023-01-11 15:47     ` Pierre-Louis Bossart
2023-01-12 10:35     ` Mukunda,Vijendar
2023-01-12 10:35       ` Mukunda,Vijendar
2023-01-12 14:47       ` Pierre-Louis Bossart
2023-01-12 14:47         ` Pierre-Louis Bossart
2023-01-11  9:02 ` [PATCH 15/19] soundwire: amd: add startup and shutdown dai ops Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:49   ` Pierre-Louis Bossart
2023-01-11 15:49     ` Pierre-Louis Bossart
2023-01-12 10:22     ` Mukunda,Vijendar
2023-01-12 10:22       ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 16/19] soundwire: amd: handle wake enable interrupt Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:54   ` Pierre-Louis Bossart
2023-01-11 15:54     ` Pierre-Louis Bossart
2023-01-12 10:21     ` Mukunda,Vijendar
2023-01-12 10:21       ` Mukunda,Vijendar
2023-01-11  9:02 ` [PATCH 17/19] soundwire: amd: add pm_prepare callback and pm ops support Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 15:58   ` Pierre-Louis Bossart
2023-01-11 15:58     ` Pierre-Louis Bossart
2023-01-12 10:14     ` Mukunda,Vijendar
2023-01-12 10:14       ` Mukunda,Vijendar
2023-01-12 14:50       ` Pierre-Louis Bossart
2023-01-12 14:50         ` Pierre-Louis Bossart
2023-01-11  9:02 ` [PATCH 18/19] ASoC: amd: ps: implement system level pm ops for soundwire dma driver Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11  9:02 ` [PATCH 19/19] ASoC: amd: ps: increase runtime suspend delay Vijendar Mukunda
2023-01-11  9:02   ` Vijendar Mukunda
2023-01-11 16:02   ` Pierre-Louis Bossart
2023-01-11 16:02     ` Pierre-Louis Bossart
2023-01-12 11:02     ` Mukunda,Vijendar
2023-01-12 11:02       ` Mukunda,Vijendar
2023-01-12 14:54       ` Pierre-Louis Bossart
2023-01-12 14:54         ` Pierre-Louis Bossart
2023-01-12 15:29         ` Limonciello, Mario
2023-01-12 15:29           ` Limonciello, Mario
2023-01-12 16:05           ` Pierre-Louis Bossart
2023-01-13 10:58             ` Mukunda,Vijendar
2023-01-13 17:33               ` Pierre-Louis Bossart
2023-01-13 19:57                 ` Mark Brown
2023-01-13 19:57                   ` Mark Brown
2023-01-16  8:35                   ` Mukunda,Vijendar
2023-01-16  8:35                     ` Mukunda,Vijendar
2023-01-16 15:02                     ` Pierre-Louis Bossart
2023-01-16 15:02                       ` Pierre-Louis Bossart
2023-01-17 11:33                       ` Mukunda,Vijendar
2023-01-17 11:33                         ` Mukunda,Vijendar
2023-01-17 11:51                         ` Pierre-Louis Bossart
2023-01-17 11:51                           ` Pierre-Louis Bossart
2023-01-17 12:16                           ` Mark Brown
2023-01-17 12:16                             ` Mark Brown
2023-01-17 12:36                             ` Pierre-Louis Bossart
2023-01-17 12:36                               ` Pierre-Louis Bossart
2023-01-11 13:36 ` [PATCH 00/19] Add soundwire support for Pink Sardine platform Pierre-Louis Bossart
2023-01-12  9:08   ` Mukunda,Vijendar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.