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 /* Raven device detection */ 122 if (pci->revision != 0x00) 123 return -ENODEV; 124 125 if (pci_enable_device(pci)) { 126 dev_err(&pci->dev, "pci_enable_device failed\n"); 127 return -ENODEV; 128 } 129 130 ret = pci_request_regions(pci, "AMD ACP3x audio"); 131 if (ret < 0) { 132 dev_err(&pci->dev, "pci_request_regions failed\n"); 133 goto disable_pci; 134 } 135 136 adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data), 137 GFP_KERNEL); 138 if (!adata) { 139 ret = -ENOMEM; 140 goto release_regions; 141 } 142 143 irqflags = IRQF_SHARED; 144 145 addr = pci_resource_start(pci, 0); 146 adata->acp3x_base = devm_ioremap(&pci->dev, addr, 147 pci_resource_len(pci, 0)); 148 if (!adata->acp3x_base) { 149 ret = -ENOMEM; 150 goto release_regions; 151 } 152 pci_set_master(pci); 153 pci_set_drvdata(pci, adata); 154 /* Save ACP_PME_EN state */ 155 adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN); 156 ret = acp3x_init(adata); 157 if (ret) 158 goto release_regions; 159 160 val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); 161 switch (val) { 162 case I2S_MODE: 163 adata->res = devm_kzalloc(&pci->dev, 164 sizeof(struct resource) * 4, 165 GFP_KERNEL); 166 if (!adata->res) { 167 ret = -ENOMEM; 168 goto de_init; 169 } 170 171 adata->res[0].name = "acp3x_i2s_iomem"; 172 adata->res[0].flags = IORESOURCE_MEM; 173 adata->res[0].start = addr; 174 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); 175 176 adata->res[1].name = "acp3x_i2s_sp"; 177 adata->res[1].flags = IORESOURCE_MEM; 178 adata->res[1].start = addr + ACP3x_I2STDM_REG_START; 179 adata->res[1].end = addr + ACP3x_I2STDM_REG_END; 180 181 adata->res[2].name = "acp3x_i2s_bt"; 182 adata->res[2].flags = IORESOURCE_MEM; 183 adata->res[2].start = addr + ACP3x_BT_TDM_REG_START; 184 adata->res[2].end = addr + ACP3x_BT_TDM_REG_END; 185 186 adata->res[3].name = "acp3x_i2s_irq"; 187 adata->res[3].flags = IORESOURCE_IRQ; 188 adata->res[3].start = pci->irq; 189 adata->res[3].end = adata->res[3].start; 190 191 adata->acp3x_audio_mode = ACP3x_I2S_MODE; 192 193 memset(&pdevinfo, 0, sizeof(pdevinfo)); 194 pdevinfo[0].name = "acp3x_rv_i2s_dma"; 195 pdevinfo[0].id = 0; 196 pdevinfo[0].parent = &pci->dev; 197 pdevinfo[0].num_res = 4; 198 pdevinfo[0].res = &adata->res[0]; 199 pdevinfo[0].data = &irqflags; 200 pdevinfo[0].size_data = sizeof(irqflags); 201 202 pdevinfo[1].name = "acp3x_i2s_playcap"; 203 pdevinfo[1].id = 0; 204 pdevinfo[1].parent = &pci->dev; 205 pdevinfo[1].num_res = 1; 206 pdevinfo[1].res = &adata->res[1]; 207 208 pdevinfo[2].name = "acp3x_i2s_playcap"; 209 pdevinfo[2].id = 1; 210 pdevinfo[2].parent = &pci->dev; 211 pdevinfo[2].num_res = 1; 212 pdevinfo[2].res = &adata->res[1]; 213 214 pdevinfo[3].name = "acp3x_i2s_playcap"; 215 pdevinfo[3].id = 2; 216 pdevinfo[3].parent = &pci->dev; 217 pdevinfo[3].num_res = 1; 218 pdevinfo[3].res = &adata->res[2]; 219 for (i = 0; i < ACP3x_DEVS; i++) { 220 adata->pdev[i] = 221 platform_device_register_full(&pdevinfo[i]); 222 if (IS_ERR(adata->pdev[i])) { 223 dev_err(&pci->dev, "cannot register %s device\n", 224 pdevinfo[i].name); 225 ret = PTR_ERR(adata->pdev[i]); 226 goto unregister_devs; 227 } 228 } 229 break; 230 default: 231 dev_info(&pci->dev, "ACP audio mode : %d\n", val); 232 break; 233 } 234 pm_runtime_set_autosuspend_delay(&pci->dev, 2000); 235 pm_runtime_use_autosuspend(&pci->dev); 236 pm_runtime_put_noidle(&pci->dev); 237 pm_runtime_allow(&pci->dev); 238 return 0; 239 240 unregister_devs: 241 if (val == I2S_MODE) 242 for (i = 0; i < ACP3x_DEVS; i++) 243 platform_device_unregister(adata->pdev[i]); 244 de_init: 245 if (acp3x_deinit(adata->acp3x_base)) 246 dev_err(&pci->dev, "ACP de-init failed\n"); 247 release_regions: 248 pci_release_regions(pci); 249 disable_pci: 250 pci_disable_device(pci); 251 252 return ret; 253 } 254 255 static int snd_acp3x_suspend(struct device *dev) 256 { 257 int ret; 258 struct acp3x_dev_data *adata; 259 260 adata = dev_get_drvdata(dev); 261 ret = acp3x_deinit(adata->acp3x_base); 262 if (ret) 263 dev_err(dev, "ACP de-init failed\n"); 264 else 265 dev_dbg(dev, "ACP de-initialized\n"); 266 267 return 0; 268 } 269 270 static int snd_acp3x_resume(struct device *dev) 271 { 272 int ret; 273 struct acp3x_dev_data *adata; 274 275 adata = dev_get_drvdata(dev); 276 ret = acp3x_init(adata); 277 if (ret) { 278 dev_err(dev, "ACP init failed\n"); 279 return ret; 280 } 281 return 0; 282 } 283 284 static const struct dev_pm_ops acp3x_pm = { 285 .runtime_suspend = snd_acp3x_suspend, 286 .runtime_resume = snd_acp3x_resume, 287 .resume = snd_acp3x_resume, 288 }; 289 290 static void snd_acp3x_remove(struct pci_dev *pci) 291 { 292 struct acp3x_dev_data *adata; 293 int i, ret; 294 295 adata = pci_get_drvdata(pci); 296 if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) { 297 for (i = 0; i < ACP3x_DEVS; i++) 298 platform_device_unregister(adata->pdev[i]); 299 } 300 ret = acp3x_deinit(adata->acp3x_base); 301 if (ret) 302 dev_err(&pci->dev, "ACP de-init failed\n"); 303 pm_runtime_forbid(&pci->dev); 304 pm_runtime_get_noresume(&pci->dev); 305 pci_release_regions(pci); 306 pci_disable_device(pci); 307 } 308 309 static const struct pci_device_id snd_acp3x_ids[] = { 310 { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2), 311 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, 312 .class_mask = 0xffffff }, 313 { 0, }, 314 }; 315 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids); 316 317 static struct pci_driver acp3x_driver = { 318 .name = KBUILD_MODNAME, 319 .id_table = snd_acp3x_ids, 320 .probe = snd_acp3x_probe, 321 .remove = snd_acp3x_remove, 322 .driver = { 323 .pm = &acp3x_pm, 324 } 325 }; 326 327 module_pci_driver(acp3x_driver); 328 329 MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com"); 330 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); 331 MODULE_DESCRIPTION("AMD ACP3x PCI driver"); 332 MODULE_LICENSE("GPL v2"); 333