1 // SPDX-License-Identifier: GPL-2.0-only 2 // This file incorporates work covered by the following copyright notice: 3 // Copyright (c) 2020 Intel Corporation 4 // Copyright(c) 2024 Advanced Micro Devices, Inc. 5 /* 6 * soc-sdw-utils.c - common SoundWire machine driver helper functions 7 */ 8 9 #include <linux/device.h> 10 #include <linux/module.h> 11 #include <linux/soundwire/sdw.h> 12 #include <linux/soundwire/sdw_type.h> 13 #include <sound/sdca_function.h> 14 #include <sound/soc_sdw_utils.h> 15 16 static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { 17 SND_SOC_DAPM_MIC("DMIC", NULL), 18 }; 19 20 static const struct snd_soc_dapm_widget generic_jack_widgets[] = { 21 SND_SOC_DAPM_HP("Headphone", NULL), 22 SND_SOC_DAPM_MIC("Headset Mic", NULL), 23 }; 24 25 static const struct snd_kcontrol_new generic_jack_controls[] = { 26 SOC_DAPM_PIN_SWITCH("Headphone"), 27 SOC_DAPM_PIN_SWITCH("Headset Mic"), 28 }; 29 30 static const struct snd_soc_dapm_widget generic_spk_widgets[] = { 31 SND_SOC_DAPM_SPK("Speaker", NULL), 32 }; 33 34 static const struct snd_kcontrol_new generic_spk_controls[] = { 35 SOC_DAPM_PIN_SWITCH("Speaker"), 36 }; 37 38 static const struct snd_soc_dapm_widget lr_spk_widgets[] = { 39 SND_SOC_DAPM_SPK("Left Spk", NULL), 40 SND_SOC_DAPM_SPK("Right Spk", NULL), 41 }; 42 43 static const struct snd_kcontrol_new lr_spk_controls[] = { 44 SOC_DAPM_PIN_SWITCH("Left Spk"), 45 SOC_DAPM_PIN_SWITCH("Right Spk"), 46 }; 47 48 static const struct snd_soc_dapm_widget rt700_widgets[] = { 49 SND_SOC_DAPM_HP("Headphones", NULL), 50 SND_SOC_DAPM_MIC("AMIC", NULL), 51 SND_SOC_DAPM_SPK("Speaker", NULL), 52 }; 53 54 static const struct snd_kcontrol_new rt700_controls[] = { 55 SOC_DAPM_PIN_SWITCH("Headphones"), 56 SOC_DAPM_PIN_SWITCH("AMIC"), 57 SOC_DAPM_PIN_SWITCH("Speaker"), 58 }; 59 60 struct asoc_sdw_codec_info codec_info_list[] = { 61 { 62 .part_id = 0x0000, /* TAS2783A */ 63 .dais = { 64 { 65 .direction = {true, true}, 66 .dai_name = "tas2783-codec", 67 .dai_type = SOC_SDW_DAI_TYPE_AMP, 68 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 69 .init = asoc_sdw_ti_amp_init, 70 .rtd_init = asoc_sdw_ti_spk_rtd_init, 71 .controls = lr_spk_controls, 72 .num_controls = ARRAY_SIZE(lr_spk_controls), 73 .widgets = lr_spk_widgets, 74 .num_widgets = ARRAY_SIZE(lr_spk_widgets), 75 }, 76 }, 77 .dai_num = 1, 78 }, 79 { 80 .part_id = 0x700, 81 .dais = { 82 { 83 .direction = {true, true}, 84 .dai_name = "rt700-aif1", 85 .dai_type = SOC_SDW_DAI_TYPE_JACK, 86 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 87 .rtd_init = asoc_sdw_rt700_rtd_init, 88 .controls = rt700_controls, 89 .num_controls = ARRAY_SIZE(rt700_controls), 90 .widgets = rt700_widgets, 91 .num_widgets = ARRAY_SIZE(rt700_widgets), 92 }, 93 }, 94 .dai_num = 1, 95 }, 96 { 97 .part_id = 0x711, 98 .version_id = 3, 99 .dais = { 100 { 101 .direction = {true, true}, 102 .dai_name = "rt711-sdca-aif1", 103 .dai_type = SOC_SDW_DAI_TYPE_JACK, 104 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 105 .init = asoc_sdw_rt_sdca_jack_init, 106 .exit = asoc_sdw_rt_sdca_jack_exit, 107 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 108 .controls = generic_jack_controls, 109 .num_controls = ARRAY_SIZE(generic_jack_controls), 110 .widgets = generic_jack_widgets, 111 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 112 }, 113 }, 114 .dai_num = 1, 115 }, 116 { 117 .part_id = 0x711, 118 .version_id = 2, 119 .dais = { 120 { 121 .direction = {true, true}, 122 .dai_name = "rt711-aif1", 123 .dai_type = SOC_SDW_DAI_TYPE_JACK, 124 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 125 .init = asoc_sdw_rt711_init, 126 .exit = asoc_sdw_rt711_exit, 127 .rtd_init = asoc_sdw_rt711_rtd_init, 128 .controls = generic_jack_controls, 129 .num_controls = ARRAY_SIZE(generic_jack_controls), 130 .widgets = generic_jack_widgets, 131 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 132 }, 133 }, 134 .dai_num = 1, 135 }, 136 { 137 .part_id = 0x712, 138 .version_id = 3, 139 .dais = { 140 { 141 .direction = {true, true}, 142 .dai_name = "rt712-sdca-aif1", 143 .dai_type = SOC_SDW_DAI_TYPE_JACK, 144 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 145 .init = asoc_sdw_rt_sdca_jack_init, 146 .exit = asoc_sdw_rt_sdca_jack_exit, 147 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 148 .controls = generic_jack_controls, 149 .num_controls = ARRAY_SIZE(generic_jack_controls), 150 .widgets = generic_jack_widgets, 151 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 152 }, 153 { 154 .direction = {true, false}, 155 .dai_name = "rt712-sdca-aif2", 156 .component_name = "rt712", 157 .dai_type = SOC_SDW_DAI_TYPE_AMP, 158 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 159 .init = asoc_sdw_rt_amp_init, 160 .exit = asoc_sdw_rt_amp_exit, 161 .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init, 162 .controls = generic_spk_controls, 163 .num_controls = ARRAY_SIZE(generic_spk_controls), 164 .widgets = generic_spk_widgets, 165 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 166 }, 167 { 168 .direction = {false, true}, 169 .dai_name = "rt712-sdca-aif3", 170 .dai_type = SOC_SDW_DAI_TYPE_MIC, 171 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 172 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 173 }, 174 }, 175 .dai_num = 3, 176 }, 177 { 178 .part_id = 0x1712, 179 .version_id = 3, 180 .dais = { 181 { 182 .direction = {false, true}, 183 .dai_name = "rt712-sdca-dmic-aif1", 184 .dai_type = SOC_SDW_DAI_TYPE_MIC, 185 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 186 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 187 }, 188 }, 189 .dai_num = 1, 190 }, 191 { 192 .part_id = 0x713, 193 .version_id = 3, 194 .dais = { 195 { 196 .direction = {true, true}, 197 .dai_name = "rt712-sdca-aif1", 198 .dai_type = SOC_SDW_DAI_TYPE_JACK, 199 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 200 .init = asoc_sdw_rt_sdca_jack_init, 201 .exit = asoc_sdw_rt_sdca_jack_exit, 202 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 203 .controls = generic_jack_controls, 204 .num_controls = ARRAY_SIZE(generic_jack_controls), 205 .widgets = generic_jack_widgets, 206 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 207 }, 208 { 209 .direction = {false, true}, 210 .dai_name = "rt712-sdca-aif3", 211 .dai_type = SOC_SDW_DAI_TYPE_MIC, 212 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 213 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 214 }, 215 }, 216 .dai_num = 2, 217 }, 218 { 219 .part_id = 0x1713, 220 .version_id = 3, 221 .dais = { 222 { 223 .direction = {false, true}, 224 .dai_name = "rt712-sdca-dmic-aif1", 225 .dai_type = SOC_SDW_DAI_TYPE_MIC, 226 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 227 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 228 }, 229 }, 230 .dai_num = 1, 231 }, 232 { 233 .part_id = 0x1308, 234 .acpi_id = "10EC1308", 235 .dais = { 236 { 237 .direction = {true, false}, 238 .dai_name = "rt1308-aif", 239 .component_name = "rt1308", 240 .dai_type = SOC_SDW_DAI_TYPE_AMP, 241 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 242 .init = asoc_sdw_rt_amp_init, 243 .exit = asoc_sdw_rt_amp_exit, 244 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 245 .controls = generic_spk_controls, 246 .num_controls = ARRAY_SIZE(generic_spk_controls), 247 .widgets = generic_spk_widgets, 248 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 249 }, 250 }, 251 .dai_num = 1, 252 .ops = &soc_sdw_rt1308_i2s_ops, 253 }, 254 { 255 .part_id = 0x1316, 256 .dais = { 257 { 258 .direction = {true, true}, 259 .dai_name = "rt1316-aif", 260 .component_name = "rt1316", 261 .dai_type = SOC_SDW_DAI_TYPE_AMP, 262 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 263 .init = asoc_sdw_rt_amp_init, 264 .exit = asoc_sdw_rt_amp_exit, 265 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 266 .controls = generic_spk_controls, 267 .num_controls = ARRAY_SIZE(generic_spk_controls), 268 .widgets = generic_spk_widgets, 269 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 270 }, 271 }, 272 .dai_num = 1, 273 }, 274 { 275 .part_id = 0x1318, 276 .dais = { 277 { 278 .direction = {true, true}, 279 .dai_name = "rt1318-aif", 280 .component_name = "rt1318", 281 .dai_type = SOC_SDW_DAI_TYPE_AMP, 282 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 283 .init = asoc_sdw_rt_amp_init, 284 .exit = asoc_sdw_rt_amp_exit, 285 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 286 .controls = generic_spk_controls, 287 .num_controls = ARRAY_SIZE(generic_spk_controls), 288 .widgets = generic_spk_widgets, 289 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 290 }, 291 }, 292 .dai_num = 1, 293 }, 294 { 295 .part_id = 0x1320, 296 .dais = { 297 { 298 .direction = {true, false}, 299 .dai_name = "rt1320-aif1", 300 .component_name = "rt1320", 301 .dai_type = SOC_SDW_DAI_TYPE_AMP, 302 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 303 .init = asoc_sdw_rt_amp_init, 304 .exit = asoc_sdw_rt_amp_exit, 305 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 306 .controls = generic_spk_controls, 307 .num_controls = ARRAY_SIZE(generic_spk_controls), 308 .widgets = generic_spk_widgets, 309 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 310 }, 311 }, 312 .dai_num = 1, 313 }, 314 { 315 .part_id = 0x1321, 316 .dais = { 317 { 318 .direction = {true, false}, 319 .dai_name = "rt1320-aif1", 320 .component_name = "rt1320", 321 .dai_type = SOC_SDW_DAI_TYPE_AMP, 322 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 323 .init = asoc_sdw_rt_amp_init, 324 .exit = asoc_sdw_rt_amp_exit, 325 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 326 .controls = generic_spk_controls, 327 .num_controls = ARRAY_SIZE(generic_spk_controls), 328 .widgets = generic_spk_widgets, 329 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 330 }, 331 }, 332 .dai_num = 1, 333 }, 334 { 335 .part_id = 0x714, 336 .version_id = 3, 337 .ignore_internal_dmic = true, 338 .dais = { 339 { 340 .direction = {false, true}, 341 .dai_name = "rt715-sdca-aif2", 342 .dai_type = SOC_SDW_DAI_TYPE_MIC, 343 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 344 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 345 }, 346 }, 347 .dai_num = 1, 348 }, 349 { 350 .part_id = 0x715, 351 .version_id = 3, 352 .ignore_internal_dmic = true, 353 .dais = { 354 { 355 .direction = {false, true}, 356 .dai_name = "rt715-sdca-aif2", 357 .dai_type = SOC_SDW_DAI_TYPE_MIC, 358 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 359 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 360 }, 361 }, 362 .dai_num = 1, 363 }, 364 { 365 .part_id = 0x714, 366 .version_id = 2, 367 .ignore_internal_dmic = true, 368 .dais = { 369 { 370 .direction = {false, true}, 371 .dai_name = "rt715-aif2", 372 .dai_type = SOC_SDW_DAI_TYPE_MIC, 373 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 374 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 375 }, 376 }, 377 .dai_num = 1, 378 }, 379 { 380 .part_id = 0x715, 381 .version_id = 2, 382 .ignore_internal_dmic = true, 383 .dais = { 384 { 385 .direction = {false, true}, 386 .dai_name = "rt715-aif2", 387 .dai_type = SOC_SDW_DAI_TYPE_MIC, 388 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 389 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 390 }, 391 }, 392 .dai_num = 1, 393 }, 394 { 395 .part_id = 0x721, 396 .version_id = 3, 397 .dais = { 398 { 399 .direction = {true, true}, 400 .dai_name = "rt721-sdca-aif1", 401 .dai_type = SOC_SDW_DAI_TYPE_JACK, 402 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 403 .init = asoc_sdw_rt_sdca_jack_init, 404 .exit = asoc_sdw_rt_sdca_jack_exit, 405 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 406 .controls = generic_jack_controls, 407 .num_controls = ARRAY_SIZE(generic_jack_controls), 408 .widgets = generic_jack_widgets, 409 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 410 }, 411 { 412 .direction = {true, false}, 413 .dai_name = "rt721-sdca-aif2", 414 .component_name = "rt721", 415 .dai_type = SOC_SDW_DAI_TYPE_AMP, 416 /* No feedback capability is provided by rt721-sdca codec driver*/ 417 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 418 .init = asoc_sdw_rt_amp_init, 419 .exit = asoc_sdw_rt_amp_exit, 420 .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init, 421 .controls = generic_spk_controls, 422 .num_controls = ARRAY_SIZE(generic_spk_controls), 423 .widgets = generic_spk_widgets, 424 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 425 }, 426 { 427 .direction = {false, true}, 428 .dai_name = "rt721-sdca-aif3", 429 .dai_type = SOC_SDW_DAI_TYPE_MIC, 430 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 431 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 432 }, 433 }, 434 .dai_num = 3, 435 }, 436 { 437 .part_id = 0x722, 438 .version_id = 3, 439 .dais = { 440 { 441 .direction = {true, true}, 442 .dai_name = "rt722-sdca-aif1", 443 .dai_type = SOC_SDW_DAI_TYPE_JACK, 444 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 445 .init = asoc_sdw_rt_sdca_jack_init, 446 .exit = asoc_sdw_rt_sdca_jack_exit, 447 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 448 .controls = generic_jack_controls, 449 .num_controls = ARRAY_SIZE(generic_jack_controls), 450 .widgets = generic_jack_widgets, 451 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 452 }, 453 { 454 .direction = {true, false}, 455 .dai_name = "rt722-sdca-aif2", 456 .component_name = "rt722", 457 .dai_type = SOC_SDW_DAI_TYPE_AMP, 458 /* No feedback capability is provided by rt722-sdca codec driver*/ 459 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 460 .init = asoc_sdw_rt_amp_init, 461 .exit = asoc_sdw_rt_amp_exit, 462 .rtd_init = asoc_sdw_rt_mf_sdca_spk_rtd_init, 463 .controls = generic_spk_controls, 464 .num_controls = ARRAY_SIZE(generic_spk_controls), 465 .widgets = generic_spk_widgets, 466 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 467 .quirk = SOC_SDW_CODEC_SPKR, 468 .quirk_exclude = true, 469 }, 470 { 471 .direction = {false, true}, 472 .dai_name = "rt722-sdca-aif3", 473 .dai_type = SOC_SDW_DAI_TYPE_MIC, 474 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 475 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 476 }, 477 }, 478 .dai_num = 3, 479 }, 480 { 481 .part_id = 0x8373, 482 .dais = { 483 { 484 .direction = {true, true}, 485 .dai_name = "max98373-aif1", 486 .component_name = "mx8373", 487 .dai_type = SOC_SDW_DAI_TYPE_AMP, 488 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 489 .init = asoc_sdw_maxim_init, 490 .rtd_init = asoc_sdw_maxim_spk_rtd_init, 491 .controls = lr_spk_controls, 492 .num_controls = ARRAY_SIZE(lr_spk_controls), 493 .widgets = lr_spk_widgets, 494 .num_widgets = ARRAY_SIZE(lr_spk_widgets), 495 }, 496 }, 497 .dai_num = 1, 498 }, 499 { 500 .part_id = 0x8363, 501 .dais = { 502 { 503 .direction = {true, false}, 504 .dai_name = "max98363-aif1", 505 .component_name = "mx8363", 506 .dai_type = SOC_SDW_DAI_TYPE_AMP, 507 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 508 .init = asoc_sdw_maxim_init, 509 .rtd_init = asoc_sdw_maxim_spk_rtd_init, 510 .controls = lr_spk_controls, 511 .num_controls = ARRAY_SIZE(lr_spk_controls), 512 .widgets = lr_spk_widgets, 513 .num_widgets = ARRAY_SIZE(lr_spk_widgets), 514 }, 515 }, 516 .dai_num = 1, 517 }, 518 { 519 .part_id = 0x5682, 520 .dais = { 521 { 522 .direction = {true, true}, 523 .dai_name = "rt5682-sdw", 524 .dai_type = SOC_SDW_DAI_TYPE_JACK, 525 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 526 .rtd_init = asoc_sdw_rt5682_rtd_init, 527 .controls = generic_jack_controls, 528 .num_controls = ARRAY_SIZE(generic_jack_controls), 529 .widgets = generic_jack_widgets, 530 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 531 }, 532 }, 533 .dai_num = 1, 534 }, 535 { 536 .part_id = 0x3556, 537 .dais = { 538 { 539 .direction = {true, false}, 540 .dai_name = "cs35l56-sdw1", 541 .component_name = "cs35l56", 542 .dai_type = SOC_SDW_DAI_TYPE_AMP, 543 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 544 .init = asoc_sdw_cs_amp_init, 545 .rtd_init = asoc_sdw_cs_spk_rtd_init, 546 .controls = generic_spk_controls, 547 .num_controls = ARRAY_SIZE(generic_spk_controls), 548 .widgets = generic_spk_widgets, 549 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 550 }, 551 { 552 .direction = {false, true}, 553 .dai_name = "cs35l56-sdw1c", 554 .dai_type = SOC_SDW_DAI_TYPE_AMP, 555 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 556 .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, 557 }, 558 }, 559 .dai_num = 2, 560 }, 561 { 562 .part_id = 0x3563, 563 .dais = { 564 { 565 .direction = {true, false}, 566 .dai_name = "cs35l56-sdw1", 567 .component_name = "cs35l56", 568 .dai_type = SOC_SDW_DAI_TYPE_AMP, 569 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 570 .init = asoc_sdw_cs_amp_init, 571 .rtd_init = asoc_sdw_cs_spk_rtd_init, 572 .controls = generic_spk_controls, 573 .num_controls = ARRAY_SIZE(generic_spk_controls), 574 .widgets = generic_spk_widgets, 575 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 576 }, 577 { 578 .direction = {false, true}, 579 .dai_name = "cs35l56-sdw1c", 580 .dai_type = SOC_SDW_DAI_TYPE_AMP, 581 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 582 .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init, 583 }, 584 }, 585 .dai_num = 2, 586 }, 587 { 588 .part_id = 0x4242, 589 .dais = { 590 { 591 .direction = {true, true}, 592 .dai_name = "cs42l42-sdw", 593 .dai_type = SOC_SDW_DAI_TYPE_JACK, 594 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 595 .rtd_init = asoc_sdw_cs42l42_rtd_init, 596 .controls = generic_jack_controls, 597 .num_controls = ARRAY_SIZE(generic_jack_controls), 598 .widgets = generic_jack_widgets, 599 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 600 }, 601 }, 602 .dai_num = 1, 603 }, 604 { 605 .part_id = 0x4243, 606 .codec_name = "cs42l43-codec", 607 .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, 608 .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar, 609 .dais = { 610 { 611 .direction = {true, false}, 612 .dai_name = "cs42l43-dp5", 613 .dai_type = SOC_SDW_DAI_TYPE_JACK, 614 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 615 .rtd_init = asoc_sdw_cs42l43_hs_rtd_init, 616 .controls = generic_jack_controls, 617 .num_controls = ARRAY_SIZE(generic_jack_controls), 618 .widgets = generic_jack_widgets, 619 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 620 }, 621 { 622 .direction = {false, true}, 623 .dai_name = "cs42l43-dp1", 624 .dai_type = SOC_SDW_DAI_TYPE_MIC, 625 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 626 .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init, 627 .widgets = generic_dmic_widgets, 628 .num_widgets = ARRAY_SIZE(generic_dmic_widgets), 629 .quirk = SOC_SDW_CODEC_MIC, 630 .quirk_exclude = true, 631 }, 632 { 633 .direction = {false, true}, 634 .dai_name = "cs42l43-dp2", 635 .dai_type = SOC_SDW_DAI_TYPE_JACK, 636 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 637 }, 638 { 639 .direction = {true, false}, 640 .dai_name = "cs42l43-dp6", 641 .component_name = "cs42l43", 642 .dai_type = SOC_SDW_DAI_TYPE_AMP, 643 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 644 .init = asoc_sdw_cs42l43_spk_init, 645 .rtd_init = asoc_sdw_cs42l43_spk_rtd_init, 646 .controls = generic_spk_controls, 647 .num_controls = ARRAY_SIZE(generic_spk_controls), 648 .widgets = generic_spk_widgets, 649 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 650 .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS, 651 }, 652 }, 653 .dai_num = 4, 654 }, 655 { 656 .part_id = 0xaaaa, /* generic codec mockup */ 657 .version_id = 0, 658 .dais = { 659 { 660 .direction = {true, true}, 661 .dai_name = "sdw-mockup-aif1", 662 .dai_type = SOC_SDW_DAI_TYPE_JACK, 663 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 664 }, 665 { 666 .direction = {true, false}, 667 .dai_name = "sdw-mockup-aif1", 668 .dai_type = SOC_SDW_DAI_TYPE_AMP, 669 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 670 }, 671 { 672 .direction = {false, true}, 673 .dai_name = "sdw-mockup-aif1", 674 .dai_type = SOC_SDW_DAI_TYPE_MIC, 675 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 676 }, 677 }, 678 .dai_num = 3, 679 }, 680 { 681 .part_id = 0xaa55, /* headset codec mockup */ 682 .version_id = 0, 683 .dais = { 684 { 685 .direction = {true, true}, 686 .dai_name = "sdw-mockup-aif1", 687 .dai_type = SOC_SDW_DAI_TYPE_JACK, 688 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 689 }, 690 }, 691 .dai_num = 1, 692 }, 693 { 694 .part_id = 0x55aa, /* amplifier mockup */ 695 .version_id = 0, 696 .dais = { 697 { 698 .direction = {true, true}, 699 .dai_name = "sdw-mockup-aif1", 700 .dai_type = SOC_SDW_DAI_TYPE_AMP, 701 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 702 }, 703 }, 704 .dai_num = 1, 705 }, 706 { 707 .part_id = 0x5555, 708 .version_id = 0, 709 .dais = { 710 { 711 .dai_name = "sdw-mockup-aif1", 712 .direction = {false, true}, 713 .dai_type = SOC_SDW_DAI_TYPE_MIC, 714 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 715 }, 716 }, 717 .dai_num = 1, 718 }, 719 }; 720 EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS"); 721 722 int asoc_sdw_get_codec_info_list_count(void) 723 { 724 return ARRAY_SIZE(codec_info_list); 725 }; 726 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, "SND_SOC_SDW_UTILS"); 727 728 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) 729 { 730 unsigned int part_id, sdw_version; 731 int i; 732 733 part_id = SDW_PART_ID(adr); 734 sdw_version = SDW_VERSION(adr); 735 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 736 /* 737 * A codec info is for all sdw version with the part id if 738 * version_id is not specified in the codec info. 739 */ 740 if (part_id == codec_info_list[i].part_id && 741 (!codec_info_list[i].version_id || 742 sdw_version == codec_info_list[i].version_id)) 743 return &codec_info_list[i]; 744 745 return NULL; 746 } 747 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, "SND_SOC_SDW_UTILS"); 748 749 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id) 750 { 751 int i; 752 753 if (!acpi_id[0]) 754 return NULL; 755 756 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 757 if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) 758 return &codec_info_list[i]; 759 760 return NULL; 761 } 762 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, "SND_SOC_SDW_UTILS"); 763 764 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index) 765 { 766 int i, j; 767 768 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 769 for (j = 0; j < codec_info_list[i].dai_num; j++) { 770 if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) { 771 *dai_index = j; 772 return &codec_info_list[i]; 773 } 774 } 775 } 776 777 return NULL; 778 } 779 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, "SND_SOC_SDW_UTILS"); 780 781 int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) 782 { 783 struct snd_soc_card *card = rtd->card; 784 struct asoc_sdw_codec_info *codec_info; 785 struct snd_soc_dai *dai; 786 const char *spk_components=""; 787 int dai_index; 788 int ret; 789 int i; 790 791 for_each_rtd_codec_dais(rtd, i, dai) { 792 codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index); 793 if (!codec_info) 794 return -EINVAL; 795 796 /* 797 * A codec dai can be connected to different dai links for capture and playback, 798 * but we only need to call the rtd_init function once. 799 * The rtd_init for each codec dai is independent. So, the order of rtd_init 800 * doesn't matter. 801 */ 802 if (codec_info->dais[dai_index].rtd_init_done) 803 continue; 804 805 /* 806 * Add card controls and dapm widgets for the first codec dai. 807 * The controls and widgets will be used for all codec dais. 808 */ 809 810 if (i > 0) 811 goto skip_add_controls_widgets; 812 813 if (codec_info->dais[dai_index].controls) { 814 ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls, 815 codec_info->dais[dai_index].num_controls); 816 if (ret) { 817 dev_err(card->dev, "%#x controls addition failed: %d\n", 818 codec_info->part_id, ret); 819 return ret; 820 } 821 } 822 if (codec_info->dais[dai_index].widgets) { 823 ret = snd_soc_dapm_new_controls(&card->dapm, 824 codec_info->dais[dai_index].widgets, 825 codec_info->dais[dai_index].num_widgets); 826 if (ret) { 827 dev_err(card->dev, "%#x widgets addition failed: %d\n", 828 codec_info->part_id, ret); 829 return ret; 830 } 831 } 832 833 skip_add_controls_widgets: 834 if (codec_info->dais[dai_index].rtd_init) { 835 ret = codec_info->dais[dai_index].rtd_init(rtd, dai); 836 if (ret) 837 return ret; 838 } 839 840 /* Generate the spk component string for card->components string */ 841 if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP && 842 codec_info->dais[dai_index].component_name) { 843 if (strlen (spk_components) == 0) 844 spk_components = 845 devm_kasprintf(card->dev, GFP_KERNEL, "%s", 846 codec_info->dais[dai_index].component_name); 847 else 848 /* Append component name to spk_components */ 849 spk_components = 850 devm_kasprintf(card->dev, GFP_KERNEL, 851 "%s+%s", spk_components, 852 codec_info->dais[dai_index].component_name); 853 } 854 855 codec_info->dais[dai_index].rtd_init_done = true; 856 857 } 858 859 if (strlen (spk_components) > 0) { 860 /* Update card components for speaker components */ 861 card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s", 862 card->components, spk_components); 863 if (!card->components) 864 return -ENOMEM; 865 } 866 867 return 0; 868 } 869 EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, "SND_SOC_SDW_UTILS"); 870 871 /* these wrappers are only needed to avoid typecast compilation errors */ 872 int asoc_sdw_startup(struct snd_pcm_substream *substream) 873 { 874 return sdw_startup_stream(substream); 875 } 876 EXPORT_SYMBOL_NS(asoc_sdw_startup, "SND_SOC_SDW_UTILS"); 877 878 int asoc_sdw_prepare(struct snd_pcm_substream *substream) 879 { 880 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 881 struct sdw_stream_runtime *sdw_stream; 882 struct snd_soc_dai *dai; 883 884 /* Find stream from first CPU DAI */ 885 dai = snd_soc_rtd_to_cpu(rtd, 0); 886 887 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 888 if (IS_ERR(sdw_stream)) { 889 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 890 return PTR_ERR(sdw_stream); 891 } 892 893 return sdw_prepare_stream(sdw_stream); 894 } 895 EXPORT_SYMBOL_NS(asoc_sdw_prepare, "SND_SOC_SDW_UTILS"); 896 897 int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd) 898 { 899 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 900 struct sdw_stream_runtime *sdw_stream; 901 struct snd_soc_dai *dai; 902 int ret; 903 904 /* Find stream from first CPU DAI */ 905 dai = snd_soc_rtd_to_cpu(rtd, 0); 906 907 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 908 if (IS_ERR(sdw_stream)) { 909 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 910 return PTR_ERR(sdw_stream); 911 } 912 913 switch (cmd) { 914 case SNDRV_PCM_TRIGGER_START: 915 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 916 case SNDRV_PCM_TRIGGER_RESUME: 917 ret = sdw_enable_stream(sdw_stream); 918 break; 919 920 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 921 case SNDRV_PCM_TRIGGER_SUSPEND: 922 case SNDRV_PCM_TRIGGER_STOP: 923 ret = sdw_disable_stream(sdw_stream); 924 break; 925 default: 926 ret = -EINVAL; 927 break; 928 } 929 930 if (ret) 931 dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); 932 933 return ret; 934 } 935 EXPORT_SYMBOL_NS(asoc_sdw_trigger, "SND_SOC_SDW_UTILS"); 936 937 int asoc_sdw_hw_params(struct snd_pcm_substream *substream, 938 struct snd_pcm_hw_params *params) 939 { 940 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 941 struct snd_soc_dai_link_ch_map *ch_maps; 942 int ch = params_channels(params); 943 unsigned int ch_mask; 944 int num_codecs; 945 int step; 946 int i; 947 948 if (!rtd->dai_link->ch_maps) 949 return 0; 950 951 /* Identical data will be sent to all codecs in playback */ 952 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 953 ch_mask = GENMASK(ch - 1, 0); 954 step = 0; 955 } else { 956 num_codecs = rtd->dai_link->num_codecs; 957 958 if (ch < num_codecs || ch % num_codecs != 0) { 959 dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n", 960 ch, num_codecs); 961 return -EINVAL; 962 } 963 964 ch_mask = GENMASK(ch / num_codecs - 1, 0); 965 step = hweight_long(ch_mask); 966 } 967 968 /* 969 * The captured data will be combined from each cpu DAI if the dai 970 * link has more than one codec DAIs. Set codec channel mask and 971 * ASoC will set the corresponding channel numbers for each cpu dai. 972 */ 973 for_each_link_ch_maps(rtd->dai_link, i, ch_maps) 974 ch_maps->ch_mask = ch_mask << (i * step); 975 976 return 0; 977 } 978 EXPORT_SYMBOL_NS(asoc_sdw_hw_params, "SND_SOC_SDW_UTILS"); 979 980 int asoc_sdw_hw_free(struct snd_pcm_substream *substream) 981 { 982 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 983 struct sdw_stream_runtime *sdw_stream; 984 struct snd_soc_dai *dai; 985 986 /* Find stream from first CPU DAI */ 987 dai = snd_soc_rtd_to_cpu(rtd, 0); 988 989 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 990 if (IS_ERR(sdw_stream)) { 991 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 992 return PTR_ERR(sdw_stream); 993 } 994 995 return sdw_deprepare_stream(sdw_stream); 996 } 997 EXPORT_SYMBOL_NS(asoc_sdw_hw_free, "SND_SOC_SDW_UTILS"); 998 999 void asoc_sdw_shutdown(struct snd_pcm_substream *substream) 1000 { 1001 sdw_shutdown_stream(substream); 1002 } 1003 EXPORT_SYMBOL_NS(asoc_sdw_shutdown, "SND_SOC_SDW_UTILS"); 1004 1005 static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, 1006 unsigned int sdw_version, 1007 unsigned int mfg_id, 1008 unsigned int part_id, 1009 unsigned int class_id, 1010 int index_in_link) 1011 { 1012 int i; 1013 1014 for (i = 0; i < adr_link->num_adr; i++) { 1015 unsigned int sdw1_version, mfg1_id, part1_id, class1_id; 1016 u64 adr; 1017 1018 /* skip itself */ 1019 if (i == index_in_link) 1020 continue; 1021 1022 adr = adr_link->adr_d[i].adr; 1023 1024 sdw1_version = SDW_VERSION(adr); 1025 mfg1_id = SDW_MFG_ID(adr); 1026 part1_id = SDW_PART_ID(adr); 1027 class1_id = SDW_CLASS_ID(adr); 1028 1029 if (sdw_version == sdw1_version && 1030 mfg_id == mfg1_id && 1031 part_id == part1_id && 1032 class_id == class1_id) 1033 return false; 1034 } 1035 1036 return true; 1037 } 1038 1039 static const char *_asoc_sdw_get_codec_name(struct device *dev, 1040 const struct asoc_sdw_codec_info *codec_info, 1041 const struct snd_soc_acpi_link_adr *adr_link, 1042 int adr_index) 1043 { 1044 u64 adr = adr_link->adr_d[adr_index].adr; 1045 unsigned int sdw_version = SDW_VERSION(adr); 1046 unsigned int link_id = SDW_DISCO_LINK_ID(adr); 1047 unsigned int unique_id = SDW_UNIQUE_ID(adr); 1048 unsigned int mfg_id = SDW_MFG_ID(adr); 1049 unsigned int part_id = SDW_PART_ID(adr); 1050 unsigned int class_id = SDW_CLASS_ID(adr); 1051 1052 if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, 1053 class_id, adr_index)) 1054 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", 1055 link_id, mfg_id, part_id, class_id); 1056 1057 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", 1058 link_id, mfg_id, part_id, class_id, unique_id); 1059 } 1060 1061 const char *asoc_sdw_get_codec_name(struct device *dev, 1062 const struct asoc_sdw_codec_info *codec_info, 1063 const struct snd_soc_acpi_link_adr *adr_link, 1064 int adr_index) 1065 { 1066 if (codec_info->codec_name) 1067 return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); 1068 1069 return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index); 1070 } 1071 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS"); 1072 1073 /* helper to get the link that the codec DAI is used */ 1074 struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card, 1075 const char *dai_name) 1076 { 1077 struct snd_soc_dai_link *dai_link; 1078 int i; 1079 int j; 1080 1081 for_each_card_prelinks(card, i, dai_link) { 1082 for (j = 0; j < dai_link->num_codecs; j++) { 1083 /* Check each codec in a link */ 1084 if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) 1085 return dai_link; 1086 } 1087 } 1088 return NULL; 1089 } 1090 EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, "SND_SOC_SDW_UTILS"); 1091 1092 void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card) 1093 { 1094 struct snd_soc_dai_link *dai_link; 1095 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 1096 int ret; 1097 int i, j; 1098 1099 for (i = 0; i < ctx->codec_info_list_count; i++) { 1100 for (j = 0; j < codec_info_list[i].dai_num; j++) { 1101 codec_info_list[i].dais[j].rtd_init_done = false; 1102 /* Check each dai in codec_info_lis to see if it is used in the link */ 1103 if (!codec_info_list[i].dais[j].exit) 1104 continue; 1105 /* 1106 * We don't need to call .exit function if there is no matched 1107 * dai link found. 1108 */ 1109 dai_link = asoc_sdw_mc_find_codec_dai_used(card, 1110 codec_info_list[i].dais[j].dai_name); 1111 if (dai_link) { 1112 /* Do the .exit function if the codec dai is used in the link */ 1113 ret = codec_info_list[i].dais[j].exit(card, dai_link); 1114 if (ret) 1115 dev_warn(card->dev, 1116 "codec exit failed %d\n", 1117 ret); 1118 break; 1119 } 1120 } 1121 } 1122 } 1123 EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, "SND_SOC_SDW_UTILS"); 1124 1125 int asoc_sdw_card_late_probe(struct snd_soc_card *card) 1126 { 1127 int ret = 0; 1128 int i; 1129 1130 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 1131 if (codec_info_list[i].codec_card_late_probe) { 1132 ret = codec_info_list[i].codec_card_late_probe(card); 1133 if (ret < 0) 1134 return ret; 1135 } 1136 } 1137 return ret; 1138 } 1139 EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, "SND_SOC_SDW_UTILS"); 1140 1141 void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 1142 int *be_id, char *name, int playback, int capture, 1143 struct snd_soc_dai_link_component *cpus, int cpus_num, 1144 struct snd_soc_dai_link_component *platform_component, 1145 int num_platforms, struct snd_soc_dai_link_component *codecs, 1146 int codecs_num, int no_pcm, 1147 int (*init)(struct snd_soc_pcm_runtime *rtd), 1148 const struct snd_soc_ops *ops) 1149 { 1150 dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id); 1151 dai_links->id = (*be_id)++; 1152 dai_links->name = name; 1153 dai_links->stream_name = name; 1154 dai_links->platforms = platform_component; 1155 dai_links->num_platforms = num_platforms; 1156 dai_links->no_pcm = no_pcm; 1157 dai_links->cpus = cpus; 1158 dai_links->num_cpus = cpus_num; 1159 dai_links->codecs = codecs; 1160 dai_links->num_codecs = codecs_num; 1161 dai_links->playback_only = playback && !capture; 1162 dai_links->capture_only = !playback && capture; 1163 dai_links->init = init; 1164 dai_links->ops = ops; 1165 } 1166 EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS"); 1167 1168 int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 1169 int *be_id, char *name, int playback, int capture, 1170 const char *cpu_dai_name, const char *platform_comp_name, 1171 const char *codec_name, const char *codec_dai_name, 1172 int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd), 1173 const struct snd_soc_ops *ops) 1174 { 1175 struct snd_soc_dai_link_component *dlc; 1176 1177 /* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */ 1178 dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL); 1179 if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name) 1180 return -ENOMEM; 1181 1182 dlc[0].dai_name = cpu_dai_name; 1183 dlc[1].name = platform_comp_name; 1184 1185 dlc[2].name = codec_name; 1186 dlc[2].dai_name = codec_dai_name; 1187 1188 asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture, 1189 &dlc[0], 1, &dlc[1], 1, &dlc[2], 1, 1190 no_pcm, init, ops); 1191 1192 return 0; 1193 } 1194 EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, "SND_SOC_SDW_UTILS"); 1195 1196 int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends) 1197 { 1198 struct device *dev = card->dev; 1199 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); 1200 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 1201 const struct snd_soc_acpi_link_adr *adr_link; 1202 int i; 1203 1204 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { 1205 *num_devs += adr_link->num_adr; 1206 1207 for (i = 0; i < adr_link->num_adr; i++) 1208 *num_ends += adr_link->adr_d[i].num_endpoints; 1209 } 1210 1211 dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends); 1212 1213 return 0; 1214 } 1215 EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, "SND_SOC_SDW_UTILS"); 1216 1217 struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks, 1218 const struct snd_soc_acpi_endpoint *new) 1219 { 1220 while (dailinks->initialised) { 1221 if (new->aggregated && dailinks->group_id == new->group_id) 1222 return dailinks; 1223 1224 dailinks++; 1225 } 1226 1227 INIT_LIST_HEAD(&dailinks->endpoints); 1228 dailinks->group_id = new->group_id; 1229 dailinks->initialised = true; 1230 1231 return dailinks; 1232 } 1233 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS"); 1234 1235 static int asoc_sdw_get_dai_type(u32 type) 1236 { 1237 switch (type) { 1238 case SDCA_FUNCTION_TYPE_SMART_AMP: 1239 case SDCA_FUNCTION_TYPE_SIMPLE_AMP: 1240 return SOC_SDW_DAI_TYPE_AMP; 1241 case SDCA_FUNCTION_TYPE_SMART_MIC: 1242 case SDCA_FUNCTION_TYPE_SIMPLE_MIC: 1243 case SDCA_FUNCTION_TYPE_SPEAKER_MIC: 1244 return SOC_SDW_DAI_TYPE_MIC; 1245 case SDCA_FUNCTION_TYPE_UAJ: 1246 case SDCA_FUNCTION_TYPE_RJ: 1247 case SDCA_FUNCTION_TYPE_SIMPLE_JACK: 1248 return SOC_SDW_DAI_TYPE_JACK; 1249 default: 1250 return -EINVAL; 1251 } 1252 } 1253 1254 /* 1255 * Check if the SDCA endpoint is present by the SDW peripheral 1256 * 1257 * @dev: Device pointer 1258 * @codec_info: Codec info pointer 1259 * @adr_link: ACPI link address 1260 * @adr_index: Index of the ACPI link address 1261 * @end_index: Index of the endpoint 1262 * 1263 * Return: 1 if the endpoint is present, 1264 * 0 if the endpoint is not present, 1265 * negative error code. 1266 */ 1267 1268 static int is_sdca_endpoint_present(struct device *dev, 1269 struct asoc_sdw_codec_info *codec_info, 1270 const struct snd_soc_acpi_link_adr *adr_link, 1271 int adr_index, int end_index) 1272 { 1273 const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index]; 1274 const struct snd_soc_acpi_endpoint *adr_end; 1275 const struct asoc_sdw_dai_info *dai_info; 1276 struct snd_soc_dai_link_component *dlc; 1277 struct snd_soc_dai *codec_dai; 1278 struct sdw_slave *slave; 1279 struct device *sdw_dev; 1280 const char *sdw_codec_name; 1281 int i; 1282 1283 dlc = kzalloc(sizeof(*dlc), GFP_KERNEL); 1284 if (!dlc) 1285 return -ENOMEM; 1286 1287 adr_end = &adr_dev->endpoints[end_index]; 1288 dai_info = &codec_info->dais[adr_end->num]; 1289 1290 dlc->dai_name = dai_info->dai_name; 1291 codec_dai = snd_soc_find_dai_with_mutex(dlc); 1292 if (!codec_dai) { 1293 dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name); 1294 kfree(dlc); 1295 return -EPROBE_DEFER; 1296 } 1297 kfree(dlc); 1298 1299 sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info, 1300 adr_link, adr_index); 1301 if (!sdw_codec_name) 1302 return -ENOMEM; 1303 1304 sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name); 1305 if (!sdw_dev) { 1306 dev_err(dev, "codec %s not found\n", sdw_codec_name); 1307 return -EINVAL; 1308 } 1309 1310 slave = dev_to_sdw_dev(sdw_dev); 1311 if (!slave) 1312 return -EINVAL; 1313 1314 /* Make sure BIOS provides SDCA properties */ 1315 if (!slave->sdca_data.interface_revision) { 1316 dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n"); 1317 return 1; 1318 } 1319 1320 for (i = 0; i < slave->sdca_data.num_functions; i++) { 1321 int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type); 1322 1323 if (dai_type == dai_info->dai_type) { 1324 dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n", 1325 dai_type, slave->sdca_data.function[i].name); 1326 return 1; 1327 } 1328 } 1329 1330 dev_dbg(&slave->dev, 1331 "SDCA device function for DAI type %d not supported, skip endpoint\n", 1332 dai_info->dai_type); 1333 1334 return 0; 1335 } 1336 1337 int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, 1338 struct asoc_sdw_dailink *soc_dais, 1339 struct asoc_sdw_endpoint *soc_ends, 1340 int *num_devs) 1341 { 1342 struct device *dev = card->dev; 1343 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 1344 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); 1345 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 1346 const struct snd_soc_acpi_link_adr *adr_link; 1347 struct asoc_sdw_endpoint *soc_end = soc_ends; 1348 int num_dais = 0; 1349 int i, j; 1350 int ret; 1351 1352 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { 1353 int num_link_dailinks = 0; 1354 1355 if (!is_power_of_2(adr_link->mask)) { 1356 dev_err(dev, "link with multiple mask bits: 0x%x\n", 1357 adr_link->mask); 1358 return -EINVAL; 1359 } 1360 1361 for (i = 0; i < adr_link->num_adr; i++) { 1362 const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; 1363 struct asoc_sdw_codec_info *codec_info; 1364 const char *codec_name; 1365 bool check_sdca = false; 1366 1367 if (!adr_dev->name_prefix) { 1368 dev_err(dev, "codec 0x%llx does not have a name prefix\n", 1369 adr_dev->adr); 1370 return -EINVAL; 1371 } 1372 1373 codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); 1374 if (!codec_info) 1375 return -EINVAL; 1376 1377 ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; 1378 1379 codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i); 1380 if (!codec_name) 1381 return -ENOMEM; 1382 1383 dev_dbg(dev, "Adding prefix %s for %s\n", 1384 adr_dev->name_prefix, codec_name); 1385 1386 soc_end->name_prefix = adr_dev->name_prefix; 1387 1388 if (codec_info->count_sidecar && codec_info->add_sidecar) { 1389 ret = codec_info->count_sidecar(card, &num_dais, num_devs); 1390 if (ret) 1391 return ret; 1392 1393 soc_end->include_sidecar = true; 1394 } 1395 1396 if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1) 1397 check_sdca = true; 1398 1399 for (j = 0; j < adr_dev->num_endpoints; j++) { 1400 const struct snd_soc_acpi_endpoint *adr_end; 1401 const struct asoc_sdw_dai_info *dai_info; 1402 struct asoc_sdw_dailink *soc_dai; 1403 int stream; 1404 1405 adr_end = &adr_dev->endpoints[j]; 1406 dai_info = &codec_info->dais[adr_end->num]; 1407 soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); 1408 1409 /* 1410 * quirk should have higher priority than the sdca properties 1411 * in the BIOS. We can't always check the DAI quirk because we 1412 * will set the mc_quirk when the BIOS doesn't provide the right 1413 * information. The endpoint will be skipped if the dai_info-> 1414 * quirk_exclude and mc_quirk are both not set if we always skip 1415 * the endpoint according to the quirk information. We need to 1416 * keep the endpoint if it is present in the BIOS. So, only 1417 * check the DAI quirk when the mc_quirk is set or SDCA endpoint 1418 * present check is not needed. 1419 */ 1420 if (dai_info->quirk & ctx->mc_quirk || !check_sdca) { 1421 /* 1422 * Check the endpoint if a matching quirk is set or SDCA 1423 * endpoint check is not necessary 1424 */ 1425 if (dai_info->quirk && 1426 !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk))) 1427 continue; 1428 } else { 1429 /* Check SDCA codec endpoint if there is no matching quirk */ 1430 ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j); 1431 if (ret < 0) 1432 return ret; 1433 1434 /* The endpoint is not present, skip */ 1435 if (!ret) 1436 continue; 1437 } 1438 1439 dev_dbg(dev, 1440 "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n", 1441 ffs(adr_link->mask) - 1, adr_dev->adr, 1442 adr_end->num, dai_info->dai_type, 1443 dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-', 1444 dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-', 1445 adr_end->aggregated ? "group" : "solo", 1446 adr_end->group_id); 1447 1448 if (adr_end->num >= codec_info->dai_num) { 1449 dev_err(dev, 1450 "%d is too many endpoints for codec: 0x%x\n", 1451 adr_end->num, codec_info->part_id); 1452 return -EINVAL; 1453 } 1454 1455 for_each_pcm_streams(stream) { 1456 if (dai_info->direction[stream] && 1457 dai_info->dailink[stream] < 0) { 1458 dev_err(dev, 1459 "Invalid dailink id %d for codec: 0x%x\n", 1460 dai_info->dailink[stream], 1461 codec_info->part_id); 1462 return -EINVAL; 1463 } 1464 1465 if (dai_info->direction[stream]) { 1466 num_dais += !soc_dai->num_devs[stream]; 1467 soc_dai->num_devs[stream]++; 1468 soc_dai->link_mask[stream] |= adr_link->mask; 1469 } 1470 } 1471 1472 num_link_dailinks += !!list_empty(&soc_dai->endpoints); 1473 list_add_tail(&soc_end->list, &soc_dai->endpoints); 1474 1475 soc_end->link_mask = adr_link->mask; 1476 soc_end->codec_name = codec_name; 1477 soc_end->codec_info = codec_info; 1478 soc_end->dai_info = dai_info; 1479 soc_end++; 1480 } 1481 } 1482 1483 ctx->append_dai_type |= (num_link_dailinks > 1); 1484 } 1485 1486 return num_dais; 1487 } 1488 EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, "SND_SOC_SDW_UTILS"); 1489 1490 MODULE_LICENSE("GPL"); 1491 MODULE_DESCRIPTION("SoundWire ASoC helpers"); 1492