1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // AMD Vangogh ACP PCI Driver 4 // 5 // Copyright (C) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved. 6 7 #include <linux/pci.h> 8 #include <linux/module.h> 9 #include <linux/io.h> 10 #include <linux/delay.h> 11 #include <linux/platform_device.h> 12 #include <linux/interrupt.h> 13 #include <linux/pm_runtime.h> 14 15 #include "acp5x.h" 16 #include "../mach-config.h" 17 18 struct acp5x_dev_data { 19 void __iomem *acp5x_base; 20 bool acp5x_audio_mode; 21 struct resource *res; 22 struct platform_device *pdev[ACP5x_DEVS]; 23 }; 24 25 static int acp5x_power_on(void __iomem *acp5x_base) 26 { 27 u32 val; 28 int timeout; 29 30 val = acp_readl(acp5x_base + ACP_PGFSM_STATUS); 31 32 if (val == 0) 33 return val; 34 35 if ((val & ACP_PGFSM_STATUS_MASK) != 36 ACP_POWER_ON_IN_PROGRESS) 37 acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, 38 acp5x_base + ACP_PGFSM_CONTROL); 39 timeout = 0; 40 while (++timeout < 500) { 41 val = acp_readl(acp5x_base + ACP_PGFSM_STATUS); 42 if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON) 43 return 0; 44 udelay(1); 45 } 46 return -ETIMEDOUT; 47 } 48 49 static int acp5x_reset(void __iomem *acp5x_base) 50 { 51 u32 val; 52 int timeout; 53 54 acp_writel(1, acp5x_base + ACP_SOFT_RESET); 55 timeout = 0; 56 while (++timeout < 500) { 57 val = acp_readl(acp5x_base + ACP_SOFT_RESET); 58 if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK) 59 break; 60 cpu_relax(); 61 } 62 acp_writel(0, acp5x_base + ACP_SOFT_RESET); 63 timeout = 0; 64 while (++timeout < 500) { 65 val = acp_readl(acp5x_base + ACP_SOFT_RESET); 66 if (!val) 67 return 0; 68 cpu_relax(); 69 } 70 return -ETIMEDOUT; 71 } 72 73 static void acp5x_enable_interrupts(void __iomem *acp5x_base) 74 { 75 acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB); 76 } 77 78 static void acp5x_disable_interrupts(void __iomem *acp5x_base) 79 { 80 acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base + 81 ACP_EXTERNAL_INTR_STAT); 82 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL); 83 acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB); 84 } 85 86 static int acp5x_init(void __iomem *acp5x_base) 87 { 88 int ret; 89 90 /* power on */ 91 ret = acp5x_power_on(acp5x_base); 92 if (ret) { 93 pr_err("ACP5x power on failed\n"); 94 return ret; 95 } 96 acp_writel(0x01, acp5x_base + ACP_CONTROL); 97 /* Reset */ 98 ret = acp5x_reset(acp5x_base); 99 if (ret) { 100 pr_err("ACP5x reset failed\n"); 101 return ret; 102 } 103 acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL); 104 acp5x_enable_interrupts(acp5x_base); 105 return 0; 106 } 107 108 static int acp5x_deinit(void __iomem *acp5x_base) 109 { 110 int ret; 111 112 acp5x_disable_interrupts(acp5x_base); 113 /* Reset */ 114 ret = acp5x_reset(acp5x_base); 115 if (ret) { 116 pr_err("ACP5x reset failed\n"); 117 return ret; 118 } 119 acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL); 120 acp_writel(0x00, acp5x_base + ACP_CONTROL); 121 return 0; 122 } 123 124 static int snd_acp5x_probe(struct pci_dev *pci, 125 const struct pci_device_id *pci_id) 126 { 127 struct acp5x_dev_data *adata; 128 struct platform_device_info pdevinfo[ACP5x_DEVS]; 129 unsigned int irqflags, flag; 130 int ret, i; 131 u32 addr, val; 132 133 /* 134 * Return if ACP config flag is defined, except when board 135 * supports SOF while it is not being enabled in kernel config. 136 */ 137 flag = snd_amd_acp_find_config(pci); 138 if (flag != FLAG_AMD_LEGACY && 139 (flag != FLAG_AMD_SOF || IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_VANGOGH))) 140 return -ENODEV; 141 142 irqflags = IRQF_SHARED; 143 if (pci->revision != 0x50) 144 return -ENODEV; 145 146 if (pci_enable_device(pci)) { 147 dev_err(&pci->dev, "pci_enable_device failed\n"); 148 return -ENODEV; 149 } 150 151 ret = pci_request_regions(pci, "AMD ACP5x audio"); 152 if (ret < 0) { 153 dev_err(&pci->dev, "pci_request_regions failed\n"); 154 goto disable_pci; 155 } 156 157 adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data), 158 GFP_KERNEL); 159 if (!adata) { 160 ret = -ENOMEM; 161 goto release_regions; 162 } 163 addr = pci_resource_start(pci, 0); 164 adata->acp5x_base = devm_ioremap(&pci->dev, addr, 165 pci_resource_len(pci, 0)); 166 if (!adata->acp5x_base) { 167 ret = -ENOMEM; 168 goto release_regions; 169 } 170 pci_set_master(pci); 171 pci_set_drvdata(pci, adata); 172 ret = acp5x_init(adata->acp5x_base); 173 if (ret) 174 goto release_regions; 175 176 val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG); 177 switch (val) { 178 case I2S_MODE: 179 adata->res = devm_kzalloc(&pci->dev, 180 sizeof(struct resource) * ACP5x_RES, 181 GFP_KERNEL); 182 if (!adata->res) { 183 ret = -ENOMEM; 184 goto de_init; 185 } 186 187 adata->res[0].name = "acp5x_i2s_iomem"; 188 adata->res[0].flags = IORESOURCE_MEM; 189 adata->res[0].start = addr; 190 adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START); 191 192 adata->res[1].name = "acp5x_i2s_sp"; 193 adata->res[1].flags = IORESOURCE_MEM; 194 adata->res[1].start = addr + ACP5x_I2STDM_REG_START; 195 adata->res[1].end = addr + ACP5x_I2STDM_REG_END; 196 197 adata->res[2].name = "acp5x_i2s_hs"; 198 adata->res[2].flags = IORESOURCE_MEM; 199 adata->res[2].start = addr + ACP5x_HS_TDM_REG_START; 200 adata->res[2].end = addr + ACP5x_HS_TDM_REG_END; 201 202 adata->res[3].name = "acp5x_i2s_irq"; 203 adata->res[3].flags = IORESOURCE_IRQ; 204 adata->res[3].start = pci->irq; 205 adata->res[3].end = adata->res[3].start; 206 207 adata->acp5x_audio_mode = ACP5x_I2S_MODE; 208 209 memset(&pdevinfo, 0, sizeof(pdevinfo)); 210 pdevinfo[0].name = "acp5x_i2s_dma"; 211 pdevinfo[0].id = 0; 212 pdevinfo[0].parent = &pci->dev; 213 pdevinfo[0].num_res = 4; 214 pdevinfo[0].res = &adata->res[0]; 215 pdevinfo[0].data = &irqflags; 216 pdevinfo[0].size_data = sizeof(irqflags); 217 218 pdevinfo[1].name = "acp5x_i2s_playcap"; 219 pdevinfo[1].id = 0; 220 pdevinfo[1].parent = &pci->dev; 221 pdevinfo[1].num_res = 1; 222 pdevinfo[1].res = &adata->res[1]; 223 224 pdevinfo[2].name = "acp5x_i2s_playcap"; 225 pdevinfo[2].id = 1; 226 pdevinfo[2].parent = &pci->dev; 227 pdevinfo[2].num_res = 1; 228 pdevinfo[2].res = &adata->res[2]; 229 230 pdevinfo[3].name = "acp5x_mach"; 231 pdevinfo[3].id = 0; 232 pdevinfo[3].parent = &pci->dev; 233 for (i = 0; i < ACP5x_DEVS; i++) { 234 adata->pdev[i] = 235 platform_device_register_full(&pdevinfo[i]); 236 if (IS_ERR(adata->pdev[i])) { 237 dev_err(&pci->dev, "cannot register %s device\n", 238 pdevinfo[i].name); 239 ret = PTR_ERR(adata->pdev[i]); 240 goto unregister_devs; 241 } 242 } 243 break; 244 default: 245 dev_info(&pci->dev, "ACP audio mode : %d\n", val); 246 } 247 pm_runtime_set_autosuspend_delay(&pci->dev, 2000); 248 pm_runtime_use_autosuspend(&pci->dev); 249 pm_runtime_put_noidle(&pci->dev); 250 pm_runtime_allow(&pci->dev); 251 return 0; 252 253 unregister_devs: 254 for (--i; i >= 0; i--) 255 platform_device_unregister(adata->pdev[i]); 256 de_init: 257 if (acp5x_deinit(adata->acp5x_base)) 258 dev_err(&pci->dev, "ACP de-init failed\n"); 259 release_regions: 260 pci_release_regions(pci); 261 disable_pci: 262 pci_disable_device(pci); 263 264 return ret; 265 } 266 267 static int snd_acp5x_suspend(struct device *dev) 268 { 269 int ret; 270 struct acp5x_dev_data *adata; 271 272 adata = dev_get_drvdata(dev); 273 ret = acp5x_deinit(adata->acp5x_base); 274 if (ret) 275 dev_err(dev, "ACP de-init failed\n"); 276 else 277 dev_dbg(dev, "ACP de-initialized\n"); 278 279 return ret; 280 } 281 282 static int snd_acp5x_resume(struct device *dev) 283 { 284 int ret; 285 struct acp5x_dev_data *adata; 286 287 adata = dev_get_drvdata(dev); 288 ret = acp5x_init(adata->acp5x_base); 289 if (ret) { 290 dev_err(dev, "ACP init failed\n"); 291 return ret; 292 } 293 return 0; 294 } 295 296 static const struct dev_pm_ops acp5x_pm = { 297 RUNTIME_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume, NULL) 298 SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume) 299 }; 300 301 static void snd_acp5x_remove(struct pci_dev *pci) 302 { 303 struct acp5x_dev_data *adata; 304 int i, ret; 305 306 adata = pci_get_drvdata(pci); 307 if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) { 308 for (i = 0; i < ACP5x_DEVS; i++) 309 platform_device_unregister(adata->pdev[i]); 310 } 311 ret = acp5x_deinit(adata->acp5x_base); 312 if (ret) 313 dev_err(&pci->dev, "ACP de-init failed\n"); 314 pm_runtime_forbid(&pci->dev); 315 pm_runtime_get_noresume(&pci->dev); 316 pci_release_regions(pci); 317 pci_disable_device(pci); 318 } 319 320 static const struct pci_device_id snd_acp5x_ids[] = { 321 { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID), 322 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, 323 .class_mask = 0xffffff }, 324 { 0, }, 325 }; 326 MODULE_DEVICE_TABLE(pci, snd_acp5x_ids); 327 328 static struct pci_driver acp5x_driver = { 329 .name = KBUILD_MODNAME, 330 .id_table = snd_acp5x_ids, 331 .probe = snd_acp5x_probe, 332 .remove = snd_acp5x_remove, 333 .driver = { 334 .pm = pm_ptr(&acp5x_pm), 335 } 336 }; 337 338 module_pci_driver(acp5x_driver); 339 340 MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 341 MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver"); 342 MODULE_LICENSE("GPL v2"); 343