linux/sound/soc/amd/renoir/rn-pci-acp3x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2//
   3// AMD Renoir ACP PCI Driver
   4//
   5//Copyright 2020 Advanced Micro Devices, Inc.
   6
   7#include <linux/pci.h>
   8#include <linux/acpi.h>
   9#include <linux/dmi.h>
  10#include <linux/module.h>
  11#include <linux/io.h>
  12#include <linux/delay.h>
  13#include <linux/platform_device.h>
  14#include <linux/interrupt.h>
  15#include <linux/pm_runtime.h>
  16
  17#include "rn_acp3x.h"
  18
  19static int acp_power_gating;
  20module_param(acp_power_gating, int, 0644);
  21MODULE_PARM_DESC(acp_power_gating, "Enable acp power gating");
  22
  23/*
  24 * dmic_acpi_check = -1 - Use ACPI/DMI method to detect the DMIC hardware presence at runtime
  25 *                 =  0 - Skip the DMIC device creation and return probe failure
  26 *                 =  1 - Force DMIC support
  27 */
  28static int dmic_acpi_check = ACP_DMIC_AUTO;
  29module_param(dmic_acpi_check, bint, 0644);
  30MODULE_PARM_DESC(dmic_acpi_check, "Digital microphone presence (-1=auto, 0=none, 1=force)");
  31
  32struct acp_dev_data {
  33        void __iomem *acp_base;
  34        struct resource *res;
  35        struct platform_device *pdev[ACP_DEVS];
  36};
  37
  38static int rn_acp_power_on(void __iomem *acp_base)
  39{
  40        u32 val;
  41        int timeout;
  42
  43        val = rn_readl(acp_base + ACP_PGFSM_STATUS);
  44
  45        if (val == 0)
  46                return val;
  47
  48        if ((val & ACP_PGFSM_STATUS_MASK) !=
  49                                ACP_POWER_ON_IN_PROGRESS)
  50                rn_writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
  51                          acp_base + ACP_PGFSM_CONTROL);
  52        timeout = 0;
  53        while (++timeout < 500) {
  54                val = rn_readl(acp_base + ACP_PGFSM_STATUS);
  55                if (!val)
  56                        return 0;
  57                udelay(1);
  58        }
  59        return -ETIMEDOUT;
  60}
  61
  62static int rn_acp_power_off(void __iomem *acp_base)
  63{
  64        u32 val;
  65        int timeout;
  66
  67        rn_writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
  68                  acp_base + ACP_PGFSM_CONTROL);
  69        timeout = 0;
  70        while (++timeout < 500) {
  71                val = rn_readl(acp_base + ACP_PGFSM_STATUS);
  72                if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
  73                        return 0;
  74                udelay(1);
  75        }
  76        return -ETIMEDOUT;
  77}
  78
  79static int rn_acp_reset(void __iomem *acp_base)
  80{
  81        u32 val;
  82        int timeout;
  83
  84        rn_writel(1, acp_base + ACP_SOFT_RESET);
  85        timeout = 0;
  86        while (++timeout < 500) {
  87                val = rn_readl(acp_base + ACP_SOFT_RESET);
  88                if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
  89                        break;
  90                cpu_relax();
  91        }
  92        rn_writel(0, acp_base + ACP_SOFT_RESET);
  93        timeout = 0;
  94        while (++timeout < 500) {
  95                val = rn_readl(acp_base + ACP_SOFT_RESET);
  96                if (!val)
  97                        return 0;
  98                cpu_relax();
  99        }
 100        return -ETIMEDOUT;
 101}
 102
 103static void rn_acp_enable_interrupts(void __iomem *acp_base)
 104{
 105        u32 ext_intr_ctrl;
 106
 107        rn_writel(0x01, acp_base + ACP_EXTERNAL_INTR_ENB);
 108        ext_intr_ctrl = rn_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
 109        ext_intr_ctrl |= ACP_ERROR_MASK;
 110        rn_writel(ext_intr_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
 111}
 112
 113static void rn_acp_disable_interrupts(void __iomem *acp_base)
 114{
 115        rn_writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base +
 116                  ACP_EXTERNAL_INTR_STAT);
 117        rn_writel(0x00, acp_base + ACP_EXTERNAL_INTR_ENB);
 118}
 119
 120static int rn_acp_init(void __iomem *acp_base)
 121{
 122        int ret;
 123
 124        /* power on */
 125        ret = rn_acp_power_on(acp_base);
 126        if (ret) {
 127                pr_err("ACP power on failed\n");
 128                return ret;
 129        }
 130        rn_writel(0x01, acp_base + ACP_CONTROL);
 131        /* Reset */
 132        ret = rn_acp_reset(acp_base);
 133        if (ret) {
 134                pr_err("ACP reset failed\n");
 135                return ret;
 136        }
 137        rn_writel(0x03, acp_base + ACP_CLKMUX_SEL);
 138        rn_acp_enable_interrupts(acp_base);
 139        return 0;
 140}
 141
 142static int rn_acp_deinit(void __iomem *acp_base)
 143{
 144        int ret;
 145
 146        rn_acp_disable_interrupts(acp_base);
 147        /* Reset */
 148        ret = rn_acp_reset(acp_base);
 149        if (ret) {
 150                pr_err("ACP reset failed\n");
 151                return ret;
 152        }
 153        rn_writel(0x00, acp_base + ACP_CLKMUX_SEL);
 154        rn_writel(0x00, acp_base + ACP_CONTROL);
 155        /* power off */
 156        if (acp_power_gating) {
 157                ret = rn_acp_power_off(acp_base);
 158                if (ret) {
 159                        pr_err("ACP power off failed\n");
 160                        return ret;
 161                }
 162        }
 163        return 0;
 164}
 165
 166static const struct dmi_system_id rn_acp_quirk_table[] = {
 167        {
 168                /* Lenovo IdeaPad S340-14API */
 169                .matches = {
 170                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
 171                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
 172                }
 173        },
 174        {
 175                /* Lenovo IdeaPad Flex 5 14ARE05 */
 176                .matches = {
 177                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
 178                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
 179                }
 180        },
 181        {
 182                /* Lenovo IdeaPad 5 15ARE05 */
 183                .matches = {
 184                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
 185                        DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
 186                }
 187        },
 188        {
 189                /* Lenovo ThinkPad E14 Gen 2 */
 190                .matches = {
 191                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
 192                        DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
 193                }
 194        },
 195        {
 196                /* Lenovo ThinkPad X395 */
 197                .matches = {
 198                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
 199                        DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
 200                }
 201        },
 202        {}
 203};
 204
 205static int snd_rn_acp_probe(struct pci_dev *pci,
 206                            const struct pci_device_id *pci_id)
 207{
 208        struct acp_dev_data *adata;
 209        struct platform_device_info pdevinfo[ACP_DEVS];
 210#if defined(CONFIG_ACPI)
 211        acpi_handle handle;
 212        acpi_integer dmic_status;
 213#endif
 214        const struct dmi_system_id *dmi_id;
 215        unsigned int irqflags;
 216        int ret, index;
 217        u32 addr;
 218
 219        /* Renoir device check */
 220        if (pci->revision != 0x01)
 221                return -ENODEV;
 222
 223        if (pci_enable_device(pci)) {
 224                dev_err(&pci->dev, "pci_enable_device failed\n");
 225                return -ENODEV;
 226        }
 227
 228        ret = pci_request_regions(pci, "AMD ACP3x audio");
 229        if (ret < 0) {
 230                dev_err(&pci->dev, "pci_request_regions failed\n");
 231                goto disable_pci;
 232        }
 233
 234        adata = devm_kzalloc(&pci->dev, sizeof(struct acp_dev_data),
 235                             GFP_KERNEL);
 236        if (!adata) {
 237                ret = -ENOMEM;
 238                goto release_regions;
 239        }
 240
 241        /* check for msi interrupt support */
 242        ret = pci_enable_msi(pci);
 243        if (ret)
 244                /* msi is not enabled */
 245                irqflags = IRQF_SHARED;
 246        else
 247                /* msi is enabled */
 248                irqflags = 0;
 249
 250        addr = pci_resource_start(pci, 0);
 251        adata->acp_base = devm_ioremap(&pci->dev, addr,
 252                                       pci_resource_len(pci, 0));
 253        if (!adata->acp_base) {
 254                ret = -ENOMEM;
 255                goto disable_msi;
 256        }
 257        pci_set_master(pci);
 258        pci_set_drvdata(pci, adata);
 259        ret = rn_acp_init(adata->acp_base);
 260        if (ret)
 261                goto disable_msi;
 262
 263        if (!dmic_acpi_check) {
 264                ret = -ENODEV;
 265                goto de_init;
 266        } else if (dmic_acpi_check == ACP_DMIC_AUTO) {
 267#if defined(CONFIG_ACPI)
 268                handle = ACPI_HANDLE(&pci->dev);
 269                ret = acpi_evaluate_integer(handle, "_WOV", NULL, &dmic_status);
 270                if (ACPI_FAILURE(ret)) {
 271                        ret = -ENODEV;
 272                        goto de_init;
 273                }
 274                if (!dmic_status) {
 275                        ret = -ENODEV;
 276                        goto de_init;
 277                }
 278#endif
 279                dmi_id = dmi_first_match(rn_acp_quirk_table);
 280                if (dmi_id && !dmi_id->driver_data) {
 281                        dev_info(&pci->dev, "ACPI settings override using DMI (ACP mic is not present)");
 282                        ret = -ENODEV;
 283                        goto de_init;
 284                }
 285        }
 286
 287        adata->res = devm_kzalloc(&pci->dev,
 288                                  sizeof(struct resource) * 2,
 289                                  GFP_KERNEL);
 290        if (!adata->res) {
 291                ret = -ENOMEM;
 292                goto de_init;
 293        }
 294
 295        adata->res[0].name = "acp_pdm_iomem";
 296        adata->res[0].flags = IORESOURCE_MEM;
 297        adata->res[0].start = addr;
 298        adata->res[0].end = addr + (ACP_REG_END - ACP_REG_START);
 299        adata->res[1].name = "acp_pdm_irq";
 300        adata->res[1].flags = IORESOURCE_IRQ;
 301        adata->res[1].start = pci->irq;
 302        adata->res[1].end = pci->irq;
 303
 304        memset(&pdevinfo, 0, sizeof(pdevinfo));
 305        pdevinfo[0].name = "acp_rn_pdm_dma";
 306        pdevinfo[0].id = 0;
 307        pdevinfo[0].parent = &pci->dev;
 308        pdevinfo[0].num_res = 2;
 309        pdevinfo[0].res = adata->res;
 310        pdevinfo[0].data = &irqflags;
 311        pdevinfo[0].size_data = sizeof(irqflags);
 312
 313        pdevinfo[1].name = "dmic-codec";
 314        pdevinfo[1].id = 0;
 315        pdevinfo[1].parent = &pci->dev;
 316        pdevinfo[2].name = "acp_pdm_mach";
 317        pdevinfo[2].id = 0;
 318        pdevinfo[2].parent = &pci->dev;
 319        for (index = 0; index < ACP_DEVS; index++) {
 320                adata->pdev[index] =
 321                                platform_device_register_full(&pdevinfo[index]);
 322                if (IS_ERR(adata->pdev[index])) {
 323                        dev_err(&pci->dev, "cannot register %s device\n",
 324                                pdevinfo[index].name);
 325                        ret = PTR_ERR(adata->pdev[index]);
 326                        goto unregister_devs;
 327                }
 328        }
 329        pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
 330        pm_runtime_use_autosuspend(&pci->dev);
 331        pm_runtime_put_noidle(&pci->dev);
 332        pm_runtime_allow(&pci->dev);
 333        return 0;
 334
 335unregister_devs:
 336        for (index = 0; index < ACP_DEVS; index++)
 337                platform_device_unregister(adata->pdev[index]);
 338de_init:
 339        if (rn_acp_deinit(adata->acp_base))
 340                dev_err(&pci->dev, "ACP de-init failed\n");
 341disable_msi:
 342        pci_disable_msi(pci);
 343release_regions:
 344        pci_release_regions(pci);
 345disable_pci:
 346        pci_disable_device(pci);
 347
 348        return ret;
 349}
 350
 351static int snd_rn_acp_suspend(struct device *dev)
 352{
 353        int ret;
 354        struct acp_dev_data *adata;
 355
 356        adata = dev_get_drvdata(dev);
 357        ret = rn_acp_deinit(adata->acp_base);
 358        if (ret)
 359                dev_err(dev, "ACP de-init failed\n");
 360        else
 361                dev_dbg(dev, "ACP de-initialized\n");
 362
 363        return ret;
 364}
 365
 366static int snd_rn_acp_resume(struct device *dev)
 367{
 368        int ret;
 369        struct acp_dev_data *adata;
 370
 371        adata = dev_get_drvdata(dev);
 372        ret = rn_acp_init(adata->acp_base);
 373        if (ret) {
 374                dev_err(dev, "ACP init failed\n");
 375                return ret;
 376        }
 377        return 0;
 378}
 379
 380static const struct dev_pm_ops rn_acp_pm = {
 381        .runtime_suspend = snd_rn_acp_suspend,
 382        .runtime_resume =  snd_rn_acp_resume,
 383        .suspend = snd_rn_acp_suspend,
 384        .resume =       snd_rn_acp_resume,
 385        .restore =      snd_rn_acp_resume,
 386        .poweroff =     snd_rn_acp_suspend,
 387};
 388
 389static void snd_rn_acp_remove(struct pci_dev *pci)
 390{
 391        struct acp_dev_data *adata;
 392        int ret, index;
 393
 394        adata = pci_get_drvdata(pci);
 395        for (index = 0; index < ACP_DEVS; index++)
 396                platform_device_unregister(adata->pdev[index]);
 397        ret = rn_acp_deinit(adata->acp_base);
 398        if (ret)
 399                dev_err(&pci->dev, "ACP de-init failed\n");
 400        pm_runtime_forbid(&pci->dev);
 401        pm_runtime_get_noresume(&pci->dev);
 402        pci_disable_msi(pci);
 403        pci_release_regions(pci);
 404        pci_disable_device(pci);
 405}
 406
 407static const struct pci_device_id snd_rn_acp_ids[] = {
 408        { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
 409        .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
 410        .class_mask = 0xffffff },
 411        { 0, },
 412};
 413MODULE_DEVICE_TABLE(pci, snd_rn_acp_ids);
 414
 415static struct pci_driver rn_acp_driver  = {
 416        .name = KBUILD_MODNAME,
 417        .id_table = snd_rn_acp_ids,
 418        .probe = snd_rn_acp_probe,
 419        .remove = snd_rn_acp_remove,
 420        .driver = {
 421                .pm = &rn_acp_pm,
 422        }
 423};
 424
 425module_pci_driver(rn_acp_driver);
 426
 427MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
 428MODULE_DESCRIPTION("AMD ACP Renoir PCI driver");
 429MODULE_LICENSE("GPL v2");
 430