[PATCH 0/9] ASoC: amd: ps: add soundwire support
This patch series add support for - Platform device creation for Soundwire Manager instances and PDM controller. - Soundwire DMA driver. - Interrupt handling for Soundwire manager related interrupts, Soundwire DMA interrupts and ACP error interrupts. - ACP PCI driver PM ops modification with respect to Soundwire Power modes.
Vijendar Mukunda (9): ASoC: amd: ps: create platform devices based on acp config ASoC: amd: ps: handle soundwire interrupts in acp pci driver ASoC: amd: ps: add soundwire dma driver for ps platform ASoC: amd: ps: add soundwire dma driver dma ops ASoC: amd: ps: add support for Soundwire DMA interrupts ASoC: amd: ps: add pm ops support for soundwire dma driver 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.
sound/soc/amd/Kconfig | 3 +- sound/soc/amd/ps/Makefile | 2 + sound/soc/amd/ps/acp63.h | 145 ++++++++- sound/soc/amd/ps/pci-ps.c | 393 +++++++++++++++++++++-- sound/soc/amd/ps/ps-sdw-dma.c | 574 ++++++++++++++++++++++++++++++++++ 5 files changed, 1095 insertions(+), 22 deletions(-) create mode 100644 sound/soc/amd/ps/ps-sdw-dma.c
Create platform devices for Soundwire Manager instances 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 --- sound/soc/amd/ps/acp63.h | 43 ++++++- sound/soc/amd/ps/pci-ps.c | 250 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 280 insertions(+), 13 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 2f94448102d0..f27f71116598 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_MANAGERS 2
/* time in ms for acp timeout */ #define ACP_TIMEOUT 500 @@ -80,6 +86,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; @@ -95,14 +107,41 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+/** + * struct acp63_dev_data - acp pci driver context + * @acp63_base: acp mmio base + * @res: resource + * @pdev: child platform device node structures + * @acp_lock: used to protect acp common registers + * @sdw_fw_node: SoundWire controller fw node handle + * @pdev_mask: platform device mask + * @pdev_count: platform devices count + * @pdm_dev_index: pdm platform device index + * @sdw0_dev_index: Soundwire Manager-0 platform device index + * @sdw1_dev_index: Soundwire Manager-1 platform device index + * @sdw_dma_dev_index: Soundwire DMA controller platform device index + * @is_dmic_dev: flag set to true if DMIC device exists + * @is_sdw_dev: flag set to true if Soundwire manager devices exists + * @acp_reset: flag set to true when bus reset is applied across all + * the active soundwire manager instances + */ + struct acp63_dev_data { void __iomem *acp63_base; 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_manager_count; + u16 sdw0_dev_index; + u16 sdw1_dev_index; + u16 sdw_dma_dev_index; + bool is_dmic_dev; + bool is_sdw_dev; + bool acp_reset; };
int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c957718abefc..f3aa08dc05b2 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -6,6 +6,7 @@ */
#include <linux/pci.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> @@ -15,6 +16,7 @@ #include <sound/pcm_params.h> #include <linux/pm_runtime.h> #include <linux/iopoll.h> +#include <linux/soundwire/sdw_amd.h>
#include "acp63.h"
@@ -119,12 +121,70 @@ 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]; + u32 sdw_manager_bitmap; + u8 count = 0; + u32 acp_sdw_power_mode = 0; + int index; + int ret; + + acp_data = dev_get_drvdata(dev); + acp_data->acp_reset = true; + /* Found controller, find links supported */ + ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list", + &sdw_manager_bitmap, 1); + + if (ret) { + dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret); + return -EINVAL; + } + count = hweight32(sdw_manager_bitmap); + /* Check count is within bounds */ + if (count > AMD_SDW_MAX_MANAGERS) { + dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS); + return -EINVAL; + } + + if (!count) { + dev_dbg(dev, "No SoundWire Managers detected\n"); + return -EINVAL; + } + dev_dbg(dev, "ACPI reports %d Soundwire Manager devices\n", count); + acp_data->sdw_manager_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, "Manager node %s not found\n", name); + return -EIO; + } + + fwnode_property_read_u32(link, "amd-sdw-power-mode", + &acp_sdw_power_mode); + /* + * when soundwire configuration is selected from acp pin config, + * based on manager instances count, acp init/de-init sequence should be + * executed as part of PM ops only when Bus reset is applied for the active + * soundwire manager instances. + */ + if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE) + acp_data->acp_reset = 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; const union acpi_object *obj; bool is_dmic_dev = false; + bool is_sdw_dev = false; + int ret;
dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); if (dmic_dev) { @@ -134,22 +194,83 @@ 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) { + acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev); + ret = sdw_amd_scan_controller(&pci->dev); + if (!ret) + is_sdw_dev = true; + } + + 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_manager_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_manager_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_manager_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, @@ -173,6 +294,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; @@ -205,8 +327,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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock; + sdw_pdata[1].acp_sdw_lock = &adata->acp_lock; + sdw_pdata->acp_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_manager", 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_manager", 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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock; + sdw_pdata[1].acp_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_manager", 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_manager", 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 manager devices found\n"); return 0; }
@@ -290,7 +514,11 @@ static int snd_acp63_probe(struct pci_dev *pci, goto de_init; } val = 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");
On 5/16/23 05:35, Vijendar Mukunda wrote:
Create platform devices for Soundwire Manager instances 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
sound/soc/amd/ps/acp63.h | 43 ++++++- sound/soc/amd/ps/pci-ps.c | 250 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 280 insertions(+), 13 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 2f94448102d0..f27f71116598 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_MANAGERS 2
/* time in ms for acp timeout */ #define ACP_TIMEOUT 500 @@ -80,6 +86,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,
+};
a comment or kernel-doc wouldn't hurt to explain the difference between ACP63_PDM_DEV_MASK and ACP63_SDW_PDM_DEV_MASK, the meaning of the 'SDW" prefix is far from obvious.
struct pdm_stream_instance { u16 num_pages; u16 channels; @@ -95,14 +107,41 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+/**
- struct acp63_dev_data - acp pci driver context
- @acp63_base: acp mmio base
- @res: resource
- @pdev: child platform device node structures
array of platform devices ?
- @acp_lock: used to protect acp common registers
- @sdw_fw_node: SoundWire controller fw node handle
- @pdev_mask: platform device mask
- @pdev_count: platform devices count
- @pdm_dev_index: pdm platform device index
- @sdw0_dev_index: Soundwire Manager-0 platform device index
- @sdw1_dev_index: Soundwire Manager-1 platform device index
- @sdw_dma_dev_index: Soundwire DMA controller platform device index
- @is_dmic_dev: flag set to true if DMIC device exists
it's not clear what 'DMIC device' refers to here and why it is exposed here.
- @is_sdw_dev: flag set to true if Soundwire manager devices exists
same, what does this track? What is the difference between 'dmic dev' and 'sdw_dev', given that there are references above to SDW_PDM. It's confusing.
- @acp_reset: flag set to true when bus reset is applied across all
- the active soundwire manager instances
SoundWire
- */
struct acp63_dev_data { void __iomem *acp63_base; 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_manager_count;
- u16 sdw0_dev_index;
- u16 sdw1_dev_index;
- u16 sdw_dma_dev_index;
- bool is_dmic_dev;
- bool is_sdw_dev;
- bool acp_reset;
};
int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c957718abefc..f3aa08dc05b2 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -6,6 +6,7 @@ */
#include <linux/pci.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> @@ -15,6 +16,7 @@ #include <sound/pcm_params.h> #include <linux/pm_runtime.h> #include <linux/iopoll.h> +#include <linux/soundwire/sdw_amd.h>
#include "acp63.h"
@@ -119,12 +121,70 @@ 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];
- u32 sdw_manager_bitmap;
- u8 count = 0;
- u32 acp_sdw_power_mode = 0;
- int index;
- int ret;
- acp_data = dev_get_drvdata(dev);
- acp_data->acp_reset = true;
- /* Found controller, find links supported */
- ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list",
&sdw_manager_bitmap, 1);
- if (ret) {
dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
return -EINVAL;
- }
- count = hweight32(sdw_manager_bitmap);
- /* Check count is within bounds */
- if (count > AMD_SDW_MAX_MANAGERS) {
dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS);
return -EINVAL;
- }
- if (!count) {
dev_dbg(dev, "No SoundWire Managers detected\n");
return -EINVAL;
- }
- dev_dbg(dev, "ACPI reports %d Soundwire Manager devices\n", count);
- acp_data->sdw_manager_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, "Manager node %s not found\n", name);
return -EIO;
}
fwnode_property_read_u32(link, "amd-sdw-power-mode",
&acp_sdw_power_mode);
/*
* when soundwire configuration is selected from acp pin config,
* based on manager instances count, acp init/de-init sequence should be
* executed as part of PM ops only when Bus reset is applied for the active
* soundwire manager instances.
try to be consistent and use the MIPI spelling in comments: "SoundWire".
*/
if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
acp_data->acp_reset = 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; const union acpi_object *obj; bool is_dmic_dev = false;
bool is_sdw_dev = false;
int ret;
dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); if (dmic_dev) {
@@ -134,22 +194,83 @@ 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) {
acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
ret = sdw_amd_scan_controller(&pci->dev);
if (!ret)
is_sdw_dev = true;
- }
- 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;
}
case ACP_CONFIG_2: case ACP_CONFIG_3:break;
- 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_manager_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_manager_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_manager_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;
it really wouldn't hurt to have a high-level description of this series of switch/ifs/else, it's very hard to review.
}
- return 0;
}
static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, @@ -173,6 +294,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;
@@ -205,8 +327,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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock;
sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
sdw_pdata->acp_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_manager", 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_manager", 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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock;
sdw_pdata[1].acp_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_manager", 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_manager", 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);
}
default:break;
dev_dbg(&pci->dev, "No PDM devices found\n");
dev_dbg(&pci->dev, "No PDM or Soundwire manager devices found\n");
what does this mean? I find this debug adds more confusion.
return 0;
}
@@ -290,7 +514,11 @@ static int snd_acp63_probe(struct pci_dev *pci, goto de_init; } val = 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");
On 16/05/23 20:02, Pierre-Louis Bossart wrote:
On 5/16/23 05:35, Vijendar Mukunda wrote:
Create platform devices for Soundwire Manager instances 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
sound/soc/amd/ps/acp63.h | 43 ++++++- sound/soc/amd/ps/pci-ps.c | 250 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 280 insertions(+), 13 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 2f94448102d0..f27f71116598 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_MANAGERS 2
/* time in ms for acp timeout */ #define ACP_TIMEOUT 500 @@ -80,6 +86,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,
+};
a comment or kernel-doc wouldn't hurt to explain the difference between ACP63_PDM_DEV_MASK and ACP63_SDW_PDM_DEV_MASK, the meaning of the 'SDW" prefix is far from obvious.
Above enum's are listed to know the platform device masks. For example - if ACP63_PDM_DEV_MASK is set, then ACP PCI driver will create platform device for PDM controller.
If ACP63_SDW_DEV_MASK is set, ACP PCI driver will create platform device nodes for soundwire manager instances based on instance count retrieved by scanning the SoundWire Controller.
If ACP63_SDW_PDM_DEV_MASK is set, ACP PCI driver will create platform device nodes for PDM controller and SoundWire manager instances.
We will add comment for the same.
struct pdm_stream_instance { u16 num_pages; u16 channels; @@ -95,14 +107,41 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+/**
- struct acp63_dev_data - acp pci driver context
- @acp63_base: acp mmio base
- @res: resource
- @pdev: child platform device node structures
array of platform devices ?
will fix it.
- @acp_lock: used to protect acp common registers
- @sdw_fw_node: SoundWire controller fw node handle
- @pdev_mask: platform device mask
- @pdev_count: platform devices count
- @pdm_dev_index: pdm platform device index
- @sdw0_dev_index: Soundwire Manager-0 platform device index
- @sdw1_dev_index: Soundwire Manager-1 platform device index
- @sdw_dma_dev_index: Soundwire DMA controller platform device index
- @is_dmic_dev: flag set to true if DMIC device exists
it's not clear what 'DMIC device' refers to here and why it is exposed here.
is_dmic_dev is to know whether PDM controller device node exists or not with in ACP PCI device ACPI scope by checking _ADDR as mentioned below.
dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
if dmic_dev exists then "is_dmic_dev" is set to true.
To avoid confusion, we will rename "dmic_dev" as "pdm_dev".
- @is_sdw_dev: flag set to true if Soundwire manager devices exists
same, what does this track? What is the difference between 'dmic dev' and 'sdw_dev', given that there are references above to SDW_PDM. It's confusing.
Similarly, is_sdw_dev flag is set if SoundWire Manager devices are available after scanning SoundWire controller listed in ACP PCI device ACPI scope.
- @acp_reset: flag set to true when bus reset is applied across all
- the active soundwire manager instances
SoundWire
Will fix it.
- */
struct acp63_dev_data { void __iomem *acp63_base; 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_manager_count;
- u16 sdw0_dev_index;
- u16 sdw1_dev_index;
- u16 sdw_dma_dev_index;
- bool is_dmic_dev;
- bool is_sdw_dev;
- bool acp_reset;
};
int snd_amd_acp_find_config(struct pci_dev *pci); diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c index c957718abefc..f3aa08dc05b2 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -6,6 +6,7 @@ */
#include <linux/pci.h> +#include <linux/bitops.h> #include <linux/module.h> #include <linux/io.h> #include <linux/delay.h> @@ -15,6 +16,7 @@ #include <sound/pcm_params.h> #include <linux/pm_runtime.h> #include <linux/iopoll.h> +#include <linux/soundwire/sdw_amd.h>
#include "acp63.h"
@@ -119,12 +121,70 @@ 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];
- u32 sdw_manager_bitmap;
- u8 count = 0;
- u32 acp_sdw_power_mode = 0;
- int index;
- int ret;
- acp_data = dev_get_drvdata(dev);
- acp_data->acp_reset = true;
- /* Found controller, find links supported */
- ret = fwnode_property_read_u32_array((acp_data->sdw_fw_node), "mipi-sdw-manager-list",
&sdw_manager_bitmap, 1);
- if (ret) {
dev_err(dev, "Failed to read mipi-sdw-manager-list: %d\n", ret);
return -EINVAL;
- }
- count = hweight32(sdw_manager_bitmap);
- /* Check count is within bounds */
- if (count > AMD_SDW_MAX_MANAGERS) {
dev_err(dev, "Manager count %d exceeds max %d\n", count, AMD_SDW_MAX_MANAGERS);
return -EINVAL;
- }
- if (!count) {
dev_dbg(dev, "No SoundWire Managers detected\n");
return -EINVAL;
- }
- dev_dbg(dev, "ACPI reports %d Soundwire Manager devices\n", count);
- acp_data->sdw_manager_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, "Manager node %s not found\n", name);
return -EIO;
}
fwnode_property_read_u32(link, "amd-sdw-power-mode",
&acp_sdw_power_mode);
/*
* when soundwire configuration is selected from acp pin config,
* based on manager instances count, acp init/de-init sequence should be
* executed as part of PM ops only when Bus reset is applied for the active
* soundwire manager instances.
try to be consistent and use the MIPI spelling in comments: "SoundWire".
Will fix it.
*/
if (acp_sdw_power_mode != AMD_SDW_POWER_OFF_MODE)
acp_data->acp_reset = 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; const union acpi_object *obj; bool is_dmic_dev = false;
bool is_sdw_dev = false;
int ret;
dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0); if (dmic_dev) {
@@ -134,22 +194,83 @@ 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) {
acp_data->sdw_fw_node = acpi_fwnode_handle(sdw_dev);
ret = sdw_amd_scan_controller(&pci->dev);
if (!ret)
is_sdw_dev = true;
- }
- 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;
}
case ACP_CONFIG_2: case ACP_CONFIG_3:break;
- 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_manager_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_manager_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_manager_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;
it really wouldn't hurt to have a high-level description of this series of switch/ifs/else, it's very hard to review.
Based on ACP PIN Config, "is_dmic_dev" , "is_sdw_dev" flags ACP PCI driver will create platform device nodes. pdev_mask value to know what combination of platform devices need to be created and pdev_count refers to total number of platform device nodes to be created.
We will add comments.
}
- return 0;
}
static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo, @@ -173,6 +294,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;
@@ -205,8 +327,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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock;
sdw_pdata[1].acp_sdw_lock = &adata->acp_lock;
sdw_pdata->acp_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_manager", 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_manager", 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->acp_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_manager", 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].acp_sdw_lock = &adata->acp_lock;
sdw_pdata[1].acp_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_manager", 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_manager", 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);
}
default:break;
dev_dbg(&pci->dev, "No PDM devices found\n");
dev_dbg(&pci->dev, "No PDM or Soundwire manager devices found\n");
what does this mean? I find this debug adds more confusion.
Currently, we are trying to create platform devices for PDM controller and SoundWire Manager instances based on ACP pin config and ACPI _ADDR fields scan under ACP PCI device scope. Earlier We have added support for ACP PDM controller. ACP PIN config supports different audio configurations other than PDM and SoundWire based audio endpoints.
If there is no pdev_mask set, it refers to default switch case. This dev_dbg statement to notify that no PDM and Soundwire manager devices found from ACPI scan.
This patch adds support for platform device creation logic for Soundwire manager instances & PDM controller combinations based on ACP PIN Config and ACPI _ADDR field scan.
Possible combination of platform device nodes:
1) ACP PDM Controller, dmic-codec, machine driver platform device node 2) ACP PDM Controller , dmic-codec, SW0 manager instance, platform device for SoundWire DMA driver 3) SW0, SW1 SoundWire manager instances, platform device for SoundWire DMA driver 3) ACP PDM Controller, dmic-codec, SDW0, SDW1 manager instances, platform device for SoundWire DMA driver
return 0;
}
@@ -290,7 +514,11 @@ static int snd_acp63_probe(struct pci_dev *pci, goto de_init; } val = 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");
On 5/17/23 03:38, Mukunda,Vijendar wrote:
On 16/05/23 20:02, Pierre-Louis Bossart wrote:
On 5/16/23 05:35, Vijendar Mukunda wrote:
Create platform devices for Soundwire Manager instances 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
sound/soc/amd/ps/acp63.h | 43 ++++++- sound/soc/amd/ps/pci-ps.c | 250 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 280 insertions(+), 13 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 2f94448102d0..f27f71116598 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_MANAGERS 2
/* time in ms for acp timeout */ #define ACP_TIMEOUT 500 @@ -80,6 +86,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,
+};
a comment or kernel-doc wouldn't hurt to explain the difference between ACP63_PDM_DEV_MASK and ACP63_SDW_PDM_DEV_MASK, the meaning of the 'SDW" prefix is far from obvious.
Above enum's are listed to know the platform device masks. For example - if ACP63_PDM_DEV_MASK is set, then ACP PCI driver will create platform device for PDM controller.
If ACP63_SDW_DEV_MASK is set, ACP PCI driver will create platform device nodes for soundwire manager instances based on instance count retrieved by scanning the SoundWire Controller.
If ACP63_SDW_PDM_DEV_MASK is set, ACP PCI driver will create platform device nodes for PDM controller and SoundWire manager instances.
We will add comment for the same.
Ah ok, I completely missed that you could have PDM, SoundWire or PDM+SoundWire configurations. I was reading this with SoundWire blinders and thought you wanted to have PDM over SoundWire or something.
dev_dbg(&pci->dev, "No PDM devices found\n");
dev_dbg(&pci->dev, "No PDM or Soundwire manager devices found\n");
what does this mean? I find this debug adds more confusion.
Currently, we are trying to create platform devices for PDM controller and SoundWire Manager instances based on ACP pin config and ACPI _ADDR fields scan under ACP PCI device scope. Earlier We have added support for ACP PDM controller. ACP PIN config supports different audio configurations other than PDM and SoundWire based audio endpoints.
If there is no pdev_mask set, it refers to default switch case. This dev_dbg statement to notify that no PDM and Soundwire manager devices found from ACPI scan.
This patch adds support for platform device creation logic for Soundwire manager instances & PDM controller combinations based on ACP PIN Config and ACPI _ADDR field scan.
Possible combination of platform device nodes:
- ACP PDM Controller, dmic-codec, machine driver platform device node
- ACP PDM Controller , dmic-codec, SW0 manager instance, platform device for SoundWire DMA driver
- SW0, SW1 SoundWire manager instances, platform device for SoundWire DMA driver
- ACP PDM Controller, dmic-codec, SDW0, SDW1 manager instances, platform device for SoundWire DMA driver
right, you really want this in the commit message so that reviewers understand the various configurations upfront. Trying to reverse-engineer the code induces migraines ;-)
Handle soundwire manager related interrupts in ACP PCI driver interrupt handler and schedule soundwire manager 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 | 42 ++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index f27f71116598..faf7be4d77c2 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -67,6 +67,10 @@ /* time in ms for acp timeout */ #define ACP_TIMEOUT 500
+#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 f3aa08dc05b2..6566ee14d300 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -56,6 +56,7 @@ static int acp63_reset(void __iomem *acp_base) static void acp63_enable_interrupts(void __iomem *acp_base) { writel(1, acp_base + ACP_EXTERNAL_INTR_ENB); + writel(BIT(ACP_ERROR_IRQ_MASK), acp_base + ACP_EXTERNAL_INTR_CNTL); }
static void acp63_disable_interrupts(void __iomem *acp_base) @@ -102,23 +103,54 @@ 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_sdw_manager *amd_manager; + 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 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + if (ext_intr_stat & BIT(ACP_SDW0_IRQ_MASK)) { + writel(BIT(ACP_SDW0_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + pdev_index = adata->sdw0_dev_index; + amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + }
- val = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT); - if (val & BIT(PDM_DMA_STAT)) { + ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + if (ext_intr_stat1 & BIT(ACP_SDW1_IRQ_MASK)) { + writel(BIT(ACP_SDW1_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + pdev_index = adata->sdw1_dev_index; + amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev); + if (amd_manager) + schedule_work(&amd_manager->amd_sdw_irq_thread); + irq_flag = 1; + } + + if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) { + writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON); + writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON); + 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); 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)
On 5/16/23 05:35, Vijendar Mukunda wrote:
Handle soundwire manager related interrupts in ACP PCI driver interrupt handler and schedule soundwire manager work queue for further processing.
"SoundWire" please
Signed-off-by: Vijendar Mukunda Vijendar.Mukunda@amd.com
sound/soc/amd/ps/acp63.h | 4 ++++ sound/soc/amd/ps/pci-ps.c | 42 ++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index f27f71116598..faf7be4d77c2 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -67,6 +67,10 @@ /* time in ms for acp timeout */ #define ACP_TIMEOUT 500
+#define ACP_SDW0_IRQ_MASK 21 +#define ACP_SDW1_IRQ_MASK 2 +#define ACP_ERROR_IRQ_MASK 29
Shouldn't this be in 0x representation or BIT/GENMASK?
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 f3aa08dc05b2..6566ee14d300 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -56,6 +56,7 @@ static int acp63_reset(void __iomem *acp_base) static void acp63_enable_interrupts(void __iomem *acp_base) { writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
- writel(BIT(ACP_ERROR_IRQ_MASK), acp_base + ACP_EXTERNAL_INTR_CNTL);
BIT(FOO_MASK) is very odd.
BIT(ACP_ERROR_IRQ) or ACP_ERROR_IRQ_MASK?
}
static void acp63_disable_interrupts(void __iomem *acp_base) @@ -102,23 +103,54 @@ 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_sdw_manager *amd_manager;
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 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
if (ext_intr_stat & BIT(ACP_SDW0_IRQ_MASK)) {
writel(BIT(ACP_SDW0_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
pdev_index = adata->sdw0_dev_index;
amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
}
- val = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
- if (val & BIT(PDM_DMA_STAT)) {
- ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
- if (ext_intr_stat1 & BIT(ACP_SDW1_IRQ_MASK)) {
writel(BIT(ACP_SDW1_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
pdev_index = adata->sdw1_dev_index;
amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
- }
- if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) {
writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_ERROR_STATUS);
irq_flag = 1;
it's not clear what this does? Looks like just filtering out interrupts without doing anything about the interrupt source?
- }
- 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); 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)
On 16/05/23 20:09, Pierre-Louis Bossart wrote:
On 5/16/23 05:35, Vijendar Mukunda wrote:
Handle soundwire manager related interrupts in ACP PCI driver interrupt handler and schedule soundwire manager work queue for further processing.
"SoundWire" please
Will fix it.
Signed-off-by: Vijendar Mukunda Vijendar.Mukunda@amd.com
sound/soc/amd/ps/acp63.h | 4 ++++ sound/soc/amd/ps/pci-ps.c | 42 ++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index f27f71116598..faf7be4d77c2 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -67,6 +67,10 @@ /* time in ms for acp timeout */ #define ACP_TIMEOUT 500
+#define ACP_SDW0_IRQ_MASK 21 +#define ACP_SDW1_IRQ_MASK 2 +#define ACP_ERROR_IRQ_MASK 29
Shouldn't this be in 0x representation or BIT/GENMASK?
All above IRQ mask fields are bit positions in the register. Will change the name and representation in Hex.
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 f3aa08dc05b2..6566ee14d300 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -56,6 +56,7 @@ static int acp63_reset(void __iomem *acp_base) static void acp63_enable_interrupts(void __iomem *acp_base) { writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
- writel(BIT(ACP_ERROR_IRQ_MASK), acp_base + ACP_EXTERNAL_INTR_CNTL);
BIT(FOO_MASK) is very odd.
BIT(ACP_ERROR_IRQ) or ACP_ERROR_IRQ_MASK?
We followed register field description name as it is. It denotes a bit position only. We will change it.
}
static void acp63_disable_interrupts(void __iomem *acp_base) @@ -102,23 +103,54 @@ 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_sdw_manager *amd_manager;
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 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
if (ext_intr_stat & BIT(ACP_SDW0_IRQ_MASK)) {
writel(BIT(ACP_SDW0_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
pdev_index = adata->sdw0_dev_index;
amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
}
- val = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
- if (val & BIT(PDM_DMA_STAT)) {
- ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
- if (ext_intr_stat1 & BIT(ACP_SDW1_IRQ_MASK)) {
writel(BIT(ACP_SDW1_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
pdev_index = adata->sdw1_dev_index;
amd_manager = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
if (amd_manager)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
- }
- if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) {
writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_ERROR_STATUS);
irq_flag = 1;
it's not clear what this does? Looks like just filtering out interrupts without doing anything about the interrupt source?
When ACP error interrupt is raised, As per our design, ACP IRQ handler will clear the error interrupt by clearing ACP_ERROR_IRQ bit in ACP_EXTERNAL_INTR_STAT.
To know the error source reason, we need to read the ACP_ERROR_STATUS, ACP_SW0_I2S_ERROR_REASON, ACP_SW1_I2S_ERROR_REASON registers. After reading the Error registers, we need to clear these registers. Currently, we haven't added read register operations for error reason registers. This is for debugging purpose only.
In Current context, we refer ACP_SW0_I2S_ERROR_REASON register to know errors like Sound Wire Bus clash detections, Command and Response Errors, BRA mode errors, FIFO underflow/overflow errors detected for SoundWire Manager -0 instance. Similarly, ACP_SW1_I2S_ERROR_REASON register referred to know errors detected for SoundWire Manager instance - 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); 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)
- if (ext_intr_stat & BIT(ACP_ERROR_IRQ_MASK)) {
writel(BIT(ACP_ERROR_IRQ_MASK), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
writel(0, adata->acp63_base + ACP_SW0_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_SW1_I2S_ERROR_REASON);
writel(0, adata->acp63_base + ACP_ERROR_STATUS);
irq_flag = 1;
it's not clear what this does? Looks like just filtering out interrupts without doing anything about the interrupt source?
When ACP error interrupt is raised, As per our design, ACP IRQ handler will clear the error interrupt by clearing ACP_ERROR_IRQ bit in ACP_EXTERNAL_INTR_STAT.
To know the error source reason, we need to read the ACP_ERROR_STATUS, ACP_SW0_I2S_ERROR_REASON, ACP_SW1_I2S_ERROR_REASON registers. After reading the Error registers, we need to clear these registers. Currently, we haven't added read register operations for error reason registers. This is for debugging purpose only.
In Current context, we refer ACP_SW0_I2S_ERROR_REASON register to know errors like Sound Wire Bus clash detections, Command and Response Errors, BRA mode errors, FIFO underflow/overflow errors detected for SoundWire Manager -0 instance. Similarly, ACP_SW1_I2S_ERROR_REASON register referred to know errors detected for SoundWire Manager instance - 1.
If you didn't add the code to deal with the errors, a comment would be welcome to clarify that the interrupts are cleared without looking for the root cause.
It's hard when reviewing code to understand if there's a miss or something that's intentionally omitted or to be added later.
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 | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 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 faf7be4d77c2..f86c60cd1565 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -111,6 +111,11 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+struct sdw_dma_dev_data { + void __iomem *acp_base; + struct mutex *acp_lock; /* used to protect acp common register access */ +}; + /** * struct acp63_dev_data - acp pci driver context * @acp63_base: acp mmio base 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..0d0889842413 --- /dev/null +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -0,0 +1,71 @@ +// 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 sdw dma component\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"); +MODULE_ALIAS("platform:" DRV_NAME);
On 5/16/23 05:35, 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 | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 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 faf7be4d77c2..f86c60cd1565 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -111,6 +111,11 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+struct sdw_dma_dev_data {
- void __iomem *acp_base;
- struct mutex *acp_lock; /* used to protect acp common register access */
+};
/**
- struct acp63_dev_data - acp pci driver context
- @acp63_base: acp mmio base
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..0d0889842413 --- /dev/null +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -0,0 +1,71 @@ +// 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 sdw dma component\n");
return -ENODEV;
return status;
- }
- 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"); +MODULE_ALIAS("platform:" DRV_NAME);
On 5/16/2023 9:40 AM, Pierre-Louis Bossart wrote:
On 5/16/23 05:35, 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 | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 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 faf7be4d77c2..f86c60cd1565 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -111,6 +111,11 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; };
+struct sdw_dma_dev_data {
- void __iomem *acp_base;
- struct mutex *acp_lock; /* used to protect acp common register access */
+};
- /**
- struct acp63_dev_data - acp pci driver context
- @acp63_base: acp mmio base
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..0d0889842413 --- /dev/null +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -0,0 +1,71 @@ +// 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 sdw dma component\n");
return -ENODEV;
return status;
Remove the other return 0 too.
IE:
if (status)
dev_err(...)
return status;
- }
- 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"); +MODULE_ALIAS("platform:" DRV_NAME);
On 16/05/23 22:20, Limonciello, Mario wrote:
On 5/16/2023 9:40 AM, Pierre-Louis Bossart wrote:
On 5/16/23 05:35, 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 | 71 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 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 faf7be4d77c2..f86c60cd1565 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -111,6 +111,11 @@ struct pdm_dev_data { struct snd_pcm_substream *capture_stream; }; +struct sdw_dma_dev_data { + void __iomem *acp_base; + struct mutex *acp_lock; /* used to protect acp common register access */ +};
/** * struct acp63_dev_data - acp pci driver context * @acp63_base: acp mmio base 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..0d0889842413 --- /dev/null +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -0,0 +1,71 @@ +// 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 sdw dma component\n"); + return -ENODEV;
return status;
Remove the other return 0 too.
IE:
if (status)
dev_err(...)
return status;
will fix it.
+ } + 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"); +MODULE_ALIAS("platform:" DRV_NAME);
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 | 76 +++++++ sound/soc/amd/ps/ps-sdw-dma.c | 417 ++++++++++++++++++++++++++++++++++ 2 files changed, 493 insertions(+)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index f86c60cd1565..7f8e1c76ba4f 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -70,6 +70,45 @@ #define ACP_SDW0_IRQ_MASK 21 #define ACP_SDW1_IRQ_MASK 2 #define ACP_ERROR_IRQ_MASK 29 +#define ACP_AUDIO0_TX_THRESHOLD 28 +#define ACP_AUDIO1_TX_THRESHOLD 26 +#define ACP_AUDIO2_TX_THRESHOLD 24 +#define ACP_AUDIO0_RX_THRESHOLD 27 +#define ACP_AUDIO1_RX_THRESHOLD 25 +#define ACP_AUDIO2_RX_THRESHOLD 23 +#define ACP_P1_AUDIO1_TX_THRESHOLD 6 +#define ACP_P1_AUDIO1_RX_THRESHOLD 5 +#define ACP_SDW_DMA_IRQ_MASK 0x1F800000 +#define ACP_P1_SDW_DMA_IRQ_MASK 0x60 +#define ACP63_SDW0_DMA_MAX_STREAMS 6 +#define ACP63_SDW1_DMA_MAX_STREAMS 2 +#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i))) +#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * (i))) +#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO1_TX_THRESHOLD - (i)) + +#define ACP_DELAY_US 5 +#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024) +#define SDW0_MEM_WINDOW_START 0x4800000 +#define ACP_SDW_SRAM_PTE_OFFSET 0x03800400 +#define SDW0_PTE_OFFSET 0x400 +#define SDW_FIFO_SIZE 0x100 +#define SDW_DMA_SIZE 0x40 +#define ACP_SDW0_FIFO_OFFSET 0x100 +#define SDW_PTE_OFFSET(i) (SDW0_PTE_OFFSET + ((i) * 0x600)) +#define ACP_SDW_FIFO_OFFSET(i) (ACP_SDW0_FIFO_OFFSET + ((i) * 0x500)) +#define SDW_MEM_WINDOW_START(i) (SDW0_MEM_WINDOW_START + ((i) * 0xC0000)) + +#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, @@ -114,6 +153,43 @@ struct pdm_dev_data { struct sdw_dma_dev_data { void __iomem *acp_base; struct mutex *acp_lock; /* used to protect acp common register access */ + struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS]; + struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS]; +}; + +struct sdw_dma_stream_instance { + u16 num_pages; + u16 channels; + u32 stream_id; + u32 instance; + dma_addr_t dma_addr; + u64 bytescount; +}; + +union acp_sdw_dma_count { + struct { + u32 low; + u32 high; + } bcount; + u64 bytescount; +}; + +struct sdw_dma_ring_buf_reg { + u32 reg_dma_size; + u32 reg_fifo_addr; + u32 reg_fifo_size; + u32 reg_ring_buf_size; + u32 reg_ring_buf_addr; + u32 water_mark_size_reg; + u32 pos_low_reg; + u32 pos_high_reg; + u32 sdw_dma_en_reg; + u32 sdw_dma_en_stat_reg; +}; + +struct sdw_dma_enable_reg { + u32 sdw_dma_en_reg; + u32 sdw_dma_en_stat_reg; };
/** diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 0d0889842413..5002ef16d980 100644 --- a/sound/soc/amd/ps/ps-sdw-dma.c +++ b/sound/soc/amd/ps/ps-sdw-dma.c @@ -12,12 +12,429 @@ #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 struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { + {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE, + ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE, + ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE, + ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE, + ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE, + ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, + {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE, + ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE, + ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH} +}; + +static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { + {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE, + ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR, + ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH}, + {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE, + ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR, + ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE, + ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH}, +}; + +static struct sdw_dma_enable_reg sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = { + {ACP_SW0_AUDIO0_TX_EN, ACP_SW0_AUDIO0_TX_EN_STATUS}, + {ACP_SW0_AUDIO1_TX_EN, ACP_SW0_AUDIO1_TX_EN_STATUS}, + {ACP_SW0_AUDIO2_TX_EN, ACP_SW0_AUDIO2_TX_EN_STATUS}, + {ACP_SW0_AUDIO0_RX_EN, ACP_SW0_AUDIO0_RX_EN_STATUS}, + {ACP_SW0_AUDIO1_RX_EN, ACP_SW0_AUDIO1_RX_EN_STATUS}, + {ACP_SW0_AUDIO2_RX_EN, ACP_SW0_AUDIO2_RX_EN_STATUS}, +}; + +static struct sdw_dma_enable_reg sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = { + {ACP_SW1_AUDIO1_TX_EN, ACP_SW1_AUDIO1_TX_EN_STATUS}, + {ACP_SW1_AUDIO1_RX_EN, ACP_SW1_AUDIO1_RX_EN_STATUS}, +}; + +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_dma_stream_instance *sdw_ins, void __iomem *acp_base, + u32 stream_id) +{ + u16 page_idx; + u32 low, high, val; + u32 sdw_dma_pte_offset; + dma_addr_t addr; + + addr = sdw_ins->dma_addr; + sdw_dma_pte_offset = SDW_PTE_OFFSET(sdw_ins->instance); + val = sdw_dma_pte_offset + (stream_id * 256); + + /* Group Enable */ + writel(ACP_SDW_SRAM_PTE_OFFSET | BIT(31), acp_base + ACPAXI2AXI_ATU_BASE_ADDR_GRP_2); + writel(PAGE_SIZE_4K_ENABLE, 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); + + writel(low, acp_base + ACP_SCRATCH_REG_0 + val); + high |= BIT(31); + writel(high, acp_base + ACP_SCRATCH_REG_0 + val + 4); + val += 8; + addr += PAGE_SIZE; + } + writel(0x1, acp_base + ACPAXI2AXI_ATU_CTRL); +} + +static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size, + u32 manager_instance) +{ + 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_fifo_offset; + u32 sdw_ring_buf_addr; + u32 sdw_ring_buf_size; + u32 sdw_mem_window_offset; + + switch (manager_instance) { + case ACP_SDW0: + reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size; + reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr; + reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + break; + case ACP_SDW1: + reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size; + reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr; + reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size; + reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size; + reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr; + break; + default: + return -EINVAL; + } + sdw_fifo_offset = ACP_SDW_FIFO_OFFSET(manager_instance); + sdw_mem_window_offset = SDW_MEM_WINDOW_START(manager_instance); + sdw_fifo_addr = sdw_fifo_offset + (stream_id * 256); + sdw_ring_buf_addr = sdw_mem_window_offset + (stream_id * ACP_SDW_RING_BUFF_ADDR_OFFSET); + sdw_ring_buf_size = size; + writel(sdw_ring_buf_size, acp_base + reg_ring_buf_size); + writel(sdw_ring_buf_addr, acp_base + reg_ring_buf_addr); + writel(sdw_fifo_addr, acp_base + reg_fifo_addr); + writel(SDW_DMA_SIZE, acp_base + reg_dma_size); + 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_stream_instance *sdw_ins; + struct snd_soc_dai *cpu_dai; + struct amd_sdw_manager *amd_manager; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + int ret; + + runtime = substream->runtime; + cpu_dai = asoc_rtd_to_cpu(prtd, 0); + amd_manager = snd_soc_dai_get_drvdata(cpu_dai); + sdw_ins = kzalloc(sizeof(*sdw_ins), GFP_KERNEL); + if (!sdw_ins) + return -ENOMEM; + + 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_ins); + return ret; + } + + sdw_ins->stream_id = cpu_dai->id; + sdw_ins->instance = amd_manager->instance; + runtime->private_data = sdw_ins; + 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_dma_stream_instance *sdw_ins; + 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; + + sdw_data = dev_get_drvdata(component->dev); + sdw_ins = substream->runtime->private_data; + if (!sdw_ins) + return -EINVAL; + stream_id = sdw_ins->stream_id; + switch (sdw_ins->instance) { + case ACP_SDW0: + sdw_data->sdw0_dma_stream[stream_id] = substream; + water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id)); + else + irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id)); + break; + case ACP_SDW1: + sdw_data->sdw1_dma_stream[stream_id] = substream; + acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1; + water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg; + irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id)); + break; + default: + return -EINVAL; + } + size = params_buffer_bytes(params); + period_bytes = params_period_bytes(params); + sdw_ins->dma_addr = substream->runtime->dma_addr; + sdw_ins->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + acp63_config_dma(sdw_ins, sdw_data->acp_base, stream_id); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size, + sdw_ins->instance); + if (ret) { + dev_err(component->dev, "Invalid DMA channel\n"); + return -EINVAL; + } + ext_intr_ctrl = readl(sdw_data->acp_base + acp_ext_intr_cntl_reg); + ext_intr_ctrl |= irq_mask; + writel(ext_intr_ctrl, sdw_data->acp_base + acp_ext_intr_cntl_reg); + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + return 0; +} + +static u64 acp63_sdw_get_byte_count(struct sdw_dma_stream_instance *sdw_ins, void __iomem *acp_base) +{ + union acp_sdw_dma_count byte_count; + u32 pos_low_reg, pos_high_reg; + + byte_count.bytescount = 0; + switch (sdw_ins->instance) { + case ACP_SDW0: + pos_low_reg = sdw0_dma_ring_buf_reg[sdw_ins->stream_id].pos_low_reg; + pos_high_reg = sdw0_dma_ring_buf_reg[sdw_ins->stream_id].pos_high_reg; + break; + case ACP_SDW1: + pos_low_reg = sdw1_dma_ring_buf_reg[sdw_ins->stream_id].pos_low_reg; + pos_high_reg = sdw1_dma_ring_buf_reg[sdw_ins->stream_id].pos_high_reg; + break; + default: + return -EINVAL; + } + if (pos_low_reg) { + byte_count.bcount.high = readl(acp_base + pos_high_reg); + byte_count.bcount.low = readl(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_dma_dev_data *sdw_data; + struct sdw_dma_stream_instance *sdw_ins; + u32 pos, buffersize; + u64 bytescount; + + sdw_data = dev_get_drvdata(comp->dev); + sdw_ins = stream->runtime->private_data; + buffersize = frames_to_bytes(stream->runtime, + stream->runtime->buffer_size); + bytescount = acp63_sdw_get_byte_count(sdw_ins, sdw_data->acp_base); + 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_data; + struct sdw_dma_stream_instance *sdw_ins; + + sdw_data = dev_get_drvdata(component->dev); + sdw_ins = substream->runtime->private_data; + if (!sdw_ins) + return -EINVAL; + switch (sdw_ins->instance) { + case ACP_SDW0: + sdw_data->sdw0_dma_stream[sdw_ins->stream_id] = NULL; + break; + case ACP_SDW1: + sdw_data->sdw1_dma_stream[sdw_ins->stream_id] = NULL; + break; + default: + return -EINVAL; + } + kfree(sdw_ins); + return 0; +} + +static int acp63_sdw_dma_start(struct snd_pcm_substream *stream, void __iomem *acp_base) +{ + struct sdw_dma_stream_instance *sdw_ins; + u32 stream_id; + u32 sdw_dma_en_reg; + u32 sdw_dma_en_stat_reg; + u32 sdw_dma_stat; + + sdw_ins = stream->runtime->private_data; + stream_id = sdw_ins->stream_id; + switch (sdw_ins->instance) { + case ACP_SDW0: + sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id].sdw_dma_en_reg; + sdw_dma_en_stat_reg = sdw0_dma_enable_reg[stream_id].sdw_dma_en_stat_reg; + break; + case ACP_SDW1: + sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id].sdw_dma_en_reg; + sdw_dma_en_stat_reg = sdw1_dma_enable_reg[stream_id].sdw_dma_en_stat_reg; + break; + default: + return -EINVAL; + } + writel(0x01, acp_base + sdw_dma_en_reg); + return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat, + (sdw_dma_stat & BIT(0)), ACP_DELAY_US, ACP_COUNTER); +} + +static int acp63_sdw_dma_stop(struct snd_pcm_substream *stream, void __iomem *acp_base) +{ + struct sdw_dma_stream_instance *sdw_ins; + u32 stream_id; + u32 sdw_dma_en_reg; + u32 sdw_dma_en_stat_reg; + u32 sdw_dma_stat; + + sdw_ins = stream->runtime->private_data; + stream_id = sdw_ins->stream_id; + switch (sdw_ins->instance) { + case ACP_SDW0: + sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id].sdw_dma_en_reg; + sdw_dma_en_stat_reg = sdw0_dma_enable_reg[stream_id].sdw_dma_en_stat_reg; + break; + case ACP_SDW1: + sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id].sdw_dma_en_reg; + sdw_dma_en_stat_reg = sdw1_dma_enable_reg[stream_id].sdw_dma_en_stat_reg; + break; + default: + return -EINVAL; + } + + writel(0, acp_base + sdw_dma_en_reg); + return readl_poll_timeout(acp_base + sdw_dma_en_stat_reg, sdw_dma_stat, !sdw_dma_stat, + ACP_DELAY_US, ACP_COUNTER); +} + +static int acp63_sdw_dma_trigger(struct snd_soc_component *comp, + struct snd_pcm_substream *substream, + int cmd) +{ + struct sdw_dma_dev_data *sdw_data; + int ret; + + sdw_data = dev_get_drvdata(comp->dev); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = acp63_sdw_dma_start(substream, sdw_data->acp_base); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = acp63_sdw_dma_stop(substream, sdw_data->acp_base); + 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)
Initialize workqueue for Soundwire 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 | 17 ++++++++ sound/soc/amd/ps/pci-ps.c | 87 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-)
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h index 7f8e1c76ba4f..f03a98a0b9e3 100644 --- a/sound/soc/amd/ps/acp63.h +++ b/sound/soc/amd/ps/acp63.h @@ -135,6 +135,20 @@ enum acp_pdev_mask { ACP63_SDW_PDM_DEV_MASK, };
+enum amd_sdw0_channel { + ACP_SDW0_AUDIO0_TX = 0, + ACP_SDW0_AUDIO1_TX, + ACP_SDW0_AUDIO2_TX, + ACP_SDW0_AUDIO0_RX, + ACP_SDW0_AUDIO1_RX, + ACP_SDW0_AUDIO2_RX, +}; + +enum amd_sdw1_channel { + ACP_SDW1_AUDIO1_TX, + ACP_SDW1_AUDIO1_RX, +}; + struct pdm_stream_instance { u16 num_pages; u16 channels; @@ -217,9 +231,12 @@ 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 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS]; + u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS]; u8 sdw_manager_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 6566ee14d300..ba8ec8442a6e 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -99,14 +99,44 @@ 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_SDW0_DMA_MAX_STREAMS; stream_index++) { + if (adata->sdw0_dma_intr_stat[stream_index]) { + if (sdw_dma_data->sdw0_dma_stream[stream_index]) + snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]); + adata->sdw0_dma_intr_stat[stream_index] = 0; + } + } + for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) { + if (adata->sdw1_dma_intr_stat[stream_index]) { + if (sdw_dma_data->sdw1_dma_stream[stream_index]) + snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]); + adata->sdw1_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_sdw_manager *amd_manager; 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) @@ -147,7 +177,57 @@ 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_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) { + if (ext_intr_stat & BIT(index)) { + writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT); + switch (index) { + case ACP_AUDIO0_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO0_TX; + break; + case ACP_AUDIO1_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO1_TX; + break; + case ACP_AUDIO2_TX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO2_TX; + break; + case ACP_AUDIO0_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO0_RX; + break; + case ACP_AUDIO1_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO1_RX; + break; + case ACP_AUDIO2_RX_THRESHOLD: + stream_id = ACP_SDW0_AUDIO2_RX; + break; + } + + adata->sdw0_dma_intr_stat[stream_id] = 1; + sdw_dma_irq_flag = 1; + } + } + } + + /* SDW1 BT RX */ + if (ext_intr_stat1 & BIT(ACP_P1_AUDIO1_RX_THRESHOLD)) { + writel(BIT(ACP_P1_AUDIO1_RX_THRESHOLD), + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1; + sdw_dma_irq_flag = 1; + } + + /* SDW1 BT TX*/ + if (ext_intr_stat1 & BIT(ACP_P1_AUDIO1_TX_THRESHOLD)) { + writel(BIT(ACP_P1_AUDIO1_TX_THRESHOLD), + adata->acp63_base + ACP_EXTERNAL_INTR_STAT1); + adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_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; @@ -229,6 +309,7 @@ static int get_acp63_device_config(u32 config, struct pci_dev *pci, struct acp63 sdw_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_SDW_ADDR, 0); if (sdw_dev) { 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(&pci->dev); if (!ret) is_sdw_dev = true; @@ -607,6 +688,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);
Add support pm ops support for soundwire dma driver.
Signed-off-by: Vijendar Mukunda Vijendar.Mukunda@amd.com --- sound/soc/amd/ps/ps-sdw-dma.c | 86 +++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+)
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c index 5002ef16d980..8901a52f7498 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"
@@ -102,6 +103,29 @@ 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; + u32 irq_mask = ACP_SDW_DMA_IRQ_MASK; + u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK; + + if (enable) { + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl |= irq_mask; + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); + ext_intr_cntl1 |= irq_mask1; + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); + } else { + ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl &= ~irq_mask; + writel(ext_intr_cntl, acp_base + ACP_EXTERNAL_INTR_CNTL); + ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1); + ext_intr_cntl1 &= ~irq_mask1; + writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1); + } +} + static void acp63_config_dma(struct sdw_dma_stream_instance *sdw_ins, void __iomem *acp_base, u32 stream_id) { @@ -470,13 +494,75 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Fail to register sdw dma component\n"); 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_resume(struct device *dev) +{ + struct sdw_dma_dev_data *sdw_data; + struct sdw_dma_stream_instance *sdw_ins; + struct snd_pcm_runtime *runtime; + u32 period_bytes, buf_size, water_mark_size_reg; + int ret; + int index; + + sdw_data = dev_get_drvdata(dev); + for (index = 0; index < ACP63_SDW0_DMA_MAX_STREAMS; index++) { + if (sdw_data->sdw0_dma_stream[index] && + sdw_data->sdw0_dma_stream[index]->runtime) { + water_mark_size_reg = sdw0_dma_ring_buf_reg[index].water_mark_size_reg; + runtime = sdw_data->sdw0_dma_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, sdw_data->acp_base, index); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, + buf_size, ACP_SDW0); + if (ret) + return ret; + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + } + } + for (index = 0; index < ACP63_SDW1_DMA_MAX_STREAMS; index++) { + if (sdw_data->sdw1_dma_stream[index] && + sdw_data->sdw1_dma_stream[index]->runtime) { + water_mark_size_reg = sdw1_dma_ring_buf_reg[index].water_mark_size_reg; + runtime = sdw_data->sdw1_dma_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, sdw_data->acp_base, index); + ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index, + buf_size, ACP_SDW1); + if (ret) + return ret; + writel(period_bytes, sdw_data->acp_base + water_mark_size_reg); + } + } + acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true); return 0; }
+static const struct dev_pm_ops acp63_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, acp63_sdw_pcm_resume) +}; + 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, }, };
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..126a973ea5d7 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-obj := 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
Hi Vijendar,
kernel test robot noticed the following build errors:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on next-20230517] [cannot apply to linus/master v6.4-rc2] [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-... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230516103543.2515097-8-Vijendar.Mukunda%40amd.co... patch subject: [PATCH 7/9] ASoC: amd: ps: enable SoundWire dma driver build config: x86_64-allyesconfig compiler: gcc-11 (Debian 11.3.0-12) 11.3.0 reproduce (this is a W=1 build): # https://github.com/intel-lab-lkp/linux/commit/9200bd753a28fb732d2b59cfe767df... 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/20230516-195625 git checkout 9200bd753a28fb732d2b59cfe767df1f66517085 # save the config file mkdir build_dir && cp config build_dir/.config make W=1 O=build_dir ARCH=x86_64 olddefconfig make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202305172323.ZFaMoDeB-lkp@intel.com/
All errors (new ones prefixed by >>):
make[6]: *** No rule to make target 'sound/soc/amd/ps/snd-ps-sdw-dma.o', needed by 'sound/soc/amd/ps/built-in.a'.
make[6]: Target 'sound/soc/amd/ps/' not remade because of errors.
On 17/05/23 21:47, kernel test robot wrote:
Hi Vijendar,
kernel test robot noticed the following build errors:
[auto build test ERROR on broonie-sound/for-next] [also build test ERROR on next-20230517] [cannot apply to linus/master v6.4-rc2] [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-... base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next patch link: https://lore.kernel.org/r/20230516103543.2515097-8-Vijendar.Mukunda%40amd.co... patch subject: [PATCH 7/9] ASoC: amd: ps: enable SoundWire dma driver build config: x86_64-allyesconfig compiler: gcc-11 (Debian 11.3.0-12) 11.3.0 reproduce (this is a W=1 build): # https://github.com/intel-lab-lkp/linux/commit/9200bd753a28fb732d2b59cfe767df... 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/20230516-195625 git checkout 9200bd753a28fb732d2b59cfe767df1f66517085 # save the config file mkdir build_dir && cp config build_dir/.config make W=1 O=build_dir ARCH=x86_64 olddefconfig make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202305172323.ZFaMoDeB-lkp@intel.com/
All errors (new ones prefixed by >>):
make[6]: *** No rule to make target 'sound/soc/amd/ps/snd-ps-sdw-dma.o', needed by 'sound/soc/amd/ps/built-in.a'.
will fix it.
make[6]: Target 'sound/soc/amd/ps/' not remade because of errors.
Update comments in Kconfig file for Pink Sardine 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 08e42082f5e9..093a7273d53d 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -136,7 +136,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".
AMD Soundwire manager 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 ba8ec8442a6e..6d688821b3c4 100644 --- a/sound/soc/amd/ps/pci-ps.c +++ b/sound/soc/amd/ps/pci-ps.c @@ -656,10 +656,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_reset) + 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; @@ -668,10 +673,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_reset) + 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;
participants (5)
-
kernel test robot
-
Limonciello, Mario
-
Mukunda,Vijendar
-
Pierre-Louis Bossart
-
Vijendar Mukunda