1 // SPDX-License-Identifier: GPL-2.0+
2 // Copyright 2018-2021 NXP
5 #include <linux/clk-provider.h>
6 #include <linux/delay.h>
7 #include <linux/dmaengine.h>
8 #include <linux/module.h>
10 #include <linux/pm_runtime.h>
11 #include <linux/rpmsg.h>
12 #include <linux/slab.h>
13 #include <sound/core.h>
14 #include <sound/dmaengine_pcm.h>
15 #include <sound/pcm_params.h>
17 #include "fsl_rpmsg.h"
20 #define FSL_RPMSG_RATES (SNDRV_PCM_RATE_8000 | \
21 SNDRV_PCM_RATE_16000 | \
23 #define FSL_RPMSG_FORMATS SNDRV_PCM_FMTBIT_S16_LE
25 /* 192kHz/32bit/2ch/60s size is 0x574e00 */
26 #define LPA_LARGE_BUFFER_SIZE (0x6000000)
28 static const unsigned int fsl_rpmsg_rates
[] = {
29 8000, 11025, 16000, 22050, 44100,
30 32000, 48000, 96000, 88200, 176400, 192000,
31 352800, 384000, 705600, 768000, 1411200, 2822400,
34 static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints
= {
35 .count
= ARRAY_SIZE(fsl_rpmsg_rates
),
36 .list
= fsl_rpmsg_rates
,
39 static int fsl_rpmsg_hw_params(struct snd_pcm_substream
*substream
,
40 struct snd_pcm_hw_params
*params
,
41 struct snd_soc_dai
*dai
)
43 struct fsl_rpmsg
*rpmsg
= snd_soc_dai_get_drvdata(dai
);
44 struct clk
*p
= rpmsg
->mclk
, *pll
= NULL
, *npll
= NULL
;
45 u64 rate
= params_rate(params
);
48 /* Get current pll parent */
49 while (p
&& rpmsg
->pll8k
&& rpmsg
->pll11k
) {
50 struct clk
*pp
= clk_get_parent(p
);
52 if (clk_is_match(pp
, rpmsg
->pll8k
) ||
53 clk_is_match(pp
, rpmsg
->pll11k
)) {
60 /* Switch to another pll parent if needed. */
62 npll
= (do_div(rate
, 8000) ? rpmsg
->pll11k
: rpmsg
->pll8k
);
63 if (!clk_is_match(pll
, npll
)) {
64 ret
= clk_set_parent(p
, npll
);
66 dev_warn(dai
->dev
, "failed to set parent %s: %d\n",
67 __clk_get_name(npll
), ret
);
71 if (!(rpmsg
->mclk_streams
& BIT(substream
->stream
))) {
72 ret
= clk_prepare_enable(rpmsg
->mclk
);
74 dev_err(dai
->dev
, "failed to enable mclk: %d\n", ret
);
78 rpmsg
->mclk_streams
|= BIT(substream
->stream
);
84 static int fsl_rpmsg_hw_free(struct snd_pcm_substream
*substream
,
85 struct snd_soc_dai
*dai
)
87 struct fsl_rpmsg
*rpmsg
= snd_soc_dai_get_drvdata(dai
);
89 if (rpmsg
->mclk_streams
& BIT(substream
->stream
)) {
90 clk_disable_unprepare(rpmsg
->mclk
);
91 rpmsg
->mclk_streams
&= ~BIT(substream
->stream
);
97 static int fsl_rpmsg_startup(struct snd_pcm_substream
*substream
,
98 struct snd_soc_dai
*cpu_dai
)
102 ret
= snd_pcm_hw_constraint_list(substream
->runtime
, 0,
103 SNDRV_PCM_HW_PARAM_RATE
,
104 &fsl_rpmsg_rate_constraints
);
109 static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops
= {
110 .startup
= fsl_rpmsg_startup
,
111 .hw_params
= fsl_rpmsg_hw_params
,
112 .hw_free
= fsl_rpmsg_hw_free
,
115 static struct snd_soc_dai_driver fsl_rpmsg_dai
= {
117 .stream_name
= "CPU-Playback",
120 .rates
= SNDRV_PCM_RATE_KNOT
,
121 .formats
= FSL_RPMSG_FORMATS
,
124 .stream_name
= "CPU-Capture",
127 .rates
= SNDRV_PCM_RATE_KNOT
,
128 .formats
= FSL_RPMSG_FORMATS
,
131 .symmetric_channels
= 1,
132 .symmetric_sample_bits
= 1,
133 .ops
= &fsl_rpmsg_dai_ops
,
136 static const struct snd_soc_component_driver fsl_component
= {
140 static const struct fsl_rpmsg_soc_data imx7ulp_data
= {
141 .rates
= SNDRV_PCM_RATE_8000
| SNDRV_PCM_RATE_16000
|
142 SNDRV_PCM_RATE_48000
,
143 .formats
= SNDRV_PCM_FMTBIT_S16_LE
,
146 static const struct fsl_rpmsg_soc_data imx8mm_data
= {
147 .rates
= SNDRV_PCM_RATE_KNOT
,
148 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
149 SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_DSD_U8
|
150 SNDRV_PCM_FMTBIT_DSD_U16_LE
| SNDRV_PCM_FMTBIT_DSD_U32_LE
,
153 static const struct fsl_rpmsg_soc_data imx8mn_data
= {
154 .rates
= SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
|
155 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
|
156 SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
|
157 SNDRV_PCM_RATE_192000
,
158 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
159 SNDRV_PCM_FMTBIT_S32_LE
,
162 static const struct fsl_rpmsg_soc_data imx8mp_data
= {
163 .rates
= SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
|
164 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
|
165 SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
|
166 SNDRV_PCM_RATE_192000
,
167 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
168 SNDRV_PCM_FMTBIT_S32_LE
,
171 static const struct fsl_rpmsg_soc_data imx93_data
= {
172 .rates
= SNDRV_PCM_RATE_16000
| SNDRV_PCM_RATE_32000
|
173 SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_96000
,
174 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
175 SNDRV_PCM_FMTBIT_S32_LE
,
178 static const struct fsl_rpmsg_soc_data imx95_data
= {
179 .rates
= SNDRV_PCM_RATE_16000
| SNDRV_PCM_RATE_32000
|
180 SNDRV_PCM_RATE_44100
| SNDRV_PCM_RATE_48000
|
181 SNDRV_PCM_RATE_88200
| SNDRV_PCM_RATE_96000
,
182 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S24_LE
|
183 SNDRV_PCM_FMTBIT_S32_LE
,
186 static const struct of_device_id fsl_rpmsg_ids
[] = {
187 { .compatible
= "fsl,imx7ulp-rpmsg-audio", .data
= &imx7ulp_data
},
188 { .compatible
= "fsl,imx8mm-rpmsg-audio", .data
= &imx8mm_data
},
189 { .compatible
= "fsl,imx8mn-rpmsg-audio", .data
= &imx8mn_data
},
190 { .compatible
= "fsl,imx8mp-rpmsg-audio", .data
= &imx8mp_data
},
191 { .compatible
= "fsl,imx8ulp-rpmsg-audio", .data
= &imx7ulp_data
},
192 { .compatible
= "fsl,imx93-rpmsg-audio", .data
= &imx93_data
},
193 { .compatible
= "fsl,imx95-rpmsg-audio", .data
= &imx95_data
},
196 MODULE_DEVICE_TABLE(of
, fsl_rpmsg_ids
);
198 static int fsl_rpmsg_probe(struct platform_device
*pdev
)
200 struct device_node
*np
= pdev
->dev
.of_node
;
201 struct snd_soc_dai_driver
*dai_drv
;
202 const char *dai_name
;
203 struct fsl_rpmsg
*rpmsg
;
206 dai_drv
= devm_kzalloc(&pdev
->dev
, sizeof(struct snd_soc_dai_driver
), GFP_KERNEL
);
209 memcpy(dai_drv
, &fsl_rpmsg_dai
, sizeof(fsl_rpmsg_dai
));
211 rpmsg
= devm_kzalloc(&pdev
->dev
, sizeof(struct fsl_rpmsg
), GFP_KERNEL
);
215 rpmsg
->soc_data
= of_device_get_match_data(&pdev
->dev
);
217 if (rpmsg
->soc_data
) {
218 dai_drv
->playback
.rates
= rpmsg
->soc_data
->rates
;
219 dai_drv
->capture
.rates
= rpmsg
->soc_data
->rates
;
220 dai_drv
->playback
.formats
= rpmsg
->soc_data
->formats
;
221 dai_drv
->capture
.formats
= rpmsg
->soc_data
->formats
;
224 /* Use rpmsg channel name as cpu dai name */
225 ret
= of_property_read_string(np
, "fsl,rpmsg-channel-name", &dai_name
);
227 if (ret
== -EINVAL
) {
228 dai_name
= "rpmsg-audio-channel";
230 dev_err(&pdev
->dev
, "Failed to get rpmsg channel name: %d!\n", ret
);
234 dai_drv
->name
= dai_name
;
236 if (of_property_read_bool(np
, "fsl,enable-lpa")) {
237 rpmsg
->enable_lpa
= 1;
238 rpmsg
->buffer_size
= LPA_LARGE_BUFFER_SIZE
;
240 rpmsg
->buffer_size
= IMX_DEFAULT_DMABUF_SIZE
;
243 /* Get the optional clocks */
244 rpmsg
->ipg
= devm_clk_get_optional(&pdev
->dev
, "ipg");
245 if (IS_ERR(rpmsg
->ipg
))
246 return PTR_ERR(rpmsg
->ipg
);
248 rpmsg
->mclk
= devm_clk_get_optional(&pdev
->dev
, "mclk");
249 if (IS_ERR(rpmsg
->mclk
))
250 return PTR_ERR(rpmsg
->mclk
);
252 rpmsg
->dma
= devm_clk_get_optional(&pdev
->dev
, "dma");
253 if (IS_ERR(rpmsg
->dma
))
254 return PTR_ERR(rpmsg
->dma
);
256 rpmsg
->pll8k
= devm_clk_get_optional(&pdev
->dev
, "pll8k");
257 if (IS_ERR(rpmsg
->pll8k
))
258 return PTR_ERR(rpmsg
->pll8k
);
260 rpmsg
->pll11k
= devm_clk_get_optional(&pdev
->dev
, "pll11k");
261 if (IS_ERR(rpmsg
->pll11k
))
262 return PTR_ERR(rpmsg
->pll11k
);
264 platform_set_drvdata(pdev
, rpmsg
);
265 pm_runtime_enable(&pdev
->dev
);
267 ret
= devm_snd_soc_register_component(&pdev
->dev
, &fsl_component
,
275 pm_runtime_disable(&pdev
->dev
);
279 static void fsl_rpmsg_remove(struct platform_device
*pdev
)
281 struct fsl_rpmsg
*rpmsg
= platform_get_drvdata(pdev
);
283 pm_runtime_disable(&pdev
->dev
);
285 if (rpmsg
->card_pdev
)
286 platform_device_unregister(rpmsg
->card_pdev
);
289 static int fsl_rpmsg_runtime_resume(struct device
*dev
)
291 struct fsl_rpmsg
*rpmsg
= dev_get_drvdata(dev
);
294 ret
= clk_prepare_enable(rpmsg
->ipg
);
296 dev_err(dev
, "failed to enable ipg clock: %d\n", ret
);
300 ret
= clk_prepare_enable(rpmsg
->dma
);
302 dev_err(dev
, "Failed to enable dma clock %d\n", ret
);
309 clk_disable_unprepare(rpmsg
->ipg
);
314 static int fsl_rpmsg_runtime_suspend(struct device
*dev
)
316 struct fsl_rpmsg
*rpmsg
= dev_get_drvdata(dev
);
318 clk_disable_unprepare(rpmsg
->dma
);
319 clk_disable_unprepare(rpmsg
->ipg
);
324 static const struct dev_pm_ops fsl_rpmsg_pm_ops
= {
325 RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend
, fsl_rpmsg_runtime_resume
,
329 static struct platform_driver fsl_rpmsg_driver
= {
330 .probe
= fsl_rpmsg_probe
,
331 .remove
= fsl_rpmsg_remove
,
334 .pm
= pm_ptr(&fsl_rpmsg_pm_ops
),
335 .of_match_table
= fsl_rpmsg_ids
,
338 module_platform_driver(fsl_rpmsg_driver
);
340 MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
341 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
342 MODULE_ALIAS("platform:fsl_rpmsg");
343 MODULE_LICENSE("GPL");