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