1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Copyright(c) 2023 Intel Corporation. All rights reserved. 4 5 #include <sound/soc.h> 6 #include "../common/soc-intel-quirks.h" 7 #include "hda_dsp_common.h" 8 #include "sof_board_helpers.h" 9 10 /* 11 * Intel HDMI DAI Link 12 */ 13 static int hdmi_init(struct snd_soc_pcm_runtime *rtd) 14 { 15 struct sof_card_private *ctx = snd_soc_card_get_drvdata(rtd->card); 16 struct snd_soc_dai *dai = snd_soc_rtd_to_codec(rtd, 0); 17 18 ctx->hdmi.hdmi_comp = dai->component; 19 20 return 0; 21 } 22 23 int sof_intel_board_card_late_probe(struct snd_soc_card *card) 24 { 25 struct sof_card_private *ctx = snd_soc_card_get_drvdata(card); 26 27 if (!ctx->hdmi_num) 28 return 0; 29 30 if (!ctx->hdmi.idisp_codec) 31 return 0; 32 33 if (!ctx->hdmi.hdmi_comp) 34 return -EINVAL; 35 36 return hda_dsp_hdmi_build_controls(card, ctx->hdmi.hdmi_comp); 37 } 38 EXPORT_SYMBOL_NS(sof_intel_board_card_late_probe, SND_SOC_INTEL_SOF_BOARD_HELPERS); 39 40 /* 41 * DMIC DAI Link 42 */ 43 static const struct snd_soc_dapm_widget dmic_widgets[] = { 44 SND_SOC_DAPM_MIC("SoC DMIC", NULL), 45 }; 46 47 static const struct snd_soc_dapm_route dmic_routes[] = { 48 {"DMic", NULL, "SoC DMIC"}, 49 }; 50 51 static int dmic_init(struct snd_soc_pcm_runtime *rtd) 52 { 53 struct snd_soc_card *card = rtd->card; 54 int ret; 55 56 ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets, 57 ARRAY_SIZE(dmic_widgets)); 58 if (ret) { 59 dev_err(rtd->dev, "fail to add dmic widgets, ret %d\n", ret); 60 return ret; 61 } 62 63 ret = snd_soc_dapm_add_routes(&card->dapm, dmic_routes, 64 ARRAY_SIZE(dmic_routes)); 65 if (ret) { 66 dev_err(rtd->dev, "fail to add dmic routes, ret %d\n", ret); 67 return ret; 68 } 69 70 return 0; 71 } 72 73 /* 74 * DAI Link Helpers 75 */ 76 static struct snd_soc_dai_link_component dmic_component[] = { 77 { 78 .name = "dmic-codec", 79 .dai_name = "dmic-hifi", 80 } 81 }; 82 83 static struct snd_soc_dai_link_component platform_component[] = { 84 { 85 /* name might be overridden during probe */ 86 .name = "0000:00:1f.3" 87 } 88 }; 89 90 int sof_intel_board_set_codec_link(struct device *dev, 91 struct snd_soc_dai_link *link, int be_id, 92 enum sof_ssp_codec codec_type, int ssp_codec) 93 { 94 struct snd_soc_dai_link_component *cpus; 95 96 dev_dbg(dev, "link %d: codec %s, ssp %d\n", be_id, 97 sof_ssp_get_codec_name(codec_type), ssp_codec); 98 99 /* link name */ 100 link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_codec); 101 if (!link->name) 102 return -ENOMEM; 103 104 /* cpus */ 105 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 106 GFP_KERNEL); 107 if (!cpus) 108 return -ENOMEM; 109 110 if (soc_intel_is_byt() || soc_intel_is_cht()) { 111 /* backward-compatibility for BYT/CHT boards */ 112 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "ssp%d-port", 113 ssp_codec); 114 } else { 115 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", 116 ssp_codec); 117 } 118 if (!cpus->dai_name) 119 return -ENOMEM; 120 121 link->cpus = cpus; 122 link->num_cpus = 1; 123 124 /* codecs - caller to handle */ 125 126 /* platforms */ 127 link->platforms = platform_component; 128 link->num_platforms = ARRAY_SIZE(platform_component); 129 130 link->id = be_id; 131 link->no_pcm = 1; 132 link->dpcm_capture = 1; 133 link->dpcm_playback = 1; 134 135 return 0; 136 } 137 EXPORT_SYMBOL_NS(sof_intel_board_set_codec_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 138 139 int sof_intel_board_set_dmic_link(struct device *dev, 140 struct snd_soc_dai_link *link, int be_id, 141 enum sof_dmic_be_type be_type) 142 { 143 struct snd_soc_dai_link_component *cpus; 144 145 /* cpus */ 146 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 147 GFP_KERNEL); 148 if (!cpus) 149 return -ENOMEM; 150 151 switch (be_type) { 152 case SOF_DMIC_01: 153 dev_dbg(dev, "link %d: dmic01\n", be_id); 154 155 link->name = "dmic01"; 156 cpus->dai_name = "DMIC01 Pin"; 157 break; 158 case SOF_DMIC_16K: 159 dev_dbg(dev, "link %d: dmic16k\n", be_id); 160 161 link->name = "dmic16k"; 162 cpus->dai_name = "DMIC16k Pin"; 163 break; 164 default: 165 dev_err(dev, "invalid be type %d\n", be_type); 166 return -EINVAL; 167 } 168 169 link->cpus = cpus; 170 link->num_cpus = 1; 171 172 /* codecs */ 173 link->codecs = dmic_component; 174 link->num_codecs = ARRAY_SIZE(dmic_component); 175 176 /* platforms */ 177 link->platforms = platform_component; 178 link->num_platforms = ARRAY_SIZE(platform_component); 179 180 link->id = be_id; 181 if (be_type == SOF_DMIC_01) 182 link->init = dmic_init; 183 link->ignore_suspend = 1; 184 link->no_pcm = 1; 185 link->dpcm_capture = 1; 186 187 return 0; 188 } 189 EXPORT_SYMBOL_NS(sof_intel_board_set_dmic_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 190 191 int sof_intel_board_set_intel_hdmi_link(struct device *dev, 192 struct snd_soc_dai_link *link, int be_id, 193 int hdmi_id, bool idisp_codec) 194 { 195 struct snd_soc_dai_link_component *cpus, *codecs; 196 197 dev_dbg(dev, "link %d: intel hdmi, hdmi id %d, idisp codec %d\n", 198 be_id, hdmi_id, idisp_codec); 199 200 /* link name */ 201 link->name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d", hdmi_id); 202 if (!link->name) 203 return -ENOMEM; 204 205 /* cpus */ 206 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 207 GFP_KERNEL); 208 if (!cpus) 209 return -ENOMEM; 210 211 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "iDisp%d Pin", hdmi_id); 212 if (!cpus->dai_name) 213 return -ENOMEM; 214 215 link->cpus = cpus; 216 link->num_cpus = 1; 217 218 /* codecs */ 219 if (idisp_codec) { 220 codecs = devm_kzalloc(dev, 221 sizeof(struct snd_soc_dai_link_component), 222 GFP_KERNEL); 223 if (!codecs) 224 return -ENOMEM; 225 226 codecs->name = "ehdaudio0D2"; 227 codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, 228 "intel-hdmi-hifi%d", hdmi_id); 229 if (!codecs->dai_name) 230 return -ENOMEM; 231 232 link->codecs = codecs; 233 } else { 234 link->codecs = &snd_soc_dummy_dlc; 235 } 236 link->num_codecs = 1; 237 238 /* platforms */ 239 link->platforms = platform_component; 240 link->num_platforms = ARRAY_SIZE(platform_component); 241 242 link->id = be_id; 243 link->init = (hdmi_id == 1) ? hdmi_init : NULL; 244 link->no_pcm = 1; 245 link->dpcm_playback = 1; 246 247 return 0; 248 } 249 EXPORT_SYMBOL_NS(sof_intel_board_set_intel_hdmi_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 250 251 int sof_intel_board_set_ssp_amp_link(struct device *dev, 252 struct snd_soc_dai_link *link, int be_id, 253 enum sof_ssp_codec amp_type, int ssp_amp) 254 { 255 struct snd_soc_dai_link_component *cpus; 256 257 dev_dbg(dev, "link %d: ssp amp %s, ssp %d\n", be_id, 258 sof_ssp_get_codec_name(amp_type), ssp_amp); 259 260 /* link name */ 261 link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-Codec", ssp_amp); 262 if (!link->name) 263 return -ENOMEM; 264 265 /* cpus */ 266 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 267 GFP_KERNEL); 268 if (!cpus) 269 return -ENOMEM; 270 271 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_amp); 272 if (!cpus->dai_name) 273 return -ENOMEM; 274 275 link->cpus = cpus; 276 link->num_cpus = 1; 277 278 /* codecs - caller to handle */ 279 280 /* platforms */ 281 link->platforms = platform_component; 282 link->num_platforms = ARRAY_SIZE(platform_component); 283 284 link->id = be_id; 285 link->no_pcm = 1; 286 link->dpcm_capture = 1; /* feedback stream or firmware-generated echo reference */ 287 link->dpcm_playback = 1; 288 289 return 0; 290 } 291 EXPORT_SYMBOL_NS(sof_intel_board_set_ssp_amp_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 292 293 int sof_intel_board_set_bt_link(struct device *dev, 294 struct snd_soc_dai_link *link, int be_id, 295 int ssp_bt) 296 { 297 struct snd_soc_dai_link_component *cpus; 298 299 dev_dbg(dev, "link %d: bt offload, ssp %d\n", be_id, ssp_bt); 300 301 /* link name */ 302 link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT", ssp_bt); 303 if (!link->name) 304 return -ENOMEM; 305 306 /* cpus */ 307 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 308 GFP_KERNEL); 309 if (!cpus) 310 return -ENOMEM; 311 312 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_bt); 313 if (!cpus->dai_name) 314 return -ENOMEM; 315 316 link->cpus = cpus; 317 link->num_cpus = 1; 318 319 /* codecs */ 320 link->codecs = &snd_soc_dummy_dlc; 321 link->num_codecs = 1; 322 323 /* platforms */ 324 link->platforms = platform_component; 325 link->num_platforms = ARRAY_SIZE(platform_component); 326 327 link->id = be_id; 328 link->no_pcm = 1; 329 link->dpcm_capture = 1; 330 link->dpcm_playback = 1; 331 332 return 0; 333 } 334 EXPORT_SYMBOL_NS(sof_intel_board_set_bt_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 335 336 int sof_intel_board_set_hdmi_in_link(struct device *dev, 337 struct snd_soc_dai_link *link, int be_id, 338 int ssp_hdmi) 339 { 340 struct snd_soc_dai_link_component *cpus; 341 342 dev_dbg(dev, "link %d: hdmi-in, ssp %d\n", be_id, ssp_hdmi); 343 344 /* link name */ 345 link->name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-HDMI", ssp_hdmi); 346 if (!link->name) 347 return -ENOMEM; 348 349 /* cpus */ 350 cpus = devm_kzalloc(dev, sizeof(struct snd_soc_dai_link_component), 351 GFP_KERNEL); 352 if (!cpus) 353 return -ENOMEM; 354 355 cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d Pin", ssp_hdmi); 356 if (!cpus->dai_name) 357 return -ENOMEM; 358 359 link->cpus = cpus; 360 link->num_cpus = 1; 361 362 /* codecs */ 363 link->codecs = &snd_soc_dummy_dlc; 364 link->num_codecs = 1; 365 366 /* platforms */ 367 link->platforms = platform_component; 368 link->num_platforms = ARRAY_SIZE(platform_component); 369 370 link->id = be_id; 371 link->no_pcm = 1; 372 link->dpcm_capture = 1; 373 374 return 0; 375 } 376 EXPORT_SYMBOL_NS(sof_intel_board_set_hdmi_in_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 377 378 static int calculate_num_links(struct sof_card_private *ctx) 379 { 380 int num_links = 0; 381 382 /* headphone codec */ 383 if (ctx->codec_type != CODEC_NONE) 384 num_links++; 385 386 /* dmic01 and dmic16k */ 387 if (ctx->dmic_be_num > 0) 388 num_links++; 389 390 if (ctx->dmic_be_num > 1) 391 num_links++; 392 393 /* idisp HDMI */ 394 num_links += ctx->hdmi_num; 395 396 /* speaker amp */ 397 if (ctx->amp_type != CODEC_NONE) 398 num_links++; 399 400 /* BT audio offload */ 401 if (ctx->bt_offload_present) 402 num_links++; 403 404 /* HDMI-In */ 405 num_links += hweight32(ctx->ssp_mask_hdmi_in); 406 407 return num_links; 408 } 409 410 int sof_intel_board_set_dai_link(struct device *dev, struct snd_soc_card *card, 411 struct sof_card_private *ctx) 412 { 413 struct snd_soc_dai_link *links; 414 int num_links; 415 int i; 416 int idx = 0; 417 int ret; 418 int ssp_hdmi_in = 0; 419 420 num_links = calculate_num_links(ctx); 421 422 links = devm_kcalloc(dev, num_links, sizeof(struct snd_soc_dai_link), 423 GFP_KERNEL); 424 if (!links) 425 return -ENOMEM; 426 427 /* headphone codec */ 428 if (ctx->codec_type != CODEC_NONE) { 429 ret = sof_intel_board_set_codec_link(dev, &links[idx], idx, 430 ctx->codec_type, 431 ctx->ssp_codec); 432 if (ret) { 433 dev_err(dev, "fail to set codec link, ret %d\n", ret); 434 return ret; 435 } 436 437 ctx->codec_link = &links[idx]; 438 idx++; 439 } 440 441 /* dmic01 and dmic16k */ 442 if (ctx->dmic_be_num > 0) { 443 /* at least we have dmic01 */ 444 ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, 445 SOF_DMIC_01); 446 if (ret) { 447 dev_err(dev, "fail to set dmic01 link, ret %d\n", ret); 448 return ret; 449 } 450 451 idx++; 452 } 453 454 if (ctx->dmic_be_num > 1) { 455 /* set up 2 BE links at most */ 456 ret = sof_intel_board_set_dmic_link(dev, &links[idx], idx, 457 SOF_DMIC_16K); 458 if (ret) { 459 dev_err(dev, "fail to set dmic16k link, ret %d\n", ret); 460 return ret; 461 } 462 463 idx++; 464 } 465 466 /* idisp HDMI */ 467 for (i = 1; i <= ctx->hdmi_num; i++) { 468 ret = sof_intel_board_set_intel_hdmi_link(dev, &links[idx], idx, 469 i, 470 ctx->hdmi.idisp_codec); 471 if (ret) { 472 dev_err(dev, "fail to set hdmi link, ret %d\n", ret); 473 return ret; 474 } 475 476 idx++; 477 } 478 479 /* speaker amp */ 480 if (ctx->amp_type != CODEC_NONE) { 481 ret = sof_intel_board_set_ssp_amp_link(dev, &links[idx], idx, 482 ctx->amp_type, 483 ctx->ssp_amp); 484 if (ret) { 485 dev_err(dev, "fail to set amp link, ret %d\n", ret); 486 return ret; 487 } 488 489 ctx->amp_link = &links[idx]; 490 idx++; 491 } 492 493 /* BT audio offload */ 494 if (ctx->bt_offload_present) { 495 ret = sof_intel_board_set_bt_link(dev, &links[idx], idx, 496 ctx->ssp_bt); 497 if (ret) { 498 dev_err(dev, "fail to set bt link, ret %d\n", ret); 499 return ret; 500 } 501 502 idx++; 503 } 504 505 /* HDMI-In */ 506 for_each_set_bit(ssp_hdmi_in, &ctx->ssp_mask_hdmi_in, 32) { 507 ret = sof_intel_board_set_hdmi_in_link(dev, &links[idx], idx, 508 ssp_hdmi_in); 509 if (ret) { 510 dev_err(dev, "fail to set hdmi-in link, ret %d\n", ret); 511 return ret; 512 } 513 514 idx++; 515 } 516 517 if (idx != num_links) { 518 dev_err(dev, "link number mismatch, idx %d, num_links %d\n", idx, 519 num_links); 520 return -EINVAL; 521 } 522 523 card->dai_link = links; 524 card->num_links = num_links; 525 526 return 0; 527 } 528 EXPORT_SYMBOL_NS(sof_intel_board_set_dai_link, SND_SOC_INTEL_SOF_BOARD_HELPERS); 529 530 MODULE_DESCRIPTION("ASoC Intel SOF Machine Driver Board Helpers"); 531 MODULE_AUTHOR("Brent Lu <brent.lu@intel.com>"); 532 MODULE_LICENSE("GPL"); 533 MODULE_IMPORT_NS(SND_SOC_INTEL_HDA_DSP_COMMON); 534 MODULE_IMPORT_NS(SND_SOC_INTEL_SOF_SSP_COMMON); 535