File Index
Files and directories
Sign in
FILE

mtk-soundcard-driver.c

File preview with syntax highlighting.

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