ASoC: amd: acp: Add generic PCI driver module for ACP device
authorAjit Kumar Pandey <AjitKumar.Pandey@amd.com>
Mon, 17 Jan 2022 11:58:51 +0000 (17:28 +0530)
committerMark Brown <broonie@kernel.org>
Fri, 28 Jan 2022 15:59:31 +0000 (15:59 +0000)
Audio Co-processor or ACP IP block on AMD's SOC is connected via
PCI bus interface, hence needs to be register as a PCI device. We
have same PCI device ID across multiple SOC's but with different
revision id for PCI hw. Add a generic PCI driver module for ACP
that registers ACP as a PCI device and also register a platform
device based on pci revision id. Any SOC's specific configuration
for ACP block will be done in platform driver probe. We have added
an initial support for ACP revision id 3 or ACP3X device.

Signed-off-by: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
Link: https://lore.kernel.org/r/20220117115854.455995-4-AjitKumar.Pandey@amd.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/amd/acp/Kconfig
sound/soc/amd/acp/Makefile
sound/soc/amd/acp/acp-pci.c [new file with mode: 0644]
sound/soc/amd/acp/amd.h
sound/soc/amd/mach-config.h

index f4ca7843391bd973d634cb015eed89900cf2d219..626e4a5cb06a82f91f5fea2ac00880e5c9fa6c2a 100644 (file)
@@ -25,6 +25,12 @@ config SND_SOC_AMD_ACP_PCM
        tristate
        select SND_SOC_ACPI if ACPI
 
+config SND_SOC_AMD_ACP_PCI
+       tristate "AMD ACP PCI Driver Support"
+       depends on X86 && PCI
+       help
+         This options enables generic PCI driver for ACP device.
+
 config SND_AMD_ASOC_RENOIR
        tristate "AMD ACP ASOC Renoir Support"
        select SND_SOC_AMD_ACP_PCM
index 66cac95432f6ea3e6abfd08e7b419b739a5e9a2e..657ddfadf0bbfd81fe95bf2fc3e105d31b6c2b7d 100644 (file)
@@ -8,6 +8,7 @@
 snd-acp-pcm-objs     := acp-platform.o
 snd-acp-i2s-objs     := acp-i2s.o
 snd-acp-pdm-objs     := acp-pdm.o
+snd-acp-pci-objs     := acp-pci.o
 
 #platform specific driver
 snd-acp-renoir-objs     := acp-renoir.o
@@ -20,6 +21,7 @@ snd-acp-sof-mach-objs     := acp-sof-mach.o
 obj-$(CONFIG_SND_SOC_AMD_ACP_PCM) += snd-acp-pcm.o
 obj-$(CONFIG_SND_SOC_AMD_ACP_I2S) += snd-acp-i2s.o
 obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
+obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
 
 obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
 
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
new file mode 100644 (file)
index 0000000..340e39d
--- /dev/null
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
+
+/*
+ * Generic PCI interface for ACP device
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "amd.h"
+#include "../mach-config.h"
+
+#define DRV_NAME "acp_pci"
+
+#define ACP3x_REG_START        0x1240000
+#define ACP3x_REG_END  0x125C000
+
+static struct platform_device *dmic_dev;
+static struct platform_device *pdev;
+
+static const struct resource acp3x_res[] = {
+       {
+               .start = 0,
+               .end = ACP3x_REG_END - ACP3x_REG_START,
+               .name = "acp_mem",
+               .flags = IORESOURCE_MEM,
+       },
+       {
+               .start = 0,
+               .end = 0,
+               .name = "acp_dai_irq",
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+       struct platform_device_info pdevinfo;
+       struct device *dev = &pci->dev;
+       const struct resource *res_acp;
+       struct acp_chip_info *chip;
+       struct resource *res;
+       unsigned int flag, addr, num_res, i;
+       int ret;
+
+       flag = snd_amd_acp_find_config(pci);
+       if (flag != FLAG_AMD_LEGACY)
+               return -ENODEV;
+
+       chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       if (pci_enable_device(pci)) {
+               dev_err(&pci->dev, "pci_enable_device failed\n");
+               return -ENODEV;
+       }
+
+       ret = pci_request_regions(pci, "AMD ACP3x audio");
+       if (ret < 0) {
+               dev_err(&pci->dev, "pci_request_regions failed\n");
+               return -ENOMEM;
+       }
+
+       pci_set_master(pci);
+
+       switch (pci->revision) {
+       case 0x01:
+               res_acp = acp3x_res;
+               num_res = ARRAY_SIZE(acp3x_res);
+               chip->name = "acp_asoc_renoir";
+               chip->acp_rev = ACP3X_DEV;
+               break;
+       default:
+               dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
+               return -EINVAL;
+       }
+
+       dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
+       if (IS_ERR(dmic_dev)) {
+               dev_err(dev, "failed to create DMIC device\n");
+               return PTR_ERR(dmic_dev);
+       }
+
+       addr = pci_resource_start(pci, 0);
+       chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
+
+       res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL);
+       if (!res) {
+               platform_device_unregister(dmic_dev);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < num_res; i++, res_acp++) {
+               res[i].name = res_acp->name;
+               res[i].flags = res_acp->flags;
+               res[i].start = addr + res_acp->start;
+               res[i].end = addr + res_acp->end;
+               if (res_acp->flags == IORESOURCE_IRQ) {
+                       res[i].start = pci->irq;
+                       res[i].end = res[i].start;
+               }
+       }
+
+       memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+       pdevinfo.name = chip->name;
+       pdevinfo.id = 0;
+       pdevinfo.parent = &pci->dev;
+       pdevinfo.num_res = num_res;
+       pdevinfo.res = &res[0];
+       pdevinfo.data = chip;
+       pdevinfo.size_data = sizeof(*chip);
+
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev)) {
+               dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
+               platform_device_unregister(dmic_dev);
+               ret = PTR_ERR(pdev);
+       }
+
+       return ret;
+};
+
+static void acp_pci_remove(struct pci_dev *pci)
+{
+       if (dmic_dev)
+               platform_device_unregister(dmic_dev);
+       if (pdev)
+               platform_device_unregister(pdev);
+}
+
+/* PCI IDs */
+static const struct pci_device_id acp_pci_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, acp_pci_ids);
+
+/* pci_driver definition */
+static struct pci_driver snd_amd_acp_pci_driver = {
+       .name = KBUILD_MODNAME,
+       .id_table = acp_pci_ids,
+       .probe = acp_pci_probe,
+       .remove = acp_pci_remove,
+};
+module_pci_driver(snd_amd_acp_pci_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS(DRV_NAME);
index 567355209a5c9576ec2009730b6c3fe0b5b00eb3..8fd38bf4d3bd60d16332288505a289c4d9af66af 100644 (file)
 #define __AMD_ACP_H
 
 #include <sound/pcm.h>
+#include <sound/soc.h>
 #include <sound/soc-acpi.h>
+#include <sound/soc-dai.h>
+
 #include "chip_offset_byte.h"
 
+#define ACP3X_DEV                      3
+
 #define I2S_SP_INSTANCE                        0x00
 #define I2S_BT_INSTANCE                        0x01
 #define DMIC_INSTANCE                  0x02
 
 #define ACP_MAX_STREAM                 6
 
+struct acp_chip_info {
+       char *name;             /* Platform name */
+       unsigned int acp_rev;   /* ACP Revision id */
+       void __iomem *base;     /* ACP memory PCI base */
+};
+
 struct acp_stream {
        struct snd_pcm_substream *substream;
        int irq_bit;
@@ -106,6 +117,9 @@ int acp_platform_unregister(struct device *dev);
 
 int acp_machine_select(struct acp_dev_data *adata);
 
+/* Machine configuration */
+int snd_amd_acp_find_config(struct pci_dev *pci);
+
 static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int direction)
 {
        u64 byte_count, low = 0, high = 0;
index feb3756d9ac4ed7dd9dc976a1c1dc41b1e6a262e..0a54567a28415e29b6e961784c1bd83512d74696 100644 (file)
@@ -14,6 +14,7 @@
 
 #define FLAG_AMD_SOF                   BIT(1)
 #define FLAG_AMD_SOF_ONLY_DMIC         BIT(2)
+#define FLAG_AMD_LEGACY                        BIT(3)
 
 #define ACP_PCI_DEV_ID                 0x15E2