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 device_node *sub_node; 93 struct snd_soc_dai_link *dai_link; 94 const char *dai_link_name; 95 int ret, i; 96 97 /* Loop over all the dai link sub nodes */ 98 for_each_available_child_of_node(dev->of_node, sub_node) { 99 if (of_property_read_string(sub_node, "link-name", 100 &dai_link_name)) { 101 of_node_put(sub_node); 102 return -EINVAL; 103 } 104 105 for_each_card_prelinks(card, i, dai_link) { 106 if (!strcmp(dai_link_name, dai_link->name)) 107 break; 108 } 109 110 if (i >= card->num_links) { 111 of_node_put(sub_node); 112 return -EINVAL; 113 } 114 115 ret = set_card_codec_info(card, sub_node, dai_link); 116 if (ret < 0) { 117 of_node_put(sub_node); 118 return ret; 119 } 120 121 ret = set_dailink_daifmt(card, sub_node, dai_link); 122 if (ret < 0) { 123 of_node_put(sub_node); 124 return ret; 125 } 126 } 127 128 return 0; 129 } 130 EXPORT_SYMBOL_GPL(parse_dai_link_info); 131 132 void clean_card_reference(struct snd_soc_card *card) 133 { 134 struct snd_soc_dai_link *dai_link; 135 int i; 136 137 /* release codec reference gotten by set_card_codec_info */ 138 for_each_card_prelinks(card, i, dai_link) 139 snd_soc_of_put_dai_link_codecs(dai_link); 140 } 141 EXPORT_SYMBOL_GPL(clean_card_reference); 142 143 int mtk_soundcard_startup(struct snd_pcm_substream *substream, 144 enum mtk_pcm_constraint_type ctype) 145 { 146 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 147 struct mtk_soc_card_data *soc_card = snd_soc_card_get_drvdata(rtd->card); 148 const struct mtk_pcm_constraints_data *mpc = &soc_card->card_data->pcm_constraints[ctype]; 149 int ret; 150 151 if (unlikely(!mpc)) 152 return -EINVAL; 153 154 ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 155 SNDRV_PCM_HW_PARAM_RATE, 156 mpc->rates); 157 if (ret < 0) { 158 dev_err(rtd->dev, "hw_constraint_list rate failed\n"); 159 return ret; 160 } 161 162 ret = snd_pcm_hw_constraint_list(substream->runtime, 0, 163 SNDRV_PCM_HW_PARAM_CHANNELS, 164 mpc->channels); 165 if (ret < 0) { 166 dev_err(rtd->dev, "hw_constraint_list channel failed\n"); 167 return ret; 168 } 169 170 return 0; 171 } 172 EXPORT_SYMBOL_GPL(mtk_soundcard_startup); 173 174 static int mtk_soundcard_playback_startup(struct snd_pcm_substream *substream) 175 { 176 return mtk_soundcard_startup(substream, MTK_CONSTRAINT_PLAYBACK); 177 } 178 179 const struct snd_soc_ops mtk_soundcard_common_playback_ops = { 180 .startup = mtk_soundcard_playback_startup, 181 }; 182 EXPORT_SYMBOL_GPL(mtk_soundcard_common_playback_ops); 183 184 static int mtk_soundcard_capture_startup(struct snd_pcm_substream *substream) 185 { 186 return mtk_soundcard_startup(substream, MTK_CONSTRAINT_CAPTURE); 187 } 188 189 const struct snd_soc_ops mtk_soundcard_common_capture_ops = { 190 .startup = mtk_soundcard_capture_startup, 191 }; 192 EXPORT_SYMBOL_GPL(mtk_soundcard_common_capture_ops); 193 194 int mtk_soundcard_common_probe(struct platform_device *pdev) 195 { 196 struct device_node *platform_node, *adsp_node, *accdet_node; 197 struct snd_soc_component *accdet_comp; 198 struct platform_device *accdet_pdev; 199 const struct mtk_soundcard_pdata *pdata; 200 struct mtk_soc_card_data *soc_card_data; 201 struct snd_soc_dai_link *orig_dai_link, *dai_link; 202 struct snd_soc_jack *jacks; 203 struct snd_soc_card *card; 204 int i, orig_num_links, ret; 205 bool needs_legacy_probe; 206 207 pdata = device_get_match_data(&pdev->dev); 208 if (!pdata) 209 return -EINVAL; 210 211 card = pdata->card_data->card; 212 card->dev = &pdev->dev; 213 orig_dai_link = card->dai_link; 214 orig_num_links = card->num_links; 215 216 ret = snd_soc_of_parse_card_name(card, "model"); 217 if (ret) 218 return ret; 219 220 if (!card->name) { 221 if (!pdata->card_name) 222 return -EINVAL; 223 224 card->name = pdata->card_name; 225 } 226 227 needs_legacy_probe = !of_property_present(pdev->dev.of_node, "audio-routing"); 228 if (needs_legacy_probe) { 229 /* 230 * If we have no .soc_probe() callback there's no way of using 231 * any legacy probe mechanism, as that cannot not be generic. 232 */ 233 if (!pdata->soc_probe) 234 return -EINVAL; 235 236 dev_info_once(&pdev->dev, "audio-routing not found: using legacy probe\n"); 237 } else { 238 ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); 239 if (ret) 240 return ret; 241 } 242 243 soc_card_data = devm_kzalloc(&pdev->dev, sizeof(*soc_card_data), GFP_KERNEL); 244 if (!soc_card_data) 245 return -ENOMEM; 246 247 soc_card_data->card_data = pdata->card_data; 248 249 jacks = devm_kcalloc(card->dev, soc_card_data->card_data->num_jacks, 250 sizeof(*jacks), GFP_KERNEL); 251 if (!jacks) 252 return -ENOMEM; 253 254 soc_card_data->card_data->jacks = jacks; 255 256 accdet_node = of_parse_phandle(pdev->dev.of_node, "mediatek,accdet", 0); 257 if (accdet_node) { 258 accdet_pdev = of_find_device_by_node(accdet_node); 259 if (accdet_pdev) { 260 accdet_comp = snd_soc_lookup_component(&accdet_pdev->dev, NULL); 261 if (accdet_comp) 262 soc_card_data->accdet = accdet_comp; 263 else 264 dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n"); 265 266 put_device(&accdet_pdev->dev); 267 } else { 268 dev_err(&pdev->dev, "No device found from mediatek,accdet property\n"); 269 } 270 271 of_node_put(accdet_node); 272 } 273 274 platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0); 275 if (!platform_node) 276 return dev_err_probe(&pdev->dev, -EINVAL, 277 "Property mediatek,platform missing or invalid\n"); 278 279 /* Check if this SoC has an Audio DSP */ 280 if (pdata->sof_priv) 281 adsp_node = of_parse_phandle(pdev->dev.of_node, "mediatek,adsp", 0); 282 else 283 adsp_node = NULL; 284 285 if (adsp_node) { 286 if (of_property_present(pdev->dev.of_node, "mediatek,dai-link")) { 287 ret = mtk_sof_dailink_parse_of(card, pdev->dev.of_node, 288 "mediatek,dai-link", 289 card->dai_link, card->num_links); 290 if (ret) { 291 of_node_put(adsp_node); 292 of_node_put(platform_node); 293 return dev_err_probe(&pdev->dev, ret, 294 "Cannot parse mediatek,dai-link\n"); 295 } 296 } 297 298 soc_card_data->sof_priv = pdata->sof_priv; 299 card->probe = mtk_sof_card_probe; 300 card->late_probe = mtk_sof_card_late_probe; 301 if (!card->topology_shortname_created) { 302 snprintf(card->topology_shortname, 32, "sof-%s", card->name); 303 card->topology_shortname_created = true; 304 } 305 card->name = card->topology_shortname; 306 } 307 308 /* 309 * Regardless of whether the ADSP is wanted and/or present in a machine 310 * specific device tree or not and regardless of whether any AFE_SOF 311 * link is present, we have to make sure that the platforms->of_node 312 * is not NULL, and set to either ADSP (adsp_node) or AFE (platform_node). 313 */ 314 for_each_card_prelinks(card, i, dai_link) { 315 if (adsp_node && !strncmp(dai_link->name, "AFE_SOF", strlen("AFE_SOF"))) 316 dai_link->platforms->of_node = adsp_node; 317 else if (!dai_link->platforms->name && !dai_link->platforms->of_node) 318 dai_link->platforms->of_node = platform_node; 319 } 320 321 if (!needs_legacy_probe) { 322 ret = parse_dai_link_info(card); 323 if (ret) 324 goto err_restore_dais; 325 } else { 326 if (adsp_node) 327 of_node_put(adsp_node); 328 of_node_put(platform_node); 329 } 330 331 if (pdata->soc_probe) { 332 ret = pdata->soc_probe(soc_card_data, needs_legacy_probe); 333 if (ret) { 334 if (!needs_legacy_probe) 335 clean_card_reference(card); 336 goto err_restore_dais; 337 } 338 } 339 snd_soc_card_set_drvdata(card, soc_card_data); 340 341 ret = devm_snd_soc_register_card(&pdev->dev, card); 342 343 if (!needs_legacy_probe) 344 clean_card_reference(card); 345 346 if (ret) { 347 dev_err_probe(&pdev->dev, ret, "Cannot register card\n"); 348 goto err_restore_dais; 349 } 350 351 return 0; 352 353 err_restore_dais: 354 card->dai_link = orig_dai_link; 355 card->num_links = orig_num_links; 356 return ret; 357 } 358 EXPORT_SYMBOL_GPL(mtk_soundcard_common_probe); 359