1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * mtk-dsp-sof-common.c -- MediaTek dsp sof common ctrl 4 * 5 * Copyright (c) 2022 MediaTek Inc. 6 * Author: Chunxu Li <chunxu.li@mediatek.com> 7 */ 8 9 #include "mtk-dsp-sof-common.h" 10 #include "mtk-soc-card.h" 11 12 /* fixup the BE DAI link to match any values from topology */ 13 int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, 14 struct snd_pcm_hw_params *params) 15 { 16 struct snd_soc_card *card = rtd->card; 17 struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); 18 struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv; 19 int i, j, ret = 0; 20 21 for (i = 0; i < sof_priv->num_streams; i++) { 22 struct snd_soc_dai *cpu_dai; 23 struct snd_soc_pcm_runtime *runtime; 24 struct snd_soc_dai_link *sof_dai_link = NULL; 25 const struct sof_conn_stream *conn = &sof_priv->conn_streams[i]; 26 27 if (strcmp(rtd->dai_link->name, conn->normal_link)) 28 continue; 29 30 for_each_card_rtds(card, runtime) { 31 if (strcmp(runtime->dai_link->name, conn->sof_link)) 32 continue; 33 34 for_each_rtd_cpu_dais(runtime, j, cpu_dai) { 35 if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) { 36 sof_dai_link = runtime->dai_link; 37 break; 38 } 39 } 40 break; 41 } 42 43 if (sof_dai_link && sof_dai_link->be_hw_params_fixup) 44 ret = sof_dai_link->be_hw_params_fixup(runtime, params); 45 46 break; 47 } 48 49 return ret; 50 } 51 EXPORT_SYMBOL_GPL(mtk_sof_dai_link_fixup); 52 53 int mtk_sof_card_probe(struct snd_soc_card *card) 54 { 55 int i; 56 struct snd_soc_dai_link *dai_link; 57 struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); 58 struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv; 59 60 /* Set stream_name to help sof bind widgets */ 61 for_each_card_prelinks(card, i, dai_link) { 62 if (dai_link->no_pcm && !dai_link->stream_name && dai_link->name) 63 dai_link->stream_name = dai_link->name; 64 } 65 66 INIT_LIST_HEAD(&sof_priv->dai_link_list); 67 68 return 0; 69 } 70 EXPORT_SYMBOL_GPL(mtk_sof_card_probe); 71 72 static struct snd_soc_pcm_runtime *mtk_sof_find_tplg_be(struct snd_soc_pcm_runtime *rtd) 73 { 74 struct snd_soc_card *card = rtd->card; 75 struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); 76 struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv; 77 struct snd_soc_pcm_runtime *fe; 78 struct snd_soc_pcm_runtime *be; 79 struct snd_soc_dpcm *dpcm; 80 int i, stream; 81 82 for_each_pcm_streams(stream) { 83 fe = NULL; 84 for_each_dpcm_fe(rtd, stream, dpcm) { 85 fe = dpcm->fe; 86 if (fe) 87 break; 88 } 89 90 if (!fe) 91 continue; 92 93 for_each_dpcm_be(fe, stream, dpcm) { 94 be = dpcm->be; 95 if (be == rtd) 96 continue; 97 98 for (i = 0; i < sof_priv->num_streams; i++) { 99 const struct sof_conn_stream *conn = &sof_priv->conn_streams[i]; 100 101 if (!strcmp(be->dai_link->name, conn->sof_link)) 102 return be; 103 } 104 } 105 } 106 107 return NULL; 108 } 109 110 /* fixup the BE DAI link to match any values from topology */ 111 static int mtk_sof_check_tplg_be_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, 112 struct snd_pcm_hw_params *params) 113 { 114 struct snd_soc_card *card = rtd->card; 115 struct mtk_soc_card_data *soc_card_data = snd_soc_card_get_drvdata(card); 116 struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv; 117 struct snd_soc_pcm_runtime *sof_be; 118 struct mtk_dai_link *dai_link; 119 int ret = 0; 120 121 sof_be = mtk_sof_find_tplg_be(rtd); 122 if (sof_be) { 123 if (sof_priv->sof_dai_link_fixup) 124 ret = sof_priv->sof_dai_link_fixup(rtd, params); 125 else if (sof_be->dai_link->be_hw_params_fixup) 126 ret = sof_be->dai_link->be_hw_params_fixup(sof_be, params); 127 } else { 128 list_for_each_entry(dai_link, &sof_priv->dai_link_list, list) { 129 if (strcmp(dai_link->name, rtd->dai_link->name) == 0) { 130 if (dai_link->be_hw_params_fixup) 131 ret = dai_link->be_hw_params_fixup(rtd, params); 132 133 break; 134 } 135 } 136 } 137 138 return ret; 139 } 140 141 int mtk_sof_card_late_probe(struct snd_soc_card *card) 142 { 143 struct snd_soc_pcm_runtime *rtd; 144 struct snd_soc_component *sof_comp = NULL; 145 struct mtk_soc_card_data *soc_card_data = 146 snd_soc_card_get_drvdata(card); 147 struct mtk_sof_priv *sof_priv = soc_card_data->sof_priv; 148 struct snd_soc_dai_link *dai_link; 149 struct mtk_dai_link *mtk_dai_link; 150 int i; 151 152 /* 1. find sof component */ 153 for_each_card_rtds(card, rtd) { 154 sof_comp = snd_soc_rtdcom_lookup(rtd, "sof-audio-component"); 155 if (sof_comp) 156 break; 157 } 158 159 if (!sof_comp) { 160 dev_info(card->dev, "probe without sof-audio-component\n"); 161 return 0; 162 } 163 164 /* 2. overwrite all BE fixups, and backup the existing fixup */ 165 for_each_card_prelinks(card, i, dai_link) { 166 if (dai_link->be_hw_params_fixup) { 167 mtk_dai_link = devm_kzalloc(card->dev, 168 sizeof(*mtk_dai_link), 169 GFP_KERNEL); 170 if (!mtk_dai_link) 171 return -ENOMEM; 172 173 mtk_dai_link->be_hw_params_fixup = dai_link->be_hw_params_fixup; 174 mtk_dai_link->name = dai_link->name; 175 176 list_add(&mtk_dai_link->list, &sof_priv->dai_link_list); 177 } 178 179 if (dai_link->no_pcm) 180 dai_link->be_hw_params_fixup = mtk_sof_check_tplg_be_dai_link_fixup; 181 } 182 183 /* 3. add route path and SOF_BE fixup callback */ 184 for (i = 0; i < sof_priv->num_streams; i++) { 185 const struct sof_conn_stream *conn = &sof_priv->conn_streams[i]; 186 struct snd_soc_pcm_runtime *sof_rtd = NULL; 187 188 for_each_card_rtds(card, rtd) { 189 if (!strcmp(rtd->dai_link->name, conn->sof_link)) { 190 sof_rtd = rtd; 191 break; 192 } 193 } 194 if (sof_rtd) { 195 int j; 196 struct snd_soc_dai *cpu_dai; 197 198 for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { 199 struct snd_soc_dapm_route route; 200 struct snd_soc_dapm_path *p = NULL; 201 struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir); 202 203 memset(&route, 0, sizeof(route)); 204 if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) { 205 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 206 route.source = conn->sof_dma; 207 route.sink = p->sink->name; 208 snd_soc_dapm_add_routes(&card->dapm, &route, 1); 209 } 210 } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) { 211 snd_soc_dapm_widget_for_each_source_path(widget, p) { 212 route.source = p->source->name; 213 route.sink = conn->sof_dma; 214 snd_soc_dapm_add_routes(&card->dapm, &route, 1); 215 } 216 } else { 217 dev_err(cpu_dai->dev, "stream dir and widget not pair\n"); 218 } 219 } 220 221 /* overwrite SOF BE fixup */ 222 sof_rtd->dai_link->be_hw_params_fixup = 223 sof_comp->driver->be_hw_params_fixup; 224 } 225 } 226 227 return 0; 228 } 229 EXPORT_SYMBOL_GPL(mtk_sof_card_late_probe); 230 231 int mtk_sof_dailink_parse_of(struct snd_soc_card *card, struct device_node *np, 232 const char *propname, struct snd_soc_dai_link *pre_dai_links, 233 int pre_num_links) 234 { 235 struct device *dev = card->dev; 236 struct snd_soc_dai_link *parsed_dai_link; 237 const char *dai_name = NULL; 238 int i, j, ret, num_links, parsed_num_links = 0; 239 240 num_links = of_property_count_strings(np, "mediatek,dai-link"); 241 if (num_links < 0 || num_links > card->num_links) { 242 dev_dbg(dev, "number of dai-link is invalid\n"); 243 return -EINVAL; 244 } 245 246 parsed_dai_link = devm_kcalloc(dev, num_links, sizeof(*parsed_dai_link), GFP_KERNEL); 247 if (!parsed_dai_link) 248 return -ENOMEM; 249 250 for (i = 0; i < num_links; i++) { 251 ret = of_property_read_string_index(np, propname, i, &dai_name); 252 if (ret) { 253 dev_dbg(dev, "ASoC: Property '%s' index %d could not be read: %d\n", 254 propname, i, ret); 255 return ret; 256 } 257 dev_dbg(dev, "ASoC: Property get dai_name:%s\n", dai_name); 258 for (j = 0; j < pre_num_links; j++) { 259 if (!strcmp(dai_name, pre_dai_links[j].name)) { 260 memcpy(&parsed_dai_link[parsed_num_links++], &pre_dai_links[j], 261 sizeof(struct snd_soc_dai_link)); 262 break; 263 } 264 } 265 } 266 267 if (parsed_num_links != num_links) 268 return -EINVAL; 269 270 card->dai_link = parsed_dai_link; 271 card->num_links = parsed_num_links; 272 273 return 0; 274 } 275 EXPORT_SYMBOL_GPL(mtk_sof_dailink_parse_of); 276