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