338 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * mtk-soundcard-driver.c -- MediaTek soundcard driver common4 *5 * Copyright (c) 2022 MediaTek Inc.6 * Author: Trevor Wu <trevor.wu@mediatek.com>7 */8 9#include <linux/module.h>10#include <linux/of.h>11#include <sound/soc.h>12 13#include "mtk-dsp-sof-common.h"14#include "mtk-soc-card.h"15#include "mtk-soundcard-driver.h"16 17static int set_card_codec_info(struct snd_soc_card *card,18 struct device_node *sub_node,19 struct snd_soc_dai_link *dai_link)20{21 struct device *dev = card->dev;22 struct device_node *codec_node;23 int ret;24 25 codec_node = of_get_child_by_name(sub_node, "codec");26 if (!codec_node) {27 dev_dbg(dev, "%s no specified codec: setting dummy.\n", dai_link->name);28 29 dai_link->codecs = &snd_soc_dummy_dlc;30 dai_link->num_codecs = 1;31 dai_link->dynamic = 1;32 return 0;33 }34 35 /* set card codec info */36 ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);37 38 of_node_put(codec_node);39 40 if (ret < 0)41 return dev_err_probe(dev, ret, "%s: codec dai not found\n",42 dai_link->name);43 44 return 0;45}46 47static int set_dailink_daifmt(struct snd_soc_card *card,48 struct device_node *sub_node,49 struct snd_soc_dai_link *dai_link)50{51 unsigned int daifmt;52 const char *str;53 int ret;54 struct {55 char *name;56 unsigned int val;57 } of_clk_table[] = {58 { "cpu", SND_SOC_DAIFMT_CBC_CFC },59 { "codec", SND_SOC_DAIFMT_CBP_CFP },60 };61 62 daifmt = snd_soc_daifmt_parse_format(sub_node, NULL);63 if (daifmt) {64 dai_link->dai_fmt &= SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;65 dai_link->dai_fmt |= daifmt;66 }67 68 /*69 * check "mediatek,clk-provider = xxx"70 * SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK area71 */72 ret = of_property_read_string(sub_node, "mediatek,clk-provider", &str);73 if (ret == 0) {74 int i;75 76 for (i = 0; i < ARRAY_SIZE(of_clk_table); i++) {77 if (strcmp(str, of_clk_table[i].name) == 0) {78 dai_link->dai_fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;79 dai_link->dai_fmt |= of_clk_table[i].val;80 break;81 }82 }83 }84 85 return 0;86}87 88int parse_dai_link_info(struct snd_soc_card *card)89{90 struct device *dev = card->dev;91 struct device_node *sub_node;92 struct snd_soc_dai_link *dai_link;93 const char *dai_link_name;94 int ret, i;95 96 /* Loop over all the dai link sub nodes */97 for_each_available_child_of_node(dev->of_node, sub_node) {98 if (of_property_read_string(sub_node, "link-name",99 &dai_link_name)) {100 of_node_put(sub_node);101 return -EINVAL;102 }103 104 for_each_card_prelinks(card, i, dai_link) {105 if (!strcmp(dai_link_name, dai_link->name))106 break;107 }108 109 if (i >= card->num_links) {110 of_node_put(sub_node);111 return -EINVAL;112 }113 114 ret = set_card_codec_info(card, sub_node, dai_link);115 if (ret < 0) {116 of_node_put(sub_node);117 return ret;118 }119 120 ret = set_dailink_daifmt(card, sub_node, dai_link);121 if (ret < 0) {122 of_node_put(sub_node);123 return ret;124 }125 }126 127 return 0;128}129EXPORT_SYMBOL_GPL(parse_dai_link_info);130 131void clean_card_reference(struct snd_soc_card *card)132{133 struct snd_soc_dai_link *dai_link;134 int i;135 136 /* release codec reference gotten by set_card_codec_info */137 for_each_card_prelinks(card, i, dai_link)138 snd_soc_of_put_dai_link_codecs(dai_link);139}140EXPORT_SYMBOL_GPL(clean_card_reference);141 142int mtk_soundcard_startup(struct snd_pcm_substream *substream,143 enum mtk_pcm_constraint_type ctype)144{145 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);146 struct mtk_soc_card_data *soc_card = snd_soc_card_get_drvdata(rtd->card);147 const struct mtk_pcm_constraints_data *mpc = &soc_card->card_data->pcm_constraints[ctype];148 int ret;149 150 if (unlikely(!mpc))151 return -EINVAL;152 153 ret = snd_pcm_hw_constraint_list(substream->runtime, 0,154 SNDRV_PCM_HW_PARAM_RATE,155 mpc->rates);156 if (ret < 0) {157 dev_err(rtd->dev, "hw_constraint_list rate failed\n");158 return ret;159 }160 161 ret = snd_pcm_hw_constraint_list(substream->runtime, 0,162 SNDRV_PCM_HW_PARAM_CHANNELS,163 mpc->channels);164 if (ret < 0) {165 dev_err(rtd->dev, "hw_constraint_list channel failed\n");166 return ret;167 }168 169 return 0;170}171EXPORT_SYMBOL_GPL(mtk_soundcard_startup);172 173static int mtk_soundcard_playback_startup(struct snd_pcm_substream *substream)174{175 return mtk_soundcard_startup(substream, MTK_CONSTRAINT_PLAYBACK);176}177 178const struct snd_soc_ops mtk_soundcard_common_playback_ops = {179 .startup = mtk_soundcard_playback_startup,180};181EXPORT_SYMBOL_GPL(mtk_soundcard_common_playback_ops);182 183static int mtk_soundcard_capture_startup(struct snd_pcm_substream *substream)184{185 return mtk_soundcard_startup(substream, MTK_CONSTRAINT_CAPTURE);186}187 188const struct snd_soc_ops mtk_soundcard_common_capture_ops = {189 .startup = mtk_soundcard_capture_startup,190};191EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops);192 193int mtk_soundcard_common_probe(struct platform_device *pdev)194{195 struct device_node *platform_node, *adsp_node;196 const struct mtk_soundcard_pdata *pdata;197 struct mtk_soc_card_data *soc_card_data;198 struct snd_soc_dai_link *orig_dai_link, *dai_link;199 struct snd_soc_jack *jacks;200 struct snd_soc_card *card;201 int i, orig_num_links, ret;202 bool needs_legacy_probe;203 204 pdata = device_get_match_data(&pdev->dev);205 if (!pdata)206 return -EINVAL;207 208 card = pdata->card_data->card;209 card->dev = &pdev->dev;210 orig_dai_link = card->dai_link;211 orig_num_links = card->num_links;212 213 ret = snd_soc_of_parse_card_name(card, "model");214 if (ret)215 return ret;216 217 if (!card->name) {218 if (!pdata->card_name)219 return -EINVAL;220 221 card->name = pdata->card_name;222 }223 224 needs_legacy_probe = !of_property_read_bool(pdev->dev.of_node, "audio-routing");225 if (needs_legacy_probe) {226 /*227 * If we have no .soc_probe() callback there's no way of using228 * any legacy probe mechanism, as that cannot not be generic.229 */230 if (!pdata->soc_probe)231 return -EINVAL;232 233 dev_info_once(&pdev->dev, "audio-routing not found: using legacy probe\n");234 } else {235 ret = snd_soc_of_parse_audio_routing(card, "audio-routing");236 if (ret)237 return ret;238 }239 240 soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL);241 if (!soc_card_data)242 return -ENOMEM;243 244 soc_card_data->card_data = pdata->card_data;245 246 jacks = devm_kcalloc(card->dev, soc_card_data->card_data->num_jacks,247 sizeof(*jacks), GFP_KERNEL);248 if (!jacks)249 return -ENOMEM;250 251 soc_card_data->card_data->jacks = jacks;252 253 platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);254 if (!platform_node)255 return dev_err_probe(&pdev->dev, -EINVAL,256 "Property mediatek,platform missing or invalid\n");257 258 /* Check if this SoC has an Audio DSP */259 if (pdata->sof_priv)260 adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0);261 else262 adsp_node = NULL;263 264 if (adsp_node) {265 if (of_property_read_bool(pdev->dev.of_node, "mediatek,dai-link")) {266 ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node,267 "mediatek,dai-link",268 card->dai_link, card->num_links);269 if (ret) {270 of_node_put(adsp_node);271 of_node_put(platform_node);272 return dev_err_probe(&pdev->dev, ret,273 "Cannot parse mediatek,dai-link\n");274 }275 }276 277 soc_card_data->sof_priv = pdata->sof_priv;278 card->probe = mtk_sof_card_probe;279 card->late_probe = mtk_sof_card_late_probe;280 if (!card->topology_shortname_created) {281 snprintf(card->topology_shortname, 32, "sof-%s", card->name);282 card->topology_shortname_created = true;283 }284 card->name = card->topology_shortname;285 }286 287 /*288 * Regardless of whether the ADSP is wanted and/or present in a machine289 * specific device tree or not and regardless of whether any AFE_SOF290 * link is present, we have to make sure that the platforms->of_node291 * is not NULL, and set to either ADSP (adsp_node) or AFE (platform_node).292 */293 for_each_card_prelinks(card, i, dai_link) {294 if (adsp_node && !strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF")))295 dai_link->platforms->of_node = adsp_node;296 else if (!dai_link->platforms->name && !dai_link->platforms->of_node)297 dai_link->platforms->of_node = platform_node;298 }299 300 if (!needs_legacy_probe) {301 ret = parse_dai_link_info(card);302 if (ret)303 goto err_restore_dais;304 } else {305 if (adsp_node)306 of_node_put(adsp_node);307 of_node_put(platform_node);308 }309 310 if (pdata->soc_probe) {311 ret = pdata->soc_probe(soc_card_data, needs_legacy_probe);312 if (ret) {313 if (!needs_legacy_probe)314 clean_card_reference(card);315 goto err_restore_dais;316 }317 }318 snd_soc_card_set_drvdata(card, soc_card_data);319 320 ret = devm_snd_soc_register_card(&pdev->dev, card);321 322 if (!needs_legacy_probe)323 clean_card_reference(card);324 325 if (ret) {326 dev_err_probe(&pdev->dev, ret, "Cannot register card\n");327 goto err_restore_dais;328 }329 330 return 0;331 332err_restore_dais:333 card->dai_link = orig_dai_link;334 card->num_links = orig_num_links;335 return ret;336}337EXPORT_SYMBOL_GPL(mtk_soundcard_common_probe);338