1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Copyright (c) 2020 BayLibre, SAS. 4 // Author: Jerome Brunet <jbrunet@baylibre.com> 5 6 #include <linux/module.h> 7 #include <linux/of_platform.h> 8 #include <sound/soc.h> 9 10 #include "meson-card.h" 11 12 int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, 13 struct snd_pcm_hw_params *params, 14 unsigned int mclk_fs) 15 { 16 struct snd_soc_pcm_runtime *rtd = substream->private_data; 17 struct snd_soc_dai *codec_dai; 18 unsigned int mclk; 19 int ret, i; 20 21 if (!mclk_fs) 22 return 0; 23 24 mclk = params_rate(params) * mclk_fs; 25 26 for_each_rtd_codec_dais(rtd, i, codec_dai) { 27 ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 28 SND_SOC_CLOCK_IN); 29 if (ret && ret != -ENOTSUPP) 30 return ret; 31 } 32 33 ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, 34 SND_SOC_CLOCK_OUT); 35 if (ret && ret != -ENOTSUPP) 36 return ret; 37 38 return 0; 39 } 40 EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); 41 42 int meson_card_reallocate_links(struct snd_soc_card *card, 43 unsigned int num_links) 44 { 45 struct meson_card *priv = snd_soc_card_get_drvdata(card); 46 struct snd_soc_dai_link *links; 47 void **ldata; 48 49 links = krealloc(priv->card.dai_link, 50 num_links * sizeof(*priv->card.dai_link), 51 GFP_KERNEL | __GFP_ZERO); 52 ldata = krealloc(priv->link_data, 53 num_links * sizeof(*priv->link_data), 54 GFP_KERNEL | __GFP_ZERO); 55 56 if (!links || !ldata) { 57 dev_err(priv->card.dev, "failed to allocate links\n"); 58 return -ENOMEM; 59 } 60 61 priv->card.dai_link = links; 62 priv->link_data = ldata; 63 priv->card.num_links = num_links; 64 return 0; 65 } 66 EXPORT_SYMBOL_GPL(meson_card_reallocate_links); 67 68 int meson_card_parse_dai(struct snd_soc_card *card, 69 struct device_node *node, 70 struct device_node **dai_of_node, 71 const char **dai_name) 72 { 73 struct of_phandle_args args; 74 int ret; 75 76 if (!dai_name || !dai_of_node || !node) 77 return -EINVAL; 78 79 ret = of_parse_phandle_with_args(node, "sound-dai", 80 "#sound-dai-cells", 0, &args); 81 if (ret) { 82 if (ret != -EPROBE_DEFER) 83 dev_err(card->dev, "can't parse dai %d\n", ret); 84 return ret; 85 } 86 *dai_of_node = args.np; 87 88 return snd_soc_get_dai_name(&args, dai_name); 89 } 90 EXPORT_SYMBOL_GPL(meson_card_parse_dai); 91 92 static int meson_card_set_link_name(struct snd_soc_card *card, 93 struct snd_soc_dai_link *link, 94 struct device_node *node, 95 const char *prefix) 96 { 97 char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", 98 prefix, node->full_name); 99 if (!name) 100 return -ENOMEM; 101 102 link->name = name; 103 link->stream_name = name; 104 105 return 0; 106 } 107 108 unsigned int meson_card_parse_daifmt(struct device_node *node, 109 struct device_node *cpu_node) 110 { 111 struct device_node *bitclkmaster = NULL; 112 struct device_node *framemaster = NULL; 113 unsigned int daifmt; 114 115 daifmt = snd_soc_of_parse_daifmt(node, DT_PREFIX, 116 &bitclkmaster, &framemaster); 117 daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; 118 119 /* If no master is provided, default to cpu master */ 120 if (!bitclkmaster || bitclkmaster == cpu_node) { 121 daifmt |= (!framemaster || framemaster == cpu_node) ? 122 SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; 123 } else { 124 daifmt |= (!framemaster || framemaster == cpu_node) ? 125 SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; 126 } 127 128 of_node_put(bitclkmaster); 129 of_node_put(framemaster); 130 131 return daifmt; 132 } 133 EXPORT_SYMBOL_GPL(meson_card_parse_daifmt); 134 135 int meson_card_set_be_link(struct snd_soc_card *card, 136 struct snd_soc_dai_link *link, 137 struct device_node *node) 138 { 139 struct snd_soc_dai_link_component *codec; 140 struct device_node *np; 141 int ret, num_codecs; 142 143 link->no_pcm = 1; 144 link->dpcm_playback = 1; 145 link->dpcm_capture = 1; 146 147 num_codecs = of_get_child_count(node); 148 if (!num_codecs) { 149 dev_err(card->dev, "be link %s has no codec\n", 150 node->full_name); 151 return -EINVAL; 152 } 153 154 codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); 155 if (!codec) 156 return -ENOMEM; 157 158 link->codecs = codec; 159 link->num_codecs = num_codecs; 160 161 for_each_child_of_node(node, np) { 162 ret = meson_card_parse_dai(card, np, &codec->of_node, 163 &codec->dai_name); 164 if (ret) { 165 of_node_put(np); 166 return ret; 167 } 168 169 codec++; 170 } 171 172 ret = meson_card_set_link_name(card, link, node, "be"); 173 if (ret) 174 dev_err(card->dev, "error setting %pOFn link name\n", np); 175 176 return ret; 177 } 178 EXPORT_SYMBOL_GPL(meson_card_set_be_link); 179 180 int meson_card_set_fe_link(struct snd_soc_card *card, 181 struct snd_soc_dai_link *link, 182 struct device_node *node, 183 bool is_playback) 184 { 185 struct snd_soc_dai_link_component *codec; 186 187 codec = devm_kzalloc(card->dev, sizeof(*codec), GFP_KERNEL); 188 if (!codec) 189 return -ENOMEM; 190 191 link->codecs = codec; 192 link->num_codecs = 1; 193 194 link->dynamic = 1; 195 link->dpcm_merged_format = 1; 196 link->dpcm_merged_chan = 1; 197 link->dpcm_merged_rate = 1; 198 link->codecs->dai_name = "snd-soc-dummy-dai"; 199 link->codecs->name = "snd-soc-dummy"; 200 201 if (is_playback) 202 link->dpcm_playback = 1; 203 else 204 link->dpcm_capture = 1; 205 206 return meson_card_set_link_name(card, link, node, "fe"); 207 } 208 EXPORT_SYMBOL_GPL(meson_card_set_fe_link); 209 210 static int meson_card_add_links(struct snd_soc_card *card) 211 { 212 struct meson_card *priv = snd_soc_card_get_drvdata(card); 213 struct device_node *node = card->dev->of_node; 214 struct device_node *np; 215 int num, i, ret; 216 217 num = of_get_child_count(node); 218 if (!num) { 219 dev_err(card->dev, "card has no links\n"); 220 return -EINVAL; 221 } 222 223 ret = meson_card_reallocate_links(card, num); 224 if (ret) 225 return ret; 226 227 i = 0; 228 for_each_child_of_node(node, np) { 229 ret = priv->match_data->add_link(card, np, &i); 230 if (ret) { 231 of_node_put(np); 232 return ret; 233 } 234 235 i++; 236 } 237 238 return 0; 239 } 240 241 static int meson_card_parse_of_optional(struct snd_soc_card *card, 242 const char *propname, 243 int (*func)(struct snd_soc_card *c, 244 const char *p)) 245 { 246 /* If property is not provided, don't fail ... */ 247 if (!of_property_read_bool(card->dev->of_node, propname)) 248 return 0; 249 250 /* ... but do fail if it is provided and the parsing fails */ 251 return func(card, propname); 252 } 253 254 static int meson_card_add_aux_devices(struct snd_soc_card *card) 255 { 256 struct device_node *node = card->dev->of_node; 257 struct snd_soc_aux_dev *aux; 258 int num, i; 259 260 num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); 261 if (num == -ENOENT) { 262 return 0; 263 } else if (num < 0) { 264 dev_err(card->dev, "error getting auxiliary devices: %d\n", 265 num); 266 return num; 267 } 268 269 aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); 270 if (!aux) 271 return -ENOMEM; 272 card->aux_dev = aux; 273 card->num_aux_devs = num; 274 275 for_each_card_pre_auxs(card, i, aux) { 276 aux->dlc.of_node = 277 of_parse_phandle(node, "audio-aux-devs", i); 278 if (!aux->dlc.of_node) 279 return -EINVAL; 280 } 281 282 return 0; 283 } 284 285 static void meson_card_clean_references(struct meson_card *priv) 286 { 287 struct snd_soc_card *card = &priv->card; 288 struct snd_soc_dai_link *link; 289 struct snd_soc_dai_link_component *codec; 290 struct snd_soc_aux_dev *aux; 291 int i, j; 292 293 if (card->dai_link) { 294 for_each_card_prelinks(card, i, link) { 295 if (link->cpus) 296 of_node_put(link->cpus->of_node); 297 for_each_link_codecs(link, j, codec) 298 of_node_put(codec->of_node); 299 } 300 } 301 302 if (card->aux_dev) { 303 for_each_card_pre_auxs(card, i, aux) 304 of_node_put(aux->dlc.of_node); 305 } 306 307 kfree(card->dai_link); 308 kfree(priv->link_data); 309 } 310 311 int meson_card_probe(struct platform_device *pdev) 312 { 313 const struct meson_card_match_data *data; 314 struct device *dev = &pdev->dev; 315 struct meson_card *priv; 316 int ret; 317 318 data = of_device_get_match_data(dev); 319 if (!data) { 320 dev_err(dev, "failed to match device\n"); 321 return -ENODEV; 322 } 323 324 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 325 if (!priv) 326 return -ENOMEM; 327 328 platform_set_drvdata(pdev, priv); 329 snd_soc_card_set_drvdata(&priv->card, priv); 330 331 priv->card.owner = THIS_MODULE; 332 priv->card.dev = dev; 333 priv->match_data = data; 334 335 ret = snd_soc_of_parse_card_name(&priv->card, "model"); 336 if (ret < 0) 337 return ret; 338 339 ret = meson_card_parse_of_optional(&priv->card, "audio-routing", 340 snd_soc_of_parse_audio_routing); 341 if (ret) { 342 dev_err(dev, "error while parsing routing\n"); 343 return ret; 344 } 345 346 ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", 347 snd_soc_of_parse_audio_simple_widgets); 348 if (ret) { 349 dev_err(dev, "error while parsing widgets\n"); 350 return ret; 351 } 352 353 ret = meson_card_add_links(&priv->card); 354 if (ret) 355 goto out_err; 356 357 ret = meson_card_add_aux_devices(&priv->card); 358 if (ret) 359 goto out_err; 360 361 ret = devm_snd_soc_register_card(dev, &priv->card); 362 if (ret) 363 goto out_err; 364 365 return 0; 366 367 out_err: 368 meson_card_clean_references(priv); 369 return ret; 370 } 371 EXPORT_SYMBOL_GPL(meson_card_probe); 372 373 int meson_card_remove(struct platform_device *pdev) 374 { 375 struct meson_card *priv = platform_get_drvdata(pdev); 376 377 meson_card_clean_references(priv); 378 379 return 0; 380 } 381 EXPORT_SYMBOL_GPL(meson_card_remove); 382 383 MODULE_DESCRIPTION("Amlogic Sound Card Utils"); 384 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 385 MODULE_LICENSE("GPL v2"); 386