1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // AMD ACP PCI Driver 4 // 5 //Copyright 2016 Advanced Micro Devices, Inc. 6 7 #include <linux/pci.h> 8 #include <linux/module.h> 9 #include <linux/io.h> 10 #include <linux/platform_device.h> 11 #include <linux/interrupt.h> 12 #include <linux/pm_runtime.h> 13 #include <linux/delay.h> 14 15 #include "acp3x.h" 16 17 struct acp3x_dev_data { 18 void __iomem *acp3x_base; 19 bool acp3x_audio_mode; 20 struct resource *res; 21 struct platform_device *pdev[ACP3x_DEVS]; 22 u32 pme_en; 23 }; 24 25 static int acp3x_power_on(struct acp3x_dev_data *adata) 26 { 27 void __iomem *acp3x_base = adata->acp3x_base; 28 u32 val; 29 int timeout; 30 31 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); 32 33 if (val == 0) 34 return val; 35 36 if (!((val & ACP_PGFSM_STATUS_MASK) == 37 ACP_POWER_ON_IN_PROGRESS)) 38 rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, 39 acp3x_base + mmACP_PGFSM_CONTROL); 40 timeout = 0; 41 while (++timeout < 500) { 42 val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); 43 if (!val) { 44 /* ACP power On clears PME_EN. 45 * Restore the value to its prior state 46 */ 47 rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN); 48 return 0; 49 } 50 udelay(1); 51 } 52 return -ETIMEDOUT; 53 } 54 55 static int acp3x_reset(void __iomem *acp3x_base) 56 { 57 u32 val; 58 int timeout; 59 60 rv_writel(1, acp3x_base + mmACP_SOFT_RESET); 61 timeout = 0; 62 while (++timeout < 500) { 63 val = rv_readl(acp3x_base + mmACP_SOFT_RESET); 64 if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) 65 break; 66 cpu_relax(); 67 } 68 rv_writel(0, acp3x_base + mmACP_SOFT_RESET); 69 timeout = 0; 70 while (++timeout < 500) { 71 val = rv_readl(acp3x_base + mmACP_SOFT_RESET); 72 if (!val) 73 return 0; 74 cpu_relax(); 75 } 76 return -ETIMEDOUT; 77 } 78 79 static int acp3x_init(struct acp3x_dev_data *adata) 80 { 81 void __iomem *acp3x_base = adata->acp3x_base; 82 int ret; 83 84 /* power on */ 85 ret = acp3x_power_on(adata); 86 if (ret) { 87 pr_err("ACP3x power on failed\n"); 88 return ret; 89 } 90 /* Reset */ 91 ret = acp3x_reset(acp3x_base); 92 if (ret) { 93 pr_err("ACP3x reset failed\n"); 94 return ret; 95 } 96 return 0; 97 } 98 99 static int acp3x_deinit(void __iomem *acp3x_base) 100 { 101 int ret; 102 103 /* Reset */ 104 ret = acp3x_reset(acp3x_base); 105 if (ret) { 106 pr_err("ACP3x reset failed\n"); 107 return ret; 108 } 109 return 0; 110 } 111 112 static int snd_acp3x_probe(struct pci_dev *pci, 113 const struct pci_device_id *pci_id) 114 { 115 struct acp3x_dev_data *adata; 116 struct platform_device_info pdevinfo[ACP3x_DEVS]; 117 unsigned int irqflags; 118 int ret, i; 119 u32 addr, val; 120 121 if (pci_enable_device(pci)) { 122 dev_err(&pci->dev, "pci_enable_device failed\n"); 123 return -ENODEV; 124 } 125 126 ret = pci_request_regions(pci, "AMD ACP3x audio"); 127 if (ret < 0) { 128 dev_err(&pci->dev, "pci_request_regions failed\n"); 129 goto disable_pci; 130 } 131 132 adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data), 133 GFP_KERNEL); 134 if (!adata) { 135 ret = -ENOMEM; 136 goto release_regions; 137 } 138 139 /* check for msi interrupt support */ 140 ret = pci_enable_msi(pci); 141 if (ret) 142 /* msi is not enabled */ 143 irqflags = IRQF_SHARED; 144 else 145 /* msi is enabled */ 146 irqflags = 0; 147 148 addr = pci_resource_start(pci, 0); 149 adata->acp3x_base = devm_ioremap(&pci->dev, addr, 150 pci_resource_len(pci, 0)); 151 if (!adata->acp3x_base) { 152 ret = -ENOMEM; 153 goto disable_msi; 154 } 155 pci_set_master(pci); 156 pci_set_drvdata(pci, adata); 157 /* Save ACP_PME_EN state */ 158 adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN); 159 ret = acp3x_init(adata); 160 if (ret) 161 goto disable_msi; 162 163 val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); 164 switch (val) { 165 case I2S_MODE: 166 adata->res = devm_kzalloc(&pci->dev, 167 sizeof(struct resource) * 4, 168 GFP_KERNEL); 169 if (!adata->res) { 170 ret = -ENOMEM; 171 goto de_init; 172 } 173 174 adata->res[0].name = "acp3x_i2s_iomem"; 175 adata->res[0].flags = IORESOURCE_MEM; 176 adata->res[0].start = addr; 177 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); 178 179 adata->res[1].name = "acp3x_i2s_sp"; 180 adata->res[1].flags = IORESOURCE_MEM; 181 adata->res[1].start = addr + ACP3x_I2STDM_REG_START; 182 adata->res[1].end = addr + ACP3x_I2STDM_REG_END; 183 184 adata->res[2].name = "acp3x_i2s_bt"; 185 adata->res[2].flags = IORESOURCE_MEM; 186 adata->res[2].start = addr + ACP3x_BT_TDM_REG_START; 187 adata->res[2].end = addr + ACP3x_BT_TDM_REG_END; 188 189 adata->res[3].name = "acp3x_i2s_irq"; 190 adata->res[3].flags = IORESOURCE_IRQ; 191 adata->res[3].start = pci->irq; 192 adata->res[3].end = adata->res[3].start; 193 194 adata->acp3x_audio_mode = ACP3x_I2S_MODE; 195 196 memset(&pdevinfo, 0, sizeof(pdevinfo)); 197 pdevinfo[0].name = "acp3x_rv_i2s_dma"; 198 pdevinfo[0].id = 0; 199 pdevinfo[0].parent = &pci->dev; 200 pdevinfo[0].num_res = 4; 201 pdevinfo[0].res = &adata->res[0]; 202 pdevinfo[0].data = &irqflags; 203 pdevinfo[0].size_data = sizeof(irqflags); 204 205 pdevinfo[1].name = "acp3x_i2s_playcap"; 206 pdevinfo[1].id = 0; 207 pdevinfo[1].parent = &pci->dev; 208 pdevinfo[1].num_res = 1; 209 pdevinfo[1].res = &adata->res[1]; 210 211 pdevinfo[2].name = "acp3x_i2s_playcap"; 212 pdevinfo[2].id = 1; 213 pdevinfo[2].parent = &pci->dev; 214 pdevinfo[2].num_res = 1; 215 pdevinfo[2].res = &adata->res[1]; 216 217 pdevinfo[3].name = "acp3x_i2s_playcap"; 218 pdevinfo[3].id = 2; 219 pdevinfo[3].parent = &pci->dev; 220 pdevinfo[3].num_res = 1; 221 pdevinfo[3].res = &adata->res[2]; 222 for (i = 0; i < ACP3x_DEVS; i++) { 223 adata->pdev[i] = 224 platform_device_register_full(&pdevinfo[i]); 225 if (IS_ERR(adata->pdev[i])) { 226 dev_err(&pci->dev, "cannot register %s device\n", 227 pdevinfo[i].name); 228 ret = PTR_ERR(adata->pdev[i]); 229 goto unregister_devs; 230 } 231 } 232 break; 233 default: 234 dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); 235 ret = -ENODEV; 236 goto disable_msi; 237 } 238 pm_runtime_set_autosuspend_delay(&pci->dev, 2000); 239 pm_runtime_use_autosuspend(&pci->dev); 240 pm_runtime_put_noidle(&pci->dev); 241 pm_runtime_allow(&pci->dev); 242 return 0; 243 244 unregister_devs: 245 if (val == I2S_MODE) 246 for (i = 0; i < ACP3x_DEVS; i++) 247 platform_device_unregister(adata->pdev[i]); 248 de_init: 249 if (acp3x_deinit(adata->acp3x_base)) 250 dev_err(&pci->dev, "ACP de-init failed\n"); 251 disable_msi: 252 pci_disable_msi(pci); 253 release_regions: 254 pci_release_regions(pci); 255 disable_pci: 256 pci_disable_device(pci); 257 258 return ret; 259 } 260 261 static int snd_acp3x_suspend(struct device *dev) 262 { 263 int ret; 264 struct acp3x_dev_data *adata; 265 266 adata = dev_get_drvdata(dev); 267 ret = acp3x_deinit(adata->acp3x_base); 268 if (ret) 269 dev_err(dev, "ACP de-init failed\n"); 270 else 271 dev_dbg(dev, "ACP de-initialized\n"); 272 273 return 0; 274 } 275 276 static int snd_acp3x_resume(struct device *dev) 277 { 278 int ret; 279 struct acp3x_dev_data *adata; 280 281 adata = dev_get_drvdata(dev); 282 ret = acp3x_init(adata); 283 if (ret) { 284 dev_err(dev, "ACP init failed\n"); 285 return ret; 286 } 287 return 0; 288 } 289 290 static const struct dev_pm_ops acp3x_pm = { 291 .runtime_suspend = snd_acp3x_suspend, 292 .runtime_resume = snd_acp3x_resume, 293 .resume = snd_acp3x_resume, 294 }; 295 296 static void snd_acp3x_remove(struct pci_dev *pci) 297 { 298 struct acp3x_dev_data *adata; 299 int i, ret; 300 301 adata = pci_get_drvdata(pci); 302 if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) { 303 for (i = 0; i < ACP3x_DEVS; i++) 304 platform_device_unregister(adata->pdev[i]); 305 } 306 ret = acp3x_deinit(adata->acp3x_base); 307 if (ret) 308 dev_err(&pci->dev, "ACP de-init failed\n"); 309 pm_runtime_forbid(&pci->dev); 310 pm_runtime_get_noresume(&pci->dev); 311 pci_disable_msi(pci); 312 pci_release_regions(pci); 313 pci_disable_device(pci); 314 } 315 316 static const struct pci_device_id snd_acp3x_ids[] = { 317 { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2), 318 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, 319 .class_mask = 0xffffff }, 320 { 0, }, 321 }; 322 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids); 323 324 static struct pci_driver acp3x_driver = { 325 .name = KBUILD_MODNAME, 326 .id_table = snd_acp3x_ids, 327 .probe = snd_acp3x_probe, 328 .remove = snd_acp3x_remove, 329 .driver = { 330 .pm = &acp3x_pm, 331 } 332 }; 333 334 module_pci_driver(acp3x_driver); 335 336 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); 337 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); 338 MODULE_DESCRIPTION("AMD ACP3x PCI driver"); 339 MODULE_LICENSE("GPL v2"); 340