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