1 // SPDX-License-Identifier: GPL-2.0
5 * The code contained herein is licensed under the GNU General Public
6 * License. You may obtain a copy of the GNU General Public License
7 * Version 2 or later at the following locations:
9 * http://www.opensource.org/licenses/gpl-license.html
10 * http://www.gnu.org/copyleft/gpl.html
13 #include <linux/module.h>
14 #include <linux/of_platform.h>
15 #include <linux/clk.h>
16 #include <sound/soc.h>
17 #include <sound/soc-dapm.h>
18 #include <linux/pm_runtime.h>
20 #include "fsl_audmix.h"
23 struct platform_device
*pdev
;
24 struct snd_soc_card card
;
25 struct platform_device
*audmix_pdev
;
26 struct platform_device
*out_pdev
;
29 struct snd_soc_dai_link
*dai
;
31 struct snd_soc_codec_conf
*dai_conf
;
33 struct snd_soc_dapm_route
*dapm_routes
;
36 static const u32 imx_audmix_rates
[] = {
37 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
40 static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints
= {
41 .count
= ARRAY_SIZE(imx_audmix_rates
),
42 .list
= imx_audmix_rates
,
45 static int imx_audmix_fe_startup(struct snd_pcm_substream
*substream
)
47 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
48 struct imx_audmix
*priv
= snd_soc_card_get_drvdata(rtd
->card
);
49 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
50 struct device
*dev
= rtd
->card
->dev
;
51 unsigned long clk_rate
= clk_get_rate(priv
->cpu_mclk
);
54 if (clk_rate
% 24576000 == 0) {
55 ret
= snd_pcm_hw_constraint_list(runtime
, 0,
56 SNDRV_PCM_HW_PARAM_RATE
,
57 &imx_audmix_rate_constraints
);
61 dev_warn(dev
, "mclk may be not supported %lu\n", clk_rate
);
64 ret
= snd_pcm_hw_constraint_minmax(runtime
, SNDRV_PCM_HW_PARAM_CHANNELS
,
69 return snd_pcm_hw_constraint_mask64(runtime
, SNDRV_PCM_HW_PARAM_FORMAT
,
73 static int imx_audmix_fe_hw_params(struct snd_pcm_substream
*substream
,
74 struct snd_pcm_hw_params
*params
)
76 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
77 struct device
*dev
= rtd
->card
->dev
;
78 bool tx
= substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
;
79 unsigned int fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_NB_NF
;
80 u32 channels
= params_channels(params
);
83 /* For playback the AUDMIX is slave, and for record is master */
84 fmt
|= tx
? SND_SOC_DAIFMT_CBS_CFS
: SND_SOC_DAIFMT_CBM_CFM
;
85 dir
= tx
? SND_SOC_CLOCK_OUT
: SND_SOC_CLOCK_IN
;
87 /* set DAI configuration */
88 ret
= snd_soc_dai_set_fmt(rtd
->cpu_dai
, fmt
);
90 dev_err(dev
, "failed to set cpu dai fmt: %d\n", ret
);
94 ret
= snd_soc_dai_set_sysclk(rtd
->cpu_dai
, FSL_SAI_CLK_MAST1
, 0, dir
);
96 dev_err(dev
, "failed to set cpu sysclk: %d\n", ret
);
101 * Per datasheet, AUDMIX expects 8 slots and 32 bits
102 * for every slot in TDM mode.
104 ret
= snd_soc_dai_set_tdm_slot(rtd
->cpu_dai
, BIT(channels
) - 1,
105 BIT(channels
) - 1, 8, 32);
107 dev_err(dev
, "failed to set cpu dai tdm slot: %d\n", ret
);
112 static int imx_audmix_be_hw_params(struct snd_pcm_substream
*substream
,
113 struct snd_pcm_hw_params
*params
)
115 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
116 struct device
*dev
= rtd
->card
->dev
;
117 bool tx
= substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
;
118 unsigned int fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_NB_NF
;
124 /* For playback the AUDMIX is slave */
125 fmt
|= SND_SOC_DAIFMT_CBM_CFM
;
127 /* set AUDMIX DAI configuration */
128 ret
= snd_soc_dai_set_fmt(rtd
->cpu_dai
, fmt
);
130 dev_err(dev
, "failed to set AUDMIX DAI fmt: %d\n", ret
);
135 static struct snd_soc_ops imx_audmix_fe_ops
= {
136 .startup
= imx_audmix_fe_startup
,
137 .hw_params
= imx_audmix_fe_hw_params
,
140 static struct snd_soc_ops imx_audmix_be_ops
= {
141 .hw_params
= imx_audmix_be_hw_params
,
144 static int imx_audmix_probe(struct platform_device
*pdev
)
146 struct device_node
*np
= pdev
->dev
.of_node
;
147 struct device_node
*audmix_np
= NULL
, *out_cpu_np
= NULL
;
148 struct platform_device
*audmix_pdev
= NULL
;
149 struct platform_device
*cpu_pdev
;
150 struct of_phandle_args args
;
151 struct imx_audmix
*priv
;
153 const char *fe_name_pref
= "HiFi-AUDMIX-FE-";
154 char *be_name
, *be_pb
, *be_cp
, *dai_name
, *capture_dai_name
;
156 if (pdev
->dev
.parent
) {
157 audmix_np
= pdev
->dev
.parent
->of_node
;
159 dev_err(&pdev
->dev
, "Missing parent device.\n");
164 dev_err(&pdev
->dev
, "Missing DT node for parent device.\n");
168 audmix_pdev
= of_find_device_by_node(audmix_np
);
170 dev_err(&pdev
->dev
, "Missing AUDMIX platform device for %s\n",
174 put_device(&audmix_pdev
->dev
);
176 num_dai
= of_count_phandle_with_args(audmix_np
, "dais", NULL
);
177 if (num_dai
!= FSL_AUDMIX_MAX_DAIS
) {
178 dev_err(&pdev
->dev
, "Need 2 dais to be provided for %s\n",
179 audmix_np
->full_name
);
183 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
187 priv
->num_dai
= 2 * num_dai
;
188 priv
->dai
= devm_kzalloc(&pdev
->dev
, priv
->num_dai
*
189 sizeof(struct snd_soc_dai_link
), GFP_KERNEL
);
193 priv
->num_dai_conf
= num_dai
;
194 priv
->dai_conf
= devm_kzalloc(&pdev
->dev
, priv
->num_dai_conf
*
195 sizeof(struct snd_soc_codec_conf
),
200 priv
->num_dapm_routes
= 3 * num_dai
;
201 priv
->dapm_routes
= devm_kzalloc(&pdev
->dev
, priv
->num_dapm_routes
*
202 sizeof(struct snd_soc_dapm_route
),
204 if (!priv
->dapm_routes
)
207 for (i
= 0; i
< num_dai
; i
++) {
208 struct snd_soc_dai_link_component
*dlc
;
210 /* for CPU/Codec/Platform x 2 */
211 dlc
= devm_kzalloc(&pdev
->dev
, 6 * sizeof(*dlc
), GFP_KERNEL
);
213 dev_err(&pdev
->dev
, "failed to allocate dai_link\n");
217 ret
= of_parse_phandle_with_args(audmix_np
, "dais", NULL
, i
,
220 dev_err(&pdev
->dev
, "of_parse_phandle_with_args failed\n");
224 cpu_pdev
= of_find_device_by_node(args
.np
);
226 dev_err(&pdev
->dev
, "failed to find SAI platform device\n");
229 put_device(&cpu_pdev
->dev
);
231 dai_name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s%s",
232 fe_name_pref
, args
.np
->full_name
+ 1);
234 dev_info(pdev
->dev
.parent
, "DAI FE name:%s\n", dai_name
);
237 out_cpu_np
= args
.np
;
239 devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s %s",
240 dai_name
, "CPU-Capture");
243 priv
->dai
[i
].cpus
= &dlc
[0];
244 priv
->dai
[i
].codecs
= &dlc
[1];
245 priv
->dai
[i
].platforms
= &dlc
[2];
247 priv
->dai
[i
].num_cpus
= 1;
248 priv
->dai
[i
].num_codecs
= 1;
249 priv
->dai
[i
].num_platforms
= 1;
251 priv
->dai
[i
].name
= dai_name
;
252 priv
->dai
[i
].stream_name
= "HiFi-AUDMIX-FE";
253 priv
->dai
[i
].codecs
->dai_name
= "snd-soc-dummy-dai";
254 priv
->dai
[i
].codecs
->name
= "snd-soc-dummy";
255 priv
->dai
[i
].cpus
->of_node
= args
.np
;
256 priv
->dai
[i
].cpus
->dai_name
= dev_name(&cpu_pdev
->dev
);
257 priv
->dai
[i
].platforms
->of_node
= args
.np
;
258 priv
->dai
[i
].dynamic
= 1;
259 priv
->dai
[i
].dpcm_playback
= 1;
260 priv
->dai
[i
].dpcm_capture
= (i
== 0 ? 1 : 0);
261 priv
->dai
[i
].ignore_pmdown_time
= 1;
262 priv
->dai
[i
].ops
= &imx_audmix_fe_ops
;
264 /* Add AUDMIX Backend */
265 be_name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
267 be_pb
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
268 "AUDMIX-Playback-%d", i
);
269 be_cp
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
270 "AUDMIX-Capture-%d", i
);
272 priv
->dai
[num_dai
+ i
].cpus
= &dlc
[3];
273 priv
->dai
[num_dai
+ i
].codecs
= &dlc
[4];
274 priv
->dai
[num_dai
+ i
].platforms
= &dlc
[5];
276 priv
->dai
[num_dai
+ i
].num_cpus
= 1;
277 priv
->dai
[num_dai
+ i
].num_codecs
= 1;
278 priv
->dai
[num_dai
+ i
].num_platforms
= 1;
280 priv
->dai
[num_dai
+ i
].name
= be_name
;
281 priv
->dai
[num_dai
+ i
].codecs
->dai_name
= "snd-soc-dummy-dai";
282 priv
->dai
[num_dai
+ i
].codecs
->name
= "snd-soc-dummy";
283 priv
->dai
[num_dai
+ i
].cpus
->of_node
= audmix_np
;
284 priv
->dai
[num_dai
+ i
].cpus
->dai_name
= be_name
;
285 priv
->dai
[num_dai
+ i
].platforms
->name
= "snd-soc-dummy";
286 priv
->dai
[num_dai
+ i
].no_pcm
= 1;
287 priv
->dai
[num_dai
+ i
].dpcm_playback
= 1;
288 priv
->dai
[num_dai
+ i
].dpcm_capture
= 1;
289 priv
->dai
[num_dai
+ i
].ignore_pmdown_time
= 1;
290 priv
->dai
[num_dai
+ i
].ops
= &imx_audmix_be_ops
;
292 priv
->dai_conf
[i
].dlc
.of_node
= args
.np
;
293 priv
->dai_conf
[i
].name_prefix
= dai_name
;
295 priv
->dapm_routes
[i
].source
=
296 devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s %s",
297 dai_name
, "CPU-Playback");
298 priv
->dapm_routes
[i
].sink
= be_pb
;
299 priv
->dapm_routes
[num_dai
+ i
].source
= be_pb
;
300 priv
->dapm_routes
[num_dai
+ i
].sink
= be_cp
;
301 priv
->dapm_routes
[2 * num_dai
+ i
].source
= be_cp
;
302 priv
->dapm_routes
[2 * num_dai
+ i
].sink
= capture_dai_name
;
305 cpu_pdev
= of_find_device_by_node(out_cpu_np
);
307 dev_err(&pdev
->dev
, "failed to find SAI platform device\n");
310 put_device(&cpu_pdev
->dev
);
312 priv
->cpu_mclk
= devm_clk_get(&cpu_pdev
->dev
, "mclk1");
313 if (IS_ERR(priv
->cpu_mclk
)) {
314 ret
= PTR_ERR(priv
->cpu_mclk
);
315 dev_err(&cpu_pdev
->dev
, "failed to get DAI mclk1: %d\n", ret
);
319 priv
->audmix_pdev
= audmix_pdev
;
320 priv
->out_pdev
= cpu_pdev
;
322 priv
->card
.dai_link
= priv
->dai
;
323 priv
->card
.num_links
= priv
->num_dai
;
324 priv
->card
.codec_conf
= priv
->dai_conf
;
325 priv
->card
.num_configs
= priv
->num_dai_conf
;
326 priv
->card
.dapm_routes
= priv
->dapm_routes
;
327 priv
->card
.num_dapm_routes
= priv
->num_dapm_routes
;
328 priv
->card
.dev
= &pdev
->dev
;
329 priv
->card
.owner
= THIS_MODULE
;
330 priv
->card
.name
= "imx-audmix";
332 platform_set_drvdata(pdev
, &priv
->card
);
333 snd_soc_card_set_drvdata(&priv
->card
, priv
);
335 ret
= devm_snd_soc_register_card(&pdev
->dev
, &priv
->card
);
337 dev_err(&pdev
->dev
, "snd_soc_register_card failed\n");
344 static struct platform_driver imx_audmix_driver
= {
345 .probe
= imx_audmix_probe
,
347 .name
= "imx-audmix",
348 .pm
= &snd_soc_pm_ops
,
351 module_platform_driver(imx_audmix_driver
);
353 MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
354 MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
355 MODULE_ALIAS("platform:imx-audmix");
356 MODULE_LICENSE("GPL v2");