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 13 #include "acp3x.h" 14 15 struct acp3x_dev_data { 16 void __iomem *acp3x_base; 17 bool acp3x_audio_mode; 18 struct resource *res; 19 struct platform_device *pdev; 20 }; 21 22 static int snd_acp3x_probe(struct pci_dev *pci, 23 const struct pci_device_id *pci_id) 24 { 25 int ret; 26 u32 addr, val; 27 struct acp3x_dev_data *adata; 28 struct platform_device_info pdevinfo; 29 unsigned int irqflags; 30 31 if (pci_enable_device(pci)) { 32 dev_err(&pci->dev, "pci_enable_device failed\n"); 33 return -ENODEV; 34 } 35 36 ret = pci_request_regions(pci, "AMD ACP3x audio"); 37 if (ret < 0) { 38 dev_err(&pci->dev, "pci_request_regions failed\n"); 39 goto disable_pci; 40 } 41 42 adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data), 43 GFP_KERNEL); 44 if (!adata) { 45 ret = -ENOMEM; 46 goto release_regions; 47 } 48 49 /* check for msi interrupt support */ 50 ret = pci_enable_msi(pci); 51 if (ret) 52 /* msi is not enabled */ 53 irqflags = IRQF_SHARED; 54 else 55 /* msi is enabled */ 56 irqflags = 0; 57 58 addr = pci_resource_start(pci, 0); 59 adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0)); 60 if (!adata->acp3x_base) { 61 ret = -ENOMEM; 62 goto release_regions; 63 } 64 pci_set_master(pci); 65 pci_set_drvdata(pci, adata); 66 67 val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); 68 switch (val) { 69 case I2S_MODE: 70 adata->res = devm_kzalloc(&pci->dev, 71 sizeof(struct resource) * 2, 72 GFP_KERNEL); 73 if (!adata->res) { 74 ret = -ENOMEM; 75 goto unmap_mmio; 76 } 77 78 adata->res[0].name = "acp3x_i2s_iomem"; 79 adata->res[0].flags = IORESOURCE_MEM; 80 adata->res[0].start = addr; 81 adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); 82 83 adata->res[1].name = "acp3x_i2s_irq"; 84 adata->res[1].flags = IORESOURCE_IRQ; 85 adata->res[1].start = pci->irq; 86 adata->res[1].end = pci->irq; 87 88 adata->acp3x_audio_mode = ACP3x_I2S_MODE; 89 90 memset(&pdevinfo, 0, sizeof(pdevinfo)); 91 pdevinfo.name = "acp3x_rv_i2s"; 92 pdevinfo.id = 0; 93 pdevinfo.parent = &pci->dev; 94 pdevinfo.num_res = 2; 95 pdevinfo.res = adata->res; 96 pdevinfo.data = &irqflags; 97 pdevinfo.size_data = sizeof(irqflags); 98 99 adata->pdev = platform_device_register_full(&pdevinfo); 100 if (IS_ERR(adata->pdev)) { 101 dev_err(&pci->dev, "cannot register %s device\n", 102 pdevinfo.name); 103 ret = PTR_ERR(adata->pdev); 104 goto unmap_mmio; 105 } 106 break; 107 default: 108 dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); 109 ret = -ENODEV; 110 goto unmap_mmio; 111 } 112 return 0; 113 114 unmap_mmio: 115 pci_disable_msi(pci); 116 iounmap(adata->acp3x_base); 117 release_regions: 118 pci_release_regions(pci); 119 disable_pci: 120 pci_disable_device(pci); 121 122 return ret; 123 } 124 125 static void snd_acp3x_remove(struct pci_dev *pci) 126 { 127 struct acp3x_dev_data *adata = pci_get_drvdata(pci); 128 129 platform_device_unregister(adata->pdev); 130 iounmap(adata->acp3x_base); 131 132 pci_disable_msi(pci); 133 pci_release_regions(pci); 134 pci_disable_device(pci); 135 } 136 137 static const struct pci_device_id snd_acp3x_ids[] = { 138 { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2), 139 .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, 140 .class_mask = 0xffffff }, 141 { 0, }, 142 }; 143 MODULE_DEVICE_TABLE(pci, snd_acp3x_ids); 144 145 static struct pci_driver acp3x_driver = { 146 .name = KBUILD_MODNAME, 147 .id_table = snd_acp3x_ids, 148 .probe = snd_acp3x_probe, 149 .remove = snd_acp3x_remove, 150 }; 151 152 module_pci_driver(acp3x_driver); 153 154 MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); 155 MODULE_DESCRIPTION("AMD ACP3x PCI driver"); 156 MODULE_LICENSE("GPL v2"); 157