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