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 | |
36 | static 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 | |
48 | static struct snd_soc_acpi_codecs amp_rt1019 = { |
49 | .num_codecs = 1, |
50 | .codecs = {"10EC1019" } |
51 | }; |
52 | |
53 | static struct snd_soc_acpi_codecs amp_max = { |
54 | .num_codecs = 1, |
55 | .codecs = {"MX98360A" } |
56 | }; |
57 | |
58 | static 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 = &_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 = &_rt1019, |
74 | }, |
75 | {}, |
76 | }; |
77 | |
78 | static 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 | |
169 | static 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 | |
188 | static 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 | |
254 | static 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 | |
264 | static 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 | |
293 | static const struct dev_pm_ops rmb_dma_pm_ops = { |
294 | SET_SYSTEM_SLEEP_PM_OPS(NULL, rmb_pcm_resume) |
295 | }; |
296 | |
297 | static 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 | |
306 | module_platform_driver(rembrandt_driver); |
307 | |
308 | MODULE_DESCRIPTION("AMD ACP Rembrandt Driver" ); |
309 | MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); |
310 | MODULE_LICENSE("Dual BSD/GPL" ); |
311 | MODULE_ALIAS("platform:" DRV_NAME); |
312 | |