Commit | Line | Data |
---|---|---|
58c8c843 AKP |
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. | |
7 | // | |
8 | // Authors: Ajit Kumar Pandey <AjitKumar.Pandey@amd.com> | |
9 | // | |
10 | ||
11 | /* | |
12 | * Hardware interface for Renoir ACP block | |
13 | */ | |
14 | ||
15 | #include <linux/platform_device.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/io.h> | |
19 | #include <sound/pcm_params.h> | |
20 | #include <sound/soc.h> | |
21 | #include <sound/soc-dai.h> | |
22 | #include <linux/dma-mapping.h> | |
23 | ||
24 | #include "amd.h" | |
25 | ||
26 | #define DRV_NAME "acp_asoc_renoir" | |
27 | ||
6a75585a AKP |
28 | #define ACP_SOFT_RST_DONE_MASK 0x00010001 |
29 | ||
30 | #define ACP_PWR_ON_MASK 0x01 | |
31 | #define ACP_PWR_OFF_MASK 0x00 | |
32 | #define ACP_PGFSM_STAT_MASK 0x03 | |
33 | #define ACP_POWERED_ON 0x00 | |
34 | #define ACP_PWR_ON_IN_PROGRESS 0x01 | |
35 | #define ACP_POWERED_OFF 0x02 | |
36 | #define DELAY_US 5 | |
37 | #define ACP_TIMEOUT 500 | |
38 | ||
39 | #define ACP_ERROR_MASK 0x20000000 | |
40 | #define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF | |
41 | ||
b24484c1 R |
42 | static struct acp_resource rsrc = { |
43 | .offset = 20, | |
44 | .no_of_ctrls = 1, | |
45 | .irqp_used = 0, | |
46 | .irq_reg_offset = 0x1800, | |
47 | .i2s_pin_cfg_offset = 0x1400, | |
48 | .i2s_mode = 0x04, | |
49 | .scratch_reg_offset = 0x12800, | |
50 | .sram_pte_offset = 0x02052800, | |
51 | }; | |
52 | ||
8a8e1b90 AKP |
53 | static struct snd_soc_acpi_codecs amp_rt1019 = { |
54 | .num_codecs = 1, | |
55 | .codecs = {"10EC1019"} | |
56 | }; | |
57 | ||
eee33bac AKP |
58 | static struct snd_soc_acpi_codecs amp_max = { |
59 | .num_codecs = 1, | |
60 | .codecs = {"MX98360A"} | |
61 | }; | |
62 | ||
8a8e1b90 AKP |
63 | static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { |
64 | { | |
65 | .id = "10EC5682", | |
abdcf728 | 66 | .drv_name = "acp3xalc56821019", |
8a8e1b90 AKP |
67 | .machine_quirk = snd_soc_acpi_codec_list, |
68 | .quirk_data = &_rt1019, | |
69 | }, | |
eee33bac AKP |
70 | { |
71 | .id = "RTL5682", | |
72 | .drv_name = "acp3xalc5682sm98360", | |
73 | .machine_quirk = snd_soc_acpi_codec_list, | |
74 | .quirk_data = &_max, | |
75 | }, | |
76 | { | |
77 | .id = "RTL5682", | |
78 | .drv_name = "acp3xalc5682s1019", | |
79 | .machine_quirk = snd_soc_acpi_codec_list, | |
80 | .quirk_data = &_rt1019, | |
81 | }, | |
8a8e1b90 AKP |
82 | { |
83 | .id = "AMDI1019", | |
84 | .drv_name = "renoir-acp", | |
85 | }, | |
86 | {}, | |
87 | }; | |
88 | ||
58c8c843 AKP |
89 | static struct snd_soc_dai_driver acp_renoir_dai[] = { |
90 | { | |
91 | .name = "acp-i2s-sp", | |
92 | .id = I2S_SP_INSTANCE, | |
93 | .playback = { | |
94 | .stream_name = "I2S SP Playback", | |
95 | .rates = SNDRV_PCM_RATE_8000_96000, | |
96 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | | |
97 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, | |
98 | .channels_min = 2, | |
99 | .channels_max = 8, | |
100 | .rate_min = 8000, | |
101 | .rate_max = 96000, | |
102 | }, | |
103 | .capture = { | |
104 | .stream_name = "I2S SP Capture", | |
105 | .rates = SNDRV_PCM_RATE_8000_48000, | |
106 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | | |
107 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, | |
108 | .channels_min = 2, | |
109 | .channels_max = 2, | |
110 | .rate_min = 8000, | |
111 | .rate_max = 48000, | |
112 | }, | |
113 | .ops = &asoc_acp_cpu_dai_ops, | |
114 | .probe = &asoc_acp_i2s_probe, | |
115 | }, | |
116 | { | |
117 | .name = "acp-i2s-bt", | |
118 | .id = I2S_BT_INSTANCE, | |
119 | .playback = { | |
120 | .stream_name = "I2S BT Playback", | |
121 | .rates = SNDRV_PCM_RATE_8000_96000, | |
122 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | | |
123 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, | |
124 | .channels_min = 2, | |
125 | .channels_max = 8, | |
126 | .rate_min = 8000, | |
127 | .rate_max = 96000, | |
128 | }, | |
129 | .capture = { | |
130 | .stream_name = "I2S BT Capture", | |
131 | .rates = SNDRV_PCM_RATE_8000_48000, | |
132 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | | |
133 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, | |
134 | .channels_min = 2, | |
135 | .channels_max = 2, | |
136 | .rate_min = 8000, | |
137 | .rate_max = 48000, | |
138 | }, | |
139 | .ops = &asoc_acp_cpu_dai_ops, | |
140 | .probe = &asoc_acp_i2s_probe, | |
141 | }, | |
def6dc25 AKP |
142 | { |
143 | .name = "acp-pdm-dmic", | |
144 | .id = DMIC_INSTANCE, | |
145 | .capture = { | |
146 | .rates = SNDRV_PCM_RATE_8000_48000, | |
147 | .formats = SNDRV_PCM_FMTBIT_S32_LE, | |
148 | .channels_min = 2, | |
149 | .channels_max = 2, | |
150 | .rate_min = 8000, | |
151 | .rate_max = 48000, | |
152 | }, | |
153 | .ops = &acp_dmic_dai_ops, | |
154 | }, | |
58c8c843 AKP |
155 | }; |
156 | ||
6a75585a AKP |
157 | static int acp3x_power_on(void __iomem *base) |
158 | { | |
159 | u32 val; | |
160 | ||
161 | val = readl(base + ACP_PGFSM_STATUS); | |
162 | ||
163 | if (val == ACP_POWERED_ON) | |
164 | return 0; | |
165 | ||
166 | if ((val & ACP_PGFSM_STAT_MASK) != ACP_PWR_ON_IN_PROGRESS) | |
167 | writel(ACP_PWR_ON_MASK, base + ACP_PGFSM_CONTROL); | |
168 | ||
169 | return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT); | |
170 | } | |
171 | ||
172 | static int acp3x_power_off(void __iomem *base) | |
173 | { | |
174 | u32 val; | |
175 | ||
176 | writel(ACP_PWR_OFF_MASK, base + ACP_PGFSM_CONTROL); | |
177 | ||
178 | return readl_poll_timeout(base + ACP_PGFSM_STATUS, val, | |
179 | (val & ACP_PGFSM_STAT_MASK) == ACP_POWERED_OFF, | |
180 | DELAY_US, ACP_TIMEOUT); | |
181 | } | |
182 | ||
183 | static int acp3x_reset(void __iomem *base) | |
184 | { | |
185 | u32 val; | |
186 | int ret; | |
187 | ||
188 | writel(1, base + ACP_SOFT_RESET); | |
189 | ||
190 | ret = readl_poll_timeout(base + ACP_SOFT_RESET, val, val & ACP_SOFT_RST_DONE_MASK, | |
191 | DELAY_US, ACP_TIMEOUT); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
195 | writel(0, base + ACP_SOFT_RESET); | |
196 | ||
197 | return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT); | |
198 | } | |
199 | ||
b24484c1 | 200 | static void acp3x_enable_interrupts(struct acp_dev_data *adata) |
6a75585a | 201 | { |
b24484c1 | 202 | struct acp_resource *rsrc = adata->rsrc; |
6a75585a AKP |
203 | u32 ext_intr_ctrl; |
204 | ||
b24484c1 R |
205 | writel(0x01, ACP_EXTERNAL_INTR_ENB(adata)); |
206 | ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); | |
6a75585a | 207 | ext_intr_ctrl |= ACP_ERROR_MASK; |
b24484c1 | 208 | writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used)); |
6a75585a AKP |
209 | } |
210 | ||
b24484c1 | 211 | static void acp3x_disable_interrupts(struct acp_dev_data *adata) |
6a75585a | 212 | { |
b24484c1 R |
213 | struct acp_resource *rsrc = adata->rsrc; |
214 | ||
215 | writel(ACP_EXT_INTR_STAT_CLEAR_MASK, | |
216 | ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used)); | |
217 | writel(0x00, ACP_EXTERNAL_INTR_ENB(adata)); | |
6a75585a AKP |
218 | } |
219 | ||
220 | static int rn_acp_init(void __iomem *base) | |
221 | { | |
222 | int ret; | |
223 | ||
224 | /* power on */ | |
225 | ret = acp3x_power_on(base); | |
226 | if (ret) | |
227 | return ret; | |
228 | ||
229 | writel(0x01, base + ACP_CONTROL); | |
230 | ||
231 | /* Reset */ | |
232 | ret = acp3x_reset(base); | |
233 | if (ret) | |
234 | return ret; | |
235 | ||
6a75585a AKP |
236 | return 0; |
237 | } | |
238 | ||
239 | static int rn_acp_deinit(void __iomem *base) | |
240 | { | |
241 | int ret = 0; | |
242 | ||
6a75585a AKP |
243 | /* Reset */ |
244 | ret = acp3x_reset(base); | |
245 | if (ret) | |
246 | return ret; | |
247 | ||
248 | writel(0x00, base + ACP_CONTROL); | |
249 | ||
250 | /* power off */ | |
251 | ret = acp3x_power_off(base); | |
252 | if (ret) | |
253 | return ret; | |
254 | ||
255 | return 0; | |
256 | } | |
58c8c843 AKP |
257 | static int renoir_audio_probe(struct platform_device *pdev) |
258 | { | |
259 | struct device *dev = &pdev->dev; | |
6a75585a | 260 | struct acp_chip_info *chip; |
58c8c843 AKP |
261 | struct acp_dev_data *adata; |
262 | struct resource *res; | |
6a75585a AKP |
263 | int ret; |
264 | ||
265 | chip = dev_get_platdata(&pdev->dev); | |
266 | if (!chip || !chip->base) { | |
267 | dev_err(&pdev->dev, "ACP chip data is NULL\n"); | |
268 | return -ENODEV; | |
269 | } | |
270 | ||
271 | if (chip->acp_rev != ACP3X_DEV) { | |
272 | dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev); | |
273 | return -ENODEV; | |
274 | } | |
275 | ||
276 | ret = rn_acp_init(chip->base); | |
277 | if (ret) { | |
278 | dev_err(&pdev->dev, "ACP Init failed\n"); | |
279 | return -EINVAL; | |
280 | } | |
58c8c843 AKP |
281 | |
282 | adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL); | |
283 | if (!adata) | |
284 | return -ENOMEM; | |
285 | ||
286 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem"); | |
287 | if (!res) { | |
288 | dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n"); | |
289 | return -ENODEV; | |
290 | } | |
291 | ||
292 | adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | |
293 | if (!adata->acp_base) | |
294 | return -ENOMEM; | |
295 | ||
899a9a7f DC |
296 | ret = platform_get_irq_byname(pdev, "acp_dai_irq"); |
297 | if (ret < 0) | |
298 | return ret; | |
299 | adata->i2s_irq = ret; | |
58c8c843 | 300 | |
58c8c843 AKP |
301 | adata->dev = dev; |
302 | adata->dai_driver = acp_renoir_dai; | |
303 | adata->num_dai = ARRAY_SIZE(acp_renoir_dai); | |
b24484c1 | 304 | adata->rsrc = &rsrc; |
58c8c843 | 305 | |
e646b51f AKP |
306 | adata->machines = snd_soc_acpi_amd_acp_machines; |
307 | acp_machine_select(adata); | |
308 | ||
58c8c843 | 309 | dev_set_drvdata(dev, adata); |
b24484c1 | 310 | acp3x_enable_interrupts(adata); |
58c8c843 AKP |
311 | acp_platform_register(dev); |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
da8a3ceb | 316 | static void renoir_audio_remove(struct platform_device *pdev) |
58c8c843 AKP |
317 | { |
318 | struct device *dev = &pdev->dev; | |
b24484c1 | 319 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
6a75585a AKP |
320 | struct acp_chip_info *chip; |
321 | int ret; | |
322 | ||
323 | chip = dev_get_platdata(&pdev->dev); | |
6a75585a | 324 | |
b24484c1 R |
325 | acp3x_disable_interrupts(adata); |
326 | ||
6a75585a | 327 | ret = rn_acp_deinit(chip->base); |
0deb0039 UKK |
328 | if (ret) |
329 | dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret)); | |
58c8c843 AKP |
330 | |
331 | acp_platform_unregister(dev); | |
58c8c843 AKP |
332 | } |
333 | ||
334 | static struct platform_driver renoir_driver = { | |
335 | .probe = renoir_audio_probe, | |
da8a3ceb | 336 | .remove_new = renoir_audio_remove, |
58c8c843 AKP |
337 | .driver = { |
338 | .name = "acp_asoc_renoir", | |
339 | }, | |
340 | }; | |
341 | ||
342 | module_platform_driver(renoir_driver); | |
343 | ||
344 | MODULE_DESCRIPTION("AMD ACP Renoir Driver"); | |
345 | MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); | |
346 | MODULE_LICENSE("Dual BSD/GPL"); | |
347 | MODULE_ALIAS("platform:" DRV_NAME); |