1 // SPDX-License-Identifier: GPL-2.0+
2 // Copyright 2017-2020 NXP
4 #include <linux/module.h>
5 #include <linux/of_platform.h>
6 #include <linux/of_reserved_mem.h>
8 #include <linux/slab.h>
10 #include <sound/soc.h>
11 #include <sound/jack.h>
12 #include <sound/control.h>
13 #include <sound/pcm_params.h>
14 #include <sound/soc-dapm.h>
15 #include "imx-pcm-rpmsg.h"
18 struct snd_soc_dai_link dai
;
19 struct snd_soc_card card
;
24 static struct dev_pm_ops lpa_pm
;
26 static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets
[] = {
27 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
28 SND_SOC_DAPM_SPK("Ext Spk", NULL
),
29 SND_SOC_DAPM_MIC("Mic Jack", NULL
),
30 SND_SOC_DAPM_MIC("Main MIC", NULL
),
33 static int imx_rpmsg_late_probe(struct snd_soc_card
*card
)
35 struct imx_rpmsg
*data
= snd_soc_card_get_drvdata(card
);
36 struct snd_soc_pcm_runtime
*rtd
= list_first_entry(&card
->rtd_list
,
37 struct snd_soc_pcm_runtime
, list
);
38 struct snd_soc_dai
*codec_dai
= snd_soc_rtd_to_codec(rtd
, 0);
39 struct device
*dev
= card
->dev
;
43 struct snd_soc_component
*codec_comp
;
44 struct device_node
*codec_np
;
45 struct device_driver
*codec_drv
;
46 struct device
*codec_dev
= NULL
;
48 codec_np
= data
->dai
.codecs
->of_node
;
50 struct platform_device
*codec_pdev
;
51 struct i2c_client
*codec_i2c
;
53 codec_i2c
= of_find_i2c_device_by_node(codec_np
);
55 codec_dev
= &codec_i2c
->dev
;
57 codec_pdev
= of_find_device_by_node(codec_np
);
59 codec_dev
= &codec_pdev
->dev
;
63 codec_comp
= snd_soc_lookup_component_nolocked(codec_dev
, NULL
);
67 struct snd_soc_dapm_context
*dapm
;
69 num_widgets
= of_property_count_strings(data
->card
.dev
->of_node
,
70 "ignore-suspend-widgets");
71 for (i
= 0; i
< num_widgets
; i
++) {
72 of_property_read_string_index(data
->card
.dev
->of_node
,
73 "ignore-suspend-widgets",
75 dapm
= snd_soc_component_get_dapm(codec_comp
);
76 snd_soc_dapm_ignore_suspend(dapm
, widgets
);
79 codec_drv
= codec_dev
->driver
;
81 memcpy(&lpa_pm
, codec_drv
->pm
, sizeof(lpa_pm
));
82 lpa_pm
.suspend
= NULL
;
86 lpa_pm
.poweroff
= NULL
;
87 lpa_pm
.restore
= NULL
;
88 codec_drv
->pm
= &lpa_pm
;
90 put_device(codec_dev
);
97 ret
= snd_soc_dai_set_sysclk(codec_dai
, 0, data
->sysclk
, SND_SOC_CLOCK_IN
);
98 if (ret
&& ret
!= -ENOTSUPP
) {
99 dev_err(dev
, "failed to set sysclk in %s\n", __func__
);
106 static int imx_rpmsg_probe(struct platform_device
*pdev
)
108 struct snd_soc_dai_link_component
*dlc
;
109 struct snd_soc_dai
*cpu_dai
;
110 struct device_node
*np
= NULL
;
111 struct of_phandle_args args
;
112 const char *platform_name
;
113 struct imx_rpmsg
*data
;
116 dlc
= devm_kzalloc(&pdev
->dev
, 3 * sizeof(*dlc
), GFP_KERNEL
);
120 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
126 data
->dai
.cpus
= &dlc
[0];
127 data
->dai
.num_cpus
= 1;
128 data
->dai
.platforms
= &dlc
[1];
129 data
->dai
.num_platforms
= 1;
130 data
->dai
.codecs
= &dlc
[2];
131 data
->dai
.num_codecs
= 1;
133 data
->dai
.name
= "rpmsg hifi";
134 data
->dai
.stream_name
= "rpmsg hifi";
135 data
->dai
.dai_fmt
= SND_SOC_DAIFMT_I2S
|
136 SND_SOC_DAIFMT_NB_NF
|
137 SND_SOC_DAIFMT_CBC_CFC
;
140 * i.MX rpmsg sound cards work on codec slave mode. MCLK will be
141 * disabled by CPU DAI driver in hw_free(). Some codec requires MCLK
142 * present at power up/down sequence. So need to set ignore_pmdown_time
143 * to power down codec immediately before MCLK is turned off.
145 data
->dai
.ignore_pmdown_time
= 1;
147 data
->dai
.cpus
->dai_name
= pdev
->dev
.platform_data
;
148 cpu_dai
= snd_soc_find_dai(data
->dai
.cpus
);
153 np
= cpu_dai
->dev
->of_node
;
155 dev_err(&pdev
->dev
, "failed to parse CPU DAI device node\n");
160 ret
= of_reserved_mem_device_init_by_idx(&pdev
->dev
, np
, 0);
162 dev_warn(&pdev
->dev
, "no reserved DMA memory\n");
164 /* Optional codec node */
165 ret
= of_parse_phandle_with_fixed_args(np
, "audio-codec", 0, 0, &args
);
167 *data
->dai
.codecs
= snd_soc_dummy_dlc
;
171 ret
= snd_soc_get_dlc(&args
, data
->dai
.codecs
);
173 dev_err(&pdev
->dev
, "Unable to get codec_dai_name\n");
177 clk
= devm_get_clk_from_child(&pdev
->dev
, args
.np
, NULL
);
179 data
->sysclk
= clk_get_rate(clk
);
182 if (!of_property_read_string(np
, "fsl,rpmsg-channel-name", &platform_name
))
183 data
->dai
.platforms
->name
= platform_name
;
185 data
->dai
.platforms
->name
= "rpmsg-audio-channel";
186 data
->dai
.playback_only
= true;
187 data
->dai
.capture_only
= true;
188 data
->card
.num_links
= 1;
189 data
->card
.dai_link
= &data
->dai
;
191 if (of_property_read_bool(np
, "fsl,rpmsg-out"))
192 data
->dai
.capture_only
= false;
194 if (of_property_read_bool(np
, "fsl,rpmsg-in"))
195 data
->dai
.playback_only
= false;
197 if (data
->dai
.playback_only
&& data
->dai
.capture_only
) {
198 dev_err(&pdev
->dev
, "no enabled rpmsg DAI link\n");
203 if (of_property_read_bool(np
, "fsl,enable-lpa"))
206 data
->card
.dev
= &pdev
->dev
;
207 data
->card
.owner
= THIS_MODULE
;
208 data
->card
.dapm_widgets
= imx_rpmsg_dapm_widgets
;
209 data
->card
.num_dapm_widgets
= ARRAY_SIZE(imx_rpmsg_dapm_widgets
);
210 data
->card
.late_probe
= imx_rpmsg_late_probe
;
212 * Inoder to use common api to get card name and audio routing.
213 * Use parent of_node for this device, revert it after finishing using
215 data
->card
.dev
->of_node
= np
;
217 ret
= snd_soc_of_parse_card_name(&data
->card
, "model");
221 if (of_property_read_bool(np
, "audio-routing")) {
222 ret
= snd_soc_of_parse_audio_routing(&data
->card
, "audio-routing");
224 dev_err(&pdev
->dev
, "failed to parse audio-routing: %d\n", ret
);
229 platform_set_drvdata(pdev
, &data
->card
);
230 snd_soc_card_set_drvdata(&data
->card
, data
);
231 ret
= devm_snd_soc_register_card(&pdev
->dev
, &data
->card
);
233 dev_err_probe(&pdev
->dev
, ret
, "snd_soc_register_card failed\n");
238 pdev
->dev
.of_node
= NULL
;
242 static struct platform_driver imx_rpmsg_driver
= {
244 .name
= "imx-audio-rpmsg",
245 .pm
= &snd_soc_pm_ops
,
247 .probe
= imx_rpmsg_probe
,
249 module_platform_driver(imx_rpmsg_driver
);
251 MODULE_DESCRIPTION("Freescale SoC Audio RPMSG Machine Driver");
252 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
253 MODULE_ALIAS("platform:imx-audio-rpmsg");
254 MODULE_LICENSE("GPL v2");