| 1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
| 2 | // |
| 3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
| 4 | // redistributing this file, you may do so under either license. |
| 5 | // |
| 6 | // Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved. |
| 7 | // |
| 8 | // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> |
| 9 | |
| 10 | /* |
| 11 | * Generic PCI interface for ACP device |
| 12 | */ |
| 13 | |
| 14 | #include <linux/delay.h> |
| 15 | #include <linux/interrupt.h> |
| 16 | #include <linux/pci.h> |
| 17 | #include <linux/platform_device.h> |
| 18 | #include <linux/module.h> |
| 19 | #include <linux/pm_runtime.h> |
| 20 | |
| 21 | #include "amd.h" |
| 22 | #include "../mach-config.h" |
| 23 | |
| 24 | #define DRV_NAME "acp_pci" |
| 25 | |
| 26 | #define ACP3x_REG_START 0x1240000 |
| 27 | #define ACP3x_REG_END 0x125C000 |
| 28 | |
| 29 | static irqreturn_t irq_handler(int irq, void *data) |
| 30 | { |
| 31 | struct acp_chip_info *chip = data; |
| 32 | |
| 33 | if (chip && chip->acp_hw_ops && chip->acp_hw_ops->irq) |
| 34 | return chip->acp_hw_ops->irq(irq, chip); |
| 35 | |
| 36 | return IRQ_NONE; |
| 37 | } |
| 38 | static void acp_fill_platform_dev_info(struct platform_device_info *pdevinfo, |
| 39 | struct device *parent, |
| 40 | struct fwnode_handle *fw_node, |
| 41 | char *name, unsigned int id, |
| 42 | const struct resource *res, |
| 43 | unsigned int num_res, |
| 44 | const void *data, |
| 45 | size_t size_data) |
| 46 | { |
| 47 | pdevinfo->name = name; |
| 48 | pdevinfo->id = id; |
| 49 | pdevinfo->parent = parent; |
| 50 | pdevinfo->num_res = num_res; |
| 51 | pdevinfo->res = res; |
| 52 | pdevinfo->data = data; |
| 53 | pdevinfo->size_data = size_data; |
| 54 | pdevinfo->fwnode = fw_node; |
| 55 | } |
| 56 | |
| 57 | static int create_acp_platform_devs(struct pci_dev *pci, struct acp_chip_info *chip, u32 addr) |
| 58 | { |
| 59 | struct platform_device_info pdevinfo; |
| 60 | struct device *parent; |
| 61 | int ret; |
| 62 | |
| 63 | parent = &pci->dev; |
| 64 | |
| 65 | if (chip->is_i2s_config || chip->is_pdm_dev) { |
| 66 | chip->res = devm_kzalloc(dev: &pci->dev, size: sizeof(struct resource), GFP_KERNEL); |
| 67 | if (!chip->res) { |
| 68 | ret = -ENOMEM; |
| 69 | goto err; |
| 70 | } |
| 71 | chip->res->flags = IORESOURCE_MEM; |
| 72 | chip->res->start = addr; |
| 73 | chip->res->end = addr + (ACP3x_REG_END - ACP3x_REG_START); |
| 74 | memset(&pdevinfo, 0, sizeof(pdevinfo)); |
| 75 | } |
| 76 | |
| 77 | memset(&pdevinfo, 0, sizeof(pdevinfo)); |
| 78 | acp_fill_platform_dev_info(pdevinfo: &pdevinfo, parent, NULL, name: chip->name, |
| 79 | id: 0, res: chip->res, num_res: 1, data: chip, size_data: sizeof(*chip)); |
| 80 | |
| 81 | chip->acp_plat_dev = platform_device_register_full(pdevinfo: &pdevinfo); |
| 82 | if (IS_ERR(ptr: chip->acp_plat_dev)) { |
| 83 | dev_err(&pci->dev, |
| 84 | "cannot register %s device\n" , pdevinfo.name); |
| 85 | ret = PTR_ERR(ptr: chip->acp_plat_dev); |
| 86 | goto err; |
| 87 | } |
| 88 | if (chip->is_pdm_dev && chip->is_pdm_config) { |
| 89 | chip->dmic_codec_dev = platform_device_register_data(parent: &pci->dev, |
| 90 | name: "dmic-codec" , |
| 91 | PLATFORM_DEVID_NONE, |
| 92 | NULL, size: 0); |
| 93 | if (IS_ERR(ptr: chip->dmic_codec_dev)) { |
| 94 | dev_err(&pci->dev, "failed to create DMIC device\n" ); |
| 95 | ret = PTR_ERR(ptr: chip->dmic_codec_dev); |
| 96 | goto unregister_acp_plat_dev; |
| 97 | } |
| 98 | } |
| 99 | return 0; |
| 100 | unregister_acp_plat_dev: |
| 101 | platform_device_unregister(chip->acp_plat_dev); |
| 102 | err: |
| 103 | return ret; |
| 104 | } |
| 105 | |
| 106 | static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) |
| 107 | { |
| 108 | struct device *dev = &pci->dev; |
| 109 | struct acp_chip_info *chip; |
| 110 | unsigned int flag, addr; |
| 111 | int ret; |
| 112 | |
| 113 | flag = snd_amd_acp_find_config(pci); |
| 114 | if (flag != FLAG_AMD_LEGACY && flag != FLAG_AMD_LEGACY_ONLY_DMIC) |
| 115 | return -ENODEV; |
| 116 | |
| 117 | chip = devm_kzalloc(dev: &pci->dev, size: sizeof(*chip), GFP_KERNEL); |
| 118 | if (!chip) |
| 119 | return -ENOMEM; |
| 120 | |
| 121 | if (pci_enable_device(dev: pci)) |
| 122 | return dev_err_probe(dev: &pci->dev, err: -ENODEV, |
| 123 | fmt: "pci_enable_device failed\n" ); |
| 124 | |
| 125 | ret = pci_request_regions(pci, "AMD ACP3x audio" ); |
| 126 | if (ret < 0) { |
| 127 | dev_err(&pci->dev, "pci_request_regions failed\n" ); |
| 128 | ret = -ENOMEM; |
| 129 | goto disable_pci; |
| 130 | } |
| 131 | |
| 132 | pci_set_master(dev: pci); |
| 133 | |
| 134 | chip->acp_rev = pci->revision; |
| 135 | switch (pci->revision) { |
| 136 | case 0x01: |
| 137 | chip->name = "acp_asoc_renoir" ; |
| 138 | chip->rsrc = &rn_rsrc; |
| 139 | chip->acp_hw_ops_init = acp31_hw_ops_init; |
| 140 | chip->machines = snd_soc_acpi_amd_acp_machines; |
| 141 | break; |
| 142 | case 0x6f: |
| 143 | chip->name = "acp_asoc_rembrandt" ; |
| 144 | chip->rsrc = &rmb_rsrc; |
| 145 | chip->acp_hw_ops_init = acp6x_hw_ops_init; |
| 146 | chip->machines = snd_soc_acpi_amd_rmb_acp_machines; |
| 147 | break; |
| 148 | case 0x63: |
| 149 | chip->name = "acp_asoc_acp63" ; |
| 150 | chip->rsrc = &acp63_rsrc; |
| 151 | chip->acp_hw_ops_init = acp63_hw_ops_init; |
| 152 | chip->machines = snd_soc_acpi_amd_acp63_acp_machines; |
| 153 | break; |
| 154 | case 0x70: |
| 155 | case 0x71: |
| 156 | case 0x72: |
| 157 | chip->name = "acp_asoc_acp70" ; |
| 158 | chip->rsrc = &acp70_rsrc; |
| 159 | chip->acp_hw_ops_init = acp70_hw_ops_init; |
| 160 | chip->machines = snd_soc_acpi_amd_acp70_acp_machines; |
| 161 | break; |
| 162 | default: |
| 163 | dev_err(dev, "Unsupported device revision:0x%x\n" , pci->revision); |
| 164 | ret = -EINVAL; |
| 165 | goto release_regions; |
| 166 | } |
| 167 | chip->flag = flag; |
| 168 | |
| 169 | addr = pci_resource_start(pci, 0); |
| 170 | chip->base = devm_ioremap(dev: &pci->dev, offset: addr, pci_resource_len(pci, 0)); |
| 171 | if (!chip->base) { |
| 172 | ret = -ENOMEM; |
| 173 | goto release_regions; |
| 174 | } |
| 175 | |
| 176 | chip->addr = addr; |
| 177 | |
| 178 | chip->acp_hw_ops_init(chip); |
| 179 | ret = acp_hw_init(chip); |
| 180 | if (ret) |
| 181 | goto release_regions; |
| 182 | |
| 183 | ret = devm_request_irq(dev, irq: pci->irq, handler: irq_handler, |
| 184 | IRQF_SHARED, devname: "ACP_I2S_IRQ" , dev_id: chip); |
| 185 | if (ret) { |
| 186 | dev_err(&pci->dev, "ACP I2S IRQ request failed %d\n" , ret); |
| 187 | goto de_init; |
| 188 | } |
| 189 | |
| 190 | check_acp_config(pci, chip); |
| 191 | if (!chip->is_pdm_dev && !chip->is_i2s_config) |
| 192 | goto skip_pdev_creation; |
| 193 | |
| 194 | ret = create_acp_platform_devs(pci, chip, addr); |
| 195 | if (ret < 0) { |
| 196 | dev_err(&pci->dev, "ACP platform devices creation failed\n" ); |
| 197 | goto de_init; |
| 198 | } |
| 199 | |
| 200 | chip->chip_pdev = chip->acp_plat_dev; |
| 201 | chip->dev = &chip->acp_plat_dev->dev; |
| 202 | |
| 203 | acp_machine_select(chip); |
| 204 | |
| 205 | INIT_LIST_HEAD(list: &chip->stream_list); |
| 206 | spin_lock_init(&chip->acp_lock); |
| 207 | skip_pdev_creation: |
| 208 | dev_set_drvdata(dev: &pci->dev, data: chip); |
| 209 | pm_runtime_set_autosuspend_delay(dev: &pci->dev, delay: 2000); |
| 210 | pm_runtime_use_autosuspend(dev: &pci->dev); |
| 211 | pm_runtime_put_noidle(dev: &pci->dev); |
| 212 | pm_runtime_allow(dev: &pci->dev); |
| 213 | return ret; |
| 214 | |
| 215 | de_init: |
| 216 | acp_hw_deinit(chip); |
| 217 | release_regions: |
| 218 | pci_release_regions(pci); |
| 219 | disable_pci: |
| 220 | pci_disable_device(dev: pci); |
| 221 | |
| 222 | return ret; |
| 223 | }; |
| 224 | |
| 225 | static int snd_acp_suspend(struct device *dev) |
| 226 | { |
| 227 | struct acp_chip_info *chip; |
| 228 | int ret; |
| 229 | |
| 230 | chip = dev_get_drvdata(dev); |
| 231 | ret = acp_hw_deinit(chip); |
| 232 | if (ret) |
| 233 | dev_err(dev, "ACP de-init failed\n" ); |
| 234 | return ret; |
| 235 | } |
| 236 | |
| 237 | static int snd_acp_resume(struct device *dev) |
| 238 | { |
| 239 | struct acp_chip_info *chip; |
| 240 | int ret; |
| 241 | |
| 242 | chip = dev_get_drvdata(dev); |
| 243 | ret = acp_hw_init(chip); |
| 244 | if (ret) |
| 245 | dev_err(dev, "ACP init failed\n" ); |
| 246 | |
| 247 | ret = acp_hw_en_interrupts(chip); |
| 248 | if (ret) |
| 249 | dev_err(dev, "ACP en-interrupts failed\n" ); |
| 250 | |
| 251 | return ret; |
| 252 | } |
| 253 | |
| 254 | static const struct dev_pm_ops acp_pm_ops = { |
| 255 | RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_resume, NULL) |
| 256 | SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume) |
| 257 | }; |
| 258 | |
| 259 | static void acp_pci_remove(struct pci_dev *pci) |
| 260 | { |
| 261 | struct acp_chip_info *chip; |
| 262 | int ret; |
| 263 | |
| 264 | chip = pci_get_drvdata(pdev: pci); |
| 265 | pm_runtime_forbid(dev: &pci->dev); |
| 266 | pm_runtime_get_noresume(dev: &pci->dev); |
| 267 | if (chip->dmic_codec_dev) |
| 268 | platform_device_unregister(chip->dmic_codec_dev); |
| 269 | if (chip->acp_plat_dev) |
| 270 | platform_device_unregister(chip->acp_plat_dev); |
| 271 | if (chip->mach_dev) |
| 272 | platform_device_unregister(chip->mach_dev); |
| 273 | |
| 274 | ret = acp_hw_deinit(chip); |
| 275 | if (ret) |
| 276 | dev_err(&pci->dev, "ACP de-init failed\n" ); |
| 277 | } |
| 278 | |
| 279 | /* PCI IDs */ |
| 280 | static const struct pci_device_id acp_pci_ids[] = { |
| 281 | { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)}, |
| 282 | { 0, } |
| 283 | }; |
| 284 | MODULE_DEVICE_TABLE(pci, acp_pci_ids); |
| 285 | |
| 286 | /* pci_driver definition */ |
| 287 | static struct pci_driver snd_amd_acp_pci_driver = { |
| 288 | .name = KBUILD_MODNAME, |
| 289 | .id_table = acp_pci_ids, |
| 290 | .probe = acp_pci_probe, |
| 291 | .remove = acp_pci_remove, |
| 292 | .driver = { |
| 293 | .pm = pm_ptr(&acp_pm_ops), |
| 294 | }, |
| 295 | }; |
| 296 | module_pci_driver(snd_amd_acp_pci_driver); |
| 297 | |
| 298 | MODULE_DESCRIPTION("AMD ACP common PCI support" ); |
| 299 | MODULE_LICENSE("Dual BSD/GPL" ); |
| 300 | MODULE_IMPORT_NS("SND_SOC_ACP_COMMON" ); |
| 301 | MODULE_ALIAS(DRV_NAME); |
| 302 | |