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