linux/sound/soc/amd/acp/acp-pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
   2//
   3// This file is provided under a dual BSD/GPLv2 license. When using or
   4// redistributing this file, you may do so under either license.
   5//
   6// Copyright(c) 2022 Advanced Micro Devices, Inc. All rights reserved.
   7//
   8// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
   9
  10/*
  11 * Generic PCI interface for ACP device
  12 */
  13
  14#include <linux/delay.h>
  15#include <linux/interrupt.h>
  16#include <linux/pci.h>
  17#include <linux/platform_device.h>
  18#include <linux/pm_runtime.h>
  19
  20#include "amd.h"
  21#include "../mach-config.h"
  22
  23#define DRV_NAME "acp_pci"
  24
  25#define ACP3x_REG_START 0x1240000
  26#define ACP3x_REG_END   0x125C000
  27
  28static struct platform_device *dmic_dev;
  29static struct platform_device *pdev;
  30
  31static const struct resource acp3x_res[] = {
  32        {
  33                .start = 0,
  34                .end = ACP3x_REG_END - ACP3x_REG_START,
  35                .name = "acp_mem",
  36                .flags = IORESOURCE_MEM,
  37        },
  38        {
  39                .start = 0,
  40                .end = 0,
  41                .name = "acp_dai_irq",
  42                .flags = IORESOURCE_IRQ,
  43        },
  44};
  45
  46static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
  47{
  48        struct platform_device_info pdevinfo;
  49        struct device *dev = &pci->dev;
  50        const struct resource *res_acp;
  51        struct acp_chip_info *chip;
  52        struct resource *res;
  53        unsigned int flag, addr, num_res, i;
  54        int ret;
  55
  56        flag = snd_amd_acp_find_config(pci);
  57        if (flag != FLAG_AMD_LEGACY)
  58                return -ENODEV;
  59
  60        chip = devm_kzalloc(&pci->dev, sizeof(*chip), GFP_KERNEL);
  61        if (!chip)
  62                return -ENOMEM;
  63
  64        if (pci_enable_device(pci)) {
  65                dev_err(&pci->dev, "pci_enable_device failed\n");
  66                return -ENODEV;
  67        }
  68
  69        ret = pci_request_regions(pci, "AMD ACP3x audio");
  70        if (ret < 0) {
  71                dev_err(&pci->dev, "pci_request_regions failed\n");
  72                return -ENOMEM;
  73        }
  74
  75        pci_set_master(pci);
  76
  77        switch (pci->revision) {
  78        case 0x01:
  79                res_acp = acp3x_res;
  80                num_res = ARRAY_SIZE(acp3x_res);
  81                chip->name = "acp_asoc_renoir";
  82                chip->acp_rev = ACP3X_DEV;
  83                break;
  84        default:
  85                dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
  86                return -EINVAL;
  87        }
  88
  89        dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
  90        if (IS_ERR(dmic_dev)) {
  91                dev_err(dev, "failed to create DMIC device\n");
  92                return PTR_ERR(dmic_dev);
  93        }
  94
  95        addr = pci_resource_start(pci, 0);
  96        chip->base = devm_ioremap(&pci->dev, addr, pci_resource_len(pci, 0));
  97
  98        res = devm_kzalloc(&pci->dev, sizeof(struct resource) * num_res, GFP_KERNEL);
  99        if (!res) {
 100                platform_device_unregister(dmic_dev);
 101                return -ENOMEM;
 102        }
 103
 104        for (i = 0; i < num_res; i++, res_acp++) {
 105                res[i].name = res_acp->name;
 106                res[i].flags = res_acp->flags;
 107                res[i].start = addr + res_acp->start;
 108                res[i].end = addr + res_acp->end;
 109                if (res_acp->flags == IORESOURCE_IRQ) {
 110                        res[i].start = pci->irq;
 111                        res[i].end = res[i].start;
 112                }
 113        }
 114
 115        memset(&pdevinfo, 0, sizeof(pdevinfo));
 116
 117        pdevinfo.name = chip->name;
 118        pdevinfo.id = 0;
 119        pdevinfo.parent = &pci->dev;
 120        pdevinfo.num_res = num_res;
 121        pdevinfo.res = &res[0];
 122        pdevinfo.data = chip;
 123        pdevinfo.size_data = sizeof(*chip);
 124
 125        pdev = platform_device_register_full(&pdevinfo);
 126        if (IS_ERR(pdev)) {
 127                dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
 128                platform_device_unregister(dmic_dev);
 129                ret = PTR_ERR(pdev);
 130        }
 131
 132        return ret;
 133};
 134
 135static void acp_pci_remove(struct pci_dev *pci)
 136{
 137        if (dmic_dev)
 138                platform_device_unregister(dmic_dev);
 139        if (pdev)
 140                platform_device_unregister(pdev);
 141}
 142
 143/* PCI IDs */
 144static const struct pci_device_id acp_pci_ids[] = {
 145        { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID)},
 146        { 0, }
 147};
 148MODULE_DEVICE_TABLE(pci, acp_pci_ids);
 149
 150/* pci_driver definition */
 151static struct pci_driver snd_amd_acp_pci_driver = {
 152        .name = KBUILD_MODNAME,
 153        .id_table = acp_pci_ids,
 154        .probe = acp_pci_probe,
 155        .remove = acp_pci_remove,
 156};
 157module_pci_driver(snd_amd_acp_pci_driver);
 158
 159MODULE_LICENSE("Dual BSD/GPL");
 160MODULE_ALIAS(DRV_NAME);
 161