@@ -5,6 +5,7 @@
* Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
*/
+#include <linux/soundwire/sdw_amd.h>
#include <sound/acp70_chip_offset_byte.h>
#define ACP_DEVICE_ID 0x15E2
@@ -28,21 +29,63 @@
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
#define ACP_ERROR_IRQ BIT(29)
+#define ACP_DMIC_DEV 2
+#define ACP70_DMIC_ADDR 2
+#define ACP70_SDW_ADDR 5
+#define AMD_SDW_MAX_MANAGERS 2
+
+enum acp_config {
+ ACP_CONFIG_0 = 0,
+ ACP_CONFIG_1,
+ ACP_CONFIG_2,
+ ACP_CONFIG_3,
+ ACP_CONFIG_4,
+ ACP_CONFIG_5,
+ ACP_CONFIG_6,
+ ACP_CONFIG_7,
+ ACP_CONFIG_8,
+ ACP_CONFIG_9,
+ ACP_CONFIG_10,
+ ACP_CONFIG_11,
+ ACP_CONFIG_12,
+ ACP_CONFIG_13,
+ ACP_CONFIG_14,
+ ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
/**
* struct acp70_dev_data - acp pci driver context
* @acp70_base: acp mmio base
* @acp_lock: used to protect acp common registers
+ * @info: SoundWire AMD information found in ACPI tables
+ * @sdw: SoundWire context for all SoundWire manager instances
* @addr: pci ioremap address
* @reg_range: ACP reigister range
* @acp_rev : ACP PCI revision id
+ * @is_sdw_dev: flag set to true when any SoundWire manager instances are available
+ * @is_pdm_dev: flag set to true when ACP PDM controller exists
+ * @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
+ * @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
*/
struct acp70_dev_data {
void __iomem *acp70_base;
struct mutex acp_lock; /* protect shared registers */
+ struct sdw_amd_acpi_info info;
+ /* sdw context allocated by SoundWire driver */
+ struct sdw_amd_ctx *sdw;
u32 addr;
u32 reg_range;
u32 acp_rev;
+ bool is_sdw_dev;
+ bool is_pdm_dev;
+ bool is_pdm_config;
+ bool is_sdw_config;
};
int snd_amd_acp_find_config(struct pci_dev *pci);
@@ -5,6 +5,8 @@
* Copyright 2024 Advanced Micro Devices, Inc.
*/
+#include <linux/acpi.h>
+#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@@ -93,6 +95,99 @@ static int acp70_deinit(void __iomem *acp_base, struct device *dev)
return 0;
}
+#if IS_ENABLED(CONFIG_SND_SOC_AMD_SOUNDWIRE)
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ struct acpi_device *sdw_dev;
+ struct acp70_dev_data *acp_data;
+
+ acp_data = dev_get_drvdata(dev);
+ if (!addr)
+ return -ENODEV;
+
+ sdw_dev = acpi_find_child_device(ACPI_COMPANION(dev), addr, 0);
+ if (!sdw_dev) {
+ pr_err("%s No SoundWire devices found\n", __func__);
+ return -ENODEV;
+ }
+
+ acp_data->info.handle = sdw_dev->handle;
+ acp_data->info.count = AMD_SDW_MAX_MANAGERS;
+ return amd_sdw_scan_controller(&acp_data->info);
+}
+#else
+static int acp_scan_sdw_devices(struct device *dev, u64 addr)
+{
+ return 0;
+}
+#endif
+
+static int get_acp70_device_config(struct pci_dev *pci, struct acp70_dev_data *acp_data)
+{
+ struct acpi_device *pdm_dev;
+ const union acpi_object *obj;
+ u32 config;
+ int ret;
+ bool is_dmic_dev = false;
+ bool is_sdw_dev = false;
+
+ config = readl(acp_data->acp70_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP_PIN_CONFIG:0x%x\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_20:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_16:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ case ACP_CONFIG_19:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+
+ if (acp_data->is_pdm_config) {
+ pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP70_DMIC_ADDR, 0);
+ if (pdm_dev) {
+ /* is_dmic_dev flag will be set when ACP PDM controller device exists */
+ if (!acpi_dev_get_property(pdm_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ is_dmic_dev = true;
+ }
+ }
+
+ if (acp_data->is_sdw_config) {
+ ret = acp_scan_sdw_devices(&pci->dev, ACP70_SDW_ADDR);
+ if (!ret && acp_data->info.link_mask)
+ is_sdw_dev = true;
+ }
+
+ acp_data->is_pdm_dev = is_dmic_dev;
+ acp_data->is_sdw_dev = is_sdw_dev;
+ dev_dbg(&pci->dev, "is_pdm_dev:%d is_sdw_dev:%d\n", is_dmic_dev, is_sdw_dev);
+ if (!is_dmic_dev && !is_sdw_dev) {
+ dev_dbg(&pci->dev, "No PDM or SoundWire manager devices found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
static int snd_acp70_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -148,6 +243,13 @@ static int snd_acp70_probe(struct pci_dev *pci,
if (ret)
goto release_regions;
+ ret = get_acp70_device_config(pci, adata);
+ /* ACP PCI driver probe should be continued even PDM or SoundWire Devices are not found */
+ if (ret) {
+ dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
+ goto skip_pdev_creation;
+ }
+skip_pdev_creation:
return 0;
release_regions:
pci_release_regions(pci);
@@ -189,4 +291,5 @@ module_pci_driver(ps_acp70_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD ACP7.0 PCI driver");
+MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
MODULE_LICENSE("GPL");
Based on acp pin configuration, add logic for scanning child devices under acp pci device acpi scope. Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com> --- sound/soc/amd/acp70/acp70.h | 43 +++++++++++++ sound/soc/amd/acp70/pci-acp70.c | 103 ++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+)