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