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 | #include <linux/pm_runtime.h> |
24 | |
25 | #include "amd.h" |
26 | #include "acp-mach.h" |
27 | |
28 | #define DRV_NAME "acp_asoc_renoir" |
29 | |
30 | static struct acp_resource rsrc = { |
31 | .offset = 20, |
32 | .no_of_ctrls = 1, |
33 | .irqp_used = 0, |
34 | .irq_reg_offset = 0x1800, |
35 | .i2s_pin_cfg_offset = 0x1400, |
36 | .i2s_mode = 0x04, |
37 | .scratch_reg_offset = 0x12800, |
38 | .sram_pte_offset = 0x02052800, |
39 | }; |
40 | |
41 | static struct snd_soc_acpi_codecs amp_rt1019 = { |
42 | .num_codecs = 1, |
43 | .codecs = {"10EC1019" } |
44 | }; |
45 | |
46 | static struct snd_soc_acpi_codecs amp_max = { |
47 | .num_codecs = 1, |
48 | .codecs = {"MX98360A" } |
49 | }; |
50 | |
51 | static struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[] = { |
52 | { |
53 | .id = "10EC5682" , |
54 | .drv_name = "acp3xalc56821019" , |
55 | .machine_quirk = snd_soc_acpi_codec_list, |
56 | .quirk_data = &_rt1019, |
57 | }, |
58 | { |
59 | .id = "RTL5682" , |
60 | .drv_name = "acp3xalc5682sm98360" , |
61 | .machine_quirk = snd_soc_acpi_codec_list, |
62 | .quirk_data = &_max, |
63 | }, |
64 | { |
65 | .id = "RTL5682" , |
66 | .drv_name = "acp3xalc5682s1019" , |
67 | .machine_quirk = snd_soc_acpi_codec_list, |
68 | .quirk_data = &_rt1019, |
69 | }, |
70 | { |
71 | .id = "AMDI1019" , |
72 | .drv_name = "renoir-acp" , |
73 | }, |
74 | { |
75 | .id = "ESSX8336" , |
76 | .drv_name = "acp3x-es83xx" , |
77 | }, |
78 | {}, |
79 | }; |
80 | |
81 | static struct snd_soc_dai_driver acp_renoir_dai[] = { |
82 | { |
83 | .name = "acp-i2s-sp" , |
84 | .id = I2S_SP_INSTANCE, |
85 | .playback = { |
86 | .stream_name = "I2S SP Playback" , |
87 | .rates = SNDRV_PCM_RATE_8000_96000, |
88 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
89 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, |
90 | .channels_min = 2, |
91 | .channels_max = 8, |
92 | .rate_min = 8000, |
93 | .rate_max = 96000, |
94 | }, |
95 | .capture = { |
96 | .stream_name = "I2S SP Capture" , |
97 | .rates = SNDRV_PCM_RATE_8000_48000, |
98 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
99 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, |
100 | .channels_min = 2, |
101 | .channels_max = 2, |
102 | .rate_min = 8000, |
103 | .rate_max = 48000, |
104 | }, |
105 | .ops = &asoc_acp_cpu_dai_ops, |
106 | }, |
107 | { |
108 | .name = "acp-i2s-bt" , |
109 | .id = I2S_BT_INSTANCE, |
110 | .playback = { |
111 | .stream_name = "I2S BT Playback" , |
112 | .rates = SNDRV_PCM_RATE_8000_96000, |
113 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
114 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, |
115 | .channels_min = 2, |
116 | .channels_max = 8, |
117 | .rate_min = 8000, |
118 | .rate_max = 96000, |
119 | }, |
120 | .capture = { |
121 | .stream_name = "I2S BT Capture" , |
122 | .rates = SNDRV_PCM_RATE_8000_48000, |
123 | .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | |
124 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE, |
125 | .channels_min = 2, |
126 | .channels_max = 2, |
127 | .rate_min = 8000, |
128 | .rate_max = 48000, |
129 | }, |
130 | .ops = &asoc_acp_cpu_dai_ops, |
131 | }, |
132 | { |
133 | .name = "acp-pdm-dmic" , |
134 | .id = DMIC_INSTANCE, |
135 | .capture = { |
136 | .rates = SNDRV_PCM_RATE_8000_48000, |
137 | .formats = SNDRV_PCM_FMTBIT_S32_LE, |
138 | .channels_min = 2, |
139 | .channels_max = 2, |
140 | .rate_min = 8000, |
141 | .rate_max = 48000, |
142 | }, |
143 | .ops = &acp_dmic_dai_ops, |
144 | }, |
145 | }; |
146 | |
147 | |
148 | static int renoir_audio_probe(struct platform_device *pdev) |
149 | { |
150 | struct device *dev = &pdev->dev; |
151 | struct acp_chip_info *chip; |
152 | struct acp_dev_data *adata; |
153 | struct resource *res; |
154 | int ret; |
155 | |
156 | chip = dev_get_platdata(dev: &pdev->dev); |
157 | if (!chip || !chip->base) { |
158 | dev_err(&pdev->dev, "ACP chip data is NULL\n" ); |
159 | return -ENODEV; |
160 | } |
161 | |
162 | if (chip->acp_rev != ACP3X_DEV) { |
163 | dev_err(&pdev->dev, "Un-supported ACP Revision %d\n" , chip->acp_rev); |
164 | return -ENODEV; |
165 | } |
166 | |
167 | adata = devm_kzalloc(dev, size: sizeof(struct acp_dev_data), GFP_KERNEL); |
168 | if (!adata) |
169 | return -ENOMEM; |
170 | |
171 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem" ); |
172 | if (!res) { |
173 | dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n" ); |
174 | return -ENODEV; |
175 | } |
176 | |
177 | adata->acp_base = devm_ioremap(dev: &pdev->dev, offset: res->start, size: resource_size(res)); |
178 | if (!adata->acp_base) |
179 | return -ENOMEM; |
180 | |
181 | ret = platform_get_irq_byname(pdev, "acp_dai_irq" ); |
182 | if (ret < 0) |
183 | return ret; |
184 | adata->i2s_irq = ret; |
185 | |
186 | adata->dev = dev; |
187 | adata->dai_driver = acp_renoir_dai; |
188 | adata->num_dai = ARRAY_SIZE(acp_renoir_dai); |
189 | adata->rsrc = &rsrc; |
190 | adata->platform = RENOIR; |
191 | adata->flag = chip->flag; |
192 | |
193 | adata->machines = snd_soc_acpi_amd_acp_machines; |
194 | acp_machine_select(adata); |
195 | |
196 | dev_set_drvdata(dev, data: adata); |
197 | acp_enable_interrupts(adata); |
198 | acp_platform_register(dev); |
199 | |
200 | pm_runtime_set_autosuspend_delay(dev: &pdev->dev, ACP_SUSPEND_DELAY_MS); |
201 | pm_runtime_use_autosuspend(dev: &pdev->dev); |
202 | pm_runtime_mark_last_busy(dev: &pdev->dev); |
203 | pm_runtime_set_active(dev: &pdev->dev); |
204 | pm_runtime_enable(dev: &pdev->dev); |
205 | return 0; |
206 | } |
207 | |
208 | static void renoir_audio_remove(struct platform_device *pdev) |
209 | { |
210 | struct device *dev = &pdev->dev; |
211 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
212 | |
213 | acp_disable_interrupts(adata); |
214 | acp_platform_unregister(dev); |
215 | } |
216 | |
217 | static int __maybe_unused rn_pcm_resume(struct device *dev) |
218 | { |
219 | struct acp_dev_data *adata = dev_get_drvdata(dev); |
220 | struct acp_stream *stream; |
221 | struct snd_pcm_substream *substream; |
222 | snd_pcm_uframes_t buf_in_frames; |
223 | u64 buf_size; |
224 | |
225 | spin_lock(lock: &adata->acp_lock); |
226 | list_for_each_entry(stream, &adata->stream_list, list) { |
227 | substream = stream->substream; |
228 | if (substream && substream->runtime) { |
229 | buf_in_frames = (substream->runtime->buffer_size); |
230 | buf_size = frames_to_bytes(runtime: substream->runtime, size: buf_in_frames); |
231 | config_pte_for_stream(adata, stream); |
232 | config_acp_dma(adata, stream, size: buf_size); |
233 | if (stream->dai_id) |
234 | restore_acp_i2s_params(substream, adata, stream); |
235 | else |
236 | restore_acp_pdm_params(substream, adata); |
237 | } |
238 | } |
239 | spin_unlock(lock: &adata->acp_lock); |
240 | return 0; |
241 | } |
242 | |
243 | static const struct dev_pm_ops rn_dma_pm_ops = { |
244 | SET_SYSTEM_SLEEP_PM_OPS(NULL, rn_pcm_resume) |
245 | }; |
246 | |
247 | static struct platform_driver renoir_driver = { |
248 | .probe = renoir_audio_probe, |
249 | .remove_new = renoir_audio_remove, |
250 | .driver = { |
251 | .name = "acp_asoc_renoir" , |
252 | .pm = &rn_dma_pm_ops, |
253 | }, |
254 | }; |
255 | |
256 | module_platform_driver(renoir_driver); |
257 | |
258 | MODULE_DESCRIPTION("AMD ACP Renoir Driver" ); |
259 | MODULE_IMPORT_NS(SND_SOC_ACP_COMMON); |
260 | MODULE_LICENSE("Dual BSD/GPL" ); |
261 | MODULE_ALIAS("platform:" DRV_NAME); |
262 | |