brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 3bbf42c Raw
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