Vijendar Mukunda | d5a932e | 2018-11-15 21:43:50 +0530 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | // |
| 3 | // AMD ACP PCI Driver |
| 4 | // |
| 5 | //Copyright 2016 Advanced Micro Devices, Inc. |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 6 | |
| 7 | #include <linux/pci.h> |
| 8 | #include <linux/module.h> |
| 9 | #include <linux/io.h> |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 10 | #include <linux/platform_device.h> |
| 11 | #include <linux/interrupt.h> |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 12 | |
| 13 | #include "acp3x.h" |
| 14 | |
| 15 | struct acp3x_dev_data { |
| 16 | void __iomem *acp3x_base; |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 17 | bool acp3x_audio_mode; |
| 18 | struct resource *res; |
| 19 | struct platform_device *pdev; |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 20 | }; |
| 21 | |
| 22 | static int snd_acp3x_probe(struct pci_dev *pci, |
| 23 | const struct pci_device_id *pci_id) |
| 24 | { |
| 25 | int ret; |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 26 | u32 addr, val; |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 27 | struct acp3x_dev_data *adata; |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 28 | struct platform_device_info pdevinfo; |
| 29 | unsigned int irqflags; |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 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 | |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 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 | |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 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); |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 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); |
Dan Carpenter | 83b12c2 | 2018-11-26 11:13:07 +0300 | [diff] [blame] | 100 | if (IS_ERR(adata->pdev)) { |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 101 | dev_err(&pci->dev, "cannot register %s device\n", |
| 102 | pdevinfo.name); |
Dan Carpenter | 83b12c2 | 2018-11-26 11:13:07 +0300 | [diff] [blame] | 103 | ret = PTR_ERR(adata->pdev); |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 104 | goto unmap_mmio; |
| 105 | } |
| 106 | break; |
| 107 | default: |
Colin Ian King | 00347e4 | 2018-11-16 13:39:43 +0000 | [diff] [blame] | 108 | dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 109 | ret = -ENODEV; |
| 110 | goto unmap_mmio; |
| 111 | } |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 112 | return 0; |
| 113 | |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 114 | unmap_mmio: |
| 115 | pci_disable_msi(pci); |
| 116 | iounmap(adata->acp3x_base); |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 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 | |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 129 | platform_device_unregister(adata->pdev); |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 130 | iounmap(adata->acp3x_base); |
Vijendar Mukunda | 7894a7e | 2018-11-12 11:04:54 +0530 | [diff] [blame] | 131 | |
| 132 | pci_disable_msi(pci); |
Maruthi Srinivas Bayyavarapu | e30d912 | 2018-11-12 11:04:53 +0530 | [diff] [blame] | 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"); |