Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 //
0003 // AMD Vangogh ACP PCI Driver
0004 //
0005 // Copyright (C) 2021 Advanced Micro Devices, Inc. All rights reserved.
0006 
0007 #include <linux/pci.h>
0008 #include <linux/module.h>
0009 #include <linux/io.h>
0010 #include <linux/delay.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/pm_runtime.h>
0014 
0015 #include "acp5x.h"
0016 
0017 struct acp5x_dev_data {
0018     void __iomem *acp5x_base;
0019     bool acp5x_audio_mode;
0020     struct resource *res;
0021     struct platform_device *pdev[ACP5x_DEVS];
0022 };
0023 
0024 static int acp5x_power_on(void __iomem *acp5x_base)
0025 {
0026     u32 val;
0027     int timeout;
0028 
0029     val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
0030 
0031     if (val == 0)
0032         return val;
0033 
0034     if ((val & ACP_PGFSM_STATUS_MASK) !=
0035                 ACP_POWER_ON_IN_PROGRESS)
0036         acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
0037                acp5x_base + ACP_PGFSM_CONTROL);
0038     timeout = 0;
0039     while (++timeout < 500) {
0040         val = acp_readl(acp5x_base + ACP_PGFSM_STATUS);
0041         if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_ON)
0042             return 0;
0043         udelay(1);
0044     }
0045     return -ETIMEDOUT;
0046 }
0047 
0048 static int acp5x_reset(void __iomem *acp5x_base)
0049 {
0050     u32 val;
0051     int timeout;
0052 
0053     acp_writel(1, acp5x_base + ACP_SOFT_RESET);
0054     timeout = 0;
0055     while (++timeout < 500) {
0056         val = acp_readl(acp5x_base + ACP_SOFT_RESET);
0057         if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
0058             break;
0059         cpu_relax();
0060     }
0061     acp_writel(0, acp5x_base + ACP_SOFT_RESET);
0062     timeout = 0;
0063     while (++timeout < 500) {
0064         val = acp_readl(acp5x_base + ACP_SOFT_RESET);
0065         if (!val)
0066             return 0;
0067         cpu_relax();
0068     }
0069     return -ETIMEDOUT;
0070 }
0071 
0072 static void acp5x_enable_interrupts(void __iomem *acp5x_base)
0073 {
0074     acp_writel(0x01, acp5x_base + ACP_EXTERNAL_INTR_ENB);
0075 }
0076 
0077 static void acp5x_disable_interrupts(void __iomem *acp5x_base)
0078 {
0079     acp_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp5x_base +
0080            ACP_EXTERNAL_INTR_STAT);
0081     acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_CNTL);
0082     acp_writel(0x00, acp5x_base + ACP_EXTERNAL_INTR_ENB);
0083 }
0084 
0085 static int acp5x_init(void __iomem *acp5x_base)
0086 {
0087     int ret;
0088 
0089     /* power on */
0090     ret = acp5x_power_on(acp5x_base);
0091     if (ret) {
0092         pr_err("ACP5x power on failed\n");
0093         return ret;
0094     }
0095     acp_writel(0x01, acp5x_base + ACP_CONTROL);
0096     /* Reset */
0097     ret = acp5x_reset(acp5x_base);
0098     if (ret) {
0099         pr_err("ACP5x reset failed\n");
0100         return ret;
0101     }
0102     acp_writel(0x03, acp5x_base + ACP_CLKMUX_SEL);
0103     acp5x_enable_interrupts(acp5x_base);
0104     return 0;
0105 }
0106 
0107 static int acp5x_deinit(void __iomem *acp5x_base)
0108 {
0109     int ret;
0110 
0111     acp5x_disable_interrupts(acp5x_base);
0112     /* Reset */
0113     ret = acp5x_reset(acp5x_base);
0114     if (ret) {
0115         pr_err("ACP5x reset failed\n");
0116         return ret;
0117     }
0118     acp_writel(0x00, acp5x_base + ACP_CLKMUX_SEL);
0119     acp_writel(0x00, acp5x_base + ACP_CONTROL);
0120     return 0;
0121 }
0122 
0123 static int snd_acp5x_probe(struct pci_dev *pci,
0124                const struct pci_device_id *pci_id)
0125 {
0126     struct acp5x_dev_data *adata;
0127     struct platform_device_info pdevinfo[ACP5x_DEVS];
0128     unsigned int irqflags;
0129     int ret, i;
0130     u32 addr, val;
0131 
0132     irqflags = IRQF_SHARED;
0133     if (pci->revision != 0x50)
0134         return -ENODEV;
0135 
0136     if (pci_enable_device(pci)) {
0137         dev_err(&pci->dev, "pci_enable_device failed\n");
0138         return -ENODEV;
0139     }
0140 
0141     ret = pci_request_regions(pci, "AMD ACP5x audio");
0142     if (ret < 0) {
0143         dev_err(&pci->dev, "pci_request_regions failed\n");
0144         goto disable_pci;
0145     }
0146 
0147     adata = devm_kzalloc(&pci->dev, sizeof(struct acp5x_dev_data),
0148                  GFP_KERNEL);
0149     if (!adata) {
0150         ret = -ENOMEM;
0151         goto release_regions;
0152     }
0153     addr = pci_resource_start(pci, 0);
0154     adata->acp5x_base = devm_ioremap(&pci->dev, addr,
0155                      pci_resource_len(pci, 0));
0156     if (!adata->acp5x_base) {
0157         ret = -ENOMEM;
0158         goto release_regions;
0159     }
0160     pci_set_master(pci);
0161     pci_set_drvdata(pci, adata);
0162     ret = acp5x_init(adata->acp5x_base);
0163     if (ret)
0164         goto release_regions;
0165 
0166     val = acp_readl(adata->acp5x_base + ACP_PIN_CONFIG);
0167     switch (val) {
0168     case I2S_MODE:
0169         adata->res = devm_kzalloc(&pci->dev,
0170                       sizeof(struct resource) * ACP5x_RES,
0171                       GFP_KERNEL);
0172         if (!adata->res) {
0173             ret = -ENOMEM;
0174             goto de_init;
0175         }
0176 
0177         adata->res[0].name = "acp5x_i2s_iomem";
0178         adata->res[0].flags = IORESOURCE_MEM;
0179         adata->res[0].start = addr;
0180         adata->res[0].end = addr + (ACP5x_REG_END - ACP5x_REG_START);
0181 
0182         adata->res[1].name = "acp5x_i2s_sp";
0183         adata->res[1].flags = IORESOURCE_MEM;
0184         adata->res[1].start = addr + ACP5x_I2STDM_REG_START;
0185         adata->res[1].end = addr + ACP5x_I2STDM_REG_END;
0186 
0187         adata->res[2].name = "acp5x_i2s_hs";
0188         adata->res[2].flags = IORESOURCE_MEM;
0189         adata->res[2].start = addr + ACP5x_HS_TDM_REG_START;
0190         adata->res[2].end = addr + ACP5x_HS_TDM_REG_END;
0191 
0192         adata->res[3].name = "acp5x_i2s_irq";
0193         adata->res[3].flags = IORESOURCE_IRQ;
0194         adata->res[3].start = pci->irq;
0195         adata->res[3].end = adata->res[3].start;
0196 
0197         adata->acp5x_audio_mode = ACP5x_I2S_MODE;
0198 
0199         memset(&pdevinfo, 0, sizeof(pdevinfo));
0200         pdevinfo[0].name = "acp5x_i2s_dma";
0201         pdevinfo[0].id = 0;
0202         pdevinfo[0].parent = &pci->dev;
0203         pdevinfo[0].num_res = 4;
0204         pdevinfo[0].res = &adata->res[0];
0205         pdevinfo[0].data = &irqflags;
0206         pdevinfo[0].size_data = sizeof(irqflags);
0207 
0208         pdevinfo[1].name = "acp5x_i2s_playcap";
0209         pdevinfo[1].id = 0;
0210         pdevinfo[1].parent = &pci->dev;
0211         pdevinfo[1].num_res = 1;
0212         pdevinfo[1].res = &adata->res[1];
0213 
0214         pdevinfo[2].name = "acp5x_i2s_playcap";
0215         pdevinfo[2].id = 1;
0216         pdevinfo[2].parent = &pci->dev;
0217         pdevinfo[2].num_res = 1;
0218         pdevinfo[2].res = &adata->res[2];
0219 
0220         pdevinfo[3].name = "acp5x_mach";
0221         pdevinfo[3].id = 0;
0222         pdevinfo[3].parent = &pci->dev;
0223         for (i = 0; i < ACP5x_DEVS; i++) {
0224             adata->pdev[i] =
0225                 platform_device_register_full(&pdevinfo[i]);
0226             if (IS_ERR(adata->pdev[i])) {
0227                 dev_err(&pci->dev, "cannot register %s device\n",
0228                     pdevinfo[i].name);
0229                 ret = PTR_ERR(adata->pdev[i]);
0230                 goto unregister_devs;
0231             }
0232         }
0233         break;
0234     default:
0235         dev_info(&pci->dev, "ACP audio mode : %d\n", val);
0236     }
0237     pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
0238     pm_runtime_use_autosuspend(&pci->dev);
0239     pm_runtime_put_noidle(&pci->dev);
0240     pm_runtime_allow(&pci->dev);
0241     return 0;
0242 
0243 unregister_devs:
0244     for (--i; i >= 0; i--)
0245         platform_device_unregister(adata->pdev[i]);
0246 de_init:
0247     if (acp5x_deinit(adata->acp5x_base))
0248         dev_err(&pci->dev, "ACP de-init failed\n");
0249 release_regions:
0250     pci_release_regions(pci);
0251 disable_pci:
0252     pci_disable_device(pci);
0253 
0254     return ret;
0255 }
0256 
0257 static int __maybe_unused snd_acp5x_suspend(struct device *dev)
0258 {
0259     int ret;
0260     struct acp5x_dev_data *adata;
0261 
0262     adata = dev_get_drvdata(dev);
0263     ret = acp5x_deinit(adata->acp5x_base);
0264     if (ret)
0265         dev_err(dev, "ACP de-init failed\n");
0266     else
0267         dev_dbg(dev, "ACP de-initialized\n");
0268 
0269     return ret;
0270 }
0271 
0272 static int __maybe_unused snd_acp5x_resume(struct device *dev)
0273 {
0274     int ret;
0275     struct acp5x_dev_data *adata;
0276 
0277     adata = dev_get_drvdata(dev);
0278     ret = acp5x_init(adata->acp5x_base);
0279     if (ret) {
0280         dev_err(dev, "ACP init failed\n");
0281         return ret;
0282     }
0283     return 0;
0284 }
0285 
0286 static const struct dev_pm_ops acp5x_pm = {
0287     SET_RUNTIME_PM_OPS(snd_acp5x_suspend,
0288                snd_acp5x_resume, NULL)
0289     SET_SYSTEM_SLEEP_PM_OPS(snd_acp5x_suspend, snd_acp5x_resume)
0290 };
0291 
0292 static void snd_acp5x_remove(struct pci_dev *pci)
0293 {
0294     struct acp5x_dev_data *adata;
0295     int i, ret;
0296 
0297     adata = pci_get_drvdata(pci);
0298     if (adata->acp5x_audio_mode == ACP5x_I2S_MODE) {
0299         for (i = 0; i < ACP5x_DEVS; i++)
0300             platform_device_unregister(adata->pdev[i]);
0301     }
0302     ret = acp5x_deinit(adata->acp5x_base);
0303     if (ret)
0304         dev_err(&pci->dev, "ACP de-init failed\n");
0305     pm_runtime_forbid(&pci->dev);
0306     pm_runtime_get_noresume(&pci->dev);
0307     pci_release_regions(pci);
0308     pci_disable_device(pci);
0309 }
0310 
0311 static const struct pci_device_id snd_acp5x_ids[] = {
0312     { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
0313     .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
0314     .class_mask = 0xffffff },
0315     { 0, },
0316 };
0317 MODULE_DEVICE_TABLE(pci, snd_acp5x_ids);
0318 
0319 static struct pci_driver acp5x_driver  = {
0320     .name = KBUILD_MODNAME,
0321     .id_table = snd_acp5x_ids,
0322     .probe = snd_acp5x_probe,
0323     .remove = snd_acp5x_remove,
0324     .driver = {
0325         .pm = &acp5x_pm,
0326     }
0327 };
0328 
0329 module_pci_driver(acp5x_driver);
0330 
0331 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
0332 MODULE_DESCRIPTION("AMD Vangogh ACP PCI driver");
0333 MODULE_LICENSE("GPL v2");