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 int acp3x_init(struct acp3x_dev_data *adata)
  80{
  81        void __iomem *acp3x_base = adata->acp3x_base;
  82        int ret;
  83
  84        /* power on */
  85        ret = acp3x_power_on(adata);
  86        if (ret) {
  87                pr_err("ACP3x power on failed\n");
  88                return ret;
  89        }
  90        /* Reset */
  91        ret = acp3x_reset(acp3x_base);
  92        if (ret) {
  93                pr_err("ACP3x reset failed\n");
  94                return ret;
  95        }
  96        return 0;
  97}
  98
  99static int acp3x_deinit(void __iomem *acp3x_base)
 100{
 101        int 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        return 0;
 110}
 111
 112static int snd_acp3x_probe(struct pci_dev *pci,
 113                           const struct pci_device_id *pci_id)
 114{
 115        struct acp3x_dev_data *adata;
 116        struct platform_device_info pdevinfo[ACP3x_DEVS];
 117        unsigned int irqflags;
 118        int ret, i;
 119        u32 addr, val;
 120
 121        if (pci_enable_device(pci)) {
 122                dev_err(&pci->dev, "pci_enable_device failed\n");
 123                return -ENODEV;
 124        }
 125
 126        ret = pci_request_regions(pci, "AMD ACP3x audio");
 127        if (ret < 0) {
 128                dev_err(&pci->dev, "pci_request_regions failed\n");
 129                goto disable_pci;
 130        }
 131
 132        adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data),
 133                             GFP_KERNEL);
 134        if (!adata) {
 135                ret = -ENOMEM;
 136                goto release_regions;
 137        }
 138
 139        /* check for msi interrupt support */
 140        ret = pci_enable_msi(pci);
 141        if (ret)
 142                /* msi is not enabled */
 143                irqflags = IRQF_SHARED;
 144        else
 145                /* msi is enabled */
 146                irqflags = 0;
 147
 148        addr = pci_resource_start(pci, 0);
 149        adata->acp3x_base = devm_ioremap(&pci->dev, addr,
 150                                        pci_resource_len(pci, 0));
 151        if (!adata->acp3x_base) {
 152                ret = -ENOMEM;
 153                goto disable_msi;
 154        }
 155        pci_set_master(pci);
 156        pci_set_drvdata(pci, adata);
 157        /* Save ACP_PME_EN state */
 158        adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
 159        ret = acp3x_init(adata);
 160        if (ret)
 161                goto disable_msi;
 162
 163        val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
 164        switch (val) {
 165        case I2S_MODE:
 166                adata->res = devm_kzalloc(&pci->dev,
 167                                          sizeof(struct resource) * 4,
 168                                          GFP_KERNEL);
 169                if (!adata->res) {
 170                        ret = -ENOMEM;
 171                        goto de_init;
 172                }
 173
 174                adata->res[0].name = "acp3x_i2s_iomem";
 175                adata->res[0].flags = IORESOURCE_MEM;
 176                adata->res[0].start = addr;
 177                adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START);
 178
 179                adata->res[1].name = "acp3x_i2s_sp";
 180                adata->res[1].flags = IORESOURCE_MEM;
 181                adata->res[1].start = addr + ACP3x_I2STDM_REG_START;
 182                adata->res[1].end = addr + ACP3x_I2STDM_REG_END;
 183
 184                adata->res[2].name = "acp3x_i2s_bt";
 185                adata->res[2].flags = IORESOURCE_MEM;
 186                adata->res[2].start = addr + ACP3x_BT_TDM_REG_START;
 187                adata->res[2].end = addr + ACP3x_BT_TDM_REG_END;
 188
 189                adata->res[3].name = "acp3x_i2s_irq";
 190                adata->res[3].flags = IORESOURCE_IRQ;
 191                adata->res[3].start = pci->irq;
 192                adata->res[3].end = adata->res[3].start;
 193
 194                adata->acp3x_audio_mode = ACP3x_I2S_MODE;
 195
 196                memset(&pdevinfo, 0, sizeof(pdevinfo));
 197                pdevinfo[0].name = "acp3x_rv_i2s_dma";
 198                pdevinfo[0].id = 0;
 199                pdevinfo[0].parent = &pci->dev;
 200                pdevinfo[0].num_res = 4;
 201                pdevinfo[0].res = &adata->res[0];
 202                pdevinfo[0].data = &irqflags;
 203                pdevinfo[0].size_data = sizeof(irqflags);
 204
 205                pdevinfo[1].name = "acp3x_i2s_playcap";
 206                pdevinfo[1].id = 0;
 207                pdevinfo[1].parent = &pci->dev;
 208                pdevinfo[1].num_res = 1;
 209                pdevinfo[1].res = &adata->res[1];
 210
 211                pdevinfo[2].name = "acp3x_i2s_playcap";
 212                pdevinfo[2].id = 1;
 213                pdevinfo[2].parent = &pci->dev;
 214                pdevinfo[2].num_res = 1;
 215                pdevinfo[2].res = &adata->res[1];
 216
 217                pdevinfo[3].name = "acp3x_i2s_playcap";
 218                pdevinfo[3].id = 2;
 219                pdevinfo[3].parent = &pci->dev;
 220                pdevinfo[3].num_res = 1;
 221                pdevinfo[3].res = &adata->res[2];
 222                for (i = 0; i < ACP3x_DEVS; i++) {
 223                        adata->pdev[i] =
 224                                platform_device_register_full(&pdevinfo[i]);
 225                        if (IS_ERR(adata->pdev[i])) {
 226                                dev_err(&pci->dev, "cannot register %s device\n",
 227                                        pdevinfo[i].name);
 228                                ret = PTR_ERR(adata->pdev[i]);
 229                                goto unregister_devs;
 230                        }
 231                }
 232                break;
 233        default:
 234                dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val);
 235                ret = -ENODEV;
 236                goto disable_msi;
 237        }
 238        pm_runtime_set_autosuspend_delay(&pci->dev, 2000);
 239        pm_runtime_use_autosuspend(&pci->dev);
 240        pm_runtime_put_noidle(&pci->dev);
 241        pm_runtime_allow(&pci->dev);
 242        return 0;
 243
 244unregister_devs:
 245        if (val == I2S_MODE)
 246                for (i = 0; i < ACP3x_DEVS; i++)
 247                        platform_device_unregister(adata->pdev[i]);
 248de_init:
 249        if (acp3x_deinit(adata->acp3x_base))
 250                dev_err(&pci->dev, "ACP de-init failed\n");
 251disable_msi:
 252        pci_disable_msi(pci);
 253release_regions:
 254        pci_release_regions(pci);
 255disable_pci:
 256        pci_disable_device(pci);
 257
 258        return ret;
 259}
 260
 261static int snd_acp3x_suspend(struct device *dev)
 262{
 263        int ret;
 264        struct acp3x_dev_data *adata;
 265
 266        adata = dev_get_drvdata(dev);
 267        ret = acp3x_deinit(adata->acp3x_base);
 268        if (ret)
 269                dev_err(dev, "ACP de-init failed\n");
 270        else
 271                dev_dbg(dev, "ACP de-initialized\n");
 272
 273        return 0;
 274}
 275
 276static int snd_acp3x_resume(struct device *dev)
 277{
 278        int ret;
 279        struct acp3x_dev_data *adata;
 280
 281        adata = dev_get_drvdata(dev);
 282        ret = acp3x_init(adata);
 283        if (ret) {
 284                dev_err(dev, "ACP init failed\n");
 285                return ret;
 286        }
 287        return 0;
 288}
 289
 290static const struct dev_pm_ops acp3x_pm = {
 291        .runtime_suspend = snd_acp3x_suspend,
 292        .runtime_resume =  snd_acp3x_resume,
 293        .resume =       snd_acp3x_resume,
 294};
 295
 296static void snd_acp3x_remove(struct pci_dev *pci)
 297{
 298        struct acp3x_dev_data *adata;
 299        int i, ret;
 300
 301        adata = pci_get_drvdata(pci);
 302        if (adata->acp3x_audio_mode == ACP3x_I2S_MODE) {
 303                for (i = 0; i < ACP3x_DEVS; i++)
 304                        platform_device_unregister(adata->pdev[i]);
 305        }
 306        ret = acp3x_deinit(adata->acp3x_base);
 307        if (ret)
 308                dev_err(&pci->dev, "ACP de-init failed\n");
 309        pm_runtime_forbid(&pci->dev);
 310        pm_runtime_get_noresume(&pci->dev);
 311        pci_disable_msi(pci);
 312        pci_release_regions(pci);
 313        pci_disable_device(pci);
 314}
 315
 316static const struct pci_device_id snd_acp3x_ids[] = {
 317        { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2),
 318        .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
 319        .class_mask = 0xffffff },
 320        { 0, },
 321};
 322MODULE_DEVICE_TABLE(pci, snd_acp3x_ids);
 323
 324static struct pci_driver acp3x_driver  = {
 325        .name = KBUILD_MODNAME,
 326        .id_table = snd_acp3x_ids,
 327        .probe = snd_acp3x_probe,
 328        .remove = snd_acp3x_remove,
 329        .driver = {
 330                .pm = &acp3x_pm,
 331        }
 332};
 333
 334module_pci_driver(acp3x_driver);
 335
 336MODULE_AUTHOR("Vishnuvardhanrao.Ravulapati@amd.com");
 337MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com");
 338MODULE_DESCRIPTION("AMD ACP3x PCI driver");
 339MODULE_LICENSE("GPL v2");
 340