Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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) 2021 Advanced Micro Devices, Inc. All rights reserved.
7//
8// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
9
10/*
11 * PCI interface for Renoir ACP device
12 */
13
14#include <linux/module.h>
15#include <linux/pci.h>
16#include <linux/platform_device.h>
17#include <sound/sof.h>
18#include <sound/soc-acpi.h>
19
20#include "../ops.h"
21#include "../sof-pci-dev.h"
22#include "../../amd/mach-config.h"
23#include "acp.h"
24#include "acp-dsp-offset.h"
25
26#define ACP3x_REG_START 0x1240000
27#define ACP3x_REG_END 0x125C000
28
29static struct platform_device *dmic_dev;
30static struct platform_device *pdev;
31
32static const struct resource renoir_res[] = {
33 {
34 .start = 0,
35 .end = ACP3x_REG_END - ACP3x_REG_START,
36 .name = "acp_mem",
37 .flags = IORESOURCE_MEM,
38 },
39 {
40 .start = 0,
41 .end = 0,
42 .name = "acp_dai_irq",
43 .flags = IORESOURCE_IRQ,
44 },
45};
46
47static const struct sof_amd_acp_desc renoir_chip_info = {
48 .rev = 3,
49 .host_bridge_id = HOST_BRIDGE_CZN,
50 .i2s_mode = 0x04,
51 .pgfsm_base = ACP3X_PGFSM_BASE,
52 .ext_intr_stat = ACP3X_EXT_INTR_STAT,
53 .dsp_intr_base = ACP3X_DSP_SW_INTR_BASE,
54 .sram_pte_offset = ACP3X_SRAM_PTE_OFFSET,
55 .i2s_pin_config_offset = ACP3X_I2S_PIN_CONFIG,
56 .hw_semaphore_offset = ACP3X_AXI2DAGB_SEM_0,
57 .acp_clkmux_sel = ACP3X_CLKMUX_SEL,
58};
59
60static const struct sof_dev_desc renoir_desc = {
61 .machines = snd_soc_acpi_amd_sof_machines,
62 .use_acpi_target_states = true,
63 .resindex_lpe_base = 0,
64 .resindex_pcicfg_base = -1,
65 .resindex_imr_base = -1,
66 .irqindex_host_ipc = -1,
67 .chip_info = &renoir_chip_info,
68 .ipc_supported_mask = BIT(SOF_IPC),
69 .ipc_default = SOF_IPC,
70 .default_fw_path = {
71 [SOF_IPC] = "amd/sof",
72 },
73 .default_tplg_path = {
74 [SOF_IPC] = "amd/sof-tplg",
75 },
76 .default_fw_filename = {
77 [SOF_IPC] = "sof-rn.ri",
78 },
79 .nocodec_tplg_filename = "sof-acp.tplg",
80 .ops = &sof_renoir_ops,
81 .ops_init = sof_renoir_ops_init,
82};
83
84static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
85{
86 struct platform_device_info pdevinfo;
87 struct device *dev = &pci->dev;
88 const struct resource *res_i2s;
89 struct resource *res;
90 unsigned int flag, i, addr;
91 int ret;
92
93 flag = snd_amd_acp_find_config(pci);
94 if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
95 return -ENODEV;
96
97 ret = sof_pci_probe(pci, pci_id);
98 if (ret != 0)
99 return ret;
100
101 dmic_dev = platform_device_register_data(dev, "dmic-codec", PLATFORM_DEVID_NONE, NULL, 0);
102 if (IS_ERR(dmic_dev)) {
103 dev_err(dev, "failed to create DMIC device\n");
104 sof_pci_remove(pci);
105 return PTR_ERR(dmic_dev);
106 }
107
108 /* Register platform device only if flag set to FLAG_AMD_SOF_ONLY_DMIC */
109 if (flag != FLAG_AMD_SOF_ONLY_DMIC)
110 return 0;
111
112 addr = pci_resource_start(pci, 0);
113 res = devm_kzalloc(&pci->dev, sizeof(struct resource) * ARRAY_SIZE(renoir_res), GFP_KERNEL);
114 if (!res) {
115 sof_pci_remove(pci);
116 platform_device_unregister(dmic_dev);
117 return -ENOMEM;
118 }
119
120 res_i2s = renoir_res;
121 for (i = 0; i < ARRAY_SIZE(renoir_res); i++, res_i2s++) {
122 res[i].name = res_i2s->name;
123 res[i].flags = res_i2s->flags;
124 res[i].start = addr + res_i2s->start;
125 res[i].end = addr + res_i2s->end;
126 if (res_i2s->flags == IORESOURCE_IRQ) {
127 res[i].start = pci->irq;
128 res[i].end = res[i].start;
129 }
130 }
131
132 memset(&pdevinfo, 0, sizeof(pdevinfo));
133
134 /*
135 * We have common PCI driver probe for ACP device but we have to support I2S without SOF
136 * for some distributions. Register platform device that will be used to support non dsp
137 * ACP's audio ends points on some machines.
138 */
139
140 pdevinfo.name = "acp_asoc_renoir";
141 pdevinfo.id = 0;
142 pdevinfo.parent = &pci->dev;
143 pdevinfo.num_res = ARRAY_SIZE(renoir_res);
144 pdevinfo.res = &res[0];
145
146 pdev = platform_device_register_full(&pdevinfo);
147 if (IS_ERR(pdev)) {
148 dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name);
149 sof_pci_remove(pci);
150 platform_device_unregister(dmic_dev);
151 ret = PTR_ERR(pdev);
152 }
153
154 return ret;
155};
156
157static void acp_pci_rn_remove(struct pci_dev *pci)
158{
159 if (dmic_dev)
160 platform_device_unregister(dmic_dev);
161 if (pdev)
162 platform_device_unregister(pdev);
163
164 return sof_pci_remove(pci);
165}
166
167/* PCI IDs */
168static const struct pci_device_id rn_pci_ids[] = {
169 { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_PCI_DEV_ID),
170 .driver_data = (unsigned long)&renoir_desc},
171 { 0, }
172};
173MODULE_DEVICE_TABLE(pci, rn_pci_ids);
174
175/* pci_driver definition */
176static struct pci_driver snd_sof_pci_amd_rn_driver = {
177 .name = KBUILD_MODNAME,
178 .id_table = rn_pci_ids,
179 .probe = acp_pci_rn_probe,
180 .remove = acp_pci_rn_remove,
181 .driver = {
182 .pm = &sof_pci_pm,
183 },
184};
185module_pci_driver(snd_sof_pci_amd_rn_driver);
186
187MODULE_LICENSE("Dual BSD/GPL");
188MODULE_IMPORT_NS(SND_SOC_SOF_AMD_COMMON);
189MODULE_IMPORT_NS(SND_SOC_SOF_PCI_DEV);