1 // SPDX-License-Identifier: GPL-2.0+ 2 // Copyright 2017-2020 NXP 3 4 #include <linux/module.h> 5 #include <linux/of.h> 6 #include <linux/of_reserved_mem.h> 7 #include <linux/platform_device.h> 8 #include <linux/i2c.h> 9 #include <linux/of_gpio.h> 10 #include <linux/slab.h> 11 #include <linux/gpio.h> 12 #include <linux/clk.h> 13 #include <sound/soc.h> 14 #include <sound/jack.h> 15 #include <sound/control.h> 16 #include <sound/pcm_params.h> 17 #include <sound/soc-dapm.h> 18 #include "imx-pcm-rpmsg.h" 19 20 struct imx_rpmsg { 21 struct snd_soc_dai_link dai; 22 struct snd_soc_card card; 23 unsigned long sysclk; 24 }; 25 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), 31 }; 32 33 static int imx_rpmsg_late_probe(struct snd_soc_card *card) 34 { 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; 40 int ret; 41 42 if (!data->sysclk) 43 return 0; 44 45 ret = snd_soc_dai_set_sysclk(codec_dai, 0, data->sysclk, SND_SOC_CLOCK_IN); 46 if (ret && ret != -ENOTSUPP) { 47 dev_err(dev, "failed to set sysclk in %s\n", __func__); 48 return ret; 49 } 50 51 return 0; 52 } 53 54 static int imx_rpmsg_probe(struct platform_device *pdev) 55 { 56 struct snd_soc_dai_link_component *dlc; 57 struct device *dev = pdev->dev.parent; 58 /* rpmsg_pdev is the platform device for the rpmsg node that probed us */ 59 struct platform_device *rpmsg_pdev = to_platform_device(dev); 60 struct device_node *np = rpmsg_pdev->dev.of_node; 61 struct of_phandle_args args; 62 const char *platform_name; 63 struct imx_rpmsg *data; 64 int ret = 0; 65 66 dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL); 67 if (!dlc) 68 return -ENOMEM; 69 70 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 71 if (!data) { 72 ret = -ENOMEM; 73 goto fail; 74 } 75 76 ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0); 77 if (ret) 78 dev_warn(&pdev->dev, "no reserved DMA memory\n"); 79 80 data->dai.cpus = &dlc[0]; 81 data->dai.num_cpus = 1; 82 data->dai.platforms = &dlc[1]; 83 data->dai.num_platforms = 1; 84 data->dai.codecs = &dlc[2]; 85 data->dai.num_codecs = 1; 86 87 data->dai.name = "rpmsg hifi"; 88 data->dai.stream_name = "rpmsg hifi"; 89 data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | 90 SND_SOC_DAIFMT_NB_NF | 91 SND_SOC_DAIFMT_CBC_CFC; 92 93 /* 94 * i.MX rpmsg sound cards work on codec slave mode. MCLK will be 95 * disabled by CPU DAI driver in hw_free(). Some codec requires MCLK 96 * present at power up/down sequence. So need to set ignore_pmdown_time 97 * to power down codec immediately before MCLK is turned off. 98 */ 99 data->dai.ignore_pmdown_time = 1; 100 101 /* Optional codec node */ 102 ret = of_parse_phandle_with_fixed_args(np, "audio-codec", 0, 0, &args); 103 if (ret) { 104 *data->dai.codecs = snd_soc_dummy_dlc; 105 } else { 106 struct clk *clk; 107 108 ret = snd_soc_get_dlc(&args, data->dai.codecs); 109 if (ret) { 110 dev_err(&pdev->dev, "Unable to get codec_dai_name\n"); 111 goto fail; 112 } 113 114 clk = devm_get_clk_from_child(&pdev->dev, args.np, NULL); 115 if (!IS_ERR(clk)) 116 data->sysclk = clk_get_rate(clk); 117 } 118 119 data->dai.cpus->dai_name = dev_name(&rpmsg_pdev->dev); 120 if (!of_property_read_string(np, "fsl,rpmsg-channel-name", &platform_name)) 121 data->dai.platforms->name = platform_name; 122 else 123 data->dai.platforms->name = "rpmsg-audio-channel"; 124 data->dai.playback_only = true; 125 data->dai.capture_only = true; 126 data->card.num_links = 1; 127 data->card.dai_link = &data->dai; 128 129 if (of_property_read_bool(np, "fsl,rpmsg-out")) 130 data->dai.capture_only = false; 131 132 if (of_property_read_bool(np, "fsl,rpmsg-in")) 133 data->dai.playback_only = false; 134 135 if (data->dai.playback_only && data->dai.capture_only) { 136 dev_err(&pdev->dev, "no enabled rpmsg DAI link\n"); 137 ret = -EINVAL; 138 goto fail; 139 } 140 141 data->card.dev = &pdev->dev; 142 data->card.owner = THIS_MODULE; 143 data->card.dapm_widgets = imx_rpmsg_dapm_widgets; 144 data->card.num_dapm_widgets = ARRAY_SIZE(imx_rpmsg_dapm_widgets); 145 data->card.late_probe = imx_rpmsg_late_probe; 146 /* 147 * Inoder to use common api to get card name and audio routing. 148 * Use parent of_node for this device, revert it after finishing using 149 */ 150 data->card.dev->of_node = np; 151 152 ret = snd_soc_of_parse_card_name(&data->card, "model"); 153 if (ret) 154 goto fail; 155 156 if (of_property_read_bool(np, "audio-routing")) { 157 ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); 158 if (ret) { 159 dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); 160 goto fail; 161 } 162 } 163 164 platform_set_drvdata(pdev, &data->card); 165 snd_soc_card_set_drvdata(&data->card, data); 166 ret = devm_snd_soc_register_card(&pdev->dev, &data->card); 167 if (ret) { 168 dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n"); 169 goto fail; 170 } 171 172 fail: 173 pdev->dev.of_node = NULL; 174 return ret; 175 } 176 177 static struct platform_driver imx_rpmsg_driver = { 178 .driver = { 179 .name = "imx-audio-rpmsg", 180 .pm = &snd_soc_pm_ops, 181 }, 182 .probe = imx_rpmsg_probe, 183 }; 184 module_platform_driver(imx_rpmsg_driver); 185 186 MODULE_DESCRIPTION("Freescale SoC Audio RPMSG Machine Driver"); 187 MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>"); 188 MODULE_ALIAS("platform:imx-audio-rpmsg"); 189 MODULE_LICENSE("GPL v2"); 190