@@ -61,6 +61,10 @@ enum acp_config {
/**
* struct acp70_dev_data - acp pci driver context
* @acp70_base: acp mmio base
+ * @res: resource
+ * @pdm_dev: ACP PDM controller platform device
+ * @dmic_codec: platform device for DMIC Codec
+ * sdw_dma_dev: platform device for SoundWire DMA controller
* @acp_lock: used to protect acp common registers
* @info: SoundWire AMD information found in ACPI tables
* @sdw: SoundWire context for all SoundWire manager instances
@@ -75,6 +79,10 @@ enum acp_config {
struct acp70_dev_data {
void __iomem *acp70_base;
+ struct resource *res;
+ struct platform_device *pdm_dev;
+ struct platform_device *dmic_codec_dev;
+ struct platform_device *sdw_dma_dev;
struct mutex acp_lock; /* protect shared registers */
struct sdw_amd_acpi_info info;
/* sdw context allocated by SoundWire driver */
@@ -12,6 +12,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include "../mach-config.h"
#include "acp70.h"
@@ -115,11 +116,54 @@ static int acp_scan_sdw_devices(struct device *dev, u64 addr)
acp_data->info.count = AMD_SDW_MAX_MANAGERS;
return amd_sdw_scan_controller(&acp_data->info);
}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ struct acp70_dev_data *acp_data;
+ struct sdw_amd_res sdw_res;
+ int ret;
+
+ acp_data = dev_get_drvdata(dev);
+ memset(&sdw_res, 0, sizeof(sdw_res));
+ sdw_res.addr = acp_data->addr;
+ sdw_res.reg_range = acp_data->reg_range;
+ sdw_res.handle = acp_data->info.handle;
+ sdw_res.parent = dev;
+ sdw_res.dev = dev;
+ sdw_res.acp_lock = &acp_data->acp_lock;
+ sdw_res.count = acp_data->info.count;
+ sdw_res.mmio_base = acp_data->acp70_base;
+ sdw_res.link_mask = acp_data->info.link_mask;
+ sdw_res.acp_rev = acp_data->acp_rev;
+ ret = sdw_amd_probe(&sdw_res, &acp_data->sdw);
+ if (ret)
+ dev_err(dev, "error: SoundWire probe failed\n");
+ return ret;
+}
+
+static int amd_sdw_exit(struct acp70_dev_data *acp_data)
+{
+ if (acp_data->sdw)
+ sdw_amd_exit(acp_data->sdw);
+ acp_data->sdw = NULL;
+
+ return 0;
+}
#else
static int acp_scan_sdw_devices(struct device *dev, u64 addr)
{
return 0;
}
+
+static int amd_sdw_probe(struct device *dev)
+{
+ return 0;
+}
+
+static int amd_sdw_exit(struct acp70_dev_data *acp_data)
+{
+ return 0;
+}
#endif
static int get_acp70_device_config(struct pci_dev *pci, struct acp70_dev_data *acp_data)
@@ -188,6 +232,102 @@ static int get_acp70_device_config(struct pci_dev *pci, struct acp70_dev_data *a
}
return 0;
}
+
+static void acp70_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp70_platform_devs(struct pci_dev *pci, struct acp70_dev_data *adata, u32 addr)
+{
+ struct platform_device_info pdevinfo;
+ struct device *parent;
+ int ret;
+
+ parent = &pci->dev;
+
+ if (adata->is_sdw_dev || adata->is_pdm_dev) {
+ adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP70_REG_END - ACP70_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ if (adata->is_pdm_dev && adata->is_pdm_config) {
+ acp70_fill_platform_dev_info(&pdevinfo, parent, NULL, "acp70_pdm_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->pdm_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->pdm_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->pdm_dev);
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp70_fill_platform_dev_info(&pdevinfo, parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ adata->dmic_codec_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->dmic_codec_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->dmic_codec_dev);
+ goto unregister_pdm_dev;
+ }
+ }
+ if (adata->is_sdw_dev && adata->is_sdw_config) {
+ ret = amd_sdw_probe(&pci->dev);
+ if (ret) {
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ acp70_fill_platform_dev_info(&pdevinfo, parent, NULL, "amd_acp70_sdw_dma",
+ 0, adata->res, 1, NULL, 0);
+
+ adata->sdw_dma_dev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(adata->sdw_dma_dev)) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo.name);
+ ret = PTR_ERR(adata->sdw_dma_dev);
+ if (adata->is_pdm_dev)
+ goto unregister_dmic_codec_dev;
+ else
+ goto de_init;
+ }
+ }
+ return 0;
+unregister_dmic_codec_dev:
+ platform_device_unregister(adata->dmic_codec_dev);
+unregister_pdm_dev:
+ platform_device_unregister(adata->pdm_dev);
+de_init:
+ if (acp70_deinit(adata->acp70_base, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
static int snd_acp70_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -249,8 +389,16 @@ static int snd_acp70_probe(struct pci_dev *pci,
dev_dbg(&pci->dev, "get acp device config failed:%d\n", ret);
goto skip_pdev_creation;
}
+ ret = create_acp70_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
+ }
skip_pdev_creation:
return 0;
+de_init:
+ if (acp70_deinit(adata->acp70_base, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
release_regions:
pci_release_regions(pci);
disable_pci:
@@ -265,6 +413,14 @@ static void snd_acp70_remove(struct pci_dev *pci)
int ret;
adata = pci_get_drvdata(pci);
+ if (adata->sdw) {
+ amd_sdw_exit(adata);
+ platform_device_unregister(adata->sdw_dma_dev);
+ }
+ if (adata->is_pdm_dev) {
+ platform_device_unregister(adata->pdm_dev);
+ platform_device_unregister(adata->dmic_codec_dev);
+ }
ret = acp70_deinit(adata->acp70_base, &pci->dev);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
@@ -291,5 +447,6 @@ module_pci_driver(ps_acp70_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_DESCRIPTION("AMD ACP7.0 PCI driver");
+MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
MODULE_LICENSE("GPL");
Create platform devices for ACP child nodes(ACP PDM controller, DMIC codec, SoundWire DMA driver, SoundWire manager instances). Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com> --- sound/soc/amd/acp70/acp70.h | 8 ++ sound/soc/amd/acp70/pci-acp70.c | 157 ++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+)