xref: /linux/sound/soc/mediatek/common/mtk-soundcard-driver.c (revision 05a54fa773284d1a7923cdfdd8f0c8dabb98bd26)
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 
18 static 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 
48 static 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 
89 int 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 }
121 EXPORT_SYMBOL_GPL(parse_dai_link_info);
122 
123 void 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 }
132 EXPORT_SYMBOL_GPL(clean_card_reference);
133 
134 int 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 }
163 EXPORT_SYMBOL_GPL(mtk_soundcard_startup);
164 
165 static int mtk_soundcard_playback_startup(struct snd_pcm_substream *substream)
166 {
167 	return mtk_soundcard_startup(substream, MTK_CONSTRAINT_PLAYBACK);
168 }
169 
170 const struct snd_soc_ops mtk_soundcard_common_playback_ops = {
171 	.startup = mtk_soundcard_playback_startup,
172 };
173 EXPORT_SYMBOL_GPL(mtk_soundcard_common_playback_ops);
174 
175 static int mtk_soundcard_capture_startup(struct snd_pcm_substream *substream)
176 {
177 	return mtk_soundcard_startup(substream, MTK_CONSTRAINT_CAPTURE);
178 }
179 
180 const struct snd_soc_ops mtk_soundcard_common_capture_ops = {
181 	.startup = mtk_soundcard_capture_startup,
182 };
183 EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops);
184 
185 int 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 
344 err_restore_dais:
345 	card->dai_link = orig_dai_link;
346 	card->num_links = orig_num_links;
347 	return ret;
348 }
349 EXPORT_SYMBOL_GPL(mtk_soundcard_common_probe);
350