xref: /linux/sound/soc/mediatek/common/mtk-dsp-sof-common.c (revision 33e02dc69afbd8f1b85a51d74d72f139ba4ca623)
10caf1120SChunxu Li // SPDX-License-Identifier: GPL-2.0
20caf1120SChunxu Li /*
30caf1120SChunxu Li  * mtk-dsp-sof-common.c  --  MediaTek dsp sof common ctrl
40caf1120SChunxu Li  *
50caf1120SChunxu Li  * Copyright (c) 2022 MediaTek Inc.
60caf1120SChunxu Li  * Author: Chunxu Li <chunxu.li@mediatek.com>
70caf1120SChunxu Li  */
80caf1120SChunxu Li 
90caf1120SChunxu Li #include "mtk-dsp-sof-common.h"
100caf1120SChunxu Li #include "mtk-soc-card.h"
110caf1120SChunxu Li 
120caf1120SChunxu Li /* fixup the BE DAI link to match any values from topology */
mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)130caf1120SChunxu Li int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
140caf1120SChunxu Li 			   struct snd_pcm_hw_params *params)
150caf1120SChunxu Li {
160caf1120SChunxu Li 	struct snd_soc_card *card = rtd->card;
170caf1120SChunxu Li 	struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
18*f8782f77SAngeloGioacchino Del Regno 	const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
190caf1120SChunxu Li 	int i, j, ret = 0;
200caf1120SChunxu Li 
210caf1120SChunxu Li 	for (i = 0; i < sof_priv->num_streams; i++) {
220caf1120SChunxu Li 		struct snd_soc_dai *cpu_dai;
230caf1120SChunxu Li 		struct snd_soc_pcm_runtime *runtime;
240caf1120SChunxu Li 		struct snd_soc_dai_link *sof_dai_link = NULL;
250caf1120SChunxu Li 		const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
260caf1120SChunxu Li 
27e3b3ec96SAngeloGioacchino Del Regno 		if (conn->normal_link && strcmp(rtd->dai_link->name, conn->normal_link))
280caf1120SChunxu Li 			continue;
290caf1120SChunxu Li 
300caf1120SChunxu Li 		for_each_card_rtds(card, runtime) {
310caf1120SChunxu Li 			if (strcmp(runtime->dai_link->name, conn->sof_link))
320caf1120SChunxu Li 				continue;
330caf1120SChunxu Li 
340caf1120SChunxu Li 			for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
350df2ec8eSKuninori Morimoto 				if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) {
360caf1120SChunxu Li 					sof_dai_link = runtime->dai_link;
370caf1120SChunxu Li 					break;
380caf1120SChunxu Li 				}
390caf1120SChunxu Li 			}
400caf1120SChunxu Li 			break;
410caf1120SChunxu Li 		}
420caf1120SChunxu Li 
430caf1120SChunxu Li 		if (sof_dai_link && sof_dai_link->be_hw_params_fixup)
440caf1120SChunxu Li 			ret = sof_dai_link->be_hw_params_fixup(runtime, params);
450caf1120SChunxu Li 
460caf1120SChunxu Li 		break;
470caf1120SChunxu Li 	}
480caf1120SChunxu Li 
490caf1120SChunxu Li 	return ret;
500caf1120SChunxu Li }
510caf1120SChunxu Li EXPORT_SYMBOL_GPL(mtk_sof_dai_link_fixup);
520caf1120SChunxu Li 
mtk_sof_card_probe(struct snd_soc_card * card)530caf1120SChunxu Li int mtk_sof_card_probe(struct snd_soc_card *card)
540caf1120SChunxu Li {
550caf1120SChunxu Li 	int i;
560caf1120SChunxu Li 	struct snd_soc_dai_link *dai_link;
574047b35cSTrevor Wu 	struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
580caf1120SChunxu Li 
590caf1120SChunxu Li 	/* Set stream_name to help sof bind widgets */
600caf1120SChunxu Li 	for_each_card_prelinks(card, i, dai_link) {
610caf1120SChunxu Li 		if (dai_link->no_pcm && !dai_link->stream_name && dai_link->name)
620caf1120SChunxu Li 			dai_link->stream_name = dai_link->name;
630caf1120SChunxu Li 	}
640caf1120SChunxu Li 
65*f8782f77SAngeloGioacchino Del Regno 	INIT_LIST_HEAD(&soc_card_data->sof_dai_link_list);
664047b35cSTrevor Wu 
670caf1120SChunxu Li 	return 0;
680caf1120SChunxu Li }
690caf1120SChunxu Li EXPORT_SYMBOL_GPL(mtk_sof_card_probe);
700caf1120SChunxu Li 
mtk_sof_find_tplg_be(struct snd_soc_pcm_runtime * rtd)714047b35cSTrevor Wu static struct snd_soc_pcm_runtime *mtk_sof_find_tplg_be(struct snd_soc_pcm_runtime *rtd)
724047b35cSTrevor Wu {
734047b35cSTrevor Wu 	struct snd_soc_card *card = rtd->card;
744047b35cSTrevor Wu 	struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
75*f8782f77SAngeloGioacchino Del Regno 	const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
764047b35cSTrevor Wu 	struct snd_soc_pcm_runtime *fe;
774047b35cSTrevor Wu 	struct snd_soc_pcm_runtime *be;
784047b35cSTrevor Wu 	struct snd_soc_dpcm *dpcm;
794047b35cSTrevor Wu 	int i, stream;
804047b35cSTrevor Wu 
814047b35cSTrevor Wu 	for_each_pcm_streams(stream) {
824047b35cSTrevor Wu 		fe = NULL;
834047b35cSTrevor Wu 		for_each_dpcm_fe(rtd, stream, dpcm) {
844047b35cSTrevor Wu 			fe = dpcm->fe;
854047b35cSTrevor Wu 			if (fe)
864047b35cSTrevor Wu 				break;
874047b35cSTrevor Wu 		}
884047b35cSTrevor Wu 
894047b35cSTrevor Wu 		if (!fe)
904047b35cSTrevor Wu 			continue;
914047b35cSTrevor Wu 
924047b35cSTrevor Wu 		for_each_dpcm_be(fe, stream, dpcm) {
934047b35cSTrevor Wu 			be = dpcm->be;
944047b35cSTrevor Wu 			if (be == rtd)
954047b35cSTrevor Wu 				continue;
964047b35cSTrevor Wu 
974047b35cSTrevor Wu 			for (i = 0; i < sof_priv->num_streams; i++) {
984047b35cSTrevor Wu 				const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
994047b35cSTrevor Wu 
1004047b35cSTrevor Wu 				if (!strcmp(be->dai_link->name, conn->sof_link))
1014047b35cSTrevor Wu 					return be;
1024047b35cSTrevor Wu 			}
1034047b35cSTrevor Wu 		}
1044047b35cSTrevor Wu 	}
1054047b35cSTrevor Wu 
1064047b35cSTrevor Wu 	return NULL;
1074047b35cSTrevor Wu }
1084047b35cSTrevor Wu 
1094047b35cSTrevor Wu /* fixup the BE DAI link to match any values from topology */
mtk_sof_check_tplg_be_dai_link_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)1104047b35cSTrevor Wu static int mtk_sof_check_tplg_be_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
1114047b35cSTrevor Wu 						struct snd_pcm_hw_params *params)
1124047b35cSTrevor Wu {
1134047b35cSTrevor Wu 	struct snd_soc_card *card = rtd->card;
1144047b35cSTrevor Wu 	struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card);
115*f8782f77SAngeloGioacchino Del Regno 	const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
1164047b35cSTrevor Wu 	struct snd_soc_pcm_runtime *sof_be;
1174047b35cSTrevor Wu 	struct mtk_dai_link *dai_link;
1184047b35cSTrevor Wu 	int ret = 0;
1194047b35cSTrevor Wu 
1204047b35cSTrevor Wu 	sof_be = mtk_sof_find_tplg_be(rtd);
1214047b35cSTrevor Wu 	if (sof_be) {
1224047b35cSTrevor Wu 		if (sof_priv->sof_dai_link_fixup)
1234047b35cSTrevor Wu 			ret = sof_priv->sof_dai_link_fixup(rtd, params);
1244047b35cSTrevor Wu 		else if (sof_be->dai_link->be_hw_params_fixup)
1254047b35cSTrevor Wu 			ret = sof_be->dai_link->be_hw_params_fixup(sof_be, params);
1264047b35cSTrevor Wu 	} else {
127*f8782f77SAngeloGioacchino Del Regno 		list_for_each_entry(dai_link, &soc_card_data->sof_dai_link_list, list) {
1284047b35cSTrevor Wu 			if (strcmp(dai_link->name, rtd->dai_link->name) == 0) {
1294047b35cSTrevor Wu 				if (dai_link->be_hw_params_fixup)
1304047b35cSTrevor Wu 					ret = dai_link->be_hw_params_fixup(rtd, params);
1314047b35cSTrevor Wu 
1324047b35cSTrevor Wu 				break;
1334047b35cSTrevor Wu 			}
1344047b35cSTrevor Wu 		}
1354047b35cSTrevor Wu 	}
1364047b35cSTrevor Wu 
1374047b35cSTrevor Wu 	return ret;
1384047b35cSTrevor Wu }
1394047b35cSTrevor Wu 
mtk_sof_card_late_probe(struct snd_soc_card * card)1400caf1120SChunxu Li int mtk_sof_card_late_probe(struct snd_soc_card *card)
1410caf1120SChunxu Li {
1420caf1120SChunxu Li 	struct snd_soc_pcm_runtime *rtd;
1430caf1120SChunxu Li 	struct snd_soc_component *sof_comp = NULL;
1440caf1120SChunxu Li 	struct mtk_soc_card_data *soc_card_data =
1450caf1120SChunxu Li 		snd_soc_card_get_drvdata(card);
146*f8782f77SAngeloGioacchino Del Regno 	const struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv;
1474047b35cSTrevor Wu 	struct snd_soc_dai_link *dai_link;
1484047b35cSTrevor Wu 	struct mtk_dai_link *mtk_dai_link;
1490caf1120SChunxu Li 	int i;
1500caf1120SChunxu Li 
1510caf1120SChunxu Li 	/* 1. find sof component */
1520caf1120SChunxu Li 	for_each_card_rtds(card, rtd) {
1530caf1120SChunxu Li 		sof_comp = snd_soc_rtdcom_lookup(rtd, "sof-audio-component");
1540caf1120SChunxu Li 		if (sof_comp)
1550caf1120SChunxu Li 			break;
1560caf1120SChunxu Li 	}
1570caf1120SChunxu Li 
1580caf1120SChunxu Li 	if (!sof_comp) {
1590caf1120SChunxu Li 		dev_info(card->dev, "probe without sof-audio-component\n");
1600caf1120SChunxu Li 		return 0;
1610caf1120SChunxu Li 	}
1620caf1120SChunxu Li 
1634047b35cSTrevor Wu 	/* 2. overwrite all BE fixups, and backup the existing fixup */
1644047b35cSTrevor Wu 	for_each_card_prelinks(card, i, dai_link) {
1654047b35cSTrevor Wu 		if (dai_link->be_hw_params_fixup) {
1664047b35cSTrevor Wu 			mtk_dai_link = devm_kzalloc(card->dev,
1674047b35cSTrevor Wu 						    sizeof(*mtk_dai_link),
1684047b35cSTrevor Wu 						    GFP_KERNEL);
1694047b35cSTrevor Wu 			if (!mtk_dai_link)
1704047b35cSTrevor Wu 				return -ENOMEM;
1714047b35cSTrevor Wu 
1724047b35cSTrevor Wu 			mtk_dai_link->be_hw_params_fixup = dai_link->be_hw_params_fixup;
1734047b35cSTrevor Wu 			mtk_dai_link->name = dai_link->name;
1744047b35cSTrevor Wu 
175*f8782f77SAngeloGioacchino Del Regno 			list_add(&mtk_dai_link->list, &soc_card_data->sof_dai_link_list);
1764047b35cSTrevor Wu 		}
1774047b35cSTrevor Wu 
1784047b35cSTrevor Wu 		if (dai_link->no_pcm)
1794047b35cSTrevor Wu 			dai_link->be_hw_params_fixup = mtk_sof_check_tplg_be_dai_link_fixup;
1804047b35cSTrevor Wu 	}
1814047b35cSTrevor Wu 
1824047b35cSTrevor Wu 	/* 3. add route path and SOF_BE fixup callback */
1830caf1120SChunxu Li 	for (i = 0; i < sof_priv->num_streams; i++) {
1840caf1120SChunxu Li 		const struct sof_conn_stream *conn = &sof_priv->conn_streams[i];
1850caf1120SChunxu Li 		struct snd_soc_pcm_runtime *sof_rtd = NULL;
1860caf1120SChunxu Li 
1870caf1120SChunxu Li 		for_each_card_rtds(card, rtd) {
1880caf1120SChunxu Li 			if (!strcmp(rtd->dai_link->name, conn->sof_link)) {
1890caf1120SChunxu Li 				sof_rtd = rtd;
1900caf1120SChunxu Li 				break;
1910caf1120SChunxu Li 			}
1924047b35cSTrevor Wu 		}
1934047b35cSTrevor Wu 		if (sof_rtd) {
1940caf1120SChunxu Li 			int j;
1950caf1120SChunxu Li 			struct snd_soc_dai *cpu_dai;
1960caf1120SChunxu Li 
1970caf1120SChunxu Li 			for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
1980caf1120SChunxu Li 				struct snd_soc_dapm_route route;
1990caf1120SChunxu Li 				struct snd_soc_dapm_path *p = NULL;
2000df2ec8eSKuninori Morimoto 				struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir);
2010df2ec8eSKuninori Morimoto 
2020caf1120SChunxu Li 				memset(&route, 0, sizeof(route));
2030df2ec8eSKuninori Morimoto 				if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) {
2040df2ec8eSKuninori Morimoto 					snd_soc_dapm_widget_for_each_sink_path(widget, p) {
2050caf1120SChunxu Li 						route.source = conn->sof_dma;
2060caf1120SChunxu Li 						route.sink = p->sink->name;
2070caf1120SChunxu Li 						snd_soc_dapm_add_routes(&card->dapm, &route, 1);
2080caf1120SChunxu Li 					}
2090df2ec8eSKuninori Morimoto 				} else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) {
2100df2ec8eSKuninori Morimoto 					snd_soc_dapm_widget_for_each_source_path(widget, p) {
2110caf1120SChunxu Li 						route.source = p->source->name;
2120caf1120SChunxu Li 						route.sink = conn->sof_dma;
2130caf1120SChunxu Li 						snd_soc_dapm_add_routes(&card->dapm, &route, 1);
2140caf1120SChunxu Li 					}
2150caf1120SChunxu Li 				} else {
2160caf1120SChunxu Li 					dev_err(cpu_dai->dev, "stream dir and widget not pair\n");
2170caf1120SChunxu Li 				}
2180caf1120SChunxu Li 			}
2190caf1120SChunxu Li 
2204047b35cSTrevor Wu 			/* overwrite SOF BE fixup */
2210caf1120SChunxu Li 			sof_rtd->dai_link->be_hw_params_fixup =
2220caf1120SChunxu Li 				sof_comp->driver->be_hw_params_fixup;
2230caf1120SChunxu Li 		}
2240caf1120SChunxu Li 	}
2250caf1120SChunxu Li 
2260caf1120SChunxu Li 	return 0;
2270caf1120SChunxu Li }
2280caf1120SChunxu Li EXPORT_SYMBOL_GPL(mtk_sof_card_late_probe);
2290caf1120SChunxu Li 
mtk_sof_dailink_parse_of(struct snd_soc_card * card,struct device_node * np,const char * propname,struct snd_soc_dai_link * pre_dai_links,int pre_num_links)2300caf1120SChunxu Li int mtk_sof_dailink_parse_of(struct snd_soc_card *card, struct device_node *np,
2310caf1120SChunxu Li 			     const char *propname, struct snd_soc_dai_link *pre_dai_links,
2320caf1120SChunxu Li 			     int pre_num_links)
2330caf1120SChunxu Li {
2340caf1120SChunxu Li 	struct device *dev = card->dev;
2350caf1120SChunxu Li 	struct snd_soc_dai_link *parsed_dai_link;
2360caf1120SChunxu Li 	const char *dai_name = NULL;
2370caf1120SChunxu Li 	int i, j, ret, num_links, parsed_num_links = 0;
2380caf1120SChunxu Li 
2390caf1120SChunxu Li 	num_links = of_property_count_strings(np, "mediatek,dai-link");
2400caf1120SChunxu Li 	if (num_links < 0 || num_links > card->num_links) {
2410caf1120SChunxu Li 		dev_dbg(dev, "number of dai-link is invalid\n");
2420caf1120SChunxu Li 		return -EINVAL;
2430caf1120SChunxu Li 	}
2440caf1120SChunxu Li 
2450caf1120SChunxu Li 	parsed_dai_link = devm_kcalloc(dev, num_links, sizeof(*parsed_dai_link), GFP_KERNEL);
2460caf1120SChunxu Li 	if (!parsed_dai_link)
2470caf1120SChunxu Li 		return -ENOMEM;
2480caf1120SChunxu Li 
2490caf1120SChunxu Li 	for (i = 0; i < num_links; i++) {
2500caf1120SChunxu Li 		ret = of_property_read_string_index(np, propname, i, &dai_name);
2510caf1120SChunxu Li 		if (ret) {
2520caf1120SChunxu Li 			dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n",
2530caf1120SChunxu Li 				propname, i, ret);
2540caf1120SChunxu Li 			return ret;
2550caf1120SChunxu Li 		}
2560caf1120SChunxu Li 		dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name);
2570caf1120SChunxu Li 		for (j = 0; j < pre_num_links; j++) {
2580caf1120SChunxu Li 			if (!strcmp(dai_name, pre_dai_links[j].name)) {
2590caf1120SChunxu Li 				memcpy(&parsed_dai_link[parsed_num_links++], &pre_dai_links[j],
2600caf1120SChunxu Li 				       sizeof(struct snd_soc_dai_link));
2610caf1120SChunxu Li 				break;
2620caf1120SChunxu Li 			}
2630caf1120SChunxu Li 		}
2640caf1120SChunxu Li 	}
2650caf1120SChunxu Li 
2660caf1120SChunxu Li 	if (parsed_num_links != num_links)
2670caf1120SChunxu Li 		return -EINVAL;
2680caf1120SChunxu Li 
2690caf1120SChunxu Li 	card->dai_link = parsed_dai_link;
2700caf1120SChunxu Li 	card->num_links = parsed_num_links;
2710caf1120SChunxu Li 
2720caf1120SChunxu Li 	return 0;
2730caf1120SChunxu Li }
2740caf1120SChunxu Li EXPORT_SYMBOL_GPL(mtk_sof_dailink_parse_of);
275