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 * https://www.opensource.org/licenses/gpl-license.html
10 * https://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>
19 #include "fsl_audmix.h"
22 struct platform_device
*pdev
;
23 struct snd_soc_card card
;
24 struct platform_device
*audmix_pdev
;
25 struct platform_device
*out_pdev
;
28 struct snd_soc_dai_link
*dai
;
30 struct snd_soc_codec_conf
*dai_conf
;
32 struct snd_soc_dapm_route
*dapm_routes
;
35 static const u32 imx_audmix_rates
[] = {
36 8000, 12000, 16000, 24000, 32000, 48000, 64000, 96000,
39 static const struct snd_pcm_hw_constraint_list imx_audmix_rate_constraints
= {
40 .count
= ARRAY_SIZE(imx_audmix_rates
),
41 .list
= imx_audmix_rates
,
44 static int imx_audmix_fe_startup(struct snd_pcm_substream
*substream
)
46 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
47 struct imx_audmix
*priv
= snd_soc_card_get_drvdata(rtd
->card
);
48 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
49 struct device
*dev
= rtd
->card
->dev
;
50 unsigned long clk_rate
= clk_get_rate(priv
->cpu_mclk
);
53 if (clk_rate
% 24576000 == 0) {
54 ret
= snd_pcm_hw_constraint_list(runtime
, 0,
55 SNDRV_PCM_HW_PARAM_RATE
,
56 &imx_audmix_rate_constraints
);
60 dev_warn(dev
, "mclk may be not supported %lu\n", clk_rate
);
63 ret
= snd_pcm_hw_constraint_minmax(runtime
, SNDRV_PCM_HW_PARAM_CHANNELS
,
68 return snd_pcm_hw_constraint_mask64(runtime
, SNDRV_PCM_HW_PARAM_FORMAT
,
72 static int imx_audmix_fe_hw_params(struct snd_pcm_substream
*substream
,
73 struct snd_pcm_hw_params
*params
)
75 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
76 struct device
*dev
= rtd
->card
->dev
;
77 bool tx
= substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
;
78 unsigned int fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_NB_NF
;
79 u32 channels
= params_channels(params
);
82 /* For playback the AUDMIX is consumer, and for record is provider */
83 fmt
|= tx
? SND_SOC_DAIFMT_BP_FP
: SND_SOC_DAIFMT_BC_FC
;
84 dir
= tx
? SND_SOC_CLOCK_OUT
: SND_SOC_CLOCK_IN
;
86 /* set DAI configuration */
87 ret
= snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd
, 0), fmt
);
89 dev_err(dev
, "failed to set cpu dai fmt: %d\n", ret
);
93 ret
= snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd
, 0), FSL_SAI_CLK_MAST1
, 0, dir
);
95 dev_err(dev
, "failed to set cpu sysclk: %d\n", ret
);
100 * Per datasheet, AUDMIX expects 8 slots and 32 bits
101 * for every slot in TDM mode.
103 ret
= snd_soc_dai_set_tdm_slot(snd_soc_rtd_to_cpu(rtd
, 0), BIT(channels
) - 1,
104 BIT(channels
) - 1, 8, 32);
106 dev_err(dev
, "failed to set cpu dai tdm slot: %d\n", ret
);
111 static int imx_audmix_be_hw_params(struct snd_pcm_substream
*substream
,
112 struct snd_pcm_hw_params
*params
)
114 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
115 struct device
*dev
= rtd
->card
->dev
;
116 bool tx
= substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
;
117 unsigned int fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_NB_NF
;
123 /* For playback the AUDMIX is consumer */
124 fmt
|= SND_SOC_DAIFMT_BC_FC
;
126 /* set AUDMIX DAI configuration */
127 ret
= snd_soc_dai_set_fmt(snd_soc_rtd_to_cpu(rtd
, 0), fmt
);
129 dev_err(dev
, "failed to set AUDMIX DAI fmt: %d\n", ret
);
134 static const struct snd_soc_ops imx_audmix_fe_ops
= {
135 .startup
= imx_audmix_fe_startup
,
136 .hw_params
= imx_audmix_fe_hw_params
,
139 static const struct snd_soc_ops imx_audmix_be_ops
= {
140 .hw_params
= imx_audmix_be_hw_params
,
143 static const char *name
[][3] = {
144 {"HiFi-AUDMIX-FE-0", "HiFi-AUDMIX-FE-1", "HiFi-AUDMIX-FE-2"},
145 {"sai-tx", "sai-tx", "sai-rx"},
146 {"AUDMIX-Playback-0", "AUDMIX-Playback-1", "CPU-Capture"},
147 {"CPU-Playback", "CPU-Playback", "AUDMIX-Capture-0"},
150 static int imx_audmix_probe(struct platform_device
*pdev
)
152 struct device_node
*np
= pdev
->dev
.of_node
;
153 struct device_node
*audmix_np
= NULL
, *out_cpu_np
= NULL
;
154 struct platform_device
*audmix_pdev
= NULL
;
155 struct platform_device
*cpu_pdev
;
156 struct of_phandle_args args
;
157 struct imx_audmix
*priv
;
159 const char *fe_name_pref
= "HiFi-AUDMIX-FE-";
160 char *be_name
, *dai_name
;
162 if (pdev
->dev
.parent
) {
163 audmix_np
= pdev
->dev
.parent
->of_node
;
165 dev_err(&pdev
->dev
, "Missing parent device.\n");
170 dev_err(&pdev
->dev
, "Missing DT node for parent device.\n");
174 audmix_pdev
= of_find_device_by_node(audmix_np
);
176 dev_err(&pdev
->dev
, "Missing AUDMIX platform device for %s\n",
180 put_device(&audmix_pdev
->dev
);
182 num_dai
= of_count_phandle_with_args(audmix_np
, "dais", NULL
);
183 if (num_dai
!= FSL_AUDMIX_MAX_DAIS
) {
184 dev_err(&pdev
->dev
, "Need 2 dais to be provided for %s\n",
185 audmix_np
->full_name
);
189 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
194 priv
->num_dai
= 2 * num_dai
;
195 priv
->dai
= devm_kcalloc(&pdev
->dev
, priv
->num_dai
,
196 sizeof(struct snd_soc_dai_link
), GFP_KERNEL
);
200 priv
->num_dai_conf
= num_dai
;
201 priv
->dai_conf
= devm_kcalloc(&pdev
->dev
, priv
->num_dai_conf
,
202 sizeof(struct snd_soc_codec_conf
),
207 priv
->num_dapm_routes
= num_dai
;
208 priv
->dapm_routes
= devm_kcalloc(&pdev
->dev
, priv
->num_dapm_routes
,
209 sizeof(struct snd_soc_dapm_route
),
211 if (!priv
->dapm_routes
)
214 for (i
= 0; i
< num_dai
; i
++) {
215 struct snd_soc_dai_link_component
*dlc
;
218 dlc
= devm_kcalloc(&pdev
->dev
, 2, sizeof(*dlc
), GFP_KERNEL
);
222 if (i
== num_dai
- 1)
223 ret
= of_parse_phandle_with_args(audmix_np
, "dais", NULL
, 0,
226 ret
= of_parse_phandle_with_args(audmix_np
, "dais", NULL
, i
,
229 dev_err(&pdev
->dev
, "of_parse_phandle_with_args failed\n");
233 cpu_pdev
= of_find_device_by_node(args
.np
);
235 dev_err(&pdev
->dev
, "failed to find SAI platform device\n");
238 put_device(&cpu_pdev
->dev
);
240 dai_name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s%s",
241 fe_name_pref
, args
.np
->full_name
);
245 dev_info(pdev
->dev
.parent
, "DAI FE name:%s\n", dai_name
);
247 if (i
== num_dai
- 1)
248 out_cpu_np
= args
.np
;
252 * platform is using soc-generic-dmaengine-pcm
255 priv
->dai
[i
].platforms
= &dlc
[0];
256 priv
->dai
[i
].codecs
= &snd_soc_dummy_dlc
;
258 priv
->dai
[i
].num_cpus
= 1;
259 priv
->dai
[i
].num_codecs
= 1;
260 priv
->dai
[i
].num_platforms
= 1;
261 priv
->dai
[i
].name
= name
[0][i
];
262 priv
->dai
[i
].stream_name
= "HiFi-AUDMIX-FE";
263 priv
->dai
[i
].cpus
->of_node
= args
.np
;
264 priv
->dai
[i
].cpus
->dai_name
= name
[1][i
];
266 priv
->dai
[i
].dynamic
= 1;
267 if (i
== num_dai
- 1)
268 priv
->dai
[i
].capture_only
= 1;
270 priv
->dai
[i
].playback_only
= 1;
271 priv
->dai
[i
].ignore_pmdown_time
= 1;
272 priv
->dai
[i
].ops
= &imx_audmix_fe_ops
;
274 /* Add AUDMIX Backend */
275 be_name
= devm_kasprintf(&pdev
->dev
, GFP_KERNEL
,
280 priv
->dai
[num_dai
+ i
].cpus
= &dlc
[1];
281 priv
->dai
[num_dai
+ i
].codecs
= &snd_soc_dummy_dlc
;
283 priv
->dai
[num_dai
+ i
].num_cpus
= 1;
284 priv
->dai
[num_dai
+ i
].num_codecs
= 1;
286 priv
->dai
[num_dai
+ i
].name
= be_name
;
287 priv
->dai
[num_dai
+ i
].cpus
->of_node
= audmix_np
;
288 priv
->dai
[num_dai
+ i
].cpus
->dai_name
= be_name
;
289 priv
->dai
[num_dai
+ i
].no_pcm
= 1;
290 if (i
== num_dai
- 1)
291 priv
->dai
[num_dai
+ i
].capture_only
= 1;
293 priv
->dai
[num_dai
+ i
].playback_only
= 1;
294 priv
->dai
[num_dai
+ i
].ignore_pmdown_time
= 1;
295 priv
->dai
[num_dai
+ i
].ops
= &imx_audmix_be_ops
;
297 priv
->dai_conf
[i
].dlc
.of_node
= args
.np
;
298 priv
->dai_conf
[i
].name_prefix
= dai_name
;
300 if (i
== num_dai
- 1) {
301 priv
->dapm_routes
[i
].sink
=
302 devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s %s",
303 dai_name
, name
[2][i
]);
304 if (!priv
->dapm_routes
[i
].sink
)
307 priv
->dapm_routes
[i
].source
= name
[3][i
];
309 priv
->dapm_routes
[i
].source
=
310 devm_kasprintf(&pdev
->dev
, GFP_KERNEL
, "%s %s",
311 dai_name
, name
[3][i
]);
312 if (!priv
->dapm_routes
[i
].source
)
315 priv
->dapm_routes
[i
].sink
= name
[2][i
];
319 cpu_pdev
= of_find_device_by_node(out_cpu_np
);
321 dev_err(&pdev
->dev
, "failed to find SAI platform device\n");
324 put_device(&cpu_pdev
->dev
);
326 priv
->cpu_mclk
= devm_clk_get(&cpu_pdev
->dev
, "mclk1");
327 if (IS_ERR(priv
->cpu_mclk
)) {
328 ret
= PTR_ERR(priv
->cpu_mclk
);
329 dev_err(&cpu_pdev
->dev
, "failed to get DAI mclk1: %d\n", ret
);
333 priv
->audmix_pdev
= audmix_pdev
;
334 priv
->out_pdev
= cpu_pdev
;
336 priv
->card
.dai_link
= priv
->dai
;
337 priv
->card
.num_links
= priv
->num_dai
;
338 priv
->card
.codec_conf
= priv
->dai_conf
;
339 priv
->card
.num_configs
= priv
->num_dai_conf
;
340 priv
->card
.dapm_routes
= priv
->dapm_routes
;
341 priv
->card
.num_dapm_routes
= priv
->num_dapm_routes
;
342 priv
->card
.dev
= &pdev
->dev
;
343 priv
->card
.owner
= THIS_MODULE
;
344 priv
->card
.name
= "imx-audmix";
346 platform_set_drvdata(pdev
, &priv
->card
);
347 snd_soc_card_set_drvdata(&priv
->card
, priv
);
349 ret
= devm_snd_soc_register_card(&pdev
->dev
, &priv
->card
);
351 dev_err(&pdev
->dev
, "snd_soc_register_card failed\n");
358 static struct platform_driver imx_audmix_driver
= {
359 .probe
= imx_audmix_probe
,
361 .name
= "imx-audmix",
362 .pm
= &snd_soc_pm_ops
,
365 module_platform_driver(imx_audmix_driver
);
367 MODULE_DESCRIPTION("NXP AUDMIX ASoC machine driver");
368 MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
369 MODULE_ALIAS("platform:imx-audmix");
370 MODULE_LICENSE("GPL v2");