Back to home page

OSCL-LXR

 
 

    


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