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 = snd_soc_substream_to_rtd(substream); 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(snd_soc_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 snd_soc_dai_link_component *dlc) 78 { 79 int ret; 80 81 if (!dlc || !node) 82 return -EINVAL; 83 84 ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); 85 if (ret) 86 return dev_err_probe(card->dev, ret, "can't parse dai\n"); 87 88 return ret; 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_daifmt_parse_format(node, NULL); 116 117 snd_soc_daifmt_parse_clock_provider_as_phandle(node, NULL, &bitclkmaster, &framemaster); 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_CBC_CFC : SND_SOC_DAIFMT_CBC_CFP; 123 } else { 124 daifmt |= (!framemaster || framemaster == cpu_node) ? 125 SND_SOC_DAIFMT_CBP_CFC : SND_SOC_DAIFMT_CBP_CFP; 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 int ret, num_codecs; 141 142 num_codecs = of_get_child_count(node); 143 if (!num_codecs) { 144 dev_err(card->dev, "be link %s has no codec\n", 145 node->full_name); 146 return -EINVAL; 147 } 148 149 codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); 150 if (!codec) 151 return -ENOMEM; 152 153 link->codecs = codec; 154 link->num_codecs = num_codecs; 155 156 for_each_child_of_node_scoped(node, np) { 157 ret = meson_card_parse_dai(card, np, codec); 158 if (ret) 159 return ret; 160 161 codec++; 162 } 163 164 ret = meson_card_set_link_name(card, link, node, "be"); 165 if (ret) 166 dev_err(card->dev, "error setting %pOFn link name\n", node); 167 168 return ret; 169 } 170 EXPORT_SYMBOL_GPL(meson_card_set_be_link); 171 172 int meson_card_set_fe_link(struct snd_soc_card *card, 173 struct snd_soc_dai_link *link, 174 struct device_node *node, 175 bool is_playback) 176 { 177 link->codecs = &snd_soc_dummy_dlc; 178 link->num_codecs = 1; 179 180 link->dynamic = 1; 181 link->dpcm_merged_format = 1; 182 link->dpcm_merged_chan = 1; 183 link->dpcm_merged_rate = 1; 184 185 if (is_playback) 186 link->playback_only = 1; 187 else 188 link->capture_only = 1; 189 190 return meson_card_set_link_name(card, link, node, "fe"); 191 } 192 EXPORT_SYMBOL_GPL(meson_card_set_fe_link); 193 194 static int meson_card_add_links(struct snd_soc_card *card) 195 { 196 struct meson_card *priv = snd_soc_card_get_drvdata(card); 197 struct device_node *node = card->dev->of_node; 198 int num, i, ret; 199 200 num = of_get_child_count(node); 201 if (!num) { 202 dev_err(card->dev, "card has no links\n"); 203 return -EINVAL; 204 } 205 206 ret = meson_card_reallocate_links(card, num); 207 if (ret) 208 return ret; 209 210 i = 0; 211 for_each_child_of_node_scoped(node, np) { 212 ret = priv->match_data->add_link(card, np, &i); 213 if (ret) 214 return ret; 215 216 i++; 217 } 218 219 return 0; 220 } 221 222 static int meson_card_parse_of_optional(struct snd_soc_card *card, 223 const char *propname, 224 int (*func)(struct snd_soc_card *c, 225 const char *p)) 226 { 227 /* If property is not provided, don't fail ... */ 228 if (!of_property_present(card->dev->of_node, propname)) 229 return 0; 230 231 /* ... but do fail if it is provided and the parsing fails */ 232 return func(card, propname); 233 } 234 235 static void meson_card_clean_references(struct meson_card *priv) 236 { 237 struct snd_soc_card *card = &priv->card; 238 struct snd_soc_dai_link *link; 239 struct snd_soc_dai_link_component *codec; 240 struct snd_soc_aux_dev *aux; 241 int i, j; 242 243 if (card->dai_link) { 244 for_each_card_prelinks(card, i, link) { 245 if (link->cpus) 246 of_node_put(link->cpus->of_node); 247 for_each_link_codecs(link, j, codec) 248 of_node_put(codec->of_node); 249 } 250 } 251 252 if (card->aux_dev) { 253 for_each_card_pre_auxs(card, i, aux) 254 of_node_put(aux->dlc.of_node); 255 } 256 257 kfree(card->dai_link); 258 kfree(priv->link_data); 259 } 260 261 int meson_card_probe(struct platform_device *pdev) 262 { 263 const struct meson_card_match_data *data; 264 struct device *dev = &pdev->dev; 265 struct meson_card *priv; 266 int ret; 267 268 data = of_device_get_match_data(dev); 269 if (!data) { 270 dev_err(dev, "failed to match device\n"); 271 return -ENODEV; 272 } 273 274 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 275 if (!priv) 276 return -ENOMEM; 277 278 platform_set_drvdata(pdev, priv); 279 snd_soc_card_set_drvdata(&priv->card, priv); 280 281 priv->card.owner = THIS_MODULE; 282 priv->card.dev = dev; 283 priv->card.driver_name = dev->driver->name; 284 priv->match_data = data; 285 286 ret = snd_soc_of_parse_card_name(&priv->card, "model"); 287 if (ret < 0) 288 return ret; 289 290 ret = meson_card_parse_of_optional(&priv->card, "audio-routing", 291 snd_soc_of_parse_audio_routing); 292 if (ret) { 293 dev_err(dev, "error while parsing routing\n"); 294 return ret; 295 } 296 297 ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", 298 snd_soc_of_parse_audio_simple_widgets); 299 if (ret) { 300 dev_err(dev, "error while parsing widgets\n"); 301 return ret; 302 } 303 304 ret = meson_card_add_links(&priv->card); 305 if (ret) 306 goto out_err; 307 308 ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs"); 309 if (ret) 310 goto out_err; 311 312 ret = devm_snd_soc_register_card(dev, &priv->card); 313 if (ret) 314 goto out_err; 315 316 return 0; 317 318 out_err: 319 meson_card_clean_references(priv); 320 return ret; 321 } 322 EXPORT_SYMBOL_GPL(meson_card_probe); 323 324 void meson_card_remove(struct platform_device *pdev) 325 { 326 struct meson_card *priv = platform_get_drvdata(pdev); 327 328 meson_card_clean_references(priv); 329 } 330 EXPORT_SYMBOL_GPL(meson_card_remove); 331 332 MODULE_DESCRIPTION("Amlogic Sound Card Utils"); 333 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 334 MODULE_LICENSE("GPL v2"); 335