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/soc_sdw_utils.h> 14 15 static const struct snd_soc_dapm_widget generic_dmic_widgets[] = { 16 SND_SOC_DAPM_MIC("DMIC", NULL), 17 }; 18 19 static const struct snd_soc_dapm_widget generic_jack_widgets[] = { 20 SND_SOC_DAPM_HP("Headphone", NULL), 21 SND_SOC_DAPM_MIC("Headset Mic", NULL), 22 }; 23 24 static const struct snd_kcontrol_new generic_jack_controls[] = { 25 SOC_DAPM_PIN_SWITCH("Headphone"), 26 SOC_DAPM_PIN_SWITCH("Headset Mic"), 27 }; 28 29 static const struct snd_soc_dapm_widget generic_spk_widgets[] = { 30 SND_SOC_DAPM_SPK("Speaker", NULL), 31 }; 32 33 static const struct snd_kcontrol_new generic_spk_controls[] = { 34 SOC_DAPM_PIN_SWITCH("Speaker"), 35 }; 36 37 static const struct snd_soc_dapm_widget maxim_widgets[] = { 38 SND_SOC_DAPM_SPK("Left Spk", NULL), 39 SND_SOC_DAPM_SPK("Right Spk", NULL), 40 }; 41 42 static const struct snd_kcontrol_new maxim_controls[] = { 43 SOC_DAPM_PIN_SWITCH("Left Spk"), 44 SOC_DAPM_PIN_SWITCH("Right Spk"), 45 }; 46 47 static const struct snd_soc_dapm_widget rt700_widgets[] = { 48 SND_SOC_DAPM_HP("Headphones", NULL), 49 SND_SOC_DAPM_MIC("AMIC", NULL), 50 SND_SOC_DAPM_SPK("Speaker", NULL), 51 }; 52 53 static const struct snd_kcontrol_new rt700_controls[] = { 54 SOC_DAPM_PIN_SWITCH("Headphones"), 55 SOC_DAPM_PIN_SWITCH("AMIC"), 56 SOC_DAPM_PIN_SWITCH("Speaker"), 57 }; 58 59 struct asoc_sdw_codec_info codec_info_list[] = { 60 { 61 .part_id = 0x700, 62 .dais = { 63 { 64 .direction = {true, true}, 65 .dai_name = "rt700-aif1", 66 .dai_type = SOC_SDW_DAI_TYPE_JACK, 67 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 68 .rtd_init = asoc_sdw_rt700_rtd_init, 69 .controls = rt700_controls, 70 .num_controls = ARRAY_SIZE(rt700_controls), 71 .widgets = rt700_widgets, 72 .num_widgets = ARRAY_SIZE(rt700_widgets), 73 }, 74 }, 75 .dai_num = 1, 76 }, 77 { 78 .part_id = 0x711, 79 .version_id = 3, 80 .dais = { 81 { 82 .direction = {true, true}, 83 .dai_name = "rt711-sdca-aif1", 84 .dai_type = SOC_SDW_DAI_TYPE_JACK, 85 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 86 .init = asoc_sdw_rt_sdca_jack_init, 87 .exit = asoc_sdw_rt_sdca_jack_exit, 88 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 89 .controls = generic_jack_controls, 90 .num_controls = ARRAY_SIZE(generic_jack_controls), 91 .widgets = generic_jack_widgets, 92 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 93 }, 94 }, 95 .dai_num = 1, 96 }, 97 { 98 .part_id = 0x711, 99 .version_id = 2, 100 .dais = { 101 { 102 .direction = {true, true}, 103 .dai_name = "rt711-aif1", 104 .dai_type = SOC_SDW_DAI_TYPE_JACK, 105 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 106 .init = asoc_sdw_rt711_init, 107 .exit = asoc_sdw_rt711_exit, 108 .rtd_init = asoc_sdw_rt711_rtd_init, 109 .controls = generic_jack_controls, 110 .num_controls = ARRAY_SIZE(generic_jack_controls), 111 .widgets = generic_jack_widgets, 112 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 113 }, 114 }, 115 .dai_num = 1, 116 }, 117 { 118 .part_id = 0x712, 119 .version_id = 3, 120 .dais = { 121 { 122 .direction = {true, true}, 123 .dai_name = "rt712-sdca-aif1", 124 .dai_type = SOC_SDW_DAI_TYPE_JACK, 125 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 126 .init = asoc_sdw_rt_sdca_jack_init, 127 .exit = asoc_sdw_rt_sdca_jack_exit, 128 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 129 .controls = generic_jack_controls, 130 .num_controls = ARRAY_SIZE(generic_jack_controls), 131 .widgets = generic_jack_widgets, 132 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 133 }, 134 { 135 .direction = {true, false}, 136 .dai_name = "rt712-sdca-aif2", 137 .dai_type = SOC_SDW_DAI_TYPE_AMP, 138 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 139 .init = asoc_sdw_rt_amp_init, 140 .exit = asoc_sdw_rt_amp_exit, 141 .rtd_init = asoc_sdw_rt712_spk_rtd_init, 142 .controls = generic_spk_controls, 143 .num_controls = ARRAY_SIZE(generic_spk_controls), 144 .widgets = generic_spk_widgets, 145 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 146 }, 147 }, 148 .dai_num = 2, 149 }, 150 { 151 .part_id = 0x1712, 152 .version_id = 3, 153 .dais = { 154 { 155 .direction = {false, true}, 156 .dai_name = "rt712-sdca-dmic-aif1", 157 .dai_type = SOC_SDW_DAI_TYPE_MIC, 158 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 159 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 160 }, 161 }, 162 .dai_num = 1, 163 }, 164 { 165 .part_id = 0x713, 166 .version_id = 3, 167 .dais = { 168 { 169 .direction = {true, true}, 170 .dai_name = "rt712-sdca-aif1", 171 .dai_type = SOC_SDW_DAI_TYPE_JACK, 172 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 173 .init = asoc_sdw_rt_sdca_jack_init, 174 .exit = asoc_sdw_rt_sdca_jack_exit, 175 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 176 .controls = generic_jack_controls, 177 .num_controls = ARRAY_SIZE(generic_jack_controls), 178 .widgets = generic_jack_widgets, 179 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 180 }, 181 }, 182 .dai_num = 1, 183 }, 184 { 185 .part_id = 0x1713, 186 .version_id = 3, 187 .dais = { 188 { 189 .direction = {false, true}, 190 .dai_name = "rt712-sdca-dmic-aif1", 191 .dai_type = SOC_SDW_DAI_TYPE_MIC, 192 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 193 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 194 }, 195 }, 196 .dai_num = 1, 197 }, 198 { 199 .part_id = 0x1308, 200 .acpi_id = "10EC1308", 201 .dais = { 202 { 203 .direction = {true, false}, 204 .dai_name = "rt1308-aif", 205 .dai_type = SOC_SDW_DAI_TYPE_AMP, 206 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 207 .init = asoc_sdw_rt_amp_init, 208 .exit = asoc_sdw_rt_amp_exit, 209 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 210 .controls = generic_spk_controls, 211 .num_controls = ARRAY_SIZE(generic_spk_controls), 212 .widgets = generic_spk_widgets, 213 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 214 }, 215 }, 216 .dai_num = 1, 217 .ops = &soc_sdw_rt1308_i2s_ops, 218 }, 219 { 220 .part_id = 0x1316, 221 .dais = { 222 { 223 .direction = {true, true}, 224 .dai_name = "rt1316-aif", 225 .dai_type = SOC_SDW_DAI_TYPE_AMP, 226 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 227 .init = asoc_sdw_rt_amp_init, 228 .exit = asoc_sdw_rt_amp_exit, 229 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 230 .controls = generic_spk_controls, 231 .num_controls = ARRAY_SIZE(generic_spk_controls), 232 .widgets = generic_spk_widgets, 233 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 234 }, 235 }, 236 .dai_num = 1, 237 }, 238 { 239 .part_id = 0x1318, 240 .dais = { 241 { 242 .direction = {true, true}, 243 .dai_name = "rt1318-aif", 244 .dai_type = SOC_SDW_DAI_TYPE_AMP, 245 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 246 .init = asoc_sdw_rt_amp_init, 247 .exit = asoc_sdw_rt_amp_exit, 248 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 249 .controls = generic_spk_controls, 250 .num_controls = ARRAY_SIZE(generic_spk_controls), 251 .widgets = generic_spk_widgets, 252 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 253 }, 254 }, 255 .dai_num = 1, 256 }, 257 { 258 .part_id = 0x1320, 259 .dais = { 260 { 261 .direction = {true, false}, 262 .dai_name = "rt1320-aif1", 263 .dai_type = SOC_SDW_DAI_TYPE_AMP, 264 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 265 .init = asoc_sdw_rt_amp_init, 266 .exit = asoc_sdw_rt_amp_exit, 267 .rtd_init = asoc_sdw_rt_amp_spk_rtd_init, 268 .controls = generic_spk_controls, 269 .num_controls = ARRAY_SIZE(generic_spk_controls), 270 .widgets = generic_spk_widgets, 271 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 272 }, 273 }, 274 .dai_num = 1, 275 }, 276 { 277 .part_id = 0x714, 278 .version_id = 3, 279 .ignore_internal_dmic = true, 280 .dais = { 281 { 282 .direction = {false, true}, 283 .dai_name = "rt715-sdca-aif2", 284 .dai_type = SOC_SDW_DAI_TYPE_MIC, 285 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 286 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 287 }, 288 }, 289 .dai_num = 1, 290 }, 291 { 292 .part_id = 0x715, 293 .version_id = 3, 294 .ignore_internal_dmic = true, 295 .dais = { 296 { 297 .direction = {false, true}, 298 .dai_name = "rt715-sdca-aif2", 299 .dai_type = SOC_SDW_DAI_TYPE_MIC, 300 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 301 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 302 }, 303 }, 304 .dai_num = 1, 305 }, 306 { 307 .part_id = 0x714, 308 .version_id = 2, 309 .ignore_internal_dmic = true, 310 .dais = { 311 { 312 .direction = {false, true}, 313 .dai_name = "rt715-aif2", 314 .dai_type = SOC_SDW_DAI_TYPE_MIC, 315 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 316 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 317 }, 318 }, 319 .dai_num = 1, 320 }, 321 { 322 .part_id = 0x715, 323 .version_id = 2, 324 .ignore_internal_dmic = true, 325 .dais = { 326 { 327 .direction = {false, true}, 328 .dai_name = "rt715-aif2", 329 .dai_type = SOC_SDW_DAI_TYPE_MIC, 330 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 331 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 332 }, 333 }, 334 .dai_num = 1, 335 }, 336 { 337 .part_id = 0x722, 338 .version_id = 3, 339 .dais = { 340 { 341 .direction = {true, true}, 342 .dai_name = "rt722-sdca-aif1", 343 .dai_type = SOC_SDW_DAI_TYPE_JACK, 344 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 345 .init = asoc_sdw_rt_sdca_jack_init, 346 .exit = asoc_sdw_rt_sdca_jack_exit, 347 .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init, 348 .controls = generic_jack_controls, 349 .num_controls = ARRAY_SIZE(generic_jack_controls), 350 .widgets = generic_jack_widgets, 351 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 352 }, 353 { 354 .direction = {true, false}, 355 .dai_name = "rt722-sdca-aif2", 356 .dai_type = SOC_SDW_DAI_TYPE_AMP, 357 /* No feedback capability is provided by rt722-sdca codec driver*/ 358 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 359 .init = asoc_sdw_rt_amp_init, 360 .exit = asoc_sdw_rt_amp_exit, 361 .rtd_init = asoc_sdw_rt722_spk_rtd_init, 362 .controls = generic_spk_controls, 363 .num_controls = ARRAY_SIZE(generic_spk_controls), 364 .widgets = generic_spk_widgets, 365 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 366 }, 367 { 368 .direction = {false, true}, 369 .dai_name = "rt722-sdca-aif3", 370 .dai_type = SOC_SDW_DAI_TYPE_MIC, 371 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 372 .rtd_init = asoc_sdw_rt_dmic_rtd_init, 373 }, 374 }, 375 .dai_num = 3, 376 }, 377 { 378 .part_id = 0x8373, 379 .dais = { 380 { 381 .direction = {true, true}, 382 .dai_name = "max98373-aif1", 383 .dai_type = SOC_SDW_DAI_TYPE_AMP, 384 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 385 .init = asoc_sdw_maxim_init, 386 .rtd_init = asoc_sdw_maxim_spk_rtd_init, 387 .controls = maxim_controls, 388 .num_controls = ARRAY_SIZE(maxim_controls), 389 .widgets = maxim_widgets, 390 .num_widgets = ARRAY_SIZE(maxim_widgets), 391 }, 392 }, 393 .dai_num = 1, 394 }, 395 { 396 .part_id = 0x8363, 397 .dais = { 398 { 399 .direction = {true, false}, 400 .dai_name = "max98363-aif1", 401 .dai_type = SOC_SDW_DAI_TYPE_AMP, 402 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 403 .init = asoc_sdw_maxim_init, 404 .rtd_init = asoc_sdw_maxim_spk_rtd_init, 405 .controls = maxim_controls, 406 .num_controls = ARRAY_SIZE(maxim_controls), 407 .widgets = maxim_widgets, 408 .num_widgets = ARRAY_SIZE(maxim_widgets), 409 }, 410 }, 411 .dai_num = 1, 412 }, 413 { 414 .part_id = 0x5682, 415 .dais = { 416 { 417 .direction = {true, true}, 418 .dai_name = "rt5682-sdw", 419 .dai_type = SOC_SDW_DAI_TYPE_JACK, 420 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 421 .rtd_init = asoc_sdw_rt5682_rtd_init, 422 .controls = generic_jack_controls, 423 .num_controls = ARRAY_SIZE(generic_jack_controls), 424 .widgets = generic_jack_widgets, 425 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 426 }, 427 }, 428 .dai_num = 1, 429 }, 430 { 431 .part_id = 0x3556, 432 .dais = { 433 { 434 .direction = {true, true}, 435 .dai_name = "cs35l56-sdw1", 436 .dai_type = SOC_SDW_DAI_TYPE_AMP, 437 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 438 .init = asoc_sdw_cs_amp_init, 439 .rtd_init = asoc_sdw_cs_spk_rtd_init, 440 .controls = generic_spk_controls, 441 .num_controls = ARRAY_SIZE(generic_spk_controls), 442 .widgets = generic_spk_widgets, 443 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 444 }, 445 }, 446 .dai_num = 1, 447 }, 448 { 449 .part_id = 0x4242, 450 .dais = { 451 { 452 .direction = {true, true}, 453 .dai_name = "cs42l42-sdw", 454 .dai_type = SOC_SDW_DAI_TYPE_JACK, 455 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 456 .rtd_init = asoc_sdw_cs42l42_rtd_init, 457 .controls = generic_jack_controls, 458 .num_controls = ARRAY_SIZE(generic_jack_controls), 459 .widgets = generic_jack_widgets, 460 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 461 }, 462 }, 463 .dai_num = 1, 464 }, 465 { 466 .part_id = 0x4243, 467 .codec_name = "cs42l43-codec", 468 .count_sidecar = asoc_sdw_bridge_cs35l56_count_sidecar, 469 .add_sidecar = asoc_sdw_bridge_cs35l56_add_sidecar, 470 .dais = { 471 { 472 .direction = {true, false}, 473 .dai_name = "cs42l43-dp5", 474 .dai_type = SOC_SDW_DAI_TYPE_JACK, 475 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 476 .rtd_init = asoc_sdw_cs42l43_hs_rtd_init, 477 .controls = generic_jack_controls, 478 .num_controls = ARRAY_SIZE(generic_jack_controls), 479 .widgets = generic_jack_widgets, 480 .num_widgets = ARRAY_SIZE(generic_jack_widgets), 481 }, 482 { 483 .direction = {false, true}, 484 .dai_name = "cs42l43-dp1", 485 .dai_type = SOC_SDW_DAI_TYPE_MIC, 486 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 487 .rtd_init = asoc_sdw_cs42l43_dmic_rtd_init, 488 .widgets = generic_dmic_widgets, 489 .num_widgets = ARRAY_SIZE(generic_dmic_widgets), 490 }, 491 { 492 .direction = {false, true}, 493 .dai_name = "cs42l43-dp2", 494 .dai_type = SOC_SDW_DAI_TYPE_JACK, 495 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 496 }, 497 { 498 .direction = {true, false}, 499 .dai_name = "cs42l43-dp6", 500 .dai_type = SOC_SDW_DAI_TYPE_AMP, 501 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, 502 .init = asoc_sdw_cs42l43_spk_init, 503 .rtd_init = asoc_sdw_cs42l43_spk_rtd_init, 504 .controls = generic_spk_controls, 505 .num_controls = ARRAY_SIZE(generic_spk_controls), 506 .widgets = generic_spk_widgets, 507 .num_widgets = ARRAY_SIZE(generic_spk_widgets), 508 .quirk = SOC_SDW_CODEC_SPKR | SOC_SDW_SIDECAR_AMPS, 509 }, 510 }, 511 .dai_num = 4, 512 }, 513 { 514 .part_id = 0xaaaa, /* generic codec mockup */ 515 .version_id = 0, 516 .dais = { 517 { 518 .direction = {true, true}, 519 .dai_name = "sdw-mockup-aif1", 520 .dai_type = SOC_SDW_DAI_TYPE_JACK, 521 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 522 }, 523 }, 524 .dai_num = 1, 525 }, 526 { 527 .part_id = 0xaa55, /* headset codec mockup */ 528 .version_id = 0, 529 .dais = { 530 { 531 .direction = {true, true}, 532 .dai_name = "sdw-mockup-aif1", 533 .dai_type = SOC_SDW_DAI_TYPE_JACK, 534 .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, 535 }, 536 }, 537 .dai_num = 1, 538 }, 539 { 540 .part_id = 0x55aa, /* amplifier mockup */ 541 .version_id = 0, 542 .dais = { 543 { 544 .direction = {true, true}, 545 .dai_name = "sdw-mockup-aif1", 546 .dai_type = SOC_SDW_DAI_TYPE_AMP, 547 .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID}, 548 }, 549 }, 550 .dai_num = 1, 551 }, 552 { 553 .part_id = 0x5555, 554 .version_id = 0, 555 .dais = { 556 { 557 .dai_name = "sdw-mockup-aif1", 558 .direction = {false, true}, 559 .dai_type = SOC_SDW_DAI_TYPE_MIC, 560 .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID}, 561 }, 562 }, 563 .dai_num = 1, 564 }, 565 }; 566 EXPORT_SYMBOL_NS(codec_info_list, SND_SOC_SDW_UTILS); 567 568 int asoc_sdw_get_codec_info_list_count(void) 569 { 570 return ARRAY_SIZE(codec_info_list); 571 }; 572 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_info_list_count, SND_SOC_SDW_UTILS); 573 574 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_part(const u64 adr) 575 { 576 unsigned int part_id, sdw_version; 577 int i; 578 579 part_id = SDW_PART_ID(adr); 580 sdw_version = SDW_VERSION(adr); 581 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 582 /* 583 * A codec info is for all sdw version with the part id if 584 * version_id is not specified in the codec info. 585 */ 586 if (part_id == codec_info_list[i].part_id && 587 (!codec_info_list[i].version_id || 588 sdw_version == codec_info_list[i].version_id)) 589 return &codec_info_list[i]; 590 591 return NULL; 592 } 593 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_part, SND_SOC_SDW_UTILS); 594 595 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_acpi(const u8 *acpi_id) 596 { 597 int i; 598 599 if (!acpi_id[0]) 600 return NULL; 601 602 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) 603 if (!memcmp(codec_info_list[i].acpi_id, acpi_id, ACPI_ID_LEN)) 604 return &codec_info_list[i]; 605 606 return NULL; 607 } 608 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_acpi, SND_SOC_SDW_UTILS); 609 610 struct asoc_sdw_codec_info *asoc_sdw_find_codec_info_dai(const char *dai_name, int *dai_index) 611 { 612 int i, j; 613 614 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 615 for (j = 0; j < codec_info_list[i].dai_num; j++) { 616 if (!strcmp(codec_info_list[i].dais[j].dai_name, dai_name)) { 617 *dai_index = j; 618 return &codec_info_list[i]; 619 } 620 } 621 } 622 623 return NULL; 624 } 625 EXPORT_SYMBOL_NS(asoc_sdw_find_codec_info_dai, SND_SOC_SDW_UTILS); 626 627 int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd) 628 { 629 struct snd_soc_card *card = rtd->card; 630 struct asoc_sdw_codec_info *codec_info; 631 struct snd_soc_dai *dai; 632 int dai_index; 633 int ret; 634 int i; 635 636 for_each_rtd_codec_dais(rtd, i, dai) { 637 codec_info = asoc_sdw_find_codec_info_dai(dai->name, &dai_index); 638 if (!codec_info) 639 return -EINVAL; 640 641 /* 642 * A codec dai can be connected to different dai links for capture and playback, 643 * but we only need to call the rtd_init function once. 644 * The rtd_init for each codec dai is independent. So, the order of rtd_init 645 * doesn't matter. 646 */ 647 if (codec_info->dais[dai_index].rtd_init_done) 648 continue; 649 650 /* 651 * Add card controls and dapm widgets for the first codec dai. 652 * The controls and widgets will be used for all codec dais. 653 */ 654 655 if (i > 0) 656 goto skip_add_controls_widgets; 657 658 if (codec_info->dais[dai_index].controls) { 659 ret = snd_soc_add_card_controls(card, codec_info->dais[dai_index].controls, 660 codec_info->dais[dai_index].num_controls); 661 if (ret) { 662 dev_err(card->dev, "%#x controls addition failed: %d\n", 663 codec_info->part_id, ret); 664 return ret; 665 } 666 } 667 if (codec_info->dais[dai_index].widgets) { 668 ret = snd_soc_dapm_new_controls(&card->dapm, 669 codec_info->dais[dai_index].widgets, 670 codec_info->dais[dai_index].num_widgets); 671 if (ret) { 672 dev_err(card->dev, "%#x widgets addition failed: %d\n", 673 codec_info->part_id, ret); 674 return ret; 675 } 676 } 677 678 skip_add_controls_widgets: 679 if (codec_info->dais[dai_index].rtd_init) { 680 ret = codec_info->dais[dai_index].rtd_init(rtd, dai); 681 if (ret) 682 return ret; 683 } 684 codec_info->dais[dai_index].rtd_init_done = true; 685 } 686 687 return 0; 688 } 689 EXPORT_SYMBOL_NS(asoc_sdw_rtd_init, SND_SOC_SDW_UTILS); 690 691 /* these wrappers are only needed to avoid typecast compilation errors */ 692 int asoc_sdw_startup(struct snd_pcm_substream *substream) 693 { 694 return sdw_startup_stream(substream); 695 } 696 EXPORT_SYMBOL_NS(asoc_sdw_startup, SND_SOC_SDW_UTILS); 697 698 int asoc_sdw_prepare(struct snd_pcm_substream *substream) 699 { 700 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 701 struct sdw_stream_runtime *sdw_stream; 702 struct snd_soc_dai *dai; 703 704 /* Find stream from first CPU DAI */ 705 dai = snd_soc_rtd_to_cpu(rtd, 0); 706 707 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 708 if (IS_ERR(sdw_stream)) { 709 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 710 return PTR_ERR(sdw_stream); 711 } 712 713 return sdw_prepare_stream(sdw_stream); 714 } 715 EXPORT_SYMBOL_NS(asoc_sdw_prepare, SND_SOC_SDW_UTILS); 716 717 int asoc_sdw_trigger(struct snd_pcm_substream *substream, int cmd) 718 { 719 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 720 struct sdw_stream_runtime *sdw_stream; 721 struct snd_soc_dai *dai; 722 int ret; 723 724 /* Find stream from first CPU DAI */ 725 dai = snd_soc_rtd_to_cpu(rtd, 0); 726 727 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 728 if (IS_ERR(sdw_stream)) { 729 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 730 return PTR_ERR(sdw_stream); 731 } 732 733 switch (cmd) { 734 case SNDRV_PCM_TRIGGER_START: 735 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 736 case SNDRV_PCM_TRIGGER_RESUME: 737 ret = sdw_enable_stream(sdw_stream); 738 break; 739 740 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 741 case SNDRV_PCM_TRIGGER_SUSPEND: 742 case SNDRV_PCM_TRIGGER_STOP: 743 ret = sdw_disable_stream(sdw_stream); 744 break; 745 default: 746 ret = -EINVAL; 747 break; 748 } 749 750 if (ret) 751 dev_err(rtd->dev, "%s trigger %d failed: %d\n", __func__, cmd, ret); 752 753 return ret; 754 } 755 EXPORT_SYMBOL_NS(asoc_sdw_trigger, SND_SOC_SDW_UTILS); 756 757 int asoc_sdw_hw_params(struct snd_pcm_substream *substream, 758 struct snd_pcm_hw_params *params) 759 { 760 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 761 struct snd_soc_dai_link_ch_map *ch_maps; 762 int ch = params_channels(params); 763 unsigned int ch_mask; 764 int num_codecs; 765 int step; 766 int i; 767 768 if (!rtd->dai_link->ch_maps) 769 return 0; 770 771 /* Identical data will be sent to all codecs in playback */ 772 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 773 ch_mask = GENMASK(ch - 1, 0); 774 step = 0; 775 } else { 776 num_codecs = rtd->dai_link->num_codecs; 777 778 if (ch < num_codecs || ch % num_codecs != 0) { 779 dev_err(rtd->dev, "Channels number %d is invalid when codec number = %d\n", 780 ch, num_codecs); 781 return -EINVAL; 782 } 783 784 ch_mask = GENMASK(ch / num_codecs - 1, 0); 785 step = hweight_long(ch_mask); 786 } 787 788 /* 789 * The captured data will be combined from each cpu DAI if the dai 790 * link has more than one codec DAIs. Set codec channel mask and 791 * ASoC will set the corresponding channel numbers for each cpu dai. 792 */ 793 for_each_link_ch_maps(rtd->dai_link, i, ch_maps) 794 ch_maps->ch_mask = ch_mask << (i * step); 795 796 return 0; 797 } 798 EXPORT_SYMBOL_NS(asoc_sdw_hw_params, SND_SOC_SDW_UTILS); 799 800 int asoc_sdw_hw_free(struct snd_pcm_substream *substream) 801 { 802 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 803 struct sdw_stream_runtime *sdw_stream; 804 struct snd_soc_dai *dai; 805 806 /* Find stream from first CPU DAI */ 807 dai = snd_soc_rtd_to_cpu(rtd, 0); 808 809 sdw_stream = snd_soc_dai_get_stream(dai, substream->stream); 810 if (IS_ERR(sdw_stream)) { 811 dev_err(rtd->dev, "no stream found for DAI %s\n", dai->name); 812 return PTR_ERR(sdw_stream); 813 } 814 815 return sdw_deprepare_stream(sdw_stream); 816 } 817 EXPORT_SYMBOL_NS(asoc_sdw_hw_free, SND_SOC_SDW_UTILS); 818 819 void asoc_sdw_shutdown(struct snd_pcm_substream *substream) 820 { 821 sdw_shutdown_stream(substream); 822 } 823 EXPORT_SYMBOL_NS(asoc_sdw_shutdown, SND_SOC_SDW_UTILS); 824 825 static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_link, 826 unsigned int sdw_version, 827 unsigned int mfg_id, 828 unsigned int part_id, 829 unsigned int class_id, 830 int index_in_link) 831 { 832 int i; 833 834 for (i = 0; i < adr_link->num_adr; i++) { 835 unsigned int sdw1_version, mfg1_id, part1_id, class1_id; 836 u64 adr; 837 838 /* skip itself */ 839 if (i == index_in_link) 840 continue; 841 842 adr = adr_link->adr_d[i].adr; 843 844 sdw1_version = SDW_VERSION(adr); 845 mfg1_id = SDW_MFG_ID(adr); 846 part1_id = SDW_PART_ID(adr); 847 class1_id = SDW_CLASS_ID(adr); 848 849 if (sdw_version == sdw1_version && 850 mfg_id == mfg1_id && 851 part_id == part1_id && 852 class_id == class1_id) 853 return false; 854 } 855 856 return true; 857 } 858 859 const char *asoc_sdw_get_codec_name(struct device *dev, 860 const struct asoc_sdw_codec_info *codec_info, 861 const struct snd_soc_acpi_link_adr *adr_link, 862 int adr_index) 863 { 864 u64 adr = adr_link->adr_d[adr_index].adr; 865 unsigned int sdw_version = SDW_VERSION(adr); 866 unsigned int link_id = SDW_DISCO_LINK_ID(adr); 867 unsigned int unique_id = SDW_UNIQUE_ID(adr); 868 unsigned int mfg_id = SDW_MFG_ID(adr); 869 unsigned int part_id = SDW_PART_ID(adr); 870 unsigned int class_id = SDW_CLASS_ID(adr); 871 872 if (codec_info->codec_name) 873 return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL); 874 else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id, 875 class_id, adr_index)) 876 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x", 877 link_id, mfg_id, part_id, class_id); 878 else 879 return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x", 880 link_id, mfg_id, part_id, class_id, unique_id); 881 882 return NULL; 883 } 884 EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, SND_SOC_SDW_UTILS); 885 886 /* helper to get the link that the codec DAI is used */ 887 struct snd_soc_dai_link *asoc_sdw_mc_find_codec_dai_used(struct snd_soc_card *card, 888 const char *dai_name) 889 { 890 struct snd_soc_dai_link *dai_link; 891 int i; 892 int j; 893 894 for_each_card_prelinks(card, i, dai_link) { 895 for (j = 0; j < dai_link->num_codecs; j++) { 896 /* Check each codec in a link */ 897 if (!strcmp(dai_link->codecs[j].dai_name, dai_name)) 898 return dai_link; 899 } 900 } 901 return NULL; 902 } 903 EXPORT_SYMBOL_NS(asoc_sdw_mc_find_codec_dai_used, SND_SOC_SDW_UTILS); 904 905 void asoc_sdw_mc_dailink_exit_loop(struct snd_soc_card *card) 906 { 907 struct snd_soc_dai_link *dai_link; 908 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 909 int ret; 910 int i, j; 911 912 for (i = 0; i < ctx->codec_info_list_count; i++) { 913 for (j = 0; j < codec_info_list[i].dai_num; j++) { 914 codec_info_list[i].dais[j].rtd_init_done = false; 915 /* Check each dai in codec_info_lis to see if it is used in the link */ 916 if (!codec_info_list[i].dais[j].exit) 917 continue; 918 /* 919 * We don't need to call .exit function if there is no matched 920 * dai link found. 921 */ 922 dai_link = asoc_sdw_mc_find_codec_dai_used(card, 923 codec_info_list[i].dais[j].dai_name); 924 if (dai_link) { 925 /* Do the .exit function if the codec dai is used in the link */ 926 ret = codec_info_list[i].dais[j].exit(card, dai_link); 927 if (ret) 928 dev_warn(card->dev, 929 "codec exit failed %d\n", 930 ret); 931 break; 932 } 933 } 934 } 935 } 936 EXPORT_SYMBOL_NS(asoc_sdw_mc_dailink_exit_loop, SND_SOC_SDW_UTILS); 937 938 int asoc_sdw_card_late_probe(struct snd_soc_card *card) 939 { 940 int ret = 0; 941 int i; 942 943 for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { 944 if (codec_info_list[i].codec_card_late_probe) { 945 ret = codec_info_list[i].codec_card_late_probe(card); 946 if (ret < 0) 947 return ret; 948 } 949 } 950 return ret; 951 } 952 EXPORT_SYMBOL_NS(asoc_sdw_card_late_probe, SND_SOC_SDW_UTILS); 953 954 void asoc_sdw_init_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 955 int *be_id, char *name, int playback, int capture, 956 struct snd_soc_dai_link_component *cpus, int cpus_num, 957 struct snd_soc_dai_link_component *platform_component, 958 int num_platforms, struct snd_soc_dai_link_component *codecs, 959 int codecs_num, int (*init)(struct snd_soc_pcm_runtime *rtd), 960 const struct snd_soc_ops *ops) 961 { 962 dev_dbg(dev, "create dai link %s, id %d\n", name, *be_id); 963 dai_links->id = (*be_id)++; 964 dai_links->name = name; 965 dai_links->platforms = platform_component; 966 dai_links->num_platforms = num_platforms; 967 dai_links->no_pcm = 1; 968 dai_links->cpus = cpus; 969 dai_links->num_cpus = cpus_num; 970 dai_links->codecs = codecs; 971 dai_links->num_codecs = codecs_num; 972 dai_links->playback_only = playback && !capture; 973 dai_links->capture_only = !playback && capture; 974 dai_links->init = init; 975 dai_links->ops = ops; 976 } 977 EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, SND_SOC_SDW_UTILS); 978 979 int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links, 980 int *be_id, char *name, int playback, int capture, 981 const char *cpu_dai_name, const char *platform_comp_name, 982 int num_platforms, const char *codec_name, 983 const char *codec_dai_name, 984 int (*init)(struct snd_soc_pcm_runtime *rtd), 985 const struct snd_soc_ops *ops) 986 { 987 struct snd_soc_dai_link_component *dlc; 988 989 /* Allocate three DLCs one for the CPU, one for platform and one for the CODEC */ 990 dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL); 991 if (!dlc || !name || !cpu_dai_name || !platform_comp_name || !codec_name || !codec_dai_name) 992 return -ENOMEM; 993 994 dlc[0].dai_name = cpu_dai_name; 995 dlc[1].name = platform_comp_name; 996 997 dlc[2].name = codec_name; 998 dlc[2].dai_name = codec_dai_name; 999 1000 asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture, 1001 &dlc[0], 1, &dlc[1], num_platforms, 1002 &dlc[2], 1, init, ops); 1003 1004 return 0; 1005 } 1006 EXPORT_SYMBOL_NS(asoc_sdw_init_simple_dai_link, SND_SOC_SDW_UTILS); 1007 1008 int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends) 1009 { 1010 struct device *dev = card->dev; 1011 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); 1012 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 1013 const struct snd_soc_acpi_link_adr *adr_link; 1014 int i; 1015 1016 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { 1017 *num_devs += adr_link->num_adr; 1018 1019 for (i = 0; i < adr_link->num_adr; i++) 1020 *num_ends += adr_link->adr_d[i].num_endpoints; 1021 } 1022 1023 dev_dbg(dev, "Found %d devices with %d endpoints\n", *num_devs, *num_ends); 1024 1025 return 0; 1026 } 1027 EXPORT_SYMBOL_NS(asoc_sdw_count_sdw_endpoints, SND_SOC_SDW_UTILS); 1028 1029 struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks, 1030 const struct snd_soc_acpi_endpoint *new) 1031 { 1032 while (dailinks->initialised) { 1033 if (new->aggregated && dailinks->group_id == new->group_id) 1034 return dailinks; 1035 1036 dailinks++; 1037 } 1038 1039 INIT_LIST_HEAD(&dailinks->endpoints); 1040 dailinks->group_id = new->group_id; 1041 dailinks->initialised = true; 1042 1043 return dailinks; 1044 } 1045 EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, SND_SOC_SDW_UTILS); 1046 1047 int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card, 1048 struct asoc_sdw_dailink *soc_dais, 1049 struct asoc_sdw_endpoint *soc_ends, 1050 int *num_devs) 1051 { 1052 struct device *dev = card->dev; 1053 struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card); 1054 struct snd_soc_acpi_mach *mach = dev_get_platdata(dev); 1055 struct snd_soc_acpi_mach_params *mach_params = &mach->mach_params; 1056 const struct snd_soc_acpi_link_adr *adr_link; 1057 struct asoc_sdw_endpoint *soc_end = soc_ends; 1058 int num_dais = 0; 1059 int i, j; 1060 int ret; 1061 1062 for (adr_link = mach_params->links; adr_link->num_adr; adr_link++) { 1063 int num_link_dailinks = 0; 1064 1065 if (!is_power_of_2(adr_link->mask)) { 1066 dev_err(dev, "link with multiple mask bits: 0x%x\n", 1067 adr_link->mask); 1068 return -EINVAL; 1069 } 1070 1071 for (i = 0; i < adr_link->num_adr; i++) { 1072 const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i]; 1073 struct asoc_sdw_codec_info *codec_info; 1074 const char *codec_name; 1075 1076 if (!adr_dev->name_prefix) { 1077 dev_err(dev, "codec 0x%llx does not have a name prefix\n", 1078 adr_dev->adr); 1079 return -EINVAL; 1080 } 1081 1082 codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); 1083 if (!codec_info) 1084 return -EINVAL; 1085 1086 ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; 1087 1088 codec_name = asoc_sdw_get_codec_name(dev, codec_info, adr_link, i); 1089 if (!codec_name) 1090 return -ENOMEM; 1091 1092 dev_dbg(dev, "Adding prefix %s for %s\n", 1093 adr_dev->name_prefix, codec_name); 1094 1095 soc_end->name_prefix = adr_dev->name_prefix; 1096 1097 if (codec_info->count_sidecar && codec_info->add_sidecar) { 1098 ret = codec_info->count_sidecar(card, &num_dais, num_devs); 1099 if (ret) 1100 return ret; 1101 1102 soc_end->include_sidecar = true; 1103 } 1104 1105 for (j = 0; j < adr_dev->num_endpoints; j++) { 1106 const struct snd_soc_acpi_endpoint *adr_end; 1107 const struct asoc_sdw_dai_info *dai_info; 1108 struct asoc_sdw_dailink *soc_dai; 1109 int stream; 1110 1111 adr_end = &adr_dev->endpoints[j]; 1112 dai_info = &codec_info->dais[adr_end->num]; 1113 soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); 1114 1115 if (dai_info->quirk && !(dai_info->quirk & ctx->mc_quirk)) 1116 continue; 1117 1118 dev_dbg(dev, 1119 "Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n", 1120 ffs(adr_link->mask) - 1, adr_dev->adr, 1121 adr_end->num, dai_info->dai_type, 1122 dai_info->direction[SNDRV_PCM_STREAM_PLAYBACK] ? 'P' : '-', 1123 dai_info->direction[SNDRV_PCM_STREAM_CAPTURE] ? 'C' : '-', 1124 adr_end->aggregated ? "group" : "solo", 1125 adr_end->group_id); 1126 1127 if (adr_end->num >= codec_info->dai_num) { 1128 dev_err(dev, 1129 "%d is too many endpoints for codec: 0x%x\n", 1130 adr_end->num, codec_info->part_id); 1131 return -EINVAL; 1132 } 1133 1134 for_each_pcm_streams(stream) { 1135 if (dai_info->direction[stream] && 1136 dai_info->dailink[stream] < 0) { 1137 dev_err(dev, 1138 "Invalid dailink id %d for codec: 0x%x\n", 1139 dai_info->dailink[stream], 1140 codec_info->part_id); 1141 return -EINVAL; 1142 } 1143 1144 if (dai_info->direction[stream]) { 1145 num_dais += !soc_dai->num_devs[stream]; 1146 soc_dai->num_devs[stream]++; 1147 soc_dai->link_mask[stream] |= adr_link->mask; 1148 } 1149 } 1150 1151 num_link_dailinks += !!list_empty(&soc_dai->endpoints); 1152 list_add_tail(&soc_end->list, &soc_dai->endpoints); 1153 1154 soc_end->link_mask = adr_link->mask; 1155 soc_end->codec_name = codec_name; 1156 soc_end->codec_info = codec_info; 1157 soc_end->dai_info = dai_info; 1158 soc_end++; 1159 } 1160 } 1161 1162 ctx->append_dai_type |= (num_link_dailinks > 1); 1163 } 1164 1165 return num_dais; 1166 } 1167 EXPORT_SYMBOL_NS(asoc_sdw_parse_sdw_endpoints, SND_SOC_SDW_UTILS); 1168 1169 MODULE_LICENSE("GPL"); 1170 MODULE_DESCRIPTION("SoundWire ASoC helpers"); 1171