linux/sound/soc/amd/raven/pci-acp3x.c
<<
>>
Prefs
   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#include <linux/pm_runtime.h>
  13#include <linux/delay.h>
  14
  15#include "acp3x.h"
  16
  17struct acp3x_dev_data {
  18        void __iomem *acp3x_base;
  19        bool acp3x_audio_mode;
  20        struct resource *res;
  21        struct platform_device *pdev[ACP3x_DEVS];
  22        u32 pme_en;
  23};
  24
  25static int acp3x_power_on(struct acp3x_dev_data *adata)
  26{
  27        void __iomem *acp3x_base = adata->acp3x_base;
  28        u32 val;
  29        int timeout;
  30
  31        val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
  32
  33        if (val == 0)
  34                return val;
  35
  36        if (!((val & ACP_PGFSM_STATUS_MASK) ==
  37                                ACP_POWER_ON_IN_PROGRESS))
  38                rv_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
  39                        acp3x_base + mmACP_PGFSM_CONTROL);
  40        timeout = 0;
  41        while (++timeout < 500) {
  42                val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS);
  43                if (!val) {
  44                        /* ACP power On clears PME_EN.
  45                         * Restore the value to its prior state
  46                         */
  47                        rv_writel(adata->pme_en, acp3x_base + mmACP_PME_EN);
  48                        return 0;
  49                }
  50                udelay(1);
  51        }
  52        return -ETIMEDOUT;
  53}
  54
  55static int acp3x_reset(void __iomem *acp3x_base)
  56{
  57        u32 val;
  58        int timeout;
  59
  60        rv_writel(1, acp3x_base + mmACP_SOFT_RESET);
  61        timeout = 0;
  62        while (++timeout < 500) {
  63                val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
  64                if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK)
  65                        break;
  66                cpu_relax();
  67        }
  68        rv_writel(0, acp3x_base + mmACP_SOFT_RESET);
  69        timeout = 0;
  70        while (++timeout < 500) {
  71                val = rv_readl(acp3x_base + mmACP_SOFT_RESET);
  72                if (!val)
  73                        return 0;
  74                cpu_relax();
  75        }
  76        return -ETIMEDOUT;
  77}
  78
  79static void acp3x_enable_interrupts(void __iomem *acp_base)
  80{
  81        rv_writel(0x01, acp_base + mmACP_EXTERNAL_INTR_ENB);
  82}
  83
  84static void acp3x_disable_interrupts(void __iomem *acp_base)
  85{
  86        rv_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
  87                  mmACP_EXTERNAL_INTR_STAT);
  88        rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_CNTL);
  89        rv_writel(0x00, acp_base + mmACP_EXTERNAL_INTR_ENB);
  90}
  91
  92static int acp3x_init(struct acp3x_dev_data *adata)
  93{
  94        void __iomem *acp3x_base = adata->acp3x_base;
  95        int ret;
  96
  97        /* power on */
  98        ret = acp3x_power_on(adata);
  99        if (ret) {
 100                pr_err("ACP3x power on failed\n");
 101                return ret;
 102        }
 103        /* Reset */
 104        ret = acp3x_reset(acp3x_base);
 105        if (ret) {
 106                pr_err("ACP3x reset failed\n");
 107                return ret;
 108        }
 109        acp3x_enable_interrupts(acp3x_base);
 110        return 0;
 111}
 112
 113static int acp3x_deinit(void __iomem *acp3x_base)
 114{
 115        int ret;
 116
 117        acp3x_disable_interrupts(acp3x_base);
 118        /* Reset */
 119        ret = acp3x_reset(acp3x_base);
 120        if (ret) {
 121                pr_err("ACP3x reset failed\n");
 122                return ret;
 123        }
 124        return 0;
 125}
 126
 127static int snd_acp3x_probe(struct pci_dev *pci,
 128                           const struct pci_device_id *pci_id)
 129{
 130        struct acp3x_dev_data *adata;
 131        struct platform_device_info pdevinfo[ACP3x_DEVS];
 132        unsigned int irqflags;
 133        int ret, i;
 134        u32 addr, val;
 135
 136        /* Raven device detection */
 137        if (pci->revision != 0x00)
 138                return -ENODEV;
 139
 140        if (pci_enable_device(pci)) {
 141                dev_err(&pci->dev, "pci_enable_device failed\n");
 142                return -ENODEV;
 143        }
 144
 145        ret = pci_request_regions(pci, "AMD ACP3x audio");
 146        if (ret < 0) {
 147                dev_err(&pci->dev, "pci_request_regions failed\n");
 148                goto disable_pci;
 149        }
 150
 151        adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
 152                             GFP_KERNEL);
 153        if (!adata) {
 154                ret = -ENOMEM;
 155                goto release_regions;
 156        }
 157
 158        irqflags = IRQF_SHARED;
 159
 160        addr = pci_resource_start(pci, 0);
 161        adata->acp3x_base = devm_ioremap(&pci->dev, addr,
 162                                        pci_resource_len(pci, 0));
 163        if (!adata->acp3x_base) {
 164                ret = -ENOMEM;
 165                goto release_regions;
 166        }
 167        pci_set_master(pci);
 168        pci_set_drvdata(pci, adata);
 169        /* Save ACP_PME_EN state */
 170        adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
 171        ret = acp3x_init(adata);
 172        if (ret)
 173                goto release_regions;
 174
 175        val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
 176        switch (val) {
 177        case I2S_MODE:
 178                adata->res = devm_kzalloc(&pci->dev,
 179                                          sizeof(struct resource) * 4,
 180                                          GFP_KERNEL);
 181                if (!adata->res) {
 182                        ret = -ENOMEM;
 183                        goto de_init;
 184                }
 185
 186                adata->res[0].name = "acp3x_i2s_iomem";
 187                adata->res[0].flags = IORESOURCE_MEM;
 188                adata->res[0].start = addr;
 189                adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
 190
 191                adata->res[1].name = "acp3x_i2s_sp";
 192                adata->res[1].flags = IORESOURCE_MEM;
 193                adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
 194                adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
 195
 196                adata->res[2].name = "acp3x_i2s_bt";
 197                adata->res[2].flags = IORESOURCE_MEM;
 198                adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
 199                adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
 200
 201                adata->res[3].name = "acp3x_i2s_irq";
 202                adata->res[3].flags = IORESOURCE_IRQ;
 203                adata->res[3].start = pci->irq;
 204                adata->res[3].end = adata->res[3].start;
 205
 206                adata->acp3x_audio_mode = ACP3x_I2S_MODE;
 207
 208                memset(&pdevinfo, 0, sizeof(pdevinfo));
 209                pdevinfo[0].name = "acp3x_rv_i2s_dma";
 210                pdevinfo[0].id = 0;
 211                pdevinfo[0].parent = &pci->dev;
 212                pdevinfo[0].num_res = 4;
 213                pdevinfo[0].res = &adata->res[0];
 214                pdevinfo[0].data = &irqflags;
 215                pdevinfo[0].size_data = sizeof(irqflags);
 216
 217                pdevinfo[1].name = "acp3x_i2s_playcap";
 218                pdevinfo[1].id = 0;
 219                pdevinfo[1].parent = &pci->dev;
 220                pdevinfo[1].num_res = 1;
 221                pdevinfo[1].res = &adata->res[1];
 222
 223                pdevinfo[2].name = "acp3x_i2s_playcap";
 224                pdevinfo[2].id = 1;
 225                pdevinfo[2].parent = &pci->dev;
 226                pdevinfo[2].num_res = 1;
 227                pdevinfo[2].res = &adata->res[1];
 228
 229                pdevinfo[3].name = "acp3x_i2s_playcap";
 230                pdevinfo[3].id = 2;
 231                pdevinfo[3].parent = &pci->dev;
 232                pdevinfo[3].num_res = 1;
 233                pdevinfo[3].res = &adata->res[2];
 234                for (i = 0; i < ACP3x_DEVS; i++) {
 235                        adata->pdev[i] =
 236                                platform_device_register_full(&pdevinfo[i]);
 237                        if (IS_ERR(adata->pdev[i])) {
 238                                dev_err(&pci->dev, "cannot register %s device\n",
 239                                        pdevinfo[i].name);
 240                                ret = PTR_ERR(adata->pdev[i]);
 241                                goto unregister_devs;
 242                        }
 243                }
 244                break;
 245        default:
 246                dev_info(&pci->dev, "ACP audio mode : %d\n", val);
 247                break;
 248        }
 249        pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
 250        pm_runtime_use_autosuspend(&pci->dev);
 251        pm_runtime_put_noidle(&pci->dev);
 252        pm_runtime_allow(&pci->dev);
 253        return 0;
 254
 255unregister_devs:
 256        if (val == I2S_MODE)
 257                for (i = 0; i < ACP3x_DEVS; i++)
 258                        platform_device_unregister(adata->pdev[i]);
 259de_init:
 260        if (acp3x_deinit(adata->acp3x_base))
 261                dev_err(&pci->dev, "ACP de-init failed\n");
 262release_regions:
 263        pci_release_regions(pci);
 264disable_pci:
 265        pci_disable_device(pci);
 266
 267        return ret;
 268}
 269
 270static int snd_acp3x_suspend(struct device *dev)
 271{
 272        int ret;
 273        struct acp3x_dev_data *adata;
 274
 275        adata = dev_get_drvdata(dev);
 276        ret = acp3x_deinit(adata->acp3x_base);
 277        if (ret)
 278                dev_err(dev, "ACP de-init failed\n");
 279        else
 280                dev_dbg(dev, "ACP de-initialized\n");
 281
 282        return 0;
 283}
 284
 285static int snd_acp3x_resume(struct device *dev)
 286{
 287        int ret;
 288        struct acp3x_dev_data *adata;
 289
 290        adata = dev_get_drvdata(dev);
 291        ret = acp3x_init(adata);
 292        if (ret) {
 293                dev_err(dev, "ACP init failed\n");
 294                return ret;
 295        }
 296        return 0;
 297}
 298
 299static const struct dev_pm_ops acp3x_pm = {
 300        .runtime_suspend = snd_acp3x_suspend,
 301        .runtime_resume =  snd_acp3x_resume,
 302        .resume =       snd_acp3x_resume,
 303};
 304
 305static void snd_acp3x_remove(struct pci_dev *pci)
 306{
 307        struct acp3x_dev_data *adata;
 308        int i, ret;
 309
 310        adata = pci_get_drvdata(pci);
 311        if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
 312                for (i = 0; i < ACP3x_DEVS; i++)
 313                        platform_device_unregister(adata->pdev[i]);
 314        }
 315        ret = acp3x_deinit(adata->acp3x_base);
 316        if (ret)
 317                dev_err(&pci->dev, "ACP de-init failed\n");
 318        pm_runtime_forbid(&pci->dev);
 319        pm_runtime_get_noresume(&pci->dev);
 320        pci_release_regions(pci);
 321        pci_disable_device(pci);
 322}
 323
 324static const struct pci_device_id snd_acp3x_ids[] = {
 325        { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
 326        .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
 327        .class_mask = 0xffffff },
 328        { 0, },
 329};
 330MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
 331
 332static struct pci_driver acp3x_driver  = {
 333        .name = KBUILD_MODNAME,
 334        .id_table = snd_acp3x_ids,
 335        .probe = snd_acp3x_probe,
 336        .remove = snd_acp3x_remove,
 337        .driver = {
 338                .pm = &acp3x_pm,
 339        }
 340};
 341
 342module_pci_driver(acp3x_driver);
 343
 344MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
 345MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
 346MODULE_DESCRIPTION("AMD ACP3x PCI driver");
 347MODULE_LICENSE("GPL v2");
 348