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.
7//
8// Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
9// V sujith kumar Reddy <Vsujithkumar.Reddy@amd.com>
10/*
11 * Hardware interface for Renoir ACP block
12 */
13
14#include <linux/platform_device.h>
15#include <linux/module.h>
16#include <linux/err.h>
17#include <linux/io.h>
18#include <sound/pcm_params.h>
19#include <sound/soc.h>
20#include <sound/soc-dai.h>
21#include <linux/dma-mapping.h>
22#include <linux/pci.h>
23#include <linux/pm_runtime.h>
24
25#include "amd.h"
26#include "../mach-config.h"
27#include "acp-mach.h"
28
29#define DRV_NAME "acp_asoc_rembrandt"
30
31#define MP1_C2PMSG_69 0x3B10A14
32#define MP1_C2PMSG_85 0x3B10A54
33#define MP1_C2PMSG_93 0x3B10A74
34#define HOST_BRIDGE_ID 0x14B5
35
36static struct acp_resource rsrc = {
37 .offset = 0,
38 .no_of_ctrls = 2,
39 .irqp_used = 1,
40 .soc_mclk = true,
41 .irq_reg_offset = 0x1a00,
42 .i2s_pin_cfg_offset = 0x1440,
43 .i2s_mode = 0x0a,
44 .scratch_reg_offset = 0x12800,
45 .sram_pte_offset = 0x03802800,
46};
47
48static struct snd_soc_acpi_codecs amp_rt1019 = {
49 .num_codecs = 1,
50 .codecs = {"10EC1019"}
51};
52
53static struct snd_soc_acpi_codecs amp_max = {
54 .num_codecs = 1,
55 .codecs = {"MX98360A"}
56};
57
58static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
59 {
60 .id = "10508825",
61 .drv_name = "rmb-nau8825-max",
62 .machine_quirk = snd_soc_acpi_codec_list,
63 .quirk_data = &amp_max,
64 },
65 {
66 .id = "AMDI0007",
67 .drv_name = "rembrandt-acp",
68 },
69 {
70 .id = "RTL5682",
71 .drv_name = "rmb-rt5682s-rt1019",
72 .machine_quirk = snd_soc_acpi_codec_list,
73 .quirk_data = &amp_rt1019,
74 },
75 {},
76};
77
78static struct snd_soc_dai_driver acp_rmb_dai[] = {
79{
80 .name = "acp-i2s-sp",
81 .id = I2S_SP_INSTANCE,
82 .playback = {
83 .stream_name = "I2S SP Playback",
84 .rates = SNDRV_PCM_RATE_8000_96000,
85 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
86 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
87 .channels_min = 2,
88 .channels_max = 8,
89 .rate_min = 8000,
90 .rate_max = 96000,
91 },
92 .capture = {
93 .stream_name = "I2S SP Capture",
94 .rates = SNDRV_PCM_RATE_8000_48000,
95 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
96 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
97 .channels_min = 2,
98 .channels_max = 2,
99 .rate_min = 8000,
100 .rate_max = 48000,
101 },
102 .ops = &asoc_acp_cpu_dai_ops,
103},
104{
105 .name = "acp-i2s-bt",
106 .id = I2S_BT_INSTANCE,
107 .playback = {
108 .stream_name = "I2S BT Playback",
109 .rates = SNDRV_PCM_RATE_8000_96000,
110 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
111 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
112 .channels_min = 2,
113 .channels_max = 8,
114 .rate_min = 8000,
115 .rate_max = 96000,
116 },
117 .capture = {
118 .stream_name = "I2S BT Capture",
119 .rates = SNDRV_PCM_RATE_8000_48000,
120 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
121 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
122 .channels_min = 2,
123 .channels_max = 2,
124 .rate_min = 8000,
125 .rate_max = 48000,
126 },
127 .ops = &asoc_acp_cpu_dai_ops,
128},
129{
130 .name = "acp-i2s-hs",
131 .id = I2S_HS_INSTANCE,
132 .playback = {
133 .stream_name = "I2S HS Playback",
134 .rates = SNDRV_PCM_RATE_8000_96000,
135 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
136 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
137 .channels_min = 2,
138 .channels_max = 8,
139 .rate_min = 8000,
140 .rate_max = 96000,
141 },
142 .capture = {
143 .stream_name = "I2S HS Capture",
144 .rates = SNDRV_PCM_RATE_8000_48000,
145 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
146 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
147 .channels_min = 2,
148 .channels_max = 8,
149 .rate_min = 8000,
150 .rate_max = 48000,
151 },
152 .ops = &asoc_acp_cpu_dai_ops,
153},
154{
155 .name = "acp-pdm-dmic",
156 .id = DMIC_INSTANCE,
157 .capture = {
158 .rates = SNDRV_PCM_RATE_8000_48000,
159 .formats = SNDRV_PCM_FMTBIT_S32_LE,
160 .channels_min = 2,
161 .channels_max = 2,
162 .rate_min = 8000,
163 .rate_max = 48000,
164 },
165 .ops = &acp_dmic_dai_ops,
166},
167};
168
169static int acp6x_master_clock_generate(struct device *dev)
170{
171 int data = 0;
172 struct pci_dev *smn_dev;
173
174 smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, HOST_BRIDGE_ID, NULL);
175 if (!smn_dev) {
176 dev_err(dev, "Failed to get host bridge device\n");
177 return -ENODEV;
178 }
179
180 smn_write(dev: smn_dev, MP1_C2PMSG_93, data: 0);
181 smn_write(dev: smn_dev, MP1_C2PMSG_85, data: 0xC4);
182 smn_write(dev: smn_dev, MP1_C2PMSG_69, data: 0x4);
183 read_poll_timeout(smn_read, data, data, DELAY_US,
184 ACP_TIMEOUT, false, smn_dev, MP1_C2PMSG_93);
185 return 0;
186}
187
188static int rembrandt_audio_probe(struct platform_device *pdev)
189{
190 struct device *dev = &pdev->dev;
191 struct acp_chip_info *chip;
192 struct acp_dev_data *adata;
193 struct resource *res;
194 u32 ret;
195
196 chip = dev_get_platdata(dev: &pdev->dev);
197 if (!chip || !chip->base) {
198 dev_err(&pdev->dev, "ACP chip data is NULL\n");
199 return -ENODEV;
200 }
201
202 if (chip->acp_rev != ACP6X_DEV) {
203 dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
204 return -ENODEV;
205 }
206
207 adata = devm_kzalloc(dev, size: sizeof(struct acp_dev_data), GFP_KERNEL);
208 if (!adata)
209 return -ENOMEM;
210
211 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
212 if (!res) {
213 dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
214 return -ENODEV;
215 }
216
217 adata->acp_base = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res));
218 if (!adata->acp_base)
219 return -ENOMEM;
220
221 res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
222 if (!res) {
223 dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
224 return -ENODEV;
225 }
226
227 adata->i2s_irq = res->start;
228 adata->dev = dev;
229 adata->dai_driver = acp_rmb_dai;
230 adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
231 adata->rsrc = &rsrc;
232 adata->platform = REMBRANDT;
233 adata->flag = chip->flag;
234 adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
235 acp_machine_select(adata);
236
237 dev_set_drvdata(dev, data: adata);
238
239 if (chip->flag != FLAG_AMD_LEGACY_ONLY_DMIC) {
240 ret = acp6x_master_clock_generate(dev);
241 if (ret)
242 return ret;
243 }
244 acp_enable_interrupts(adata);
245 acp_platform_register(dev);
246 pm_runtime_set_autosuspend_delay(dev: &pdev->dev, ACP_SUSPEND_DELAY_MS);
247 pm_runtime_use_autosuspend(dev: &pdev->dev);
248 pm_runtime_mark_last_busy(dev: &pdev->dev);
249 pm_runtime_set_active(dev: &pdev->dev);
250 pm_runtime_enable(dev: &pdev->dev);
251 return 0;
252}
253
254static void rembrandt_audio_remove(struct platform_device *pdev)
255{
256 struct device *dev = &pdev->dev;
257 struct acp_dev_data *adata = dev_get_drvdata(dev);
258
259 acp_disable_interrupts(adata);
260 acp_platform_unregister(dev);
261 pm_runtime_disable(dev: &pdev->dev);
262}
263
264static int __maybe_unused rmb_pcm_resume(struct device *dev)
265{
266 struct acp_dev_data *adata = dev_get_drvdata(dev);
267 struct acp_stream *stream;
268 struct snd_pcm_substream *substream;
269 snd_pcm_uframes_t buf_in_frames;
270 u64 buf_size;
271
272 if (adata->flag != FLAG_AMD_LEGACY_ONLY_DMIC)
273 acp6x_master_clock_generate(dev);
274
275 spin_lock(lock: &adata->acp_lock);
276 list_for_each_entry(stream, &adata->stream_list, list) {
277 substream = stream->substream;
278 if (substream && substream->runtime) {
279 buf_in_frames = (substream->runtime->buffer_size);
280 buf_size = frames_to_bytes(runtime: substream->runtime, size: buf_in_frames);
281 config_pte_for_stream(adata, stream);
282 config_acp_dma(adata, stream, size: buf_size);
283 if (stream->dai_id)
284 restore_acp_i2s_params(substream, adata, stream);
285 else
286 restore_acp_pdm_params(substream, adata);
287 }
288 }
289 spin_unlock(lock: &adata->acp_lock);
290 return 0;
291}
292
293static const struct dev_pm_ops rmb_dma_pm_ops = {
294 SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume)
295};
296
297static struct platform_driver rembrandt_driver = {
298 .probe = rembrandt_audio_probe,
299 .remove_new = rembrandt_audio_remove,
300 .driver = {
301 .name = "acp_asoc_rembrandt",
302 .pm = &rmb_dma_pm_ops,
303 },
304};
305
306module_platform_driver(rembrandt_driver);
307
308MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
309MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
310MODULE_LICENSE("Dual BSD/GPL");
311MODULE_ALIAS("platform:" DRV_NAME);
312

source code of linux/sound/soc/amd/acp/acp-rembrandt.c