162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2020 BayLibre, SAS. 462306a36Sopenharmony_ci// Author: Jerome Brunet <jbrunet@baylibre.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/of_platform.h> 862306a36Sopenharmony_ci#include <sound/soc.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "meson-card.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ciint meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream, 1362306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 1462306a36Sopenharmony_ci unsigned int mclk_fs) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 1762306a36Sopenharmony_ci struct snd_soc_dai *codec_dai; 1862306a36Sopenharmony_ci unsigned int mclk; 1962306a36Sopenharmony_ci int ret, i; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (!mclk_fs) 2262306a36Sopenharmony_ci return 0; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci mclk = params_rate(params) * mclk_fs; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci for_each_rtd_codec_dais(rtd, i, codec_dai) { 2762306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, 2862306a36Sopenharmony_ci SND_SOC_CLOCK_IN); 2962306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 3062306a36Sopenharmony_ci return ret; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ret = snd_soc_dai_set_sysclk(asoc_rtd_to_cpu(rtd, 0), 0, mclk, 3462306a36Sopenharmony_ci SND_SOC_CLOCK_OUT); 3562306a36Sopenharmony_ci if (ret && ret != -ENOTSUPP) 3662306a36Sopenharmony_ci return ret; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciint meson_card_reallocate_links(struct snd_soc_card *card, 4362306a36Sopenharmony_ci unsigned int num_links) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct meson_card *priv = snd_soc_card_get_drvdata(card); 4662306a36Sopenharmony_ci struct snd_soc_dai_link *links; 4762306a36Sopenharmony_ci void **ldata; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci links = krealloc(priv->card.dai_link, 5062306a36Sopenharmony_ci num_links * sizeof(*priv->card.dai_link), 5162306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 5262306a36Sopenharmony_ci if (!links) 5362306a36Sopenharmony_ci goto err_links; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ldata = krealloc(priv->link_data, 5662306a36Sopenharmony_ci num_links * sizeof(*priv->link_data), 5762306a36Sopenharmony_ci GFP_KERNEL | __GFP_ZERO); 5862306a36Sopenharmony_ci if (!ldata) 5962306a36Sopenharmony_ci goto err_ldata; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci priv->card.dai_link = links; 6262306a36Sopenharmony_ci priv->link_data = ldata; 6362306a36Sopenharmony_ci priv->card.num_links = num_links; 6462306a36Sopenharmony_ci return 0; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cierr_ldata: 6762306a36Sopenharmony_ci kfree(links); 6862306a36Sopenharmony_cierr_links: 6962306a36Sopenharmony_ci dev_err(priv->card.dev, "failed to allocate links\n"); 7062306a36Sopenharmony_ci return -ENOMEM; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_reallocate_links); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ciint meson_card_parse_dai(struct snd_soc_card *card, 7662306a36Sopenharmony_ci struct device_node *node, 7762306a36Sopenharmony_ci struct snd_soc_dai_link_component *dlc) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (!dlc || !node) 8262306a36Sopenharmony_ci return -EINVAL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ret = snd_soc_of_get_dlc(node, NULL, dlc, 0); 8562306a36Sopenharmony_ci if (ret) 8662306a36Sopenharmony_ci return dev_err_probe(card->dev, ret, "can't parse dai\n"); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci return ret; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_parse_dai); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int meson_card_set_link_name(struct snd_soc_card *card, 9362306a36Sopenharmony_ci struct snd_soc_dai_link *link, 9462306a36Sopenharmony_ci struct device_node *node, 9562306a36Sopenharmony_ci const char *prefix) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s", 9862306a36Sopenharmony_ci prefix, node->full_name); 9962306a36Sopenharmony_ci if (!name) 10062306a36Sopenharmony_ci return -ENOMEM; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci link->name = name; 10362306a36Sopenharmony_ci link->stream_name = name; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciunsigned int meson_card_parse_daifmt(struct device_node *node, 10962306a36Sopenharmony_ci struct device_node *cpu_node) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct device_node *bitclkmaster = NULL; 11262306a36Sopenharmony_ci struct device_node *framemaster = NULL; 11362306a36Sopenharmony_ci unsigned int daifmt; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci daifmt = snd_soc_daifmt_parse_format(node, NULL); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci snd_soc_daifmt_parse_clock_provider_as_phandle(node, NULL, &bitclkmaster, &framemaster); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* If no master is provided, default to cpu master */ 12062306a36Sopenharmony_ci if (!bitclkmaster || bitclkmaster == cpu_node) { 12162306a36Sopenharmony_ci daifmt |= (!framemaster || framemaster == cpu_node) ? 12262306a36Sopenharmony_ci SND_SOC_DAIFMT_CBS_CFS : SND_SOC_DAIFMT_CBS_CFM; 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci daifmt |= (!framemaster || framemaster == cpu_node) ? 12562306a36Sopenharmony_ci SND_SOC_DAIFMT_CBM_CFS : SND_SOC_DAIFMT_CBM_CFM; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci of_node_put(bitclkmaster); 12962306a36Sopenharmony_ci of_node_put(framemaster); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return daifmt; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_parse_daifmt); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint meson_card_set_be_link(struct snd_soc_card *card, 13662306a36Sopenharmony_ci struct snd_soc_dai_link *link, 13762306a36Sopenharmony_ci struct device_node *node) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct snd_soc_dai_link_component *codec; 14062306a36Sopenharmony_ci struct device_node *np; 14162306a36Sopenharmony_ci int ret, num_codecs; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci num_codecs = of_get_child_count(node); 14462306a36Sopenharmony_ci if (!num_codecs) { 14562306a36Sopenharmony_ci dev_err(card->dev, "be link %s has no codec\n", 14662306a36Sopenharmony_ci node->full_name); 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL); 15162306a36Sopenharmony_ci if (!codec) 15262306a36Sopenharmony_ci return -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci link->codecs = codec; 15562306a36Sopenharmony_ci link->num_codecs = num_codecs; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for_each_child_of_node(node, np) { 15862306a36Sopenharmony_ci ret = meson_card_parse_dai(card, np, codec); 15962306a36Sopenharmony_ci if (ret) { 16062306a36Sopenharmony_ci of_node_put(np); 16162306a36Sopenharmony_ci return ret; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci codec++; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = meson_card_set_link_name(card, link, node, "be"); 16862306a36Sopenharmony_ci if (ret) 16962306a36Sopenharmony_ci dev_err(card->dev, "error setting %pOFn link name\n", np); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return ret; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_set_be_link); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ciint meson_card_set_fe_link(struct snd_soc_card *card, 17662306a36Sopenharmony_ci struct snd_soc_dai_link *link, 17762306a36Sopenharmony_ci struct device_node *node, 17862306a36Sopenharmony_ci bool is_playback) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci link->codecs = &asoc_dummy_dlc; 18162306a36Sopenharmony_ci link->num_codecs = 1; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci link->dynamic = 1; 18462306a36Sopenharmony_ci link->dpcm_merged_format = 1; 18562306a36Sopenharmony_ci link->dpcm_merged_chan = 1; 18662306a36Sopenharmony_ci link->dpcm_merged_rate = 1; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (is_playback) 18962306a36Sopenharmony_ci link->dpcm_playback = 1; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci link->dpcm_capture = 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return meson_card_set_link_name(card, link, node, "fe"); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_set_fe_link); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic int meson_card_add_links(struct snd_soc_card *card) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct meson_card *priv = snd_soc_card_get_drvdata(card); 20062306a36Sopenharmony_ci struct device_node *node = card->dev->of_node; 20162306a36Sopenharmony_ci struct device_node *np; 20262306a36Sopenharmony_ci int num, i, ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci num = of_get_child_count(node); 20562306a36Sopenharmony_ci if (!num) { 20662306a36Sopenharmony_ci dev_err(card->dev, "card has no links\n"); 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ret = meson_card_reallocate_links(card, num); 21162306a36Sopenharmony_ci if (ret) 21262306a36Sopenharmony_ci return ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci i = 0; 21562306a36Sopenharmony_ci for_each_child_of_node(node, np) { 21662306a36Sopenharmony_ci ret = priv->match_data->add_link(card, np, &i); 21762306a36Sopenharmony_ci if (ret) { 21862306a36Sopenharmony_ci of_node_put(np); 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci i++; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int meson_card_parse_of_optional(struct snd_soc_card *card, 22962306a36Sopenharmony_ci const char *propname, 23062306a36Sopenharmony_ci int (*func)(struct snd_soc_card *c, 23162306a36Sopenharmony_ci const char *p)) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci /* If property is not provided, don't fail ... */ 23462306a36Sopenharmony_ci if (!of_property_read_bool(card->dev->of_node, propname)) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* ... but do fail if it is provided and the parsing fails */ 23862306a36Sopenharmony_ci return func(card, propname); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void meson_card_clean_references(struct meson_card *priv) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct snd_soc_card *card = &priv->card; 24462306a36Sopenharmony_ci struct snd_soc_dai_link *link; 24562306a36Sopenharmony_ci struct snd_soc_dai_link_component *codec; 24662306a36Sopenharmony_ci struct snd_soc_aux_dev *aux; 24762306a36Sopenharmony_ci int i, j; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (card->dai_link) { 25062306a36Sopenharmony_ci for_each_card_prelinks(card, i, link) { 25162306a36Sopenharmony_ci if (link->cpus) 25262306a36Sopenharmony_ci of_node_put(link->cpus->of_node); 25362306a36Sopenharmony_ci for_each_link_codecs(link, j, codec) 25462306a36Sopenharmony_ci of_node_put(codec->of_node); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (card->aux_dev) { 25962306a36Sopenharmony_ci for_each_card_pre_auxs(card, i, aux) 26062306a36Sopenharmony_ci of_node_put(aux->dlc.of_node); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci kfree(card->dai_link); 26462306a36Sopenharmony_ci kfree(priv->link_data); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciint meson_card_probe(struct platform_device *pdev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci const struct meson_card_match_data *data; 27062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 27162306a36Sopenharmony_ci struct meson_card *priv; 27262306a36Sopenharmony_ci int ret; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci data = of_device_get_match_data(dev); 27562306a36Sopenharmony_ci if (!data) { 27662306a36Sopenharmony_ci dev_err(dev, "failed to match device\n"); 27762306a36Sopenharmony_ci return -ENODEV; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 28162306a36Sopenharmony_ci if (!priv) 28262306a36Sopenharmony_ci return -ENOMEM; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 28562306a36Sopenharmony_ci snd_soc_card_set_drvdata(&priv->card, priv); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci priv->card.owner = THIS_MODULE; 28862306a36Sopenharmony_ci priv->card.dev = dev; 28962306a36Sopenharmony_ci priv->card.driver_name = dev->driver->name; 29062306a36Sopenharmony_ci priv->match_data = data; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci ret = snd_soc_of_parse_card_name(&priv->card, "model"); 29362306a36Sopenharmony_ci if (ret < 0) 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = meson_card_parse_of_optional(&priv->card, "audio-routing", 29762306a36Sopenharmony_ci snd_soc_of_parse_audio_routing); 29862306a36Sopenharmony_ci if (ret) { 29962306a36Sopenharmony_ci dev_err(dev, "error while parsing routing\n"); 30062306a36Sopenharmony_ci return ret; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ret = meson_card_parse_of_optional(&priv->card, "audio-widgets", 30462306a36Sopenharmony_ci snd_soc_of_parse_audio_simple_widgets); 30562306a36Sopenharmony_ci if (ret) { 30662306a36Sopenharmony_ci dev_err(dev, "error while parsing widgets\n"); 30762306a36Sopenharmony_ci return ret; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ret = meson_card_add_links(&priv->card); 31162306a36Sopenharmony_ci if (ret) 31262306a36Sopenharmony_ci goto out_err; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs"); 31562306a36Sopenharmony_ci if (ret) 31662306a36Sopenharmony_ci goto out_err; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ret = devm_snd_soc_register_card(dev, &priv->card); 31962306a36Sopenharmony_ci if (ret) 32062306a36Sopenharmony_ci goto out_err; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ciout_err: 32562306a36Sopenharmony_ci meson_card_clean_references(priv); 32662306a36Sopenharmony_ci return ret; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_probe); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ciint meson_card_remove(struct platform_device *pdev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct meson_card *priv = platform_get_drvdata(pdev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci meson_card_clean_references(priv); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return 0; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(meson_card_remove); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ciMODULE_DESCRIPTION("Amlogic Sound Card Utils"); 34162306a36Sopenharmony_ciMODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); 34262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 343