228 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * AMD RPL ACP PCI Driver4 *5 * Copyright 2022 Advanced Micro Devices, Inc.6 */7 8#include <linux/pci.h>9#include <linux/module.h>10#include <linux/io.h>11#include <linux/delay.h>12#include <linux/platform_device.h>13#include <linux/pm_runtime.h>14 15#include "rpl_acp6x.h"16 17struct rpl_dev_data {18 void __iomem *acp6x_base;19};20 21static int rpl_power_on(void __iomem *acp_base)22{23 u32 val;24 int timeout;25 26 val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);27 28 if (!val)29 return val;30 31 if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)32 rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);33 timeout = 0;34 while (++timeout < 500) {35 val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);36 if (!val)37 return 0;38 udelay(1);39 }40 return -ETIMEDOUT;41}42 43static int rpl_reset(void __iomem *acp_base)44{45 u32 val;46 int timeout;47 48 rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);49 timeout = 0;50 while (++timeout < 500) {51 val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);52 if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)53 break;54 cpu_relax();55 }56 rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);57 timeout = 0;58 while (++timeout < 500) {59 val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);60 if (!val)61 return 0;62 cpu_relax();63 }64 return -ETIMEDOUT;65}66 67static int rpl_init(void __iomem *acp_base)68{69 int ret;70 71 /* power on */72 ret = rpl_power_on(acp_base);73 if (ret) {74 pr_err("ACP power on failed\n");75 return ret;76 }77 rpl_acp_writel(0x01, acp_base + ACP_CONTROL);78 /* Reset */79 ret = rpl_reset(acp_base);80 if (ret) {81 pr_err("ACP reset failed\n");82 return ret;83 }84 rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);85 return 0;86}87 88static int rpl_deinit(void __iomem *acp_base)89{90 int ret;91 92 /* Reset */93 ret = rpl_reset(acp_base);94 if (ret) {95 pr_err("ACP reset failed\n");96 return ret;97 }98 rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);99 rpl_acp_writel(0x00, acp_base + ACP_CONTROL);100 return 0;101}102 103static int snd_rpl_probe(struct pci_dev *pci,104 const struct pci_device_id *pci_id)105{106 struct rpl_dev_data *adata;107 u32 addr;108 int ret;109 110 /* RPL device check */111 switch (pci->revision) {112 case 0x62:113 break;114 default:115 dev_dbg(&pci->dev, "acp6x pci device not found\n");116 return -ENODEV;117 }118 if (pci_enable_device(pci)) {119 dev_err(&pci->dev, "pci_enable_device failed\n");120 return -ENODEV;121 }122 123 ret = pci_request_regions(pci, "AMD ACP6x audio");124 if (ret < 0) {125 dev_err(&pci->dev, "pci_request_regions failed\n");126 goto disable_pci;127 }128 129 adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),130 GFP_KERNEL);131 if (!adata) {132 ret = -ENOMEM;133 goto release_regions;134 }135 136 addr = pci_resource_start(pci, 0);137 adata->acp6x_base = devm_ioremap(&pci->dev, addr,138 pci_resource_len(pci, 0));139 if (!adata->acp6x_base) {140 ret = -ENOMEM;141 goto release_regions;142 }143 pci_set_master(pci);144 pci_set_drvdata(pci, adata);145 ret = rpl_init(adata->acp6x_base);146 if (ret)147 goto release_regions;148 pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);149 pm_runtime_use_autosuspend(&pci->dev);150 pm_runtime_put_noidle(&pci->dev);151 pm_runtime_allow(&pci->dev);152 153 return 0;154release_regions:155 pci_release_regions(pci);156disable_pci:157 pci_disable_device(pci);158 159 return ret;160}161 162static int __maybe_unused snd_rpl_suspend(struct device *dev)163{164 struct rpl_dev_data *adata;165 int ret;166 167 adata = dev_get_drvdata(dev);168 ret = rpl_deinit(adata->acp6x_base);169 if (ret)170 dev_err(dev, "ACP de-init failed\n");171 return ret;172}173 174static int __maybe_unused snd_rpl_resume(struct device *dev)175{176 struct rpl_dev_data *adata;177 int ret;178 179 adata = dev_get_drvdata(dev);180 ret = rpl_init(adata->acp6x_base);181 if (ret)182 dev_err(dev, "ACP init failed\n");183 return ret;184}185 186static const struct dev_pm_ops rpl_pm = {187 SET_RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)188 SET_SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)189};190 191static void snd_rpl_remove(struct pci_dev *pci)192{193 struct rpl_dev_data *adata;194 int ret;195 196 adata = pci_get_drvdata(pci);197 ret = rpl_deinit(adata->acp6x_base);198 if (ret)199 dev_err(&pci->dev, "ACP de-init failed\n");200 pm_runtime_forbid(&pci->dev);201 pm_runtime_get_noresume(&pci->dev);202 pci_release_regions(pci);203 pci_disable_device(pci);204}205 206static const struct pci_device_id snd_rpl_ids[] = {207 { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),208 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,209 .class_mask = 0xffffff },210 { 0, },211};212MODULE_DEVICE_TABLE(pci, snd_rpl_ids);213 214static struct pci_driver rpl_acp6x_driver = {215 .name = KBUILD_MODNAME,216 .id_table = snd_rpl_ids,217 .probe = snd_rpl_probe,218 .remove = snd_rpl_remove,219 .driver = {220 .pm = &rpl_pm,221 }222};223 224module_pci_driver(rpl_acp6x_driver);225 226MODULE_DESCRIPTION("AMD ACP RPL PCI driver");227MODULE_LICENSE("GPL v2");228