mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
ASoC: AMD: ACP7.x initial PCI driver bring-up
Vijendar Mukunda <Vijendar.Mukunda@amd.com> says: This series adds initial AMD ACP 7.x support for ACP7.D / 7.E / 7.F platforms. Compared to earlier ACP generations, ACP7.x includes substantial design changes, including an updated register set/layout. For that reason, the ACP7.x implementation is placed under a separate sound/soc/amd/acp7x/ directory instead of extending older-generation code paths, keeping ACP7.x-specific logic and register definitions cleanly separated and easier to maintain. This initial version is intentionally focused on the core PCI driver bring-up: register definitions, probe/remove, basic helper wiring, and system sleep + runtime PM integration. A follow-up series will add support for additional Audio I/O blocks, including SoundWire and the ACP PDM controller. The primary goal of this series is to unblock power validation, since the ACP IP currently does not have a driver available with PM ops support on these platforms. Link: https://patch.msgid.link/20260507181251.20594-1-Vijendar.Mukunda@amd.com
This commit is contained in:
commit
75b992accc
2519
include/sound/acp7x_chip_offset_byte.h
Normal file
2519
include/sound/acp7x_chip_offset_byte.h
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -174,5 +174,15 @@ config SND_SOC_AMD_PS_MACH
|
|||
Say m if you have such a device.
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_AMD_ACP7X
|
||||
tristate "AMD Audio Coprocessor-v7.x support"
|
||||
depends on X86 && PCI && ACPI
|
||||
help
|
||||
This option enables Audio Coprocessor i.e ACP7.x
|
||||
variants support(ACP7.D/7.E/7.F). By enabling this flag build
|
||||
will be triggered for ACP PCI driver.
|
||||
Say m if you have such a device.
|
||||
If unsure select "N".
|
||||
|
||||
endif
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -18,3 +18,4 @@ obj-$(CONFIG_SND_SOC_AMD_ACP6x) += yc/
|
|||
obj-$(CONFIG_SND_AMD_ACP_CONFIG) += acp/
|
||||
obj-$(CONFIG_SND_AMD_ACP_CONFIG) += snd-acp-config.o
|
||||
obj-$(CONFIG_SND_SOC_AMD_PS) += ps/
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP7X) += acp7x/
|
||||
|
|
|
|||
5
sound/soc/amd/acp7x/Makefile
Normal file
5
sound/soc/amd/acp7x/Makefile
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# ACP7.x variants platform Support
|
||||
snd-pci-acp7x-y := pci-acp7x.o acp7x-common.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_AMD_ACP7X) += snd-pci-acp7x.o
|
||||
125
sound/soc/amd/acp7x/acp7x-common.c
Normal file
125
sound/soc/amd/acp7x/acp7x-common.c
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AMD ACP PCI driver callback routines for ACP7.x
|
||||
* platforms.
|
||||
*
|
||||
* Copyright 2026 Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "acp7x.h"
|
||||
|
||||
static int acp7x_power_on(void __iomem *acp_base)
|
||||
{
|
||||
u32 val = 0;
|
||||
|
||||
val = readl(acp_base + ACP_PGFSM_STATUS);
|
||||
if (!(val & ACP7X_PGFSM_STATUS_MASK))
|
||||
return 0;
|
||||
|
||||
writel(ACP7X_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
|
||||
val = readl(acp_base + ACP_PGFSM_CONTROL);
|
||||
return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val,
|
||||
((val & ACP7X_PGFSM_STATUS_MASK) == 0), DELAY_US, ACP7X_TIMEOUT);
|
||||
}
|
||||
|
||||
static int acp7x_reset(void __iomem *acp_base)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
writel(1, acp_base + ACP_SOFT_RESET);
|
||||
ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
|
||||
val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
|
||||
DELAY_US, ACP7X_TIMEOUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0, acp_base + ACP_SOFT_RESET);
|
||||
return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP7X_TIMEOUT);
|
||||
}
|
||||
|
||||
static int acp7x_init(void __iomem *acp_base, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acp7x_power_on(acp_base);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP power on failed\n");
|
||||
return ret;
|
||||
}
|
||||
writel(0x01, acp_base + ACP_CONTROL);
|
||||
ret = acp7x_reset(acp_base);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
writel(0, acp_base + ACP_ZSC_DSP_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acp7x_deinit(void __iomem *acp_base, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = acp7x_reset(acp_base);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp7x_suspend(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = acp_hw_deinit(adata, dev);
|
||||
if (ret)
|
||||
dev_err(dev, "ACP de-init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp7x_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = acp_hw_init(adata, dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "ACP init failed\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp7x_resume(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = dev_get_drvdata(dev);
|
||||
ret = acp_hw_init(adata, dev);
|
||||
if (ret)
|
||||
dev_err(dev, "ACP init failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void acp7x_hw_init_ops(struct acp_hw_ops *hw_ops)
|
||||
{
|
||||
hw_ops->acp_init = acp7x_init;
|
||||
hw_ops->acp_deinit = acp7x_deinit;
|
||||
hw_ops->acp_suspend = snd_acp7x_suspend;
|
||||
hw_ops->acp_resume = snd_acp7x_resume;
|
||||
hw_ops->acp_suspend_runtime = snd_acp7x_suspend;
|
||||
hw_ops->acp_resume_runtime = snd_acp7x_runtime_resume;
|
||||
}
|
||||
110
sound/soc/amd/acp7x/acp7x.h
Normal file
110
sound/soc/amd/acp7x/acp7x.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* AMD Common ACP header file for ACP7.X variants(ACP7.D/7.E/7.F)
|
||||
*
|
||||
* Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SOUND_SOC_AMD_ACP7X_H
|
||||
#define __SOUND_SOC_AMD_ACP7X_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <sound/acp7x_chip_offset_byte.h>
|
||||
|
||||
#define ACP_DEVICE_ID 0x15E2
|
||||
#define ACP7X_REG_START 0x1240000
|
||||
#define ACP7X_REG_END 0x125C000
|
||||
|
||||
#define ACP7D_PCI_REV 0x7D
|
||||
#define ACP7E_PCI_REV 0x7E
|
||||
#define ACP7F_PCI_REV 0x7F
|
||||
|
||||
/* Common register helper bits used by acp7x-common.c */
|
||||
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
|
||||
|
||||
#define DELAY_US 5
|
||||
#define ACP7X_TIMEOUT 5000
|
||||
|
||||
#define ACP7X_PGFSM_CNTL_POWER_ON_MASK 7
|
||||
#define ACP7X_PGFSM_STATUS_MASK 0x3F
|
||||
|
||||
/* time in ms for runtime suspend delay */
|
||||
#define ACP_SUSPEND_DELAY_MS 2000
|
||||
|
||||
struct acp_hw_ops {
|
||||
int (*acp_init)(void __iomem *acp_base, struct device *dev);
|
||||
int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
|
||||
int (*acp_suspend)(struct device *dev);
|
||||
int (*acp_resume)(struct device *dev);
|
||||
int (*acp_suspend_runtime)(struct device *dev);
|
||||
int (*acp_resume_runtime)(struct device *dev);
|
||||
};
|
||||
|
||||
struct acp7x_dev_data {
|
||||
void __iomem *acp7x_base;
|
||||
struct acp_hw_ops *hw_ops;
|
||||
u32 addr;
|
||||
u32 reg_range;
|
||||
u32 acp_rev;
|
||||
};
|
||||
|
||||
void acp7x_hw_init_ops(struct acp_hw_ops *hw_ops);
|
||||
|
||||
static inline int acp_hw_init(struct acp7x_dev_data *adata, struct device *dev)
|
||||
{
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_init)
|
||||
return adata->hw_ops->acp_init(adata->acp7x_base, dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int acp_hw_deinit(struct acp7x_dev_data *adata, struct device *dev)
|
||||
{
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
|
||||
return adata->hw_ops->acp_deinit(adata->acp7x_base, dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int acp_hw_suspend(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata = dev_get_drvdata(dev);
|
||||
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
|
||||
return adata->hw_ops->acp_suspend(dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int acp_hw_resume(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata = dev_get_drvdata(dev);
|
||||
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
|
||||
return adata->hw_ops->acp_resume(dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int acp_hw_suspend_runtime(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata = dev_get_drvdata(dev);
|
||||
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
|
||||
return adata->hw_ops->acp_suspend_runtime(dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int acp_hw_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct acp7x_dev_data *adata = dev_get_drvdata(dev);
|
||||
|
||||
if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
|
||||
return adata->hw_ops->acp_resume_runtime(dev);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int snd_amd_acp_find_config(struct pci_dev *pci);
|
||||
|
||||
#endif /* __SOUND_SOC_AMD_ACP7X_H */
|
||||
168
sound/soc/amd/acp7x/pci-acp7x.c
Normal file
168
sound/soc/amd/acp7x/pci-acp7x.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AMD common ACP PCI driver for ACP7.x variants
|
||||
* which includes ACP7.D/7.E/7.F and future variants
|
||||
* with same register layout.
|
||||
*
|
||||
* Copyright 2026 Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "acp7x.h"
|
||||
|
||||
static int acp_hw_init_ops(struct acp7x_dev_data *adata, struct pci_dev *pci)
|
||||
{
|
||||
adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
|
||||
GFP_KERNEL);
|
||||
if (!adata->hw_ops)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (adata->acp_rev) {
|
||||
case ACP7D_PCI_REV:
|
||||
case ACP7E_PCI_REV:
|
||||
case ACP7F_PCI_REV:
|
||||
acp7x_hw_init_ops(adata->hw_ops);
|
||||
break;
|
||||
default:
|
||||
dev_err(&pci->dev, "ACP device not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_acp7x_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
struct acp7x_dev_data *adata;
|
||||
u32 addr;
|
||||
u32 flag;
|
||||
int ret;
|
||||
|
||||
flag = snd_amd_acp_find_config(pci);
|
||||
if (flag)
|
||||
return -ENODEV;
|
||||
/* ACP PCI revision id check for ACP7.x platforms */
|
||||
switch (pci->revision) {
|
||||
case ACP7D_PCI_REV:
|
||||
case ACP7E_PCI_REV:
|
||||
case ACP7F_PCI_REV:
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pci_enable_device(pci)) {
|
||||
dev_err(&pci->dev, "pci_enable_device failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci_request_regions(pci, "AMD ACP7.x audio");
|
||||
if (ret < 0) {
|
||||
dev_err(&pci->dev, "pci_request_regions failed\n");
|
||||
goto disable_pci;
|
||||
}
|
||||
adata = devm_kzalloc(&pci->dev, sizeof(struct acp7x_dev_data),
|
||||
GFP_KERNEL);
|
||||
if (!adata) {
|
||||
ret = -ENOMEM;
|
||||
goto release_regions;
|
||||
}
|
||||
addr = pci_resource_start(pci, 0);
|
||||
adata->acp7x_base = devm_ioremap(&pci->dev, addr,
|
||||
pci_resource_len(pci, 0));
|
||||
if (!adata->acp7x_base) {
|
||||
ret = -ENOMEM;
|
||||
goto release_regions;
|
||||
}
|
||||
adata->addr = addr;
|
||||
adata->reg_range = ACP7X_REG_END - ACP7X_REG_START;
|
||||
adata->acp_rev = pci->revision;
|
||||
pci_set_master(pci);
|
||||
pci_set_drvdata(pci, adata);
|
||||
ret = acp_hw_init_ops(adata, pci);
|
||||
if (ret) {
|
||||
dev_err(&pci->dev, "ACP hw ops init failed\n");
|
||||
goto release_regions;
|
||||
}
|
||||
ret = acp_hw_init(adata, &pci->dev);
|
||||
if (ret)
|
||||
goto release_regions;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pci->dev);
|
||||
pm_runtime_put_noidle(&pci->dev);
|
||||
pm_runtime_allow(&pci->dev);
|
||||
return 0;
|
||||
|
||||
release_regions:
|
||||
pci_release_regions(pci);
|
||||
disable_pci:
|
||||
pci_disable_device(pci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp_suspend(struct device *dev)
|
||||
{
|
||||
return acp_hw_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp_runtime_resume(struct device *dev)
|
||||
{
|
||||
return acp_hw_runtime_resume(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused snd_acp_resume(struct device *dev)
|
||||
{
|
||||
return acp_hw_resume(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops acp7x_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
|
||||
};
|
||||
|
||||
static void snd_acp7x_remove(struct pci_dev *pci)
|
||||
{
|
||||
struct acp7x_dev_data *adata;
|
||||
int ret;
|
||||
|
||||
adata = pci_get_drvdata(pci);
|
||||
ret = acp_hw_deinit(adata, &pci->dev);
|
||||
if (ret)
|
||||
dev_err(&pci->dev, "ACP de-init failed\n");
|
||||
pm_runtime_forbid(&pci->dev);
|
||||
pm_runtime_get_noresume(&pci->dev);
|
||||
pci_release_regions(pci);
|
||||
pci_disable_device(pci);
|
||||
}
|
||||
|
||||
static const struct pci_device_id snd_acp7x_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
|
||||
.class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
|
||||
.class_mask = 0xffffff },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, snd_acp7x_ids);
|
||||
|
||||
static struct pci_driver acp7x_pci_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = snd_acp7x_ids,
|
||||
.probe = snd_acp7x_probe,
|
||||
.remove = snd_acp7x_remove,
|
||||
.driver = {
|
||||
.pm = &acp7x_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
module_pci_driver(acp7x_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
|
||||
MODULE_DESCRIPTION("AMD ACP PCI driver for ACP7.X");
|
||||
MODULE_LICENSE("GPL");
|
||||
Loading…
Reference in New Issue
Block a user