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